首页 文章

使用Doctrine在zf3中自定义会话SaveHandler

提问于
浏览
2

使用ZF3和Doctrine时如何在数据库中实现会话?

手册说:

在某些情况下,您可能希望创建一个保存处理程序,其中当前不存在保存处理程序 . 创建自定义保存处理程序与创建自定义PHP保存处理程序非常相似 . 所有保存处理程序必须实现Zend \ Session \ SaveHandler \ SaveHandlerInterface . 通常,如果您的保存处理程序有选项,您将创建另一个选项类来配置保存处理程序 .

我试图创建实现此接口的自定义类,但我收到以下错误:

expects a class implementing Zend\Session\Storage\StorageInterface'

使用此配置:

'session_storage' => [
//        'type' => SessionArrayStorage::class (with array storage works ok)
        'type' => \Application\Session\SaveHandler\Doctrine::class (tried to implement suggested interface)
    ],

请注意,手册建议 SaveHandlerInterface ,但期望 StorageInterface .

这个怎么样的例子?

Edit:

我目前的实施 .

global.php

'session_config' => [
        // Session cookie will expire in 1 hour.
        'cookie_lifetime' => 60*60*1,
        // Session data will be stored on server maximum for 30 days.
        'gc_maxlifetime'     => 60*60*24*30,
    ],
    // Session manager configuration.
    'session_manager' => [
        // Session validators (used for security).
        'validators' => [
            RemoteAddr::class,
            HttpUserAgent::class,
        ]
    ],
    // Session storage configuration.
    'session_storage' => [
        'type' => \Application\Session\Storage\Doctrine::class,
    ],
    'session_containers' => [
        'UserSession'
    ]

Module.php

/**
     * This method is called once the MVC bootstrapping is complete.
     */
    public function onBootstrap(MvcEvent $event)
    {
        $application = $event->getApplication();
        $serviceManager = $application->getServiceManager();



        // The following line instantiates the SessionManager and automatically
        // makes the SessionManager the 'default' one
        /** @var SessionManager $sessionManager */
        $sessionManager = $serviceManager->get(SessionManager::class);

        $entityManager =  $serviceManager->get('doctrine.entitymanager.orm_default');

        /** @var Doctrine $storage */
        $storage = $sessionManager->getStorage();
        $storage->setEntityManager($        
    }

Application\Session\Storage\Doctrine.php

class Doctrine implements
    IteratorAggregate,
    StorageInterface,
    StorageInitializationInterface
{
    public function setEntityManager($em) {  
        $this->entityManager = $em;
    }

    // ...
    // other functions as required by interfaces
}

这是有效的,但缺点是Doctrine Storage只能在这个模块中使用,我会在每个请求(Boostrap)上专门注入它,而不是在它真正需要时(Factory) .

更新:

我写了 SaveHandler ,但看起来像有 Value 的请求后没有保留 .

这是代码:

class Doctrine extends ArrayStorage implements SaveHandlerInterface {

    /**
     * @param string $session_id
     * @return string Encdoded session data string
     */
    public function read($session_id)
    {
        $entity = $this->getEntity($session_id);
        if ($entity) {
            return $entity->getSessionData();
//          sample output:
//          string '__ZF|a:2:{s:20:"_REQUEST_ACCESS_TIME";d:1501933765.497678;s:6:"_VALID";a:3:{s:25:"Zend\Session\Validator\Id";s:26:"3kr15rhi6ijhneu7rruro9gr76";s:33:"Zend\Session\Validator\RemoteAddr";s:9:"127.0.0.1";s:36:"Zend\Session\Validator\HttpUserAgent";s:133:"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/57.0.2987.98 Chrome/57.0.2987.98 Safari/537.36";}}FlashMessenger|C:23:"Zend\Stdlib\ArrayObject":205:{a:4:{s:7:"storage";a:0:{}s:4:"flag";i:2;s:13:"iteratorClass";s:13:"ArrayI'... (length=645)
//          note that counter is not present            
        }
    }

    /**
     * @param string $session_id
     * @param string $session_data Encoded session data string
     * @return bool
     */
    public function write($session_id, $session_data)
    {
//        sample input ($session_data):
//        string '__ZF|a:2:{s:20:"_REQUEST_ACCESS_TIME";d:1501934933.9573331;s:6:"_VALID";a:3:{s:25:"Zend\Session\Validator\Id";s:26:"3kr15rhi6ijhneu7rruro9gr76";s:33:"Zend\Session\Validator\RemoteAddr";s:9:"127.0.0.1";s:36:"Zend\Session\Validator\HttpUserAgent";s:133:"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/57.0.2987.98 Chrome/57.0.2987.98 Safari/537.36";}}UserSession|C:23:"Zend\Stdlib\ArrayObject":223:{a:4:{s:7:"storage";a:1:{s:7:"counter";i:1;}s:4:"flag";i:2;s:13:"iteratorCla'... (length=918)
//        (note that counter variable is set)

        $entity = $this->getEntity($session_id);

        $entity->setSessionData($session_data);
        $entity->setLifetime($this->getLifeTime());

        $this->getEntityManager()->persist($entity);
        $this->getEntityManager()->flush($entity);

        return true;
    }

    /**
     * @param string $session_id
     * @return Entity|null
     */
    public function getEntity($session_id)
    {
        $this->entity = $this->getRepository()->find($session_id);

        if (!$this->entity) {
            $this->entity = new $this->entityName;
            $this->entity->setId($session_id);
        }

        return $this->entity;
    }

    //  ....

 }

2 回答

  • 1

    实际上,您需要实现这些接口,因为PHP需要 SaveHandlerInterface ,ZF3需要 StorageInterface . 存储处理程序是它们之间的一种网关 .

    这应该工作 . 您可以在工厂中注入所有依赖项 .

    Application/src/DoctrineSaveHandler.php

    namespace Application;
    
    use Zend\Session\SaveHandler\SaveHandlerInterface;
    use Zend\Session\Storage\ArrayStorage;
    
    class DoctrineSaveHandler extends ArrayStorage implements SaveHandlerInterface
    {
        public function close () {}
        public function destroy ($session_id) {}
        public function gc ($maxlifetime) {}
        public function open ($save_path, $name)  {}
        public function read ($session_id)  {}
        public function write ($session_id, $session_data)  {}
    }
    

    config/autoload/global.php

    "service_manager" => [
        'aliases' => [
            \Zend\Session\SaveHandler\SaveHandlerInterface::class => \Zend\Session\Storage\StorageInterface::class
        ],
        'factories' => [
            \Zend\Session\Storage\StorageInterface::class => function () {
                // -------------------------------
                // YOU NEED A PROPER FACTORY HERE!
                // -------------------------------
                return new DoctrineSaveHandler();
            },
        ]
    ]
    
  • 0

    说实话,我没有使用带有保存处理程序功能的doctrine来管理会话 . 但是,让我告诉你应该如何构建 Zend\Session 的每个部分,特别是 SessionManager::class .

    SessionArrayStorage::class 实现 Zend\Session\Storage\StorageInterface 并用于存储会话数据,以支持 SessionManager::class .

    实际上 Zend\Session 的这一部分做得很棒 . 它可以替代 $_SESSION 超全局,并使用 ArrayObject::class 来自 Zend\Stdlib . 它将为您提供极大的灵活性,这意味着您将能够使用这些功能:属性访问,元数据存储,锁定和不变性 . (老实说,我没有使用所有这些) .

    'session_storage' => [
        // 'type' => SessionArrayStorage::class (with array storage works ok)
        'type' => \Application\Session\SaveHandler\Doctrine::class (tried to implement suggested interface)
    ],
    

    现在重点是你使用的是自定义保存处理程序而不是不正确的存储 . 因为保存处理程序没有实现 Zend\Session\Storage\StorageInterface . 这就是你得到那种错误的原因 .

    保存处理程序通常用于在数据库,文件或缓存系统中存储会话数据 . 正在制作自定义保存处理程序意味着您正在实现 Zend\Session\SaveHandler\SaveHandlerInterface . 因此,您必须使用 open($savePath, $name)read($id)write($id, $data)destroy($id) 等 .

    因此,要完全配置 SessionManager::class 以管理会话,您需要提供三个内容:会话配置,会话存储和保存处理程序 . 例如

    $sessionManager = new SessionManager(
        $sessionConfig,
        $sessionStorage,
        // provide your save handler here
        $sessionSaveHandler
    );
    
    // this keeps this configuration in mind in later calls of 'Container::class'
    Container::setDefaultManager($sessionManager);
    return $sessionManager;
    

    现在我们已经配置了 SessionManager::class . 我们可以在需要时调用它 . 例如,在使用一个人的登录凭据验证之后 .

    $session = $e->getApplication()
        ->getServiceManager()
        ->get(SessionManager::class);
    $session->start();
    

    在此之后,我们将能够使用 Zend\Session 组件的 Container::class 部分如下

    // this would use the above configuration 
    $container = new Container('initialized');
    
    $session->getSaveHandler()->open('path', 'session-name');
    
    // Write data to db, files etc
    // "$contents" must be serialized data; coentents can be: id, email etc
    $session->getSaveHandler()->write($session->getId(), $contents);
    
    // Read data
    $storedData = $session->getSaveHandler()->read($session->getId());
    

    现在我们可以使用任何自定义属性和存储值

    $container->storedData = $storedData;
    $container->remoteAddr = 127.0.0.1;
    

    接下来我们需要检索这些值,我们就可以得到它们

    $container = new Container('initialized');
    print_r($container->storedData);
    
    //or
    echo $container->remoteAddr;
    

    希望这会对你有所帮助!

相关问题