Welcome to Abdul Malik Ikhsan's Blog

Zend Framework 2 : Getting Closer with EventManager

Posted in Tutorial PHP, Zend Framework 2 by samsonasik on March 30, 2013

zf2-zendframework2I have posted a dozen of posts that utilize EventManager, but i think, it’s time to post detail about EventManager itself. Zend Framework 2 has component named EventManager. An EventManager is an object that aggregates listeners for one or more named events, and which triggers events, which a Listener is a callback ( closure, static method, typical array callback ) that can react to an event, and An Event is an action that is triggered by an EventManager.
This is a simple example :

use Zend\EventManager\EventManager;

$events = new EventManager;
$events->attach('do', function($e) {
    $event  = $e->getName();
    $params = $e->getParams();
    
    printf(
        'Handled event "%s" with parameter "%s"',
        $event,
        json_encode($params)
    );
});

$params = array('foo' => 'bar','baz' => 'bat');
$events->trigger('do', null, $params); //event, target, parameter
//print : Handled event "do" with parameter "{"foo":"bar","baz":"bat"}"

Features

  1. Wildcard Attachment
  2. Shared Manager
  3. Short-Circuiting
  4. Listener response Aggregation

Ok, let’s learn one by one.
1.Wildcard Attachment
We can attach to many events at once.
for example :

$events = new EventManager();
$events->attach(array('these', 'are', 'event', 'names'), $callback);

or using ‘*’ to make $callback available in all events.

$events = new EventManager();
$events->attach('*', $callback);

2.Shared Manager
This is to allow attaching to events when you don’t have access to the object. You should implements Zend\EventManager\EventManagerAwareInterface to make it run.
For example, we create a class like the following :

use Zend\EventManager\EventManager;
use Zend\EventManager\EventManagerAwareInterface;
use Zend\EventManager\EventManagerInterface;

class Foo implements EventManagerAwareInterface 
{
    protected $events;
    
    public function setEventManager(EventManagerInterface $events) 
    {
        $this->events = $events;
        return $this;
    }
    
    public function getEventManager()
    {
        if (!$this->events) { 
             $this->setEventManager(new EventManager(__CLASS__)); 
        }
        return $this->events;
    }
    
    public function bar($baz, $bat = null) {
        $params = compact('baz', 'bat');
        $this->getEventManager()->trigger(__FUNCTION__, $this, $params);
    }
}

and you can attach to it by :

use Zend\EventManager\SharedEventManager;

$sharedEvent = new SharedEventManager;
$sharedEvent->attach('Foo', 'bar', function($e) {
    $event  = $e->getName();
    $target = get_class($e->getTarget());
    $params = json_encode($e->getParams());

    printf(
        '%s called on %s, using params %s',
        $event,
        $target,
        $params
    );
});

$foo = new Foo();
$foo->getEventManager()->setSharedManager($sharedEvent);
$foo->bar('bazvalue', 'batvalue');
//print : bar called on Foo, using params {"baz":"bazvalue","bat":"batvalue"} 

We can call other class too, let’s assume we have two class that had virtually no knowledge of each other.
Let’s create class 1 named Class1 :

use Zend\EventManager\EventManager;
use Zend\EventManager\EventManagerAwareInterface;
use Zend\EventManager\EventManagerInterface;

class Class1 implements EventManagerAwareInterface {
    
    protected $events;
    
    public function setEventManager(EventManagerInterface $events) {
        $this->events = $events;
        return $this;
    }
    
    public function getEventManager() {
        if (!$this->events) { $this->setEventManager(new EventManager(__CLASS__)); }
        return $this->events;
    }
    
    public function run() {
        $this->getEventManager()->trigger('cls',$this);
    }
}

and class 2 named Class2 :

use Zend\EventManager\Event;
                    
class Class2 {
    public function listen(Event $e) {
        echo get_class($this) . ' has been called by ' . get_class($e->getTarget());
    }
}

Check it :

use Zend\EventManager\StaticEventManager;
                     
StaticEventManager::getInstance()->attach('Class1', 'cls', array(new Class2, 'listen'));
$cls = new Class1();
$cls->run();
 //print : Class2 has been called by Class1 

3.Short-Circuiting
This feature utilize if a particular result is obtained, or if a listener determines that something is wrong, or that it can return something quicker than the target.
for example, we have a class FooShortCircuit :

use Zend\EventManager\EventManager;
use Zend\EventManager\EventManagerAwareInterface;
use Zend\EventManager\EventManagerInterface;

class FooShortCircuit implements EventManagerAwareInterface
{
    protected $events;
    
    public function setEventManager(EventManagerInterface $events) {
        $this->events = $events;
        return $this;
    }
    
    public function getEventManager() {
        if (!$this->events) { $this->setEventManager(new EventManager(__CLASS__)); }
        return $this->events;
    }
    
    public function execute($obj)
    {
        $argv = array();
        $results = $this->getEventManager()->triggerUntil(__FUNCTION__, $this, $argv,
                function($v) use ($obj) {
            return ($obj instanceof Foo); 
        });
         
        // if $obj instanceof foo, so stopped 
        if ($results->stopped()) {
            return $results->last();
        }
        // continue...
        echo 'hei, i\'m continue :p';
    }
}

if $obj in execute function instance of Foo class, it will stopped.
Let’s create a default execution :

$sharedEvent = new SharedEventManager;
$sharedEvent->attach('FooShortCircuit', 'execute', function($e) {
    echo 'standard execution...';
});

if we pass new Foo() to execute() function, it should execute ‘standard execution…’ only, and stopped. Let’s check it :

$fooShortCircuit = new FooShortCircuit;
$fooShortCircuit->getEventManager()->setSharedManager($sharedEvent);
$fooShortCircuit->execute(new Foo()) ;
//print : standard execution...

4.Listener response Aggregation
Single class can listen to multiple events.
For example, we have a class :

use Zend\EventManager\EventManager;
use Zend\EventManager\EventManagerAwareInterface;
use Zend\EventManager\EventManagerInterface;

class Baz  implements EventManagerAwareInterface
{
     protected $events;
    
    public function setEventManager(EventManagerInterface $events) {
        $this->events = $events;
        return $this;
    }
    
    public function getEventManager() {
        if (!$this->events) { $this->setEventManager(new EventManager(__CLASS__)); }
        return $this->events;
    }
   
    public function get($id)
    {
        $params = compact('id');
        $results = $this->getEventManager()->trigger('Bar.pre', $this, $params);

        // If an event stopped propagation, return the value
        if ($results->stopped()) {
            return $results->last();
        }

        // do some work...

        $this->getEventManager()->trigger('Bar.post', $this, $params);
    }
}

and make a class that listen to multiple events.

use Zend\EventManager\ListenerAggregateInterface;
use Zend\EventManager\EventInterface;

class Bar implements ListenerAggregateInterface
{
    protected $listeners = array();
    
    public function attach(EventManagerInterface $e)
    {  
        $this->listeners[] = $e->attach('Bar.pre', array($this, 'load'));
        $this->listeners[] = $e->attach('Bar.post', array($this, 'save'));
    }
    
    public function detach(EventManagerInterface $e) 
    {
        foreach ($this->listeners as $index => $listener) {
            if ($e->detach($listener)) {
                unset($this->listeners[$index]);
            }
        }
    }
    
    public function load(EventInterface $e) { echo 'load...'; }
    public function save(EventInterface $e) { echo 'save...'; }
}

Let’s aggregate it :

$baz = new Baz;
$barListeners = new Bar;
$baz->getEventManager()->attachAggregate($barListeners);

$baz->get(1);

EventManager in ZF2 MVC Architecture
So, how EventManager works in ZF2 MVC Architecture ? Everything is an event !

  • A bootstrap is an Event
  • A Routing is an Event
  • A Dispatch is an Event
  • A Rendering View is an Event
  • etc

To make event triggered early, place it to init() function in Module.php

class Module
{ 
    public function init(ModuleManager $m)
    {
        $event = $m->getEventManager()->getSharedManager();
        $event->attach('Zend\Mvc\Application', 'bootstrap', function($e) { 
            echo 'executed on bootstrap app process <br />'; 
        });
    }
}

And if init() is too early, ModuleManager automatically registers the onBootstrap method if found in the module.

class Module
{
    public function onBootstrap(MvcEvent $e)
    {
        $event = $e->getApplication()->getEventManager();
       
        $event->attach('route', function($e) {
            echo 'executed on route process'; 
        });

         $event->attach('dispatch', function($e) {
            echo 'executed on dispatch process'; 
         });

        $event->attach('dispatch.error', function($e) {
            echo $e->getParam('exception');
            echo 'executed only if found an error <br />,
            for ex : sm not found <br />'; 
        });

        $event->attach('render', function($e) {
            $e->getViewModel()->setVariable('test', 'test');
            echo 'executed on render process <br />';
        });

         $event->attach('render.error', function($e) {
            echo 'executed on render  error found'; 
         });

         $event->attach('finish', function($e) {
            echo 'executed on finish process'; 
         });
    }
}

A sample Use Case
We want to check if logged in, setting up the userid variable of controllers.

class Module
{
    public function onBootstrap(MvcEvent $e)
    {
        $event->getSharedManager()
          ->attach('Zend\Mvc\Controller\AbstractActionController',
                    'dispatch', 
         function($e) {
              $controller = $e->getTarget();
    
             //check if logged in, setting up the userid variable of controllers
            if ($e->getApplication()->getServiceManager()->get('AuthService')
                                            ->hasIdentity()) {
                   $users = $e->getApplication()->getServiceManager()
                            ->get('SanAuth\Model\AuthStorage')->read();

                   $controller->userid = $users['id'];
             }
        }, 100);
    }
}

So Full ? make it separate…

class Module
{
    public function onBootstrap(MvcEvent $e)
    {
        $event = $e->getApplication()->getEventManager();
        $event->getSharedManager()
          ->attach('Zend\Mvc\Controller\AbstractActionController',
                    'dispatch',
                array($this, 'settingUpControllerVariables'), 100
        );
    }

    public function settingUpControllerVariables(MvcEvent $e)
    {
        $controller = $e->getTarget();
    
        //check if logged in, setting up the userid variable of controllers
        if ($e->getApplication()->getServiceManager()
                            ->get('AuthService')
                            ->hasIdentity()) {
            $users = $e->getApplication()->getServiceManager()
                            ->get('SanAuth\Model\AuthStorage')->read();

            $controller->userid = $users['id'];
        }
    }
}

Hm…, i think it’s enough 😀

References :

Advertisements

Zend Framework 2 : Cache Module Config and Module Classmap

Posted in Tutorial PHP, Zend Framework 2 by samsonasik on March 13, 2013

zf2-zendframework2We already know, that all module configs and class maps over modules are merged. But merging in every request is not a good idea because it will pay the time. If our application is run on production environment, our ZF2 application should use cache to cache config and the classmap too (Whether or not you create a class map for your modules classes, class map cache will be created automatically.). So, it can reduce time access, and of course, reduce memory to process it in the next time refresh.
I will show you how to use it.
1. Create a cache directory like the following
Screen Shot 2013-03-13 at 2.50.25 AM
2. Make the cache directory writable

sudo chmod -R 777 /path/to/zf21_learn/data/cache/modulecache

3. Configure config/application.config.php

return array(
    'modules' => array(
        'Application',
        'ZFTool',
        'Album'
        'ZendDeveloperTools'
    ),
    'module_listener_options' => array(
        'module_paths' => array(
            './module',
            './vendor'
            ),
        'config_glob_paths' => array('config/autoload/{,*.}{global,local}.php'),
        
        // Whether or not to enable a configuration cache.
        // If enabled, the merged configuration will be cached and used in
        // subsequent requests.
        'config_cache_enabled' => true,
        // The key used to create the configuration cache file name.
        'config_cache_key' => "2245023265ae4cf87d02c8b6ba991139",

        // Whether or not to enable a module class map cache.
        // If enabled, creates a module class map cache which will be used
        // by in future requests, to reduce the autoloading process.
        'module_map_cache_enabled' => true,
        // The key used to create the class map cache file name.
        'module_map_cache_key' => "496fe9daf9baed5ab03314f04518b928",
        
        
        // The path in which to cache merged configuration.
        'cache_dir' => "./data/cache/modulecache",
    )
);

4. Test run your ZF2 App

Once you’ve run, your cache files will be created like the following :
Screen Shot 2013-03-13 at 2.57.51 AM

References :
1. https://github.com/zendframework/ZendSkeletonApplication/blob/master/config/application.config.php

Zend Framework 2 : Working with SQL Server

Posted in Tutorial PHP, Zend Framework 2 by samsonasik on March 8, 2013

zf2-zendframework2 In some situation, work on windows environment is not as easy as you’ve imagine. SQL Server is a powerfull database platform with complex features and it means complex requirements too. In this post, I will show you step by step to work with.

1. Install Your SQL Server
2. Download SqlSrv extension for PHP, you can download from http://www.microsoft.com/en-us/download/details.aspx?id=20098 , I’m prefer using *V30.EXE
3. Run the executable file, and you will find an input which show message “Please type the location where you want to place the extacted files. : “, fill in with your php ext directory.
4. Download Microsoft® SQL Server® 2012 Native Client here : http://www.microsoft.com/en-us/download/details.aspx?id=29065 and install the lib. ( choose between X64 Package (sqlncli.msi) or X86 Package (sqlncli.msi) ).
5. Register SqlSrv extension into php extension in php.ini

// or 54 based on your php version
extension=php_sqlsrv_53_ts.dll

6. Restart your web server
7. Configure your db adapter

'db' => array(
    'driver' => 'Sqlsrv',
    'server' => 'ACER-PC',
    'Database' => 'YourDB',
    'USER' => 'sa', //if you're using sa
    'password' => 'yourpassword'
);

Done !, let’s run.

Note : It’s work for SQL Server 2008.

Symfony 2 : Using Symfony2 Bundle in Zend Framework 2 Project

Posted in Symfony2 Framework, Tutorial PHP, Zend Framework 2 by samsonasik on March 2, 2013

symfony2-distributionsSymfony2 comes with Bundle, Zend Framework 2 comes with Module, The reusability code is coming. We can use a piece of them in other framework if we want. I think, besides of write about using ZF2 Module in SF2 project, it worth to write about it ( SF2 Bundle in ZF2 Project ).

Ok, let’s start, for example, I have an SF2 Bundle like the following :
SF2BUNDLE
Which has classes like the following :
a. ExampleDependentInterface

namespace San\MySFBundle\Classes;

interface ExampleDependentInterface
{
    public function run($parameter);
}

b. ExampleDependent class that implement ExampleDependentInterface

namespace San\MySFBundle\Classes;

class ExampleDependent implements ExampleDependentInterface
{
    public function run($environment)
    {
        echo 'ExampleDependent run in '.$environment;
    }
}

c. ExampleClass class that pass class implemented ExampleDependentInterface

namespace San\MySFBundle\Classes;

class ExampleClass
{
    protected $dependent;

    public function __construct(ExampleDependentInterface $dependent)
    {
        $this->dependent = $dependent;
    }

    public function run($env = 'Symfony2')
    {
       $this->dependent->run($env);
    }
}

Now, register ExampleClass in services.php ( resources/config/services.php)

use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;

$container->setDefinition(
    'my_sample_service', new Definition('San\MySFBundle\Classes\ExampleDependent')
);

$container->setDefinition(
    'san_my_sf.example',
    new Definition(
        'San\MySFBundle\Classes\ExampleClass',
        array(
            new Reference('my_sample_service')
        )
    )
);

Bundle ‘definitions’ done!, let’s copy to ZF2 Project like the following :
ZF2withSF2Bundle

When Bundle copied, let’s configure Our ZF2 App.
1. create file named SF2AppKernel.php in ZF2 App ( config/SF2AppKernel.php )

use Symfony\Component\HttpKernel\Kernel;
use Symfony\Component\Config\Loader\LoaderInterface;

class SF2AppKernel extends Kernel
{
    public function registerBundles()
    {
        $bundles = array(
            new San\MySFBundle\SanMySFBundle(),
        );

        return $bundles;
    }

    public function registerContainerConfiguration(LoaderInterface $loader){}
}

2. add require symfony/symfony to your composer.json

........
    "require": {
        "php": ">=5.3.3",
        "zendframework/zendframework": "2.*",
        "zendframework/zftool": "dev-master",
        "symfony/symfony": "2.1.*"
    }
........

4. Type

composer update

3. modify init_autoloader.php

if (file_exists('vendor/autoload.php')) {
    $loader = include 'vendor/autoload.php';
    $loader->add('San\\', __DIR__.'/module/SF2Bundles');
}
..........

4. Modify public/index.php to initialize SF2 Bundles and SF2 Container, and register the Container into ZF2 ServiceManager

use Zend\ServiceManager\ServiceManager;
use Zend\Mvc\Service;

define('REQUEST_MICROTIME', microtime(true));
/**
 * This makes our life easier when dealing with paths. Everything is relative
 * to the application root now.
 */
chdir(dirname(__DIR__));

// Setup autoloading
require 'init_autoloader.php';

//load SF2 App Kernel here...
require 'config/SF2AppKernel.php';
$kernel = new SF2AppKernel('prod', true);
$kernel->loadClassCache();

//initialize Bundles and Container
$kernel->boot();

$SF2Container = $kernel->getContainer();

/* extract
    Zend\Mvc\Application::init(require 'config/application.config.php')->run();
    to the following to register SF2Container into ZF2 ServiceManager
*/
$configuration = require 'config/application.config.php';
$smConfig = isset($configuration['service_manager']) ? $configuration['service_manager'] : array();
$serviceManager = new ServiceManager(new Service\ServiceManagerConfig($smConfig));
$serviceManager->setService('ApplicationConfig', $configuration);
$serviceManager->get('ModuleManager')->loadModules();

$serviceManager->setService('SF2Container', $SF2Container);

//run...
$serviceManager->get('Application')->bootstrap()->run();

5. Ok, configuration done!, let’s call the SF2 Bundle Service via Controller in Zend Framework 2 App :

$sl = $this->getServiceLocator()
$sl->get('SF2Container')->get('san_my_sf.example')
                        ->run('ZF2');

References :
1. https://speakerdeck.com/skoop/zend-framework-2-and-symfony2-the-perfect-team-zendcon

Zend Framework 2 : Using Zend Framework 2 Module in Symfony 2 Project

Posted in Symfony2 Framework, Tutorial PHP, Zend Framework 2 by samsonasik on March 1, 2013

zf2-zendframework2In ZF2, Module completely become re-usable code, It can be called even without Zend Framework MVC stack. I will give you extreme example, I will call my ZF2 module that contains service called by Symfony 2 Framework. It’s very rare, but sometime, you don’t need to Reinvent Your created module, even it created in other environment.

For example, I have a ZF2 Module like the following :

zfmodule
Which has Service like the following :
a. Service Class ( ZFModule\Service\MySampleService.php )

namespace ZFModule\Service;

class MySampleService
{
    protected $config;
    
    public function getConfig()
    {
        return $this->config;
    }
    
    public function setConfig(array $config)
    {
        $this->config = $config;
    }
}

b. The Service Factory ( ZFModule\Service\MySampleServiceFactory )

namespace ZFModule\Service;

use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;

class MySampleServiceFactory implements FactoryInterface
{
    public function createService(ServiceLocatorInterface $serviceLocator)
    {
        $config = $serviceLocator->get('Config');
        
        $service  = new MySampleService;
        $service->setConfig($config);

        return $service;
    }
}

c. Register MySampleService into ZF2 Service Manager

namespace ZFModule;

class Module
{
    public function getConfig(){/*common code here */}
    public function getAutoloaderConfig(){/*common code here*/}

    public function getServiceConfig()
    {
        return array(
            'factories' => array(
                'MySampleService' => 'ZFModule\Service\MySampleServiceFactory'  
            ),
        );
    }
}

Ok, let’s go step by step :
1. Copy the module to Your Symfony Project.
symfonyprjzfmodule
2. add require zendframework/zendframework to your composer.json

.....
require {
"php": ">=5.3.3",
"symfony/symfony": "2.1.*",
"zendframework/zendframework" : "2.1.3"
}
.....

3. Type :

composer update

4. Create a class to get Service From Your ZF2Module (SymfonyPrj/src/Acme\DemoBundle\Service\MyZFModuleService.php )

namespace Acme\DemoBundle\Service;

use Zend\ServiceManager\ServiceManager;
use Zend\Mvc\Service\ServiceManagerConfig;

class MyZFModuleService 
{
    protected $sampleZFService;
    
    public function __construct()
    {
        $serviceManager = new ServiceManager(new ServiceManagerConfig(array()));
        $serviceManager->setService('ApplicationConfig', array(
               'modules' => array(
                   'ZFModule'
               ),
               'module_listener_options' => array()
        ));
        $serviceManager->get('ModuleManager')->loadModules();
        
        //MySampleService is a Service from Your ZF2 Module 
        $this->sampleZFService = $serviceManager->get('MySampleService');
    }
    
    public function dump()
    {
        echo '<pre>';
        print_r($this->sampleZFService->getConfig());
        echo '</pre>';
    }
}

5. Register to Symfony Services :

...
       <service id="MyZFModuleService" class="Acme\DemoBundle\Service\MyZFModuleService">
       </service>
...

6. Ok, check it in your Symfony2 Controller Action :

$serviceZF = $this->get('MyZFModuleService');
$serviceZF->dump();

References :
1. http://zend-framework-community.634137.n4.nabble.com/Doctrine-2-module-is-tight-to-MVC-td4659346.html