written by Daniel Pötzinger CTO
Sessions with Extbase
Sessions with Extbase
21.11.2011 | Categories: Web Development , Technologies & Open Source
About author Daniel Pötzinger Daniel Pötzinger CTO

Sessions are used to save informations that are bound to the current website user. In most cases this informations can be transient - meaning they are only relevant for the time where the user visits the website. 

A typical example is something like a Basket or informations about the current Marketingcampaign for the visitor.

Therefore in most cases the Session is a relevant concept for your Domain. Imagine a simple BasketController, you may want to use something like this:

 

class BasketController {   
   ...
   public function addItemAction(Product $product) {
      $basket = $this->userSession->getBasket();
      $basket->addItemByProduct($product);
      $this->userSession->saveBasket($basket);
   }
 }

If you have a look at the attached UML scetch: That means there is a Session Class in the Domain Layer of your application, that should be of scope singleton and defines a well defined Interface for your Domain. (In this example to get the basket Instance, that acts as Aggregate Root for the Items in the basket).

For the actual storage of the Objects in the Session the Persitence Implementation is part of your applications system Layer. Here you can find a sample SessionStorage Implementation that works for TYPO3 4.x:

 

class Tx_Extkey_System_Session_SessionStorage implements t3lib_Singleton {
        
         const SESSIONNAMESPACE = 'tx_extkey';
        
         /**
          * Returns the object stored in the user´s session
          * @param string $key
          * @return Object the stored object
          */
         public function get($key) {
                 $sessionData = $this->getFrontendUser()->getKey('ses', self::SESSIONNAMESPACE.$key);
                 if ($sessionData == '') {
                          throw new LogicException('No value for key found in session '.$key);
                 }
                 return $sessionData;
         }
        
         /**
          * checks if object is stored in the user´s session
          * @param string $key
          * @return boolean
          */
         public function has($key) {
                 $sessionData = $this->getFrontendUser()->getKey('ses', self::SESSIONNAMESPACE.$key);
                 if ($sessionData == '') {
                          return false;
                 }
                 return true;
         }
 
         /**
          * Writes something to storage
          * @param string $key
          * @param string $value
          * @return       void
          */
         public function set($key,$value) {
                 $this->getFrontendUser()->setKey('ses', self::SESSIONNAMESPACE.$key, $value);
                 $this->getFrontendUser()->storeSessionData();
         }
        
         /**
          * Writes a object to the session if the key is empty it used the classname
          * @param object $object
          * @param string $key
          * @return       void
          */
         public function storeObject($object,$key=null) {
                 if (is_null($key)) {
                          $key = get_class($object);
                 }
                 return $this->set($key,serialize($object));        
         }
        
         /**
          * Writes something to storage
          * @param string $key
          * @return       object
          */
         public function getObject($key) {
                 return unserialize($this->get($key));              
         }
 
         /**
          * Cleans up the session: removes the stored object from the PHP session
          * @param string $key
          * @return       void
          */
         public function clean($key) {
                 $this->getFrontendUser()->setKey('ses', self::SESSIONNAMESPACE.$key, NULL);
                 $this->getFrontendUser()->storeSessionData();
         }
        
         /**
          * Gets a frontend user which is taken from the global registry or as fallback from TSFE->fe_user.
          *
          * @return       ux_tslib_feUserAuth       The current extended frontend user object
          * @throws       LogicException
          */
         protected function getFrontendUser() {
                 if ($GLOBALS ['TSFE']->fe_user) {
                          return $GLOBALS ['TSFE']->fe_user;
                 }
                 throw new LogicException ( 'No Frontentuser found in session!' );
         }
}

And finally this is the sample code for the UserSession Class:

 

class Tx_Extkey_Domain_UserSession implements t3lib_Singleton {
         /**
          * @var Tx_Extkey_System_Session_SessionStorage
          */
         private $sessionStorage;
         
...      
         public function __construct(Tx_Extkey_System_Session_SessionStorage $sessionStorage) {
                 $this->sessionStorage = $sessionStorage;
         }
         
         public function getBasket() {
                 if ($this->sessionStorage->has('Basket')) {
                          return $this->sessionStorage->getObject('Basket');
                 }
                 else {
                          return $this->objectManager->create('Basket');
                 }
         }
         
         public function saveBasket(Basket $basket) {
                 $this->sessionStorage->storeObject($basket);
         }
                 
}

Serialisation drawbacks..

The objects are stored in the User Session as serialized string. This can cause several problems:

  • Not all associated framework objects are serializable. For example you will have problems serializing domain entities (too big, lost connection to persitence manager...)
  • Cyclic relations will cause problems
  • The serialized version can simple be too big

Thats where you need to think what data should be stored in your serialized objects. PHP5 gives you the possibility to clean up with your data in the __sleep method and to reconstitute them in the __wakeup method. This is where you:

  • can store ids instead of entitys and try to get them with a repository on wakeup
  • throw injected singletons away and try to get them back on __wakeup

Dependency Injection

There are two ways of injecting objects after deserialization:

  • in the __wakeup method using the Extbase ObjectManager (t3lib_div::makeInstance('Tx_Extbase_Object_ObjectManager')
  • explicit In the UserSession Class like:
         public function getBasket() {
                 if ($this->sessionStorage->has('Basket')) {
                          $basket =  $this->sessionStorage->getObject('Basket'); 
                        $basket->injectSomeThing($this->objectManager->get('Something'));
                 }
                 else {
                          return $this->objectManager->create('Basket');
                 }
         }

In FLOW3

Well in FLOW3 you don't need to deal with a "SessionStorage" Implementation and think about __sleep and __wakeup... this is all handled by the Framework and the @scope session  annotation.

See Roberts Post about this:

robertlemke.de/blog/posts/2010/08/19/session-handling-and-object-serialization

Full article