Wednesday 5 March 2014

Adventures in Zend Framework 2: Episode 3: Major Features

ZF2 Structure

A quick glance at the ZF2 directory structure instantly reveals its primary focus as a framework designed with web applications in mind. In addition to regular requirements for software development like caching, authentication, logging, cryptography and MVC components, you will so find classes for forms, http connections, soap and XmlRpc.

Zend Framework 2 directory structure
In this post, I'm going to give an overview of two of ZF2's most important components, the Event Manager and the Service Manager, an understanding of which is key to expanding the usefulness of the framework to your application.

The Event Manager

Perhaps the biggest change from ZF1 to ZF2 was the focus on event driven architecture. The whole application flow depends on events - a change in the application triggers an event and listeners can be attached to each event so that further changes can be made when desired. Each listener is usually passed information about the event, thus allowing it to set logic as to how to proceed given different event variables. Thus for example, one could attach listeners to the dispatch.error event which could log information about the error or send an e-mail to a system administrator.

Most of the events listeners attach to will be associated with the MVC flow but it is possible to create custom events and listeners which can be triggered manually and listened to accordingly. Custom events require their own event manager and are triggered with a name plus optional parameters for a target object, an array of arguments and a callback function.

use Zend\EventManager\EventManager

$event = new EventManager('star-wars');
$event->trigger('death star destruction', null, array('rebels' => array("Luke Skywalker", "Han Solo", "Princess Leia"));


Event listening is generally established during the Bootstrap MVC phrase, often in the OnBootstrap method of Module.php. As module boostrapping can require a lot of code, VuFind 2 adds a custom Bootstrapper class which allows it to create different methods for each initiation requirement such as forcing the user to an error page if the system is unavailable. A listener is added to an event via the attach method which accepts the event name (or an array of event names), an optional callback and a priority as arguments.

VuFind/Module
class Module {
...
    public function onBootstrap(MvcEvent $e)
    {
        $bootstrapper = new Bootstrapper($e);
        $bootstrapper->bootstrap();
    }

}

VuFind/Bootstrapper

class Boostrapper {

public function __construct(MvcEvent $event)
    {
        $this->event = $event;
        $this->events = event->getApplication()->getEventManager();
    }

    public function bootstrap()
    {
        // automatically call all methods starting with "init":
        $methods = get_class_methods($this);
        foreach ($methods as $method) {
            if (substr($method, 0, 4) == 'init') {
                $this->$method();
            }
        }

    }

    protected function initSystemStatus()
    {
        // If the system is unavailable, forward to a different place:
        if (isset($this->config->System->available)
            && !$this->config->System->available
        ) {
            $callback = function ($e) {
                $routeMatch = new RouteMatch(
                    array('controller' => 'Error', 'action' => 'Unavailable'), 1
                );
                $routeMatch->setMatchedRouteName('error-unavailable');
                $e->setRouteMatch($routeMatch);
            };
            $this->events->attach('route', $callback);
        }
    }
}

MVC events have their own event manager which is why in the code above, we can instantly call $this->events->attach(). If you have created your own events however, you need to access them via the shared event manager. Using the VuFind code as a base, we could therefore do something like:

    protected function initHolonet()
    {

        $callback = function ($e) {
            $rebels = $e->getParam('rebels');
            $sm = $e->getApplication()->getServiceManager();
            $holonet = $sm->get('holonet');

            return $holonet->transmit('palpatine@imperial.net', $rebels);
        };
        $sharedManager = $this->events->getSharedManager();
        $sharedManager->attach('star-wars',
'death star destruction', $callback);
    }

The priority argument is extremely useful as it allows us to stack our listeners according to the order in which they should run. Calling stopPropagation(true) on an event will also stop any subsequent listeners from executing, something which is very handy when an error occurs or a dependency is not available. The result of the listener callback is returned as an argument to the callback of the event trigger which allows for further processing if required.

The Service Manager

If the Event Manager determines when you run your code, the Service Manager is primarily designed to help with how you run it. The Service Manager is essentially a registry of services which can be intelligently initiated with a simple key. In the initHolonet example above, the Service Manager was accessed via
$sm = $e->getApplication()->getServiceManager();
and the holonet service instance was loaded with

$sm->get('holonet');

Though it is possible to define a service almost anywhere, it makes sense to do so either in the module configuration file or the module class itself for ease of reference.

The service manager accepts the following primary configuration keys:
1) invokeables
Classes which can be invokes without configuration
2) factories
Objects which require configuration, often with other service manager services
3) abstract Factories
Factories that can can create multiple services based on the name supplied
4) services
Objects which have already been instantiated
5) initializers
Services which initialise other services whenever they are created
6) aliases
Alias a key to a known service key
7) shared
Determines whether or not a service should be shared or if a new service should be created with every call
8) allow_override
Whether or not the services can be overridden by other modules
When defined in module.config.php, a service manager configuration might look something like this:
module.config.php
return array(
    ...
    

    service_manager => array(
    

    'invokables' => array(
        'Anakin' => '\Empire\Personel\AnakinSkywalker';

        'Empire\Transmitter' => \Empire\Resources\Transmitter
     ),

    'factories' => array(
        'holonet' => function ($sm) {
            $transmitter =
$sm->getServiceLocator()->get('Empire\Transmitter');
            return new \Empire\Services\Holonet($transmitter);
        }
    ),


    'abstract_factories' => array(
        'RebelAlliance\Services\PeronselManager',
    ),


    'services' => array(
        'light' => new \Force\LightSide(),

        'dark' => new \Force\DarkSide()
    ),


    'initializers' => array(
        function ($instance, $sm) {
            if ($instance instanceof
\Empire\Resources\Transmitter){
        $instance->setFrequencies($sm->get('ImperialFrequencies'));
            }
        },

        'RebelAlliance\Services\Initializers\Jedi',
        'Empire\Services\Initializers\Sith'
    ),

    'aliases' => array(
        'DarthVader' => 'Anakin';
    ),


    'shared' => array(
        'holonet' => false,
    ),
    

    )
);



View Helpers, Controllers, Controller Plugins and Form Elements effectively have their own service managers and their configurations needs to be returned with their own keys instead of "service_manager". These keys are "view_helpers", "controllers", "controller_plugins" and "form_elements".

If you wished to achieve something similar in the Module Class for a service manager configuration, you can use the getServiceConfig method to return a configuration array:


Module.php
 
public function getServiceConfig() {
   return array(
     'services' => array(
       'light' => new \Force\LightSide(),
       'dark' => new \Force\DarkSide()
      ),

  );
}


The getViewHelperConfig(), getControllerConfig(), getControllerPluginConfig() and getFormElementConfig() methods are also available for their service managers.

Service Manager: Basic Functionality


When  a call is made to the get method of the service manager with a service key, the service manager checks its registry of instances, invokables, factories, aliases and abstract factories for that key. If the key is found, it calls its create method with that key which in turn instantiates the service according to the service type (invokable, factory etc).

Zend/ServiceManager/ServiceManager

if (isset($this->instances[$cName])) {
    return $this->instances[$cName];

}

if (!$instance) {
    if (
        isset($this->invokableClasses[$cName])
        || isset($this->factories[$cName])
        || isset($this->aliases[$cName])
        || $this->canCreateFromAbstractFactory($cName, $name)
    ) {
        $instance = $this->create(array($cName, $name));
    } elseif ($usePeeringServiceManagers && !$this->retrieveFromPeeringManagerFirst) {
        $instance = $this->retrieveFromPeeringManager($name);
    }

}

Once instantiated, the service manager also applies the initializers to each instance, either through the initializer initialize method or via the supplied function.

Zend/ServiceManager/ServiceManager 

foreach ($this->initializers as $initializer) {
  if ($initializer instanceof InitializerInterface) {
     $initializer->initialize($instance, $this);
  } else {
     call_user_func($initializer, $instance, $this);
  }
}


The initalize method might therefore look something like:

public function initialize($instance, ServiceLocatorInterface $serviceLocator) {

    if ($instance instanceof \Force\ForceAwareInterface) {
         $instance->setLightSide($sm->get('light'));
         $instance->setDarkSide($sm->get('dark'));
    }

    if (method_exists($instance, 'setHolonet')) {
        $instance->setHolonet('holonet);
    }

}

Service Manager: Abstract Factories

Abstract Factories effectively allow you to create your own logic for instantiating service classes. They must implement Zend\ServiceManager\AbstractFactoryInterface which requires that you define a canCreateServiceWithName method and a createServiceWithName method. VuFind defines it's own AbstractPluginFactory as follows:

abstract class AbstractPluginFactory implements AbstractFactoryInterface
{
    protected $defaultNamespace;

    protected $classSuffix = '';

    protected function getClassName($name, $requestedName)
    {
        // If we have a FQCN, return it as-is; otherwise, prepend the default prefix:
        if (strpos($requestedName, '\\') !== false
            && class_exists($requestedName)
        ) {
            return $requestedName;
        }
        // First try the raw service name, then try a normalized version:
        $finalName = $this->defaultNamespace . '\\' . $requestedName
            . $this->classSuffix;
        if (!class_exists($finalName)) {
            $finalName = $this->defaultNamespace . '\\' . ucwords(strtolower($name))
                . $this->classSuffix;
        }
        return $finalName;
    }

    public function canCreateServiceWithName(ServiceLocatorInterface $serviceLocator,
        $name, $requestedName
    ) {
        $className = $this->getClassName($name, $requestedName);
        return class_exists($className);
    }

    public function createServiceWithName(ServiceLocatorInterface $serviceLocator,
        $name, $requestedName
    ) {
        $class = $this->getClassName($name, $requestedName);
        return new $class();
    }
}



The key here is that the getClassName method is used to determine the class name, largely via a combination of default namespaces and class suffixes. This makes it easy to create a number of services which share either a common namespace, common class suffix or combination of both. It's also possible to pass in and instantiate a fully qualified namespace.

If desired, it would also be possible to initialise class dependencies as part of the createServiceWithName method. As the Service Locator is passed in as the first argument, it would be easy to access other services. Zend applications will be easier to debug and maintain if you stick to the same principles for all your services. If you decide to initialise dependencies as part of the createServiceWithName for one abstract factory, always initialise dependencies for your abstract factories there. For my money, the best place to perform your initialisation is with the specifically designed initialiser option of the Service Manager. You can either have one initializer responsible for all your instances or you can create multiple initializers according to instance type. If these are all referenced in module.config.php, they will be easy to find and understand.

Service Manager: Plugin Manager

The Plugin Manager is an extension of the service manager which allows users to establish a collection of services according to a specific set of criteria, perhaps via interfaces or ancestor class. It automatically registers an initializer which should be used to verify that a plugin instance is of a valid type, provides for a validatePlugin method and accepts an array of options for the constructor, which can be used to configure the plugin when retrieved.

VuFind extends the Zend Plugin manager to include a getExpectedInterface, thus forcing anything that extends it to implement that method. The validePlugin method then checks to make sure that the plugin is an instance of the value returned by the getExpectedInterface method to ensure plugin integrity.

public function validatePlugin($plugin)
{
    $expectedInterface = $this->getExpectedInterface();
    if (!($plugin instanceof $expectedInterface)) {
        throw new ServiceManagerRuntimeException(
            'Plugin ' . get_class($plugin) . ' does not belong to '
            . $expectedInterface
        );
    }
}

abstract protected function getExpectedInterface();

Once instantiated, a user can then call get on the plugin manager to retrieve the desired instance.

$serviceLocator()->get('RebelAlliance\ResourcesPluginManager')
    ->get('millennium-falcon);

Coming Up...

My next post will look at some of the basics of ZF2's MVC implementation.

Tuesday 4 March 2014

Adventures in Zend Framework 2: Episode 2: The Basics

You've Been Framed!

Zend Framework 2 (ZF2) describes itself as "the most popular framework for modern, high-perfoming PHP applications" [1]. It is an open source project which is primarily used for developing web applications and services which uses "100% object-oriented code and utilises most of the new features of PHP 5.3, namely namespaces, late static binding, lambda functions and closures." [2] It is essentially a library of components which can be used separately or together, designed to work most effectively within an MVC implementation.

My first experiences of ZF2 came through utilising VuFind 2 [3] and much of what I am going to write about ZF2 is done so through the prism of VuFind. With that said, I have begun to use ZF2 in other projects and some of the experience I have of certain components such as Forms and Access Control Lists has come through my own experimentation.

Funny Bones

The first port of call for anyone interested in ZF2 (after reading as much literature as possible) should probably be the Skeleton Application available at http://framework.zend.com/downloads/skeleton-app. It offers a step-by-step guide to getting a ZF2 application started using the most common components like databases and forms and gives a basic introduction to the MVC concept. As part of the installation process, the user will also be encouraged to use Composer [4], a dependency manager for PHP which makes adding and using third party software extremely easy. I also recommend familarising yourself with Git [5] and Github [6] as the preferred version control system.

MVC

The Model-View-Controller software pattern to which ZF2 adheres is basically a means of arranging code into logical but integrated sections, separating a developer's representation of information "from the ways that information is presented to or accepted from the user". [7] The model encapsulates the core code complete with the logic and functions required to generate or manipulate data, the view constitutes any outputted representation of this information whilst the controller effectively acts as a  go-between the model and the view, accepting and converting user input into commands for each.

Explaining how the MVC process works in ZF2 could take up the entire contents of a book, let alone a blog post. At the beginning, I suffered some sleepless nights as my brain tried to make sense of some terribly complex diagrams which I managed to find online. Anything I offer here will necessarily be a gross simplification but it may at least offer a base for further exploration.


1) Bootstrap
All the requirements for running the application (including dependencies) are prepared
2) Route
A user request is matched to a controller and action
3) Dispatch
The controller processes the application logic chain
4) Render
The information is represented to the user
5) Finish
The MVC process is complete
Each of these "phases" have different "hooks" which allow a developer to attach or inject logic when required. Some "hooks" are already predefined (such as just before and just after rendering takes place) but a developer can also add their own. In ZF2, these "hooks" are actually "events" (as it made clear by naming conventions which use "pre", "post", "on" etc as descriptions) and an "Event Listener" is responsible for dealing with the separate MVC phases. I will cover the Event Manager and listening to events in a later post.

This MVC structure is partly revealed in the directory structure of the Skeleton Application:





A simplified flow of  information through a ZF2 application would therefore run something like this:
  1. A user makes a request via a url
  2. Web server redirects force all requests via the index.php file in the public folder (unless the requested filename actually exists e.g. images, css files etc)
  3. index.php begins the auto loading process by requiring init_autoloader.php and initialising any dependencies in the vendor directory (including ZF2 which is a dependency of the Skeleton Application)
  4. index.php begins the MVC process, using the configuration settings found in config/application.config.php. Settings include an array of module namespaces (e.g. "Application"), the locations in which modules should be found (e.g. "modules" and "vendor"), overriding configuration files and options for caching.
  5. Modules are loaded and initialised via the Module Manager, largely through the operation of Module.php (which contains "onBootstrap" and "init" methods plus methods for establishing the module configuration and library location e.g. module/Application/src/Application) and config/module.config.php. Module configuration files are merged with the application configuration to create the final configuration.
  6. The Service Locator (responsible for locating predefined services) plus the Router, View and Event services are set up so that they can be used in the Bootstrap phase.
  7. After bootstrapping, the MVC logic is processed as the route is determined from the url, a controller and action are selected, the model code is executed, the response is determined (and rendered if required) and the request is finished.
Coming Up...

In my next post, I'll highlight some of ZF2's major features. It was only once I'd got my head around them that the ZF2 penny began to drop.

[1] http://framework.zend.com/
[2] http://framework.zend.com/about/
[3] http://www.vufind.org
[4] https://getcomposer.org/
[5] http://git-scm.com/
[6] https://github.com/
[7] http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller

Monday 3 March 2014

Adventures in Zend Framework 2: Episode 1: An Ode to VuFind

Beginnings

When I first began working for Swansea University in 2009, I have to admit, I was more of a scripter than a developer. Having studied history at university and arriving in the job with no official qualifications in computing, my suitability for the role was based entirely on personal study and experimentation.

My knowledge of front end web desgin and PHP had largely been acquired  via a general computing interest, a year's stint in a primary school I.C.T. department, several years experience in Swansea Public Library Service and the odd request to build a website for a friend or acquaintance.

Fortunately, the primary piece of software for which I was to be responsible - VuFind [1] - was written in PHP. By examining the source code and using the excellent user community, I was able to demonstrate enough familiarity with the VuFind to propose a development path to suit the goals of the project.

VuFind: OOPs I did it again

The first lesson I received from VuFind (or more accurately from the user community, particularly Demian Katz, the project leader) was Object Orientated Programming (OOP) [2]. Up until that point, I had only ever used PHP's native functions, perhaps augmented with a few libraries which had been recommended in "Teach Yourself" type books which were accessed via includes.

Moving from scripting to OOP was probably the most important step I have taken in the last five years. OOP not only makes developing applications far more efficient as frequently used code can be put into methods to be utilised whenever required, it also opens up a world of tried and tested methodologies which can speed up the development process, resulting in code which is more robust, portable and easier to debug.

For the first time, I wasn't just writing code to get things done - I was writing code mindful of considerations such as accepted standards, performance, portability, repository integration and usefulness to others. It felt good to be able to make some small contributions to the direction of the VuFind project and to see some of my patches integrated into the core repository.

VuFind: The Gift that keeps on giving

VuFind 1.0 had introduced me to a world of inheritance, dependency injection, modular programming, encapsulation, interfaces and abstracts. It had also forced me to learn more about the Apache web server, to take my first steps into Unix via a Virtual Box installation of Ubuntu and to consider repository management via SVN. By the time VuFind was launched for the South West Wales Higher Education Partnership (SWWHEP), I was responsible for building and configuring an Ubuntu server from scratch on a VMWare platform.

My experience on the SWWHEP project was also responsible for landing me a permanent job at Swansea University as part of the Web Team. Again, this was a fantastic opportunity as I would be working with professional developers who were willing to help me fill in the gaps which still existed as a result of my rather ad hoc career. If VuFind primarily helped expand what I was able to develop, the move to the web team greatly improved how I was able to develop code. I began to use an integrated development environment for the first time, switched to Unix via Fedora and was introduced to concepts such as test driven development, automated deployment and continuous integration.

VuFind & Zend

VuFind 1.0 had a loose, custom MVC framework. As the project began to grow however, the user community decided that it would be beneficial to base future developments on a tried and tested framework. VuFind 2.0 was initially developed on Zend Framework 1.8 but as Zend Framework 2 was released during this process, VuFind was eventually re-factored to take advantage of these developments. I had never used a framework before and when the beta versions of VuFind 2.0 were released, I realised that Zend 2 used many concepts outside of my comfort zone. As with OOP however, thanks to Demian Katz and the VuFind community, I was able to ask questions and get very helpful answers. Working with the web team also meant that I could tap into their knowledge to resolve particular issues.

Using the experience gained working with VuFind 2, I plan to write a series of brief articles on using the Zend 2 platform. I don't expect to provide any great revelations or insights as I am definitely a consumer rather than an innovator when it comes to programming. Nevertheless, the articles should at least serve me as "revision notes" and they may save some poor soul from hours agonising over an issue which has a simple solution, something which became apparent to me only after the fact!

[1] www.vufind.org
[2] http://en.wikipedia.org/wiki/Object-oriented_programming