Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
ManagerFactory
n/a
0 / 0
n/a
0 / 0
14
n/a
0 / 0
 getOptions
n/a
0 / 0
n/a
0 / 0
4
 getHandler
n/a
0 / 0
n/a
0 / 0
2
 registerShutdownFunction
n/a
0 / 0
n/a
0 / 0
2
 __invoke
n/a
0 / 0
n/a
0 / 0
6
1<?php
2
3/**
4 * Factory for instantiating Session Manager
5 *
6 * PHP version 8
7 *
8 * Copyright (C) Villanova University 2016.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2,
12 * as published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
22 *
23 * @category VuFind
24 * @package  Session_Handlers
25 * @author   Demian Katz <demian.katz@villanova.edu>
26 * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
27 * @link     https://vufind.org/wiki/development Wiki
28 */
29
30namespace VuFind\Session;
31
32use Laminas\ServiceManager\Exception\ServiceNotCreatedException;
33use Laminas\ServiceManager\Exception\ServiceNotFoundException;
34use Laminas\ServiceManager\Factory\FactoryInterface;
35use Laminas\Session\SessionManager;
36use Psr\Container\ContainerExceptionInterface as ContainerException;
37use Psr\Container\ContainerInterface;
38
39/**
40 * Factory for instantiating Session Manager
41 *
42 * @category VuFind
43 * @package  Session_Handlers
44 * @author   Demian Katz <demian.katz@villanova.edu>
45 * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
46 * @link     https://vufind.org/wiki/development Wiki
47 *
48 * @codeCoverageIgnore
49 */
50class ManagerFactory implements FactoryInterface
51{
52    /**
53     * Build the options array.
54     *
55     * @param ContainerInterface $container Service manager
56     *
57     * @return array
58     */
59    protected function getOptions(ContainerInterface $container)
60    {
61        $cookieManager = $container->get(\VuFind\Cookie\CookieManager::class);
62        // Set options only if we are not running from CLI
63        $options = 'cli' !== PHP_SAPI ? [
64            'cookie_httponly' => $cookieManager->isHttpOnly(),
65            'cookie_path' => $cookieManager->getPath(),
66            'cookie_secure' => $cookieManager->isSecure(),
67            'cookie_samesite' => $cookieManager->getSameSite(),
68        ] : [];
69
70        $domain = $cookieManager->getDomain();
71        if (!empty($domain)) {
72            $options['cookie_domain'] = $domain;
73        }
74
75        $name = $cookieManager->getSessionName();
76        if (!empty($name)) {
77            $options['name'] = $name;
78        }
79
80        return $options;
81    }
82
83    /**
84     * Set up the session handler by retrieving all the pieces from the service
85     * manager and injecting appropriate dependencies.
86     *
87     * @param ContainerInterface $container Service manager
88     *
89     * @return array
90     */
91    protected function getHandler(ContainerInterface $container)
92    {
93        // Load and validate session configuration:
94        $config = $container->get(\VuFind\Config\PluginManager::class)
95            ->get('config');
96        if (!isset($config->Session->type)) {
97            throw new \Exception('Cannot initialize session; configuration missing');
98        }
99
100        return $container->get(\VuFind\Session\PluginManager::class)
101            ->get($config->Session->type);
102    }
103
104    /**
105     * According to the PHP manual, session_write_close should always be
106     * registered as a shutdown function when using an object as a session
107     * handler: http://us.php.net/manual/en/function.session-set-save-handler.php
108     *
109     * This method sets that up.
110     *
111     * @param SessionManager $sessionManager Session manager instance
112     *
113     * @return void
114     */
115    protected function registerShutdownFunction(SessionManager $sessionManager)
116    {
117        register_shutdown_function(
118            function () use ($sessionManager) {
119                // If storage is immutable, the session is already closed:
120                if (!$sessionManager->getStorage()->isImmutable()) {
121                    $sessionManager->writeClose();
122                }
123            }
124        );
125    }
126
127    /**
128     * Create an object
129     *
130     * @param ContainerInterface $container     Service manager
131     * @param string             $requestedName Service being created
132     * @param null|array         $options       Extra options (optional)
133     *
134     * @return object
135     *
136     * @throws ServiceNotFoundException if unable to resolve the service.
137     * @throws ServiceNotCreatedException if an exception is raised when
138     * creating a service.
139     * @throws ContainerException&\Throwable if any other error occurs
140     */
141    public function __invoke(
142        ContainerInterface $container,
143        $requestedName,
144        array $options = null
145    ) {
146        if (!empty($options)) {
147            throw new \Exception('Unexpected options passed to factory.');
148        }
149
150        // Build configuration:
151        $sessionConfig = new \Laminas\Session\Config\SessionConfig();
152        $sessionConfig->setOptions($this->getOptions($container));
153
154        // Build session manager and attach handler:
155        $sessionManager = new $requestedName($sessionConfig);
156        $sessionManager->setSaveHandler($this->getHandler($container));
157
158        // Start up the session:
159        $sessionManager->start();
160
161        // Verify that any existing session has the correct path to avoid using
162        // a cookie from a service higher up in the path hierarchy.
163        $storage = new \Laminas\Session\Container('SessionState', $sessionManager);
164        if (null !== $storage->cookiePath) {
165            if ($storage->cookiePath != $sessionConfig->getCookiePath()) {
166                // Disable writes temporarily to keep the existing session intact
167                $sessionManager->getSaveHandler()->disableWrites();
168                // Regenerate session ID and reset the session data
169                $sessionManager->regenerateId(false);
170                session_unset();
171                $sessionManager->getSaveHandler()->enableWrites();
172                $storage->cookiePath = $sessionConfig->getCookiePath();
173            }
174        } else {
175            $storage->cookiePath = $sessionConfig->getCookiePath();
176        }
177
178        // Set session start time:
179        if (empty($storage->sessionStartTime)) {
180            $storage->sessionStartTime = time();
181        }
182
183        // Check if we need to immediately stop it based on the settings object
184        // (which may have been informed by a controller that sessions should not
185        // be written as part of the current process):
186        $settings = $container->get(\VuFind\Session\Settings::class);
187        if ($settings->setSessionManager($sessionManager)->isWriteDisabled()) {
188            $sessionManager->getSaveHandler()->disableWrites();
189        } else {
190            // If the session is not disabled, we should set up the normal
191            // shutdown function:
192            $this->registerShutdownFunction($sessionManager);
193        }
194
195        return $sessionManager;
196    }
197}