Oct 13 2013

Introducing Service Docs

Service Docs is a project developed by myself and Mikey, a talented colleague. It'll generate HTML API documentation from a Guzzle service description. Check out the demo.

Guzzle, for those that aren't aware, is an excellent PHP HTTP client.

We have a lot of ideas for enhancements and of course, it's on Github.

Sep 28 2012

Cloning proxied Doctrine 2 entities

Simply using $cloned = clone $proxy; and persisting does not work.

To get round this, you can do the following (contrived example ahoy):

$user = $em->find('User', 1);

$clonedUser = clone $user;

$addressRef = $user->getAddress()->getRef();

// remove address proxy from identity map to ensure we get actual entity
$em->getUnitOfWork()->removeFromIdentityMap($user->getAddress());

$address = $em->find('Address', $addressRef);

$clonedAddress = clone $address;

$clonedUser->setAddress($clonedAddress);

There is definitely a better way of doing this, after trying different approaches and a fair amount of googling I haven't found one though.

Any comments, criticisms or alternative solutions welcomed!

Jan 15 2012

Integrating Zend Framework 1 and Pimple

This post will describe a way to integrate Zend Framework 1 and Pimple. A complete working version of the code is available on github.

Thankfully, Zend Framework 2 features its own Dependency Injection Container. Happy days. Still, if you're not prepared to wait, you may find this useful.

What is Pimple?

Pimple is a small Dependency Injection Container for PHP 5.3 that consists of just one file and one class (about 50 lines of code).

If you aren't too familiar with Dependency Injection and why it's useful, check out these slides by Fabien Potencier.

Why bother?

It allows you to mock dependencies in your controllers within your controller tests. In this example, the Doctrine 2 entity manager is added into the container. If you're just after Zend Framework 1 and Doctrine 2 integration, you will certainly find this post from the Mayflower blog helpful.

Add a bootstrap resource plugin for the container

The bootstrap resource plugin in this example adds the entity manager closure into Pimple. The init method returns the container, thus it can be retrieved from within a controller.

More information on resource plugins

// library/Example/Application/Resource/Container.php
namespace Example\Application\Resource;

use Zend_Application_Resource_ResourceAbstract,
    Doctrine\ORM\EntityManager,
    Doctrine\ORM\Tools\Setup;

class Container extends Zend_Application_Resource_ResourceAbstract
{
    public $_explicitType = 'container';

    protected $container;

    public function __construct($options = null, $container = null)
    {
        parent::__construct($options);

        $this->container = $container;
    }

    public function init() 
    {
        $options = $this->getBootstrap()->getOptions();
        $db = $options['db'];

        $this->container['entityManager'] = $this->container->share(function ($c) use ($db) {
            $isDevMode = APPLICATION_ENV == 'development';

            $config = Setup::createAnnotationMetadataConfiguration(array(APPLICATION_PATH . '/models'), $isDevMode);

            return EntityManager::create($db, $config);
        });

        return $this->container;
    }
}

Resource plugins can be added from the application configuration. However, it's important that it is instantiated outside of the configuration to allow injection of the Pimple container. This is an extremely important step for testing.

// public/index.php

// ...

$resource = new \Example\Application\Resource\Container(null, new Pimple);
$application->getBootstrap()->registerPluginResource($resource);

$application->bootstrap()
            ->run();

Create the controller

Within the action, the container is retrieved from the bootstrap.

// application/controllers/IndexController.php
class IndexController extends Zend_Controller_Action
{
    public function indexAction()
    {
        $bootstrap = $this->getInvokeArg('bootstrap');
        $container = $bootstrap->getResource('container');

        $em = $container['entityManager'];

        $this->view->items = $em->getRepository('Application_Model_Item')->findAll();
    }
}

Add a test for the controller

I've used the PHPUnit mock library for the example. Mockery is better though. Didn't want to throw another library into the mix!

// tests/application/controllers/IndexController.php
class IndexControllerTest extends Zend_Test_PHPUnit_ControllerTestCase
{
    protected $container;

    protected function setUp()
    {
        $this->bootstrap = new Zend_Application(APPLICATION_ENV, APPLICATION_PATH . '/configs/application.ini');
        $this->container = new Pimple;
        $resource = new Example\Application\Resource\Container(null, $this->container);
        $this->bootstrap->getBootstrap()->registerPluginResource($resource);
        parent::setUp();
    }

    public function testIndexAction()
    {
        $items = array(
            $this->createItem('test1', 'test1value'),
            $this->createItem('test2', 'test2value'),
        );

        // mock the entity repository
        $repo = $this->getMockBuilder('Doctrine\ORM\EntityRepository')
            ->disableOriginalConstructor()
            ->getMock();

        $repo->expects($this->once())
            ->method('findAll')
            ->will($this->returnValue($items));

        // mock the entity manager
        $em = $this->getMockBuilder('Doctrine\ORM\EntityManager')
            ->disableOriginalConstructor()
            ->getMock();

        $em->expects($this->once())
            ->method('getRepository')
            ->with($this->equalTo('Application_Model_Item'))
            ->will($this->returnValue($repo));

        // replace the entity manager closure in the container
        $this->container['entityManager'] = $em;

        $this->dispatch('/');

        $this->assertQueryContentContains("h1", "Items");
        $this->assertQueryContentContains('ul li', 'test1: test1value');
        $this->assertQueryContentContains('ul li', 'test2: test2value');
    }

    protected function createItem($name, $value)
    {
        $item = new Application_Model_Item;
        $item->setName($name);
        $item->setValue($value);
        return $item;
    }
}

Improvements

  • Populate Pimple outside of the bootstrap resource plugin. This step allows you to have an empty container during testing (no forgetting to replace objects in the container).
  • Allow easier access to the container via an action helper or by extending Zend_Controller_Action.
Aug 15 2011

Implementing Twitter sign-in with Silex and PHP

Set-up

  • Install pecl oauth
  • Grab the Silex .phar archive
  • Create a Twitter application

pecl oauth

Install the pecl oauth extension

For example:

$ pecl install oauth

Silex

For those not acquainted with Silex:

Silex is a PHP microframework for PHP 5.3

A microframework provides the guts for building simple single-file apps.

It's awesome.

All you need is the silex.phar file and you're away. Download it.

For the example, I've setup a virtual host of example.local on my development machine. The apache module mod_rewrite and a .htaccess file are used to rewrite requests to index.php.

# .htaccess
<IfModule mod_rewrite.c>
    Options -MultiViews

    RewriteEngine On
    #RewriteBase /path/to/app
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^(.*)$ index.php [QSA,L]
</IfModule>

Create a Twitter application

Create a Twitter app

Once you've created your application, you'll need your consumer key and consumer secret. These are used to sign every request made to the API.

Implementation

A complete summary along with a pretty diagram of what we're trying to achieve can be found here.

Basic OAuth request cycle:

  • Retrieve a request token
  • Request authorization
  • Exchange the request token for an access token

More information - https://dev.twitter.com/docs/auth/oauth

Basic Silex application

// index.php
define('CONS_KEY', 'Application consumer key');
define('CONS_SECRET', 'Application consumer secret');

require_once __DIR__.'/silex.phar'; 

$app = new Silex\Application(); 

// register the session extension
$app->register(new Silex\Extension\SessionExtension());

$app->get('/', function() use($app) { 
    $username = $app['session']->get('username');

    if ($username == null) {
        return 'Welcome Guest. <a href="/login">Login</a>'; 
    } else {
        return 'Welcome ' . $app->escape($username);
    }
}); 

$app->run(); 

Add the /login route

The first part of the process requires the retrieval of a request token. Authorization is requested by redirecting to https://twitter.com/oauth/authenticate.

// index.php
$app->get('/login', function () use ($app) {
    // check if the user is already logged-in
    if (null !== ($username = $app['session']->get('username'))) {
        return $app->redirect('/');
    }

    $oauth = new OAuth(CONS_KEY, CONS_SECRET, OAUTH_SIG_METHOD_HMACSHA1, OAUTH_AUTH_TYPE_URI);
    $request_token = $oauth->getRequestToken('https://twitter.com/oauth/request_token');

    $app['session']->set('secret', $request_token['oauth_token_secret']);

    return $app->redirect('https://twitter.com/oauth/authenticate?oauth_token=' . $request_token['oauth_token']);
});

Add the /auth route

In my example setup the URL would be http://example.local/auth. Set your Twitter application callback URL in the app settings to be the auth route.

Within this action, the request token is exchanged with the access token which allows usage of the Twitter API on behalf of the user.

// index.php
$app->get('/auth', function() use ($app) {
    // check if the user is already logged-in
    if (null !== ($username = $app['session']->get('username'))) {
        return $app->redirect('/');
    }

    $oauth_token = $app['request']->get('oauth_token');

    if ($oauth_token == null) {
        $app->abort(400, 'Invalid token');
    }

    $secret = $app['session']->get('secret');

    $oauth = new OAuth(CONS_KEY, CONS_SECRET, OAUTH_SIG_METHOD_HMACSHA1, OAUTH_AUTH_TYPE_URI);
    $oauth->setToken($oauth_token, $secret);

    try {
        $oauth_token_info = $oauth->getAccessToken('https://twitter.com/oauth/access_token');
    } catch (OAuthException $e) {
        $app->abort(401, $e->getMessage());
    }

    // retrieve Twitter user details
    $oauth->setToken($oauth_token_info['oauth_token'], $oauth_token_info['oauth_token_secret']);
    $oauth->fetch('https://twitter.com/account/verify_credentials.json');
    $json = json_decode($oauth->getLastResponse());

    $app['session']->set('username', $json->screen_name);

    return $app->redirect('/');
});

Resources

Apr 15 2011

DbUnit talk at PHPSW in Bristol

I had the pleasure of talking to the PHPSW group in Bristol on Wednesday (13/04/2011). Considering it was my first talk, I felt it went quite well. Had some good comments :) and only small amounts of heckling. Overall a really good experience and something I'd definitely like to do again.

The talk was about the PHPUnit database code testing extension, DbUnit. I'm really fond of this extension and it seems to have a lack of decent documentation on the 'net. Ever since Benjamin Eberlei penned a comprehensive tutorial on it, I've been hooked.

Talk slides


Resources:

Code examples on github

PHPUnit Manual

Benjamin Eberlei's tutorial

Mike Lively's blog

Testing LAMP applications by Sebastian Bergmann

Testing PHP/MySQL Applications with PHPUnit/DbUnit by Sebastian Bergmann

Database Testing with PHPUnit and MySQL by Matthew Turland

Feb 17 2011

Logging queries in Doctrine 1.2

The developers of Doctrine have made the task of logging queries trivial, thanks to their event listener architecture.

One just needs to create a class which extends Doctrine_EventListener:

class QueryDebuggerListener extends Doctrine_EventListener
{
    protected $queries;

    public function preStmtExecute(Doctrine_Event $event)
    {   
        $query = $event->getQuery();
        $params = $event->getParams();

        //the below makes some naive assumptions about the queries being logged
        while (sizeof($params) > 0) {
            $param = array_shift($params); 

            if (!is_numeric($param)) {
                $param = sprintf("'%s'", $param);
            }   

            $query = substr_replace($query, $param, strpos($query, '?'), 1); 
        }   

        $this->queries[] = $query;
    }

    public function getQueries()
    {   
        return $this->queries;
    }
}

And add the event listener:

$c = Doctrine_Manager::connection($conn);
$queryDbg = new QueryDebuggerListener();
$c->addListener($queryDbg);

I like to use the above to get a feel for how many queries are being performed.

For example, I was using $foo->Bar['field'] to retrieve a field value from a relation at one point. The code can result in another query, which could be avoided by using a join on the original query for the record. See the Doctrine documentation for a clearer example.

Feb 05 2011

Redirecting from an old route in Zend Framework

Firstly ensure your old route has been created:

routes.new.route = "new"
routes.new.defaults.controller = index
routes.new.defaults.action = new
routes.new.defaults.module = default
routes.new.defaults.id = ""
routes.new.reqs.id = \d+

routes.old.route = "old"
routes.old.defaults.controller = index
routes.old.defaults.action = old
routes.old.defaults.module = default
routes.old.defaults.id = ""
routes.old.reqs.id = \d+

In the controller:

class IndexController extends Zend_Controller_Action 
{
    ...

    public function oldAction()
    {
        $redirector = $this->_helper->getHelper('Redirector');
        $redirector->setCode(301);
        return $redirector->gotoRoute($this->_getAllParams(), 'new');
    }
}

For reference, examples developed with Zend Framework 1.11.2

Feb 05 2011

Setting a default page title in Zend Framework and modifying per action

There may be a better way of doing this, I'm open to suggestions. Still, my goal was to have a default page title, manipulating when required in an action.

I initially tried to just use $this->headTitle('foobar'); in my layout and prepend in the controller action. This didn't work out too well, due to the order in which they're executed - append and prepend are reversed. Not too intuitive.

Therefore, I created an _init method in the Bootstrap class:

public function _initTitle()
{
    $view = $this->bootstrap('view')->getResource('view'); 
    $view->headTitle('foobar');
}

Modifying the title in the action is very simple:

public function fooAction()
{
    $this->view->headTitle()->prepend('foo | ');
}

Accessing the foo action will yield:

<title>foo | foobar</title>

For reference example was written with Zend Framework 1.11.2.

Jan 19 2011

Using Zend_Form_Decorators to compose a form as table rows

Ahh Zend form decorators, they certainly have a reputation of being quite difficult to pick up. I suffered similar problems but persevered to get the table layout I wanted.

$form->setElementDecorators(array(
    'ViewHelper',
    array('Description', array('tag' => 'span')),
    array('Errors'),
    array('HtmlTag', array('tag' => 'td')),
    array('Label', array('tag' => 'th', 'requiredSuffix' => ' *')),
    array(array('row' => 'HtmlTag'), array('tag' => 'tr')),
));

The above code will result in the following (albeit not quite as neatly):

<tr>
    <th>Label *</th>
    <td>
        <input type="text" /><span class="hint">Description</span>
        <ul class="errors"><li>Error</li></ul>
    </td>
</tr>

For reference example was written with Zend Framework 1.11.2.

Jan 12 2011

Exporting a git tag in a Phing target and subsequently processing the files

Composing releases with git archive is very useful, especially when making use of the .gitattributes file to select what is archived.

One way to make use of this command in Phing, is to do the following:

<target name="build" depends="prepare">
    <exec command="git archive ${git_tag} | tar -x -C ./_build" />
</target>

This allows processing of the archived files. For example, the target could be expanded to strip PHP comments:

<target name="build" depends="prepare">
    <exec command="git archive ${git_tag} | tar -x -C ./_build" />

    <reflexive>
        <fileset dir="./_build">
            <include pattern="**/*.php" />
        </fileset>
        <filterchain>
            <stripphpcomments />
        </filterchain>
    </reflexive>
</target>

Info

I'm Ade Slade, a PHP web developer.