2016-07-13 95 views
0

Zend Session Manager from the tutorial開始會話時,它會生成會話密鑰並將大量數據發佈到會話中。但我有一個會議系統已經設置了我自己的會話密鑰和一組不同的會話數據。我如何改變Zend配置來使用我的代替?如何將我的會話數據庫用於Zend的會話管理器?

以供參考,在這裏是Zend會議:

array (size=2) 
    '__ZF' => 
    array (size=2) 
     '_REQUEST_ACCESS_TIME' => float 1468447555.1396 
     '_VALID' => 
     array (size=3) 
      'Zend\Session\Validator\Id' => string 'xxxxxxxxxxxxxxxxxxxxxxxxxx' (length=26) 
      'Zend\Session\Validator\RemoteAddr' => string '--ip addr--' (length=13) 
      'Zend\Session\Validator\HttpUserAgent' => string '--user agent info--' (length=114) 
    'initialized' => 
    object(Zend\Stdlib\ArrayObject)[371] 
     protected 'storage' => 
     array (size=3) 
      'init' => int 1 
      'remoteAddr' => string '--ip addr--' (length=13) 
      'httpUserAgent' => string '--user agent info--' (length=114) 
     protected 'flag' => int 2 
     protected 'iteratorClass' => string 'ArrayIterator' (length=13) 
     protected 'protectedProperties' => 
     array (size=4) 
      0 => string 'storage' (length=7) 
      1 => string 'flag' (length=4) 
      2 => string 'iteratorClass' (length=13) 
      3 => string 'protectedProperties' (length=19) 

這裏就是我目前存儲喜歡(這是在數據庫中,所以我目前正在與學說實體引用它)看起來會話信息:

object(MyModule\Entity\MySession)[550] 
    protected 'sessionid' => string 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' (length=40) 
    protected 'data1' => string 'xxxxx' (length=5) 
    protected 'data2' => string 'xxxxxxxxxxxx' (length=12) 
    protected 'datatime' => 
    object(DateTime)[547] 
     public 'date' => string '2016-07-13 17:05:52.000000' (length=26) 
     public 'timezone_type' => int 3 
     public 'timezone' => string 'xxxxxxxxxxxxxxx' (length=15) 
    protected 'data3' => boolean false 
    protected 'data4' => string '' (length=0) 
    protected 'data5' => int 9 
    protected 'data6' => int 17765 
    protected 'data7' => boolean false 

我的會話管理器代碼來自this SO answer,所以我提供了一個鏈接,而不是repasting它,搞亂了這個問題。

我想使用Zend會話管理器而不是簡單地引用我存儲的會話信息與Doctrine的原因是我的程序和存儲的會話信息之間有一層 - 這樣我就可以改變訪問方式會話信息,而不必更改我的整個程序。

回答

0

我最終通過擴展SessionManager,SessionStorage和SessionSaveHandler類並重寫了一些功能來解決這個問題。我也改變了Module.php和module.config.php文件。這就是更改後的樣子:

module.config.php

<?php 

/* ...required use statements... */ 

return array(
    'session' => array(
     'config' => array(
      'class' => 'Zend\Session\Config\SessionConfig', 
      'options' => array(
       'name' => [my session name], 
      ), 
     ), 
     'storage' => 'MySession\Model\MySessionStorage', 
     'save_handler' => 'MySession\Model\MySessionSaveHandler' 
    ), 
    'service_manager' => array(
     'factories' => array(
      'session_service' => function($serviceManager) { 
       $entityManager = $serviceManager->get('Doctrine\ORM\EntityManager'); 

       return new SessionService($entityManager, 'MySession'); 
      }, 
      'MySession\Model\MySessionSaveHandler' => function($serviceManager) { 
       $sess = $serviceManager->get('onmysession_service'); 
       /* @var $adapter \Zend\Db\Adapter\Adapter */ 
       $adapter = $sm->get('Zend\Db\Adapter\Adapter'); 
       $tableGateway = new TableGateway('mytablename', $adapter); 
       return new MySessionSaveHandler($tableGateway, new DbTableGatewayOptions(), $sess); 
      }, 
      'MySessionManager' => function ($sm) { 
       $config = $sm->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'])) { 
         // class should be fetched from service manager since it will require constructor arguments 
         $sessionSaveHandler = $sm->get($session['save_handler']); 
        } 

        $sessionManager = new MySessionManager($sessionConfig, $sessionStorage, $sessionSaveHandler); 
       } else { 
        $sessionManager = new MySessionManager(); 
       } 
       MySession::setDefaultManager($sessionManager); 
       return $sessionManager; 
      }, 
     ), 
    ), 
    'db' => array(
     [db info here] 
    ), 
    /*************************************************************************************************************** 
    * Below is the doctrine configuration which holds information about the entities in this module and some 
    * other doctrine stuff like orm drivers etc. 
    ***************************************************************************************************************/ 
    'doctrine' => array(
     'driver' => array(
      'session_entities' => array(
       'class' =>'Doctrine\ORM\Mapping\Driver\AnnotationDriver', 
       'cache' => 'array', 
       'paths' => array(__DIR__ . '/../src/MySession/Entity') 
      ), 
      'orm_default' => array(
       'drivers' => array(
        'MySession\Entity' => 'session_entities' 
       ), 
      ), 
     ), 
    ), 
); 

Module.php

<?php 

namespace MySession; 

/* ...required use statements... */ 

/*************************************************************************************************** 
* This class holds a few utility functions related to loading the module and accessing config 
* files for the module etc. These functions are primarily used by Zend under the hood. 
***************************************************************************************************/ 
class Module implements AutoloaderProviderInterface, ConfigProviderInterface 
{ 
    public function onBootstrap(MvcEvent $e) { 
     $eventManager  = $e->getApplication()->getEventManager(); 

     // create the session manager 
     $moduleRouteListener = new ModuleRouteListener(); 
     $moduleRouteListener->attach($eventManager); 
     $sessionManager = $e->getApplication() 
          ->getServiceManager() 
          ->get('MySessionManager'); 
     $sessionManager  ->start(); 

     // attach dispatch listener to validate user session 
     $eventManager->attach(MvcEvent::EVENT_DISPATCH, array($sessionManager, 'handleSessionValidation')); // TODO: we already handleSessionValidation on bootstrap, find out if it's necessary to do it on dispatch as well 
    } 

    /*************************************************************************************************** 
    * Returns the location of the module.config.php file. This function is used by the Zend Framework 
    * underneath the hood. 
    ***************************************************************************************************/ 
    public function getConfig() 
    { 
     return include __DIR__ . '/config/module.config.php'; 
    } 

    /*************************************************************************************************** 
    * Returns the Zend StandardAutoLoader which contains the directory structure of the module source 
    * folder. 
    ***************************************************************************************************/ 
    public function getAutoloaderConfig() 
    { 
     return array(
      'Zend\Loader\StandardAutoloader' => array(
       'namespaces' => array(
        __NAMESPACE__ => __DIR__ . '/src/' . __NAMESPACE__, 
       ), 
      ), 
     ); 
    } 
} 

MySessionManager

<?php 

namespace MySession\Model; 

/* ...required use statements... */ 

class MySessionManager extends SessionManager 
{ 
    /** 
    * Is this session valid? 
    * 
    * A simple validation: checks if a row for the session name exists in the database 
    * 
    * @return bool 
    */ 
    public function isValid() 
    { 
     $id = $_COOKIE[SessionVariableNames::$SESSION_NAME]; 
     return !is_null($this->getSaveHandler()->readMetadata($id)); 
    } 

    /** 
    * checks if the session is valid and dies if not. 
    */ 
    public function handleSessionValidation() { 
     if(stristr($_SERVER["SCRIPT_NAME"],"login.php")) 
     { 
      // we don't need to check the session at the login page 
      return; 
     } 

     if (!$this->isValid()) { 
      die("Not logged in.") 
     } 
    } 

    /** 
    * Start session 
    * 
    * If no session currently exists, attempt to start it. Calls 
    * {@link isValid()} once session_start() is called, and raises an 
    * exception if validation fails. 
    * 
    * @param bool $preserveStorage  If set to true, current session storage will not be overwritten by the 
    *          contents of $_SESSION. 
    * @return void 
    * @throws RuntimeException 
    */ 
    public function start($preserveStorage = false) 
    { 
     if ($this->sessionExists()) { 
      return; 
     } 

     $saveHandler = $this->getSaveHandler(); 
     if ($saveHandler instanceof SaveHandlerInterface) { 
      // register the session handler with ext/session 
      $this->registerSaveHandler($saveHandler); 
     } 

     // check if old session data exists and merge it with new data if so 
     $oldSessionData = []; 
     if (isset($_SESSION)) { 
      $oldSessionData = $_SESSION; 
     } 

     session_start(); 

     if ($oldSessionData instanceof \Traversable 
      || (! empty($oldSessionData) && is_array($oldSessionData)) 
     ) { 
      $_SESSION = ArrayUtils::merge($oldSessionData, $_SESSION, true); // this may not act like you'd expect, because the sessions are stored in ArrayObjects, so the second will always overwrite the first 
     } 

     $storage = $this->getStorage(); 

     // Since session is starting, we need to potentially repopulate our 
     // session storage 
     if ($storage instanceof SessionStorage && $_SESSION !== $storage) { 
      if (!$preserveStorage) { 
       $storage->fromArray($_SESSION); 
      } 
      $_SESSION = $storage; 
     } elseif ($storage instanceof StorageInitializationInterface) { 
      $storage->init($_SESSION); 
     } 

     $this->handleSessionValidation(); 
    } 

    /** 
    * Write session to save handler and close 
    * 
    * Once done, the Storage object will be marked as isImmutable. 
    * 
    * @return void 
    */ 
    public function writeClose() 
    { 
     // The assumption is that we're using PHP's ext/session. 
     // session_write_close() will actually overwrite $_SESSION with an 
     // empty array on completion -- which leads to a mismatch between what 
     // is in the storage object and $_SESSION. To get around this, we 
     // temporarily reset $_SESSION to an array, and then re-link it to 
     // the storage object. 
     // 
     // Additionally, while you _can_ write to $_SESSION following a 
     // session_write_close() operation, no changes made to it will be 
     // flushed to the session handler. As such, we now mark the storage 
     // object isImmutable. 
     $storage = $this->getStorage(); 
     if (!$storage->isImmutable()) { 
      $_SESSION = $storage->toArray(true); 
      $this->saveHandler->writeMetadata(null, '_metadata'); 
      $this->saveHandler->writeData($_SESSION['_data']); 
      session_write_close(); 
      $storage->fromArray($_SESSION); 
      $storage->markImmutable(); 
     } 
    } 
} 

MySessionStorage

<?php 

namespace MySession\Model; 

/* ...required use statements... */ 

class MySessionStorage extends SessionArrayStorage 
{ 
    /** 
    * Set storage metadata 
    * 
    * Metadata is used to store information about the data being stored in the 
    * object. Some example use cases include: 
    * - Setting expiry data 
    * - Maintaining access counts 
    * - localizing session storage 
    * - etc. 
    * 
    * @param string      $key 
    * @param mixed      $value 
    * @param bool      $overwriteArray Whether to overwrite or merge array values; by default, merges 
    * @return ArrayStorage 
    * @throws Exception\RuntimeException 
    */ 
    public function setMetadata($key, $value, $overwriteArray = false) 
    { 
     if ($this->isImmutable()) { 
      throw new Exception\RuntimeException(
       sprintf('Cannot set key "%s" as storage is marked isImmutable', $key) 
      ); 
     } 

     // set the value 
     $sessVar = $_SESSION['_metadata']; 
     if (isset($sessVar[$key]) && is_array($value)) { 
      // data is array, check if we're replacing the whole array or modify/add to it 
      if ($overwriteArray) { 
       $sessVar[$key] = $value; 
      } else { 
       $sessVar[$key] = array_replace_recursive($sessVar[$key], $value); 
      } 
     } else { 
      // data is not an array, set or remove it in the session 
      if ((null === $value) && isset($sessVar[$key])) { 
       // remove data 
       $array = $sessVar; 
       unset($array[$key]); 
       $_SESSION[SessionVariableNames::$SESSION_METADATA] = $array; // we can't use $sessVar here because it's only a copy of $_SESSION 
       unset($array); 
      } elseif (null !== $value) { 
       // add data 
       $sessVar[$key] = $value; 
      } 
     } 

     return $this; 
    } 

    /** 
    * Retrieve metadata for the storage object or a specific metadata key 
    * 
    * Looks at session db for the metadata 
    * 
    * Returns false if no metadata stored, or no metadata exists for the given 
    * key. 
    * 
    * @param null|int|string $key 
    * @return mixed 
    */ 
    public function getMetadata($key = null) 
    { 
     if (!isset($_SESSION)) { 
      return false; 
     } 

     if (null === $key) { 
      return $_SESSION; 
     } 

     if (!array_key_exists($key, $_SESSION)) { 
      return false; 
     } 

     return $_SESSION[$key]; 
    } 

    /** 
    * Set the request access time 
    * 
    * @param float  $time 
    * @return ArrayStorage 
    */ 
    protected function setRequestAccessTime($time) 
    { 
     // make a metadata write call, since that sets a timestamp 
     $this->setMetadata('datatime', new DateTime("now")); 

     return $this; 
    } 
} 

MySessionSaveHandler

<?php 

namespace MySession\Model; 

/* ...required use statements... */ 

/** 
* This class is the back end of the $_SESSION variable, when used together with a SessionStorage and SessionManager in a ZF module 
*/ 
class MySessionSaveHandler implements SaveHandlerInterface 
{ 
    protected $sessionService; 
    private $tableGateway; 
    private $options; 
    private $sessionName; 
    private $sessionSavePath; 
    private $lifetime; 

    public function __construct(
     TableGateway $tableGateway, 
     DbTableGatewayOptions $options, 
     ISessionService $sessionService) 
    { 
     $this->tableGateway = $tableGateway; 
     $this->options  = $options; 
     $this->sessionService = $sessionService; 
    } 

    protected function getSessionService() 
    { 
     return $this->sessionService; 
    } 

    /** 
    * Read session data 
    * 
    * @param string $id 
    * @return string 
    */ 
    public function read($id) 
    { 
     // Get data from database 
     $metadata = $this->readMetadata($id); 

     // Put data in PHP-session-serialized form 
     $data = "_metadata|".serialize($metadata); 
     return $data; 
    } 

    /** 
    * Read session metadata 
    * 
    * @param string $id 
    * @return mixed 
    */ 
    public function readMetadata($id = null) 
    { 
     if (is_null($id)) 
     { 
      if (!array_key_exists('sessionid', $_COOKIE)) 
      { 
       // can't get id from cookie 
       return null; 
      } 
      $id = $_COOKIE['sessionid']; 
     } 
     if ($data = $this->getSessionService()->findById($id)) 
     { 
      return $data->getArrayCopy(); 
     } 
     return null; 
    } 

    /** deprecated, use writeMetadata instead 
    * Write session data 
    * 
    * @param string $id 
    * @param string $data 
    * @return bool 
    * Note sessions use an alternative serialization method. 
    */ 
    public function write($id, $data) 
    { 
     // don't use this because $data is serialized strangely and can't be automatically inserted into my table 
    } 

    /** 
    * Write session metadata 
    * 
    * @param string $id 
    * @param array $data an associative array matching a row in the table 
    * @return mixed 
    */ 
    public function writeMetadata($id = null, $data = null) 
    { 
     if (is_null($id)) 
     { 
      if (!array_key_exists('sessionid', $_COOKIE)) 
      { 
       // can't get id from cookie 
       return null; 
      } 
      $id = $_COOKIE['sessionid']; 
     } 

     // get the session info from the database so we can modify it 
     $sessionService = $this->getSessionService(); 
     $session = $sessionService->findByID($id); 
     if (is_null($session)) { 
      $session = new \MyModule\Entity\MySession(); 
     } 
     if (!is_null($data)) 
     { 
      // overwrite the stored data 
      $session->setDataFromArray($data); 
     } 
     return $sessionService->save($session); 
    } 

    /** 
    * Destroy session - deletes data from session table 
    * 
    * @param string $id The session ID being destroyed. 
    * @return bool 
    * The return value (usually TRUE on success, FALSE on failure). 
    * Note this value is returned internally to PHP for processing. 
    */ 
    public function destroy($id) 
    { 
     $this->getSessionService()->delete($id); 

     return true; 
    } 

    /** 
    * Garbage Collection - cleanup old sessions 
    * 
    * @param int $maxlifetime 
    * Sessions that have not updated for 
    * the last maxlifetime seconds will be removed. 
    * @return bool 
    * The return value (usually TRUE on success, FALSE on failure). 
    * Note this value is returned internally to PHP for processing. 
    */ 
    public function gc($maxlifetime) 
    { 
     $metadata = $this->readMetadata(); // gets session id from cookie, then gets session from that 
     if (!is_null($metadata)) 
     { 
      $datatime = $metadata['datatime']; 
      $previousTime = (new DateTime($datatime))->getTimestamp(); 

      // if (current time - datatime) > maxlifetime, destroy the session 
      $val = time() - $previousTime; 
      if ($val > $maxlifetime) { 
       $this->destroy($metadata['sessionid']); 
      } 
     } 
    } 
} 

所有這一切的最終結果是,你可以簡單地通過訪問$ _SESSION變量訪問存儲在數據庫中的信息,因爲數據變從數據庫加載到bootstrap中的$ _SESSION變量中,並且在會話關閉時將$ _SESSION變量寫回到數據庫中(據我瞭解,當頁面發送給客戶端時)。