rulururu

post Data Mapper Pattern in PHP

December 17th, 2008

Filed under: Code, PHP, Zend Framework — Brenton Alker @ 08:58

I have been trying to get together a post on the Data Mapper pattern since I started experimenting with it in a personal project. It seems to me to be a fantastic answer to the decoupling of in-memory data objects and the data store. I still don’t have all the answers, but Rob Allen’s recent post On models in a Zend Framework application, and the associated discussion provoked me to publish some of what I do have.

Warning: This post will be quite long and is very code heavy, but I hope it illustrates some of my current thoughts.

Anyway, lets get down to it.

I will be showing 4 classes. Two (2) Abstract “library” classes, and 2 examples of their use;

And now, the code. Instead of breaking the code into little pieces, I’ll add inline comments to explain what’s going on.

Classes

Library Classes

MapperAbstract

MapperAbstract is the base class for mappers. It defines a standard interface for all mappers.


abstract class MapperAbstract
{
    /**
     * Create a new instance of the DomainObject that this
     * mapper is responsible for. Optionally populating it
     * from a data array.
     *
     * @param array $data
     * @return DomainObjectAbstract
     */
    public function create(array $data = null) {
        $obj = $this->_create();
        if ($data) {
            $obj = $this->populate($obj, $data);
        }
        return $obj;
    }

    /**
     * Save the DomainObject
     *
     * Store the DomainObject in persistent storage. Either insert
     * or update the store as required.
     *
     * @param DomainObjectAbstract $obj
     */
    public function save(DomainObjectAbstract $obj)
    {
        if (is_null($obj->getId())) {
            $this->_insert($obj);
        } else {
            $this->_update($obj);
        }
    }

    /**
     * Delete the DomainObject
     *
     * Delete the DomainObject from persistent storage.
     *
     * @param DomainObjectAbstract $obj
     */
    public function delete(DomainObjectAbstract $obj)
    {
        $this->_delete($obj);
    }

    /**
     * Populate the DomainObject with the values
     * from the data array.
     *
     * To be implemented by the concrete mapper class
     *
     * @param DomainObjectAbstract $obj
     * @param array $data
     * @return DomainObjectAbstract
     */
    abstract public function populate(DomainObjectAbstract $obj, array $data);

    /**
     * Create a new instance of a DomainObject
     *
     * @return DomainObjectAbstract
     */
    abstract protected function _create();

    /**
     * Insert the DomainObject to persistent storage
     *
     * @param DomainObjectAbstract $obj
     */
    abstract protected function _insert(DomainObjectAbstract $obj);

    /**
     * Update the DomainObject in persistent storage
     *
     * @param DomainObjectAbstract $obj
     */
    abstract protected function _update(DomainObjectAbstract $obj);

    /**
     * Delete the DomainObject from peristent Storage
     *
     * @param DomainObjectAbstract $obj
     */
    abstract protected function _delete(DomainObjectAbstract $obj);
}

DomainObjectAbstract

A basic class describing an object that is part of the Domain Model. This model assumes that all DomainObjects have an integer as the primary key, but it would be a trivial change to make it a GUID for example. It also ensures the ID is immutable.


abstract class DomainObjectAbstract
{
    protected $_id = null;

    /**
     * Get the ID of this object (unique to the
     * object type)
     *
     * @return int
     */
    public function getId()
    {
        return $this->_id;
    }

    /**
     * Set the id for this object.
     *
     * @param int $id
     * @return int
     * @throws Exception	If the id on the object is already set
     */
    public function setId($id)
    {
        if (!is_null($this->_id)) {
            throw new Exception('ID is immutable');
        }
        return $this->_id = $id;
    }
}

Example Implementation Classes

User

A concrete DomainObject describing a user. This is a very barebones example, usually getters/setters would be used to set protected properties.


class User extends DomainObjectAbstract
{
    public $firstname;
    public $lastname;
    public $username;

    /**
     * Get the full name of the User
     *
     * Demonstrates how other functions can be
     * added to the DomainObject
     *
     * @return string
     */
    public function getName()
    {
        return $this->firstname . ' ' . $this->lastname;
    }
}

UserMapper

A concrete implementation of a mapper between the User domain object and persistent storage.


class UserMapper extends MapperAbstract
{
    /**
     * Fetch a user object by ID
     *
     * An example skeleton of a "Fetch" function showing
     * how the database data ($dataFromDb) is used to
     * create a new User instance via the create function.
     *
     * @param string $id
     * @return User
     */
    public function findById($id)
    {
        // Query database for User with $id
        // ...
        $dataFromDb = array(
                'id'        => $id,
                'firstname' => 'Brenton',
                'lastname'  => '',
                'username'  => 'Tekerson',
            );
        return $this->create($dataFromDb);
    }

    /**
     * Poplate the User (DomainObject) with
     * the data array.
     *
     * This is a very simple example, but the mapping
     * can be as complex as required.
     *
     * @param DomainObjectAbstract $obj
     * @param array $data
     * @return User
     */
    public function populate(DomainObjectAbstract $obj, array $data)
    {
        $obj->setId($data['id']);
        $obj->firstname = $data['firstname'];
        $obj->lastname  = $data['lastname'];
        $obj->username  = $data['username'];
        return $obj;
    }

    /**
     * Create a new User DomainObject
     *
     * @return User
     */
    protected function _create()
    {
        return new User();
    }

    /**
     * Insert the DomainObject in persistent storage
     *
     * This may include connecting to the database
     * and running an insert statement.
     *
     * @param DomainObjectAbstract $obj
     */
    protected function _insert(DomainObjectAbstract $obj)
    {
        // ...
    }

    /**
     * Update the DomainObject in persistent storage
     *
     * This may include connecting to the database
     * and running an update statement.
     *
     * @param DomainObjectAbstract $obj
     */
    protected function _update(DomainObjectAbstract $obj)
    {
        // ...
    }

    /**
     * Delete the DomainObject from persistent storage
     *
     * This may include connecting to the database
     * and running a delete statement.
     *
     * @param DomainObjectAbstract $obj
     */
    protected function _delete(DomainObjectAbstract $obj)
    {
        // ...
    }
}

Example

Ok, I realize that I’ve dumped a lot of code and we’re nearly finished. Now we finally get to see how it all fits together to do something. Here, I am creating a simple example where I fetch a User based on their ID, change the Users name, and store it back to the database.


// Initialise the Mapper.
$userMapper = new UserMapper();

// Fetch and manipulate the User object
$user = $userMapper->findById(1);
$user->lastname = 'Alker';

// Tell the UserMapper that the User needs to be saved.
$userMapper->save($user);

Well, I hope everything makes sense. This is a very simplified version of the concepts I have been working on.

These simple examples could be expanded in may directions. Such as;

Validation of the DomainObject fields, I haven’t decided if the validation should be contained in the DomainObject or the Mapper.

An identity map to ensure only 1 instance of each DomainObject exists in memory at a time.

A UnitOfWork to track which objects need saving, deleting and to co-ordinate roll backs on error, or the order of persistence to satisfy foreign key constraints.

The Mapper could access many data stores; RDBMSs, Session/Cookie data or web services to name a few. Remember, the Mapper decouples the DomainObject completely from the persistent storage

Anyway, I hope this provides a fairly simple concrete example that you can build upon to create a more complete solution that you can then share with the rest of us.

post trim() Validation

July 15th, 2008

Filed under: Code, PHP — Brenton Alker @ 13:11

While writing a fairly standard sign-up/log-in system, I got to the point of validating the password to make sure it only contained acceptable characters. Now for me this would usually mean a regular expression. But, since this system wasn’t for me I decided to make the “valid characters” configurable, and generally the people configuring it won’t be able to write a regular expression.

My solution is to use php’s trim() (or more specifically rtrim(), but it doesn’t really matter) by passing the users input as the first argument, and the string of valid characters as the second; I should get an empty string in return. So the test, only allowing lowercase letters, becomes:

if ('' !== rtrim($input, 'abcdefghijklmnopqrstuvwxyz')) {
return false;
}

Simple! I don’t know how it compares speed wise with other methods, but it seems simple and effective and I can’t imagine it’s terribly slow unless your strings get larger.

I’m also not sure how it would handle multi-byte characters. But I don’t think it would be a problem if it doesn’t. Anyone got any insight?

post An Attempt at Restricted Auto-Login

July 4th, 2008

Filed under: Code, PHP — Brenton Alker @ 16:33

The system I have been building is a direct marketing system (don’t hate me, it’s opt-in) and to be compliant with their ethics policy it requires “2 click unsubscribe” functionality from all its email campaigns; 1 click on the link in the email, and 1 click on a big “Don’t Ever Send Me Email Again” button. So people actually have an option to opt out at any time, as any good (and I use that word in the relative sense) email marketing system should.

The problem this poses for me though, is how can I let a member unsubscribe without authenticating themselves? Authentication would take more than 2 clicks! The obvious solution is to not require authentication by simply adding the members identifier (email address, user name, database id, whatever can identify them uniquely) to the link. So I would have a URL something like:

http://example.com/member/unsubscribe?member=1234

And simply make all the options on that page act on the member identified by the identifier. Great! Until a malicious netizen comes along, seeing the scheme decides to systematically unsubscribe all my members. This would be achieved easily by guessing the identifiers, made even easier because they’re sequential, and clicking the unsubscribe button.

The initial solution is to use some other “key” in the link, one that is not so easy to guess. So a key was added, giving the url a form like:

http://example.com/member/unsubscribe?member=1234&key=SECRETKEY

Were SECRETKEY is a key they is generated randomly when the user signs up and stored with their account details. Using this, the system would allow a user to log in simply by clicking the link, they could then unsubscribe.

The problem being, of course, that by simply logging the user into their account anyone who has the link has full access to the account without needing to know any more information. So they could then read (and change) email addresses, passwords etc. Not ideal.

My solution has been to take the key a step further, and limit it to only the intended page. In this case the unsubscribe page. So my new url looks like this:

http://example.com/member/unsubscribe?token=1234:HASH

I’ve removed the member variable and combined it into a delimited token. This was mainly because I thought it looked a little nicer, it would function just as well as a separate variable. The real change is in the HASH. The new hash still uses the SECRETKEY that was used in the previous iteration, except it is now combined with the url that I want to give them access to, and an added salt so all tokens can be invalidated if required. In PHP, this looks something like this:

$allowedUrl = '/member/unsubscribe';
$hash = md5($salt . $allowedUrl . ':' . $secretKey);
$token = $id . ':' . $hash;

On the page, to allow access, the token is decomposed, the member’s identifier extracted, and their key retrieved. Their key is then used in the same process to generate a hash for the URL they are requesting. If the hashes match, they have access. To make it a little more user friendly, the allowed URL is added to their session, so they still have access to the page even if they lose the token from the URL. If they hit any other page with the restricted URL in their session they are logged out and sent back to re-authenticate.

One shortfall that I am aware of, and that was pointed out in a discussion on #phpc, is the members secret keys should ideally be rotated, so it can only be used once. This would mean that stealing a link would at worst allow one login, and only to one page. This may be implemented if it doesn’t impact too much on usability (that classic trade-off, security vs. convenience).

Now, I am not a security expert and as such don’t recommend anyone take my advice (is this good enough for a disclaimer), but apart from the one caveat mentioned, I think this solution meets the requirements without forfeiting too much in security. Any comments or advice on the technique are, as always, appreciated.

ruldrurd
Next Page »
Powered by WordPress, Web Design by Laurentiu Piron
Entries (RSS) and Comments (RSS)