Categories

Archives

Did You Know?

When interviewing PHP developers about typical web site vulnerabilities, most of them know about SQL Injections and Cross-Site Scripting (XSS). However, it really bugs me that many of them haven't heard of Cross-Site Request Forgery (CSRF).

Recent Comments

Tags

asp audio browser bug business coalesce code crash Database db debian extension framework imap internet legions linux metaverse mysql obscurity patch PHP postgresql properties release scp Second Life second life security session social media sound sql ssh subversion tables tortoisesvn tribes ubuntu virtual world web windows zend zend framework zf

Zend Framework: Coding by Convention - Part 2

A number of components sometimes accept strings or arrays as parameter. Developers coming from statically typed languages might cringe a bit, but this is really just about taking advantage of the flexibility of the language. With flexibility comes freedom and complexity, so having an established convention helps quite a bit.

One component that makes extensive use of accepting both strings and arrays is Zend_Db_Select. When building queries, it allows you to specify parameter either way:

// SELECT username FROM users WHERE id = 1
$select->from('users', 'username')->where('id = ?', 1);

// Same query again, with array syntax
$select->from(array('users'), array('username'))->where('id = ?', 1);

This can make for much more readable queries when things become more complex:

class Friendships extends Zend_Db_Table_Abstract
{

    /**
     * Select users suggested as friends
     *
     * Algorithm picks out your friends' friends that you don't already
     * have added to your list, but which at least $threshold friends have in common.
     *
     * @param string $useruuid Avatar's UUID
     * @param int $threshold How many friends must have the person in common
     * @return Zend_Db_Table_Rowset_Abstract
     */
    public function getSuggested($useruuid, $threshold = 4,
                                 $page = null, $itemsPerPage = null)
    {

        $select = $this->select()
            ->setIntegrityCheck(false)

            // friends
            ->from(array('f' => $this->_name),
                   array()) // no columns
            ->where('f.useruuid = ? AND f.dateaccepted IS NOT NULL', $useruuid)

            // friends' friends that aren't me
            ->join(array('ff' => $this->_name),
                   'f.frienduuid = ff.useruuid AND ' .
            	   'ff.frienduuid != f.useruuid AND ' .
                   'ff.dateaccepted IS NOT NULL',
                   array()) // no columns

            ->join(array('u' => 'users'),
                   'ff.frienduuid = u.uuid',
                   array('*'))

            // my friends again (left joined)
            ->joinLeft(array('mf' => $this->_name),
                       'ff.frienduuid = mf.frienduuid AND ' .
                       'mf.useruuid = f.useruuid AND ' .
                   	   'mf.dateaccepted IS NOT NULL',
                       array()) // no columns

            // filter my friends' friends that aren't my friends
            ->where('mf.frienduuid IS NULL')

            ->group('ff.frienduuid') // group by suggested friends
            ->having('COUNT(f.frienduuid) >= ?', $threshold)
            ->order('COUNT(f.frienduuid) DESC');

        if ($page !== null or $itemsPerPage !== null) {
        	$select->limitPage($page, $itemsPerPage);
        }

        return $this->fetchAll($select);
    }

}

Zend Framework: Coding by Convention

This is really something I've been wanting to point out because, for one, I very much like and agree with the approach, and second, it's something that any developer using the Zend Framework should digest and take into consideration when writing their own code.

There are numerous components that will accept configuration options, and usually that method is called setOptions($array) and it accepts an associative array (key/value pairs) as parameter.

And often, setOptions() iterates over the array and calls setOption($key, $value) as demonstrated by the following example:

public function setOptions($options)
{
    foreach ($options as $option => $value) {
        $this->setOption($option, $value);
    }
    return $this;
}

And typically, setOption() takes the first parameter (the key) and checks whether there's a method that matches "set" followed by the key name. For example, setOption('active', true) will end up calling setActive(true). The code often looks similar to the following:

public function setOption($option, $value)
{
    $method = 'set' . $option;
    if (is_method($this, $method)) {
        $this->$method($value);
    } else {
        throw new Exception('Unknown option: ' . $option);
    }
    return $this;
}

This comes in handy when you're trying to configure a component that accepts a plethora of options, which you can then conveniently store in an array.

Furthermore, the constructor of a class could accept $options as first parameter (as is often the case), and also check whether it is an instance of Zend_Config, in which case it first converts it using $options = $options->toArray();

public function __construct($options = null)
{
    if ($options instanceof Zend_Config) {
        $options = $options->toArray();
    }

    if (is_array($options)) {
        $this->setOptions($options);
    }
}

Another convention you may have noticed is that the setter methods tend to "return $this;". This is known as fluent interface and allows for more concise, readable code by being able to chain method calls.

The Logic behind naming roads

I-440 wraps around Raleigh

I-440 wraps around Raleigh

I currently live in the triangle area in North Carolina. This morning, actually, just a few minutes ago, I overheard the announcer on the NBC17 news say that I440, the circular interstate that wraps around Raleigh, will be renamed.

At the moment the naming convention is "the inner beltline" for the lane that moves clockwise, and "the outer beltline" for the lane that moves counter-clockwise. It makes sense logically, because there's no real distinct north. east, south, or west.

But apparently this convention is confusing people and so the road names will change to be I440 east and I440 west, a modification that'll apparently cost around 40 million dollars.

Although it makes sense, it'll be strange being on the southern half of I440 east which is actually moving west and vice versa.

Implementing short URLs using case-sensitive Routes with Zend Framework

Since short URLs are all the rage these days, I wanted to outfit one of my websites with its own short URL capability.

A simple way to accomplish this is to base62 encode a numeric identifier (the database table's primary key). In order to identify a URL as a short URL, it will be prefixed with an uppercase 'S'. So, a short URL would be something like this: http://mixoom.com/Snfup8.

With regular expressions it's simple to turn off and on case sensitivity using (?-i) and (?i) respectively. So ultimately the new route needed in order to accomplish mapping the short URL is the following:

$route = new Portal_Controller_Router_Route_Regex(
    '(?-i)S([\w\d]+)',
    array('controller' => 'photos',
          'action'     => 'shorturl'),
    array('shortid' => 1)
);

Using Zend_Acl with your model

Zend_Acl is an excellent component that provides Access Control List (ACL) functionality. In most cases the goal is to manage user access to resources. access to to manage all things related to user access. In a nutshell, a role

to any kind of resource. But unfortunate it doesn't quite live up to its full potential just yet, due to a few implementation details as outlined in tickets and ZF-4460. The latter of the two also has comments that include a few examples for a workaround.

I'm using the following class which gives any custom Assert object access to the actual Resource passed to it.

/**
 * The current Zend_Acl design does not allow for
 * using a custom Role and Resource objects and expect that they'll make it through
 * to custom assertions.
 * See http://framework.zend.com/issues/browse/ZF-1722
 * and http://framework.zend.com/issues/browse/ZF-4460
 */
class My_Acl extends Zend_Acl
{

    /**
     * Returns the identified Resource
     *
     * The $resource parameter can either be a Resource or a Resource identifier.
     *
     * @param  Zend_Acl_Resource_Interface|string $resource
     * @throws Zend_Acl_Exception
     * @return Zend_Acl_Resource_Interface
     */
    public function get($resource)
    {
        if (!$this->has($resource)) {
            require_once 'Zend/Acl/Exception.php';
            throw new Zend_Acl_Exception("Resource '$resource' not found");
        }

        if ($resource instanceof Zend_Acl_Resource_Interface) {
            return $resource;
        }

        return $this->_resources[$resource]['instance'];
    }

}

Unfortunately, this doesn't fix the issue of the Role making it through to an assertion, but in most of my cases that's the acting user anyway, so I don't even try to grab the passed in $role and instead use the identity straight from Zend_Auth.

/**
 * Ensure the photo is owned by the user with $role
 *
 */
class PhotoOwnerAssertion implements Zend_Acl_Assert_Interface
{
    public function assert(Zend_Acl $acl,
                           Zend_Acl_Role_Interface $role = null,
                           Zend_Acl_Resource_Interface $resource = null,
                           $privilege = null)
    {
        if (!$resource instanceof Photos_Row) {
            return false;
        }
        /* @var $resource Photos_Row */

        /*
         * Workaround; the current Zend_Acl design does not allow for
         * using a custom Role interface and expect that it'll make it through.
         * See http://framework.zend.com/issues/browse/ZF-1722 and
         * http://framework.zend.com/issues/browse/ZF-4460
         */
        $role = Zend_Auth::getInstance()->getIdentity();

        return $resource->getOwnerId() == $role;
    }

}

When setting up the ACL, I provide an instance of the custom assertion which will then provide the proper access control. It's fairly well encapsulated (other than the bug workarounds).

/**
 * Only allow owners to view, edit, and delete their photos
 */
$acl->allow('member', 'Photo', array('view', 'edit', 'delete'), new PhotoOwnerAssertion());

In this case, the Photos_Row class must also provide a getOwnerId() method.

class Photos_Row extends Zend_Db_Table_Row_Abstract
                 implements Zend_Acl_Resource_Interface
{

    /**
     * Resource type (for use with ACL)
     *
     * @see Zend_Acl_Resource_Interface
     *
     * @return string
     */
    public function getResourceId()
    {
        return 'Photo';
    }

    /**
     * Return the photo owner's UUID
     *
     * @see UgcItem
     *
     * @return string
     */
    public function getOwnerId()
    {
        return $this->avataruuid;
    }

}

A little more abstraction and the custom assertion can be used for models other than photos.