2015-05-02

Check permissions in ZF2 Zend/Navigation with ZfcRbac

ZfcRbac is not tailored to work with Zend/Navigation. There aren’t any builded native solutions that gives ZfcRbac permission to enable or disable entry of menu. The solution for that problem is quite simple.
I found solution in this blog: http://blog.webdevilopers.net/check-zend-navigation-page-permissions-with-zfcrbac/. I based on it. But this solution caused a problem. Positions in menu are displayed only if perrmision is defined. If permission is not defined, menu’s posiotin is displayed.
I’ll show you how to implement checking perrmisions and enabled / disabled menu positions with ZfcRbac. There is only one change in RbacListener:


class RbacListener
{
  protected $authorizationService;


  public function __construct(AuthorizationServiceInterface $authorizationService)
  {
      $this->authorizationService = $authorizationService;
  }


  public function accept(EventInterface $event)
  {
      $page = $event->getParam('page');


      if (! $page instanceof AbstractPage) {
          return;
      }


      $permission = $page->getPermission();


      if (is_null($permission)) {
          $event->stopPropagation();
          return false;
      }


      $event->stopPropagation();


      return $this->authorizationService->isGranted($permission);
  }
}

2015-05-01

Do not mess your code!

PHP is great language that gives us flexibility. PHP has weak typing. On the other side, many programmers abuse it. They create little monsters and spaghetti code.


In this post I will show you, on simple example, how you should prevent yourself from ambiguous return value.


Base principle: You should always return the same type.


My favorite example:


/**
* @return mixed
*/
public function getProductList()
{
   $resultArray = $this->products->get();


   return empty($resultArray) ? $resultArray : null;
}


What’s wrong with it? Return value is ambiguous. In next step in your code, after calling this method you must check what method returned to you.

if ($return === null) {
   //do something...
} else {
   //do something...
}


In this example you have two statements to check. More clear is this code:


/**
* @return array
*/
public function getProductList()
{
   $resultArray = $this->products->get();


   return $resultArray;
}

In above example, you always gets the same type of result. On the other hand, you may use designed pattern, but this is the other solution, not described in this post.

2015-02-15

Session expire under Zend Framework 2

Few days ago I met a problem with session expire under Zend Framework 2. I set gc_maxlifetime in SessionManager but my session didn’t expire. Ok, you’ll say that I can set session.gc_maxlifetime in php.ini. Yes, I can and it’s one of the solutions. But in my solution, I want to manage session lifetime in application configuration. I looked deeper in ZF2 session implementation to find the problem and solutions. This is what I found.

My configuration looks like this:
<?php
//…
'session' => [
        'config' => [
            'class' => 'Zend\Session\Config\SessionConfig',
            'options' => [
                'name' => 'session_name',
                'use_cookies' => true,
                'gc_maxlifetime' => 1800
            ]
        ],
        'storage' => 'Zend\Session\Storage\SessionArrayStorage'
    ],
//…

And my SessionManager factory:

<?php
//…
class SessionManagerFactory implements FactoryInterface
{

    public function createService(ServiceLocatorInterface $serviceLocator)
    {
        $config = $serviceLocator->get('config');

        if (isset($config['session'])) {
            $session = $config['session'];

            $sessionConfig = null;
            if (isset($session['config'])) {
                $class = isset($session['config']['class']) ? $session['config']['class'] : 'Zend\Session\Config\SessionConfig';
                $options = isset($session['config']['options']) ? $session['config']['options'] : array();
                $sessionConfig = new $class();
                $sessionConfig->setOptions($options);
            }

            $sessionStorage = null;
            if (isset($session['storage'])) {
                $class = $session['storage'];
                $sessionStorage = new $class();
            }

            $sessionSaveHandler = null;
            if (isset($session['save_handler'])) {
                will require constructor arguments
                $sessionSaveHandler = $serviceLocator->get($session['save_handler']);
            }

            $sessionManager = new SessionManager($sessionConfig, $sessionStorage, $sessionSaveHandler);
        } else {
            $sessionManager = new SessionManager();
        }

        return $sessionManager;
    }
}

Ok, next step, I created session Container:

$sessionManager = $serviceLocator->get('my_project\SessionManager');        $container = new Container(’container_name’, sessionManager);

And what happened after 30 minutes? Nothing. My session still existed.

Let’s go deeper.

After first request Container will be created. If we print $_SESSION we will see something like this:

__ZF =>
[’container_name’] =>
['EXPIRE'] => 1423757542


After 30 minutes $_SESSION looks like this:
__ZF =>
[’container_name’] => null


And finally, my implementation to check if session has expired looks like this:

//...
abstract class ContainerFactoryAbstract implements FactoryInterface
{

    private $containerName;

    private $config;

    private $sessionManager;

    public function createService(ServiceLocatorInterface $serviceLocator)
    {
        if (is_null($this->containerName)) {
            throw new RuntimeException('Is not set name of container!');
        }

        $this->config = $serviceLocator->get('Config');
        $this->sessionManager = $serviceLocator->get('SysSession\SessionManager');

        $container = $this->getContainer();

        if ($this->isSessionExpired($this->containerName)) {
            $this->sessionManager->destroy();
        }

        if ($this->getSessionMaxLifeTime()) {
            $container->setExpirationSeconds($this->getSessionMaxLifeTime());
        }

        return $container;
    }

    public function setContainerName($name)
    {
        $this->containerName = $name;
    }

    private function getContainer()
    {
        return new Container($this->containerName, $this->sessionManager);
    }

    private function isSetInitialSecureData(Container $container)
    {
        return isset($container->init);
    }

    private function isSessionExpired($containerName)
    {
        if (! $this->isSetSessionMaxLifeTime($this->config)) {
            return false;
        }

        return (isset($_SESSION['__ZF'][$containerName]) && (! $this->isSetExpireTimestamp($containerName)));
    }

    private function isSetSessionMaxLifeTime()
    {
        return isset($this->config['session']['config']['options']['gc_maxlifetime']);
    }

    private function getSessionMaxLifeTime()
    {
        if (! $this->isSetSessionMaxLifeTime()) {
            throw new RuntimeException('Is not set value for gc_maxlifetime session!');
        }

        return $this->config['session']['config']['options']['gc_maxlifetime'];
    }

    private function isSetExpireTimestamp($containerName)
    {
        return isset($_SESSION['__ZF'][$containerName]['EXPIRE']);
    }
}