PHP7 box to test your Zend Framework 2 applications

If you want to test your Zend Framework 2(ZF2) or any PHP application with PHP7 we have a good news for you! We have prepared new isolated development environment based on our previous box.

PHP7 is the next generation of PHP that promises big performance improvement (around 1,5 to 2,1 times faster than PHP 5.6 ) and additional syntactic sugar.

At the moment PHP7 is still in heavy development and the installation and updates in your existing operating system can be challenging. Therefore we have prepared for you a script that can help you create an isolated development environment with PHP7 and the sample application from “Learn ZF2″ in it for your testing pleasure. Of course you can add your own PHP applications to the box and test if they are working with PHP7.

Here are the steps that you need to create your own dev box with PHP7.

First you will need to install the latest version of Vagrant and Virtualbox. If you are using Ubuntu 14 you can check the instructions on that external page.

Then you need to clone the learnzf2-box repository using the following command:

git clone -b php7 https://github.com/slaff/learnzf2-box.git learnzf2-box-php7

And when the repository is cloned you have to start the virtual machine using

cd learnzf2-box-php7
vagrant up

After some minutes you will have brand new isolated development environment with Ubuntu 14, apache web server with PHP7 and the latest version of the “Learn ZF2″ source code from github.

In order to ssh to the virtual machine you can type

vagrant ssh

And in order to access the web server from outside you can type in your browser http://localhost:8080

The source code is in a shared folder named dev and can be accessed and modified both from inside the virtual machine and from your physical machine.

Easy Performance Win in ZF2 with Route Caching

As a follow-up to our Zend Framework 2 (ZF2) performance article we created a composer package that can be used right away in your ZF2 application.

We have noticed that in real world applications routing can take
300 ms or more. With this module the routing can take less than 40 ms.

Installation

Run the following command from the root folder of your ZF2 application.

php composer.phar require learnzf2/route-cache:*

Setup

First: To enable the module make sure to add it to the list of modules in your config/application.config.php.

Second: This module requires a cache service that is called “var-cache”. The cache service
should be able to store variables. If you already have such a service but it has a different name then you can use aliases in the service_manager section of your application configuration. This can be achieved by adding the following lines:

3
4
5
6
7
8
9
'service_manager' = array(
 // ...
 'aliases' => array (
 // ...
 'var-cache' => '<your-cache-service-name>'
 )
),

If you don’t have a cache service in your application then copy the file
vendor/learnzf2/route-cache/config/cache.local.php.dist to config/autoload/cache.local.php.

Enjoy!

Isolated development environment for “Learn ZF2″

You Asked – We Listened. We have created an isolated development environment for “Learn ZF2″ using vagrant and virtualbox.

One of our readers Paulius Mačernis has asked us for instructions how to build isolated development environment for the “Learn ZF2″ book. Since yesterday we have Vagrant build file that can help you have your very own isolated development environment. You can apply these instructions for Windows, Linux and Mac OS X.

First you will need to install the latest version of Vagrant and Virtualbox. If you are using Ubuntu 14 you can check the instructions on that external page.

Then you need to clone the learnzf2-box repository using the following command:

git clone https://github.com/slaff/learnzf2-box.git

And when the repository is cloned you have to start the virtual machine using

cd learnzf2-box
vagrant up

After some minutes you will have brand new isolated development environment with Ubuntu 14, apache web server, php 5.5 with mcrypt and sqlite modules and the latest version of the “Learn ZF2″ source code from github.

In order to ssh to the virtual machine you can type

vagrant ssh

And in order to access the web server from outside you can type in your browser http://localhost:8080

The source code is in a shared folder named dev and can be accessed and modified both from inside the virtual machine and from your physical machine.

Route Cache in Zend Framework 2

There are several ways to improve the performance of a Zend Framework 2 (ZF2) application and in the “Learn ZF2″ I explained some of them. But not all of them. Below you will find an information how to speed up the ZF2 application routing.

In a typical ZF2 application evaluating the routes and finding a match can be time-consuming task. The more non-literal routes your application has the slower the route matching process will be. Below I will present you one possible way to speed up that process.

In ZF2 routing means finding a controller, action and set of parameters that correspond to the current URL and given request parameters. In ZF2 the routing process starts when the MVC ROUTE event is triggered. There is a listener with priority 1(one) attached to this event that takes care to find a route match. What we can do is attach a new listener to the MVC ROUTE event but with higher priority. This way our listener will be executed before the actual matching process is started. In it we can check against a cache system if for the provided path of the URL there is already a cached data that contains the match parameters. In addition we can add a second listener to the MVC ROUTE event with lower priority. It will be executed after the matching is done and we can use it to save the route match data to our caching system. A good initial place to put those listeners will be in the Application Module.php ( module/Application/Module.php )

The initial version of our two listeners looks like this:

<?php
namespace Application;
 
use Zend\Mvc\MvcEvent;
 
class Module
{   public function onBootstrap(MvcEvent $event) 
    {
        $eventManager        = $event->getApplication()->getEventManager();
 
        //..
        $eventManager->attach(MvcEvent::EVENT_ROUTE, array($this, 'routeLoad'), 999);
        $eventManager->attach(MvcEvent::EVENT_ROUTE, array($this, 'routeSave'), 0);
    }
 
    /**
     * Method that tries to load cached routes and speed up the matching
     * @param MvcEvent $event
     */
    public function routeLoad(MvcEvent $event)
    {
        // @todo: check if we have data in our cache
    }
 
    /**
     * Method that tries to save a route match into a cache system
     * @param MvcEvent $event
     */
    public function routeSave(MvcEvent $event)
    {
        $match = $event->getRouteMatch();
        if(!$match) {
            return;
        }
 
        // @todo: save the route match into the cache.
    }

We should take care to cache the route match for URLs that produce the same result. A good example of a request for which the routing can be cached is the case when we have GET request and no query params. For such events we can set a flag route-cacheable. Additionally we should set a flag in the event that specifies if a cached route match was used or it was calculated during this request. That will help us avoid caching already cached routes. So the code will start to look like this

// ...
 
    /**
     * Method that tries to load cached routes and speed up the matching
     * @param MvcEvent $event
     */
    public function routeLoad(MvcEvent $event)
    {
        $request = $event->getRequest(); 
        if(!(
            $request->getMethod() == \Zend\Http\Request::METHOD_GET && 
            $request->getQuery()->count() == 0
            )) {
            // We do not cache route match for requests that can produce
            // different match.    
            return ;
        }
 
        $event->setParam('route-cacheable', true);
 
        // @todo: check if we have data in our cache
        $inCache = false; // @todo: get the actual value
 
        if($inCache) {
            $event->setParam('route-cached', true);
        }
    }
 
    /**
     * Method that tries to save a route match into a cache system
     * @param MvcEvent $event
     */
    public function routeSave(MvcEvent $event)
    {
        $match = $event->getRouteMatch();
        if(!$match) {
            return;
        }
 
        if($event->getParam('route-cached') || !$event->getParam('route-cacheable')) {
            return;
        }
 
        // @todo: save the route match into the cache.
    }

The real trick to speed up the route matching is not to bypass it but to put on the top a route definition that is very fast to match. This definition should use the Literal route type and should contain the route name and route parameters stored in the cache from the actual matching. Creating such a route can be done with:

$cachedRoute = \Zend\Mvc\Router\Http\Literal::factory($cachedData);

The router contains multiple routes ordered in a stack. And this stack is evaluated from top to bottom. On the top are the routes with higher priority and on the bottom are the ones with lower priority. We need to put this cached router on the top of the stack of routes that we have. This can be done with a code like:

$router = $event->getRouter();
$router->addRoute($cachedData['name'], $cachedRoute, 99999);

In the routeLoad method the new code will look like this:

/**
     * Method that tries to load cached routes and speed up the matching
     * @param MvcEvent $event
     */
    public function routeLoad(MvcEvent $event)
    {
        $request = $event->getRequest(); 
        if(!(
            $request->getMethod() == \Zend\Http\Request::METHOD_GET && 
            $request->getQuery()->count() == 0
            )) {
            // We do not cache route match for requests that can produce
            // different match.    
            return ;
        }
 
        $event->setParam('route-cacheable', true);
 
        // @todo: check if we have data in our cache
        $cachedData = false; // @todo: get the actual value
 
        if(!empty($cachedData)) {
            $event->setParam('route-cached', true);
 
            $cachedRoute = \Zend\Mvc\Router\Http\Literal::factory($cachedData);
            $router = $event->getRouter();
            $router->addRoute($cachedData['name'], $cachedRoute, 99999);
        }
    }

Saving the actual route match in the cache should be done in our routeSave method. We can use the var-cache service that you know from the “Learn ZF2″ book code. The final version of our routeSave method will look like this:

/**
     * Method that tries to save a route match into a cache system
     * @param MvcEvent $event
     */
    public function routeSave(MvcEvent $event)
    {
        $match = $event->getRouteMatch();
        if(!$match) {
            return;
        }
 
        if($event->getParam('route-cached') || !$event->getParam('route-cacheable')) {
            return;
        }
 
        $path = $event->getRequest()
                ->getUri()
                ->getPath();
 
        // save the route match into the cache.
        $cache = $event->getApplication()->getServiceManager()->get('var-cache');
        $cacheKey = $this->getCacheKey($path);
        $data = array (
          'name' => $event->getRouteMatch()->getMatchedRouteName(),
          'route' => $path,
          'defaults' => $event->getRouteMatch()->getParams(),
        );
        $cache->setItem($cacheKey, $data);
    }

And the final version of the routeLoad method will look like this:

  /**
     * Method that tries to load cached routes and speed up the matching
     * @param MvcEvent $event
     */
    public function routeLoad(MvcEvent $event)
    {
        $request = $event->getRequest(); 
        if(!(
            $request->getMethod() == \Zend\Http\Request::METHOD_GET && 
            $request->getQuery()->count() == 0
            )) {
            // We do not cache route match for requests that can produce
            // different match.    
            return ;
        }
 
        $event->setParam('route-cacheable', true);
 
        // check if we have data in our cache
        $path = $event->getRequest()
                      ->getUri()
                      ->getPath();
 
        $cache = $event->getApplication()->getServiceManager()->get('var-cache');
        $cacheKey = $this->getCacheKey($path);
        $cachedData = $cache->getItem($cacheKey); 
 
        if(!empty($cachedData)) {
            $event->setParam('route-cached', true);
 
            $cachedRoute = \Zend\Mvc\Router\Http\Literal::factory($cachedData);
            $router = $event->getRouter();
            $router->addRoute($cachedData['name'], $cachedRoute, 99999);
        }
    }

Update: 2nd of April, 2015: We have create a composer package that you can use directly in your ZF2 application and take speed up your route matching. Take a look the github page of the project  for information how to install and setup the plugin.

Happy ZF2 coding and good luck with improving the performance of your ZF2 application!

Plans for Zend Framework 3

Matthew Weier O’Phinney, the Zend Framework (ZF) Project Leader, announced the plans for the upcoming Zend Framework 3. Some of the things that are in the pipeline for Zend Framework 3 are:

  • Separating components into individual, versioned projects. This enables broader re-use and higher velocity of innovation.
  • Strong emphasis on HTTP messages, with Matthew leading the PSR-7 specification.
  • Updating our existing full-stack MVC framework to depend on the newly independent components for better reuse and simplicity. ZF2 MVC applications will have a documented upgrade path to ZF3 requiring minimal changes.
  • Embracing middleware runtime patterns as a lighter weight alternative to the enterprise MVC framework stack.
  • Enabling Apigility to work as a middleware stack, for better performance and simplicity, with the same streamlined, powerful user experience.
  • Optimizing for PHP 7, but supporting PHP 5.5 onwards.

In addition Andi Gutmans said: “The community has been looking for clear direction and we are giving it. “.

Stay tuned for more details and examples on the future Zend Framework 3.

Learn ZF2 is recommended from the Zend Framework Gods

If you haven’t seen these tweets take a look and see who is recommending the book:

Matthew Weier O’Phinney (ZF Project Lead – something like ZF2 God)

Evan Coury (ZF core dev team – sort of ZF2 semi-god)

Michelangelo van Dam (One of Europes’ most popular PHP evangelists)

Updated version of “Learn ZF2″.

I am happy to announce that “Learn ZF2″ has an updated version. The Kindle version is updated and published, soon to be followed by the paperback version (Amazon needs 3-7 days to update it in all stores), PDF and ePub versions.

This new version wouldn’t have been possible if it was not for the help, hints, recommendations and lines of code that our great contributors sent us. I would like to thank all of them: (in no particular order) Matt Comeione, Dmitry Oxman, Tim van Steenbergen, smozgur, Daniel Ventura, Matt X. Thanks a lot guys. You rock!

Zend Framework 2 is running on Intel Edison !

Intel Edison After some hours of compiling and tuning we are happy to announce that Zend Framework 2 and the Training Center application coming with the “Learn ZF2″ book is running on an Intel Edison, which is an embedded computer the size of a postage stamp. Below are some screenshots to prove this.

Now nothing can stop us from wearing our Zend Framework 2 application or carrying it everywhere with us :)