From 8d3a0624efd0c5f400df67478d5b657e3db04698 Mon Sep 17 00:00:00 2001
From: Demian Katz <demian.katz@villanova.edu>
Date: Wed, 29 Aug 2012 08:26:41 -0400
Subject: [PATCH] Set up session handlers to use a plugin manager; adjusted
 handler classes to use a setConfig() method instead of constructor injection
 to allow plugin manager compatibility.

---
 module/VuFind/src/VuFind/Bootstrap.php        | 53 +++++++---
 .../ServiceManager/AbstractPluginFactory.php  | 97 +++++++++++++++++++
 .../ServiceManager/AbstractPluginManager.php  | 65 +++++++++++++
 .../src/VuFind/Session/AbstractBase.php       | 10 +-
 module/VuFind/src/VuFind/Session/Database.php |  8 +-
 module/VuFind/src/VuFind/Session/File.php     | 59 +++++------
 module/VuFind/src/VuFind/Session/Memcache.php | 46 ++++-----
 .../src/VuFind/Session/PluginFactory.php      | 48 +++++++++
 .../src/VuFind/Session/PluginManager.php      | 55 +++++++++++
 9 files changed, 365 insertions(+), 76 deletions(-)
 create mode 100644 module/VuFind/src/VuFind/ServiceManager/AbstractPluginFactory.php
 create mode 100644 module/VuFind/src/VuFind/ServiceManager/AbstractPluginManager.php
 create mode 100644 module/VuFind/src/VuFind/Session/PluginFactory.php
 create mode 100644 module/VuFind/src/VuFind/Session/PluginManager.php

diff --git a/module/VuFind/src/VuFind/Bootstrap.php b/module/VuFind/src/VuFind/Bootstrap.php
index ddc785c468a..361b4de6c42 100644
--- a/module/VuFind/src/VuFind/Bootstrap.php
+++ b/module/VuFind/src/VuFind/Bootstrap.php
@@ -31,7 +31,8 @@ use VuFind\Config\Reader as ConfigReader,
     VuFind\Theme\Initializer as ThemeInitializer,
     VuFind\Translator\Translator, Zend\Console\Console,
     Zend\Db\TableGateway\Feature\GlobalAdapterFeature as DbGlobalAdapter,
-    Zend\Mvc\MvcEvent, Zend\Mvc\Router\Http\RouteMatch;
+    Zend\Mvc\MvcEvent, Zend\Mvc\Router\Http\RouteMatch,
+    Zend\ServiceManager\Config as ServiceManagerConfig;
 
 /**
  * VuFind Bootstrapper
@@ -77,7 +78,36 @@ class Bootstrap
     }
 
     /**
-     * Set up the default database adapter.  Do this first since the session handler
+     * Set up plugin managers.
+     *
+     * @return void
+     */
+    protected function initPluginManagers()
+    {
+        $serviceManager = $this->event->getApplication()->getServiceManager();
+        $config = new ServiceManagerConfig(
+            array(
+                'abstract_factories' => array('VuFind\Session\PluginFactory'),
+                'invokables' => array(
+                    'database' => 'VuFind\Session\Database',
+                    'file' => 'VuFind\Session\File',
+                    'memcache' => 'VuFind\Session\Memcache',
+                ),
+                'aliases' => array(
+                    // for legacy 1.x compatibility
+                    'filesession' => 'File',
+                    'memcachesession' => 'Memcache',
+                    'mysqlsession' => 'Database',
+                ),
+            )
+        );
+        $serviceManager->setService(
+            'SessionHandlerManager', new \VuFind\Session\PluginManager($config)
+        );
+    }
+
+    /**
+     * Set up the default database adapter.  Do this early since the session handler
      * may depend on database access.
      *
      * @return void
@@ -105,21 +135,14 @@ class Bootstrap
             throw new \Exception('Cannot initialize session; configuration missing');
         }
 
-        // Register a session manager in the service manager:
+        // Set up the session handler by retrieving all the pieces from the service
+        // manager and injecting appropriate dependencies:
         $serviceManager = $this->event->getApplication()->getServiceManager();
         $sessionManager = $serviceManager->get('SessionManager');
-
-        // Set up session handler (after manipulating the type setting for legacy
-        // compatibility -- VuFind 1.x used MySQL instead of Database and had
-        // "Session" as part of the configuration string):
-        $type = ucwords(
-            str_replace('session', '', strtolower($this->config->Session->type))
-        );
-        if ($type == 'Mysql') {
-            $type = 'Database';
-        }
-        $class = 'VuFind\Session\\' . $type;
-        $sessionManager->setSaveHandler(new $class($this->config->Session));
+        $sessionHandlerManager = $serviceManager->get('SessionHandlerManager');
+        $sessionHandler = $sessionHandlerManager->get($this->config->Session->type);
+        $sessionHandler->setConfig($this->config->Session);
+        $sessionManager->setSaveHandler($sessionHandler);
 
         // Start up the session:
         $sessionManager->start();
diff --git a/module/VuFind/src/VuFind/ServiceManager/AbstractPluginFactory.php b/module/VuFind/src/VuFind/ServiceManager/AbstractPluginFactory.php
new file mode 100644
index 00000000000..b5d9a4aea58
--- /dev/null
+++ b/module/VuFind/src/VuFind/ServiceManager/AbstractPluginFactory.php
@@ -0,0 +1,97 @@
+<?php
+/**
+ * VuFind Abstract Plugin Factory
+ *
+ * PHP version 5
+ *
+ * Copyright (C) Villanova University 2010.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * @category VuFind2
+ * @package  ServiceManager
+ * @author   Demian Katz <demian.katz@villanova.edu>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     http://vufind.org/wiki/building_a_recommendations_module Wiki
+ */
+namespace VuFind\ServiceManager;
+use Zend\ServiceManager\AbstractFactoryInterface,
+    Zend\ServiceManager\ServiceLocatorInterface;
+
+/**
+ * VuFind Abstract Plugin Factory
+ *
+ * @category VuFind2
+ * @package  ServiceManager
+ * @author   Demian Katz <demian.katz@villanova.edu>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     http://vufind.org/wiki/building_a_search_object Wiki
+ */
+abstract class AbstractPluginFactory implements AbstractFactoryInterface
+{
+    protected $defaultNamespace;
+
+    /**
+     * Get the name of a class for a given plugin name.
+     *
+     * @param string $name           Name of service
+     * @param string $requestedName  Unfiltered name of service
+     *
+     * @return string                Fully qualified class name
+     */
+    protected function getClassName($name, $requestedName)
+    {
+        // If we have a FQCN, return it as-is; otherwise, prepend the default prefix:
+        if (strpos($name, '\\') === false) {
+            // First try the raw service name, then try a normalized version:
+            $name = $this->defaultNamespace . '\\' . $requestedName;
+            if (!class_exists($name)) {
+                $name = $this->defaultNamespace . '\\' . ucwords(strtolower($name));
+            }
+        }
+        return $name;
+    }
+
+    /**
+     * Can we create a service for the specified name?
+     *
+     * @param ServiceLocatorInterface $serviceLocator Service locator
+     * @param string                  $name           Name of service
+     * @param string                  $requestedName  Unfiltered name of service
+     *
+     * @return bool
+     */
+    public function canCreateServiceWithName(ServiceLocatorInterface $serviceLocator,
+        $name, $requestedName
+    ) {
+        $className = $this->getClassName($name, $requestedName);
+        return class_exists($className);
+    }
+
+    /**
+     * Create a service for the specified name.
+     *
+     * @param ServiceLocatorInterface $serviceLocator Service locator
+     * @param string                  $name           Name of service
+     * @param string                  $requestedName  Unfiltered name of service
+     *
+     * @return object
+     */
+    public function createServiceWithName(ServiceLocatorInterface $serviceLocator,
+        $name, $requestedName
+    ) {
+        $class = $this->getClassName($name, $requestedName);
+        return new $class();
+    }
+}
diff --git a/module/VuFind/src/VuFind/ServiceManager/AbstractPluginManager.php b/module/VuFind/src/VuFind/ServiceManager/AbstractPluginManager.php
new file mode 100644
index 00000000000..3201131af92
--- /dev/null
+++ b/module/VuFind/src/VuFind/ServiceManager/AbstractPluginManager.php
@@ -0,0 +1,65 @@
+<?php
+/**
+ * VuFind Plugin Manager
+ *
+ * PHP version 5
+ *
+ * Copyright (C) Villanova University 2010.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * @category VuFind2
+ * @package  ServiceManager
+ * @author   Demian Katz <demian.katz@villanova.edu>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     http://vufind.org/wiki/building_a_recommendations_module Wiki
+ */
+namespace VuFind\ServiceManager;
+use Zend\ServiceManager\AbstractPluginManager as Base,
+    Zend\ServiceManager\Exception\RuntimeException as ServiceManagerRuntimeException;
+
+/**
+ * VuFind Plugin Manager
+ *
+ * @category VuFind2
+ * @package  ServiceManager
+ * @author   Demian Katz <demian.katz@villanova.edu>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     http://vufind.org/wiki/building_a_search_object Wiki
+ */
+abstract class AbstractPluginManager extends Base
+{
+    protected $expectedInterface;
+
+    /**
+     * Validate the plugin
+     *
+     * Checks that the filter loaded is either a valid callback or an instance
+     * of FilterInterface.
+     *
+     * @param mixed $plugin Plugin to validate
+     *
+     * @throws ServiceManagerRuntimeException if invalid
+     * @return void
+     */
+    public function validatePlugin($plugin)
+    {
+        if (!($plugin instanceof $this->expectedInterface)) {
+            throw new ServiceManagerRuntimeException(
+                'Plugin ' . get_class($plugin) . ' does not belong to '
+                . $this->expectedInterface
+            );
+        }
+    }
+}
\ No newline at end of file
diff --git a/module/VuFind/src/VuFind/Session/AbstractBase.php b/module/VuFind/src/VuFind/Session/AbstractBase.php
index 15c52e4ba06..1772cfacdf5 100644
--- a/module/VuFind/src/VuFind/Session/AbstractBase.php
+++ b/module/VuFind/src/VuFind/Session/AbstractBase.php
@@ -40,19 +40,23 @@ use VuFind\Db\Table\Search as SearchTable,
  */
 abstract class AbstractBase implements SaveHandlerInterface
 {
-    public $lifetime = 3600;
+    protected $lifetime = 3600;
+    protected $config = null;
 
     /**
-     * Constructor.
+     * Set configuration.
      *
      * @param \Zend\Config\Config $config Session configuration ([Session] section of
      * config.ini)
+     *
+     * @return void
      */
-    public function __construct($config)
+    public function setConfig($config)
     {
         if (isset($config->lifetime)) {
             $this->lifetime = $config->lifetime;
         }
+        $this->config = $config;
     }
 
     /**
diff --git a/module/VuFind/src/VuFind/Session/Database.php b/module/VuFind/src/VuFind/Session/Database.php
index c1ee7abd8c9..8855c1db1d2 100644
--- a/module/VuFind/src/VuFind/Session/Database.php
+++ b/module/VuFind/src/VuFind/Session/Database.php
@@ -44,17 +44,11 @@ class Database extends AbstractBase
 
     /**
      * Constructor.
-     *
-     * @param \Zend\Config\Config $config Session configuration ([Session] section of
-     * config.ini)
      */
-    public function __construct($config)
+    public function __construct()
     {
         // Create database connection:
         $this->table = new SessionTable();
-
-        // Call standard session initialization from this point.
-        parent::__construct($config);
     }
 
     /**
diff --git a/module/VuFind/src/VuFind/Session/File.php b/module/VuFind/src/VuFind/Session/File.php
index 86c708999c9..2ddb7a3b1cb 100644
--- a/module/VuFind/src/VuFind/Session/File.php
+++ b/module/VuFind/src/VuFind/Session/File.php
@@ -38,36 +38,37 @@ namespace VuFind\Session;
  */
 class File extends AbstractBase
 {
-    protected $path;
+    protected $path = false;
 
     /**
-     * Constructor.
+     * Get the file path for writing sessions.
      *
-     * @param \Zend\Config\Config $config Session configuration ([Session] section of
-     * config.ini)
+     * @throws \Exception
+     * @return string
      */
-    public function __construct($config)
+    protected function getPath()
     {
-        // Set defaults if nothing set in config file.
-        if (isset($config->file_save_path)) {
-            $this->path = $config->file_save_path;
-        } else {
-            $tempdir = function_exists('sys_get_temp_dir')
-                ? sys_get_temp_dir() : DIRECTORY_SEPARATOR . 'tmp';
-            $this->path = $tempdir . DIRECTORY_SEPARATOR . 'vufind_sessions';
-        }
+        if (!$this->path) {
+            // Set defaults if nothing set in config file.
+            if (isset($this->config->file_save_path)) {
+                $this->path = $this->config->file_save_path;
+            } else {
+                $tempdir = function_exists('sys_get_temp_dir')
+                    ? sys_get_temp_dir() : DIRECTORY_SEPARATOR . 'tmp';
+                $this->path = $tempdir . DIRECTORY_SEPARATOR . 'vufind_sessions';
+            }
 
-        // Die if the session directory does not exist and cannot be created.
-        if (!file_exists($this->path) || !is_dir($this->path)) {
-            if (!@mkdir($this->path)) {
-                throw new \Exception(
-                    "Cannot access session save path: " . $this->path
-                );
+            // Die if the session directory does not exist and cannot be created.
+            if (!file_exists($this->path) || !is_dir($this->path)) {
+                if (!mkdir($this->path)) {
+                    throw new \Exception(
+                        "Cannot access session save path: " . $this->path
+                    );
+                }
             }
         }
 
-        // Call standard session initialization from this point.
-        parent::__construct($config);
+        return $this->path;
     }
 
     /**
@@ -80,7 +81,7 @@ class File extends AbstractBase
      */
     public function read($sess_id)
     {
-        $sess_file = $this->path . '/sess_' . $sess_id;
+        $sess_file = $this->getPath() . '/sess_' . $sess_id;
         if (!file_exists($sess_file)) {
             return '';
         }
@@ -91,7 +92,7 @@ class File extends AbstractBase
             return '';
         }
 
-        return (string)@file_get_contents($sess_file);
+        return (string)file_get_contents($sess_file);
     }
 
     /**
@@ -104,8 +105,8 @@ class File extends AbstractBase
      */
     public function write($sess_id, $data)
     {
-        $sess_file = $this->path . '/sess_' . $sess_id;
-        if ($fp = @fopen($sess_file, "w")) {
+        $sess_file = $this->getPath() . '/sess_' . $sess_id;
+        if ($fp = fopen($sess_file, "w")) {
             $return = fwrite($fp, $data);
             fclose($fp);
             if ($return !== false) {
@@ -133,8 +134,8 @@ class File extends AbstractBase
         parent::destroy($sess_id);
 
         // Perform file-specific cleanup:
-        $sess_file = $this->path . '/sess_' . $sess_id;
-        return(@unlink($sess_file));
+        $sess_file = $this->getPath() . '/sess_' . $sess_id;
+        return(unlink($sess_file));
     }
 
     /**
@@ -147,9 +148,9 @@ class File extends AbstractBase
      */
     public function gc($maxlifetime)
     {
-        foreach (glob($this->path . "/sess_*") as $filename) {
+        foreach (glob($this->getPath() . "/sess_*") as $filename) {
             if (filemtime($filename) + $maxlifetime < time()) {
-                @unlink($filename);
+                unlink($filename);
             }
         }
         return true;
diff --git a/module/VuFind/src/VuFind/Session/Memcache.php b/module/VuFind/src/VuFind/Session/Memcache.php
index 5783c1cfb99..977f25b59a2 100644
--- a/module/VuFind/src/VuFind/Session/Memcache.php
+++ b/module/VuFind/src/VuFind/Session/Memcache.php
@@ -41,32 +41,34 @@ namespace VuFind\Session;
  */
 class Memcache extends AbstractBase
 {
-    protected $connection;
+    protected $connection = false;
 
     /**
-     * Constructor.
+     * Get connection to Memcache
      *
-     * @param \Zend\Config\Config $config Session configuration ([Session] section of
-     * config.ini)
+     * @throws \Exception
+     * @return \Memcache
      */
-    public function __construct($config)
+    public function getConnection()
     {
-        // Set defaults if nothing set in config file.
-        $host = isset($config->memcache_host) ? $config->memcache_host : 'localhost';
-        $port = isset($config->memcache_port) ? $config->memcache_port : 11211;
-        $timeout = isset($config->memcache_connection_timeout)
-            ? $config->memcache_connection_timeout : 1;
+        if (!$this->connection) {
+            // Set defaults if nothing set in config file.
+            $host = isset($this->config->memcache_host)
+                ? $this->config->memcache_host : 'localhost';
+            $port = isset($this->config->memcache_port)
+                ? $this->config->memcache_port : 11211;
+            $timeout = isset($this->config->memcache_connection_timeout)
+                ? $this->config->memcache_connection_timeout : 1;
 
-        // Connect to Memcache:
-        $this->connection = new \Memcache();
-        if (!@$this->connection->connect($host, $port, $timeout)) {
-            throw new \Exception(
-                "Could not connect to Memcache (host = {$host}, port = {$port})."
-            );
+            // Connect to Memcache:
+            $this->connection = new \Memcache();
+            if (!$this->connection->connect($host, $port, $timeout)) {
+                throw new \Exception(
+                    "Could not connect to Memcache (host = {$host}, port = {$port})."
+                );
+            }
         }
-
-        // Call standard session initialization from this point.
-        parent::__construct($config);
+        return $this->connection;
     }
 
     /**
@@ -79,7 +81,7 @@ class Memcache extends AbstractBase
      */
     public function read($sess_id)
     {
-        return $this->connection->get("vufind_sessions/{$sess_id}");
+        return $this->getConnection()->get("vufind_sessions/{$sess_id}");
     }
 
     /**
@@ -92,7 +94,7 @@ class Memcache extends AbstractBase
      */
     public function write($sess_id, $data)
     {
-        return $this->connection->set(
+        return $this->getConnection()->set(
             "vufind_sessions/{$sess_id}", $data, 0, $this->lifetime
         );
     }
@@ -111,6 +113,6 @@ class Memcache extends AbstractBase
         parent::destroy($sess_id);
 
         // Perform Memcache-specific cleanup:
-        return $this->connection->delete("vufind_sessions/{$sess_id}");
+        return $this->getConnection()->delete("vufind_sessions/{$sess_id}");
     }
 }
\ No newline at end of file
diff --git a/module/VuFind/src/VuFind/Session/PluginFactory.php b/module/VuFind/src/VuFind/Session/PluginFactory.php
new file mode 100644
index 00000000000..8ac38a7dff5
--- /dev/null
+++ b/module/VuFind/src/VuFind/Session/PluginFactory.php
@@ -0,0 +1,48 @@
+<?php
+/**
+ * Session handler plugin factory
+ *
+ * PHP version 5
+ *
+ * Copyright (C) Villanova University 2010.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * @category VuFind2
+ * @package  Session_Handlers
+ * @author   Demian Katz <demian.katz@villanova.edu>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     http://vufind.org/wiki/creating_a_session_handler Wiki
+ */
+namespace VuFind\Session;
+
+/**
+ * Session handler plugin factory
+ *
+ * @category VuFind2
+ * @package  Session_Handlers
+ * @author   Demian Katz <demian.katz@villanova.edu>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     http://vufind.org/wiki/creating_a_session_handler Wiki
+ */
+class PluginFactory extends \VuFind\ServiceManager\AbstractPluginFactory
+{
+    /**
+     * Constructor
+     */
+    public function __construct()
+    {
+        $this->defaultNamespace = 'VuFind\Session';
+    }
+}
\ No newline at end of file
diff --git a/module/VuFind/src/VuFind/Session/PluginManager.php b/module/VuFind/src/VuFind/Session/PluginManager.php
new file mode 100644
index 00000000000..fc71c7c13ed
--- /dev/null
+++ b/module/VuFind/src/VuFind/Session/PluginManager.php
@@ -0,0 +1,55 @@
+<?php
+/**
+ * Session handler plugin manager
+ *
+ * PHP version 5
+ *
+ * Copyright (C) Villanova University 2010.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * @category VuFind2
+ * @package  Session_Handlers
+ * @author   Demian Katz <demian.katz@villanova.edu>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     http://vufind.org/wiki/creating_a_session_handler Wiki
+ */
+namespace VuFind\Session;
+use Zend\ServiceManager\ConfigInterface;
+
+/**
+ * Session handler plugin manager
+ *
+ * @category VuFind2
+ * @package  Session_Handlers
+ * @author   Demian Katz <demian.katz@villanova.edu>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     http://vufind.org/wiki/creating_a_session_handler Wiki
+ */
+class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager
+{
+    /**
+     * Constructor
+     *
+     * Add a default initializer to ensure the plugin is valid after instance
+     * creation.
+     *
+     * @param null|ConfigInterface $configuration Configuration
+     */
+    public function __construct(ConfigInterface $configuration = null)
+    {
+        $this->expectedInterface = 'Zend\Session\SaveHandler\SaveHandlerInterface';
+        parent::__construct($configuration);
+    }
+}
\ No newline at end of file
-- 
GitLab