add Horde_Controller_Scanner for processing controller directories in a way compatibl...
authorChuck Hagenbuch <chuck@horde.org>
Sat, 24 Jan 2009 20:19:46 +0000 (15:19 -0500)
committerChuck Hagenbuch <chuck@horde.org>
Sat, 24 Jan 2009 20:19:46 +0000 (15:19 -0500)
framework/Controller/lib/Horde/Controller/Dispatcher.php
framework/Controller/lib/Horde/Controller/Scanner.php [new file with mode: 0644]
framework/Controller/package.xml

index 294b23e..46249a4 100644 (file)
@@ -74,6 +74,13 @@ class Horde_Controller_Dispatcher
             $this->_controllerDir .= '/';
         }
 
+        // Set the mapper's controller directory and controllerScan
+        if ($this->_controllerDir && !$this->_mapper->directory) {
+            $this->_mapper->directory = $this->_controllerDir;
+        }
+        $scanner = new Horde_Controller_Scanner($this->_mapper);
+        $this->_mapper->controllerScan = $scanner->getCallback();
+
         // Make sure views directory, if set, ends in a /.
         if ($this->_viewsDir && substr($this->_viewsDir, -1) != '/') {
             $this->_viewsDir .= '/';
diff --git a/framework/Controller/lib/Horde/Controller/Scanner.php b/framework/Controller/lib/Horde/Controller/Scanner.php
new file mode 100644 (file)
index 0000000..57a2cf6
--- /dev/null
@@ -0,0 +1,170 @@
+<?php
+/**
+ * Copyright 2007 Maintainable Software, LLC
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * @author     Mike Naberezny <mike@maintainable.com>
+ * @author     Derek DeVries <derek@maintainable.com>
+ * @author     Chuck Hagenbuch <chuck@horde.org>
+ * @license    http://opensource.org/licenses/bsd-license.php
+ * @category   Horde
+ * @package    Horde_Controller
+ */
+
+/**
+ * Horde_Routes_Mapper requires a list of all possible controller names
+ * in order to build the regular expressions it uses for matching routes.
+ * It uses a callback, controllerScan, to get this list.
+ *
+ * Depending on the routes connected to the mapper, it may be possible to
+ * determine all of the controller names from the routes themselves.  If
+ * not, the filesystem must be scanned to determine the controller names.
+ *
+ * This class contains two controllerScan strategies, one that scans the
+ * filesystem and one that doesn't, and can determine the most efficient
+ * strategy to use for a given mapper.
+ *
+ * @author     Mike Naberezny <mike@maintainable.com>
+ * @author     Derek DeVries <derek@maintainable.com>
+ * @author     Chuck Hagenbuch <chuck@horde.org>
+ * @license    http://opensource.org/licenses/bsd-license.php
+ * @category   Horde
+ * @package    Horde_Controller
+ */
+class Horde_Controller_Scanner
+{
+    /**
+     * @var Horde_Routes_Mapper
+     */
+    protected $_mapper;
+
+    /**
+     * controllerScan strategy selected for this mapper.
+     * @var callback
+     */
+    protected $_callback;
+
+    /**
+     * Array of controller names collected from route hardcodes
+     * @var array
+     */
+    protected $_controllers;
+
+
+    /**
+     * Constructor.  Analyze the routes connected to this mapper to
+     * select a controllerScan strategy.
+     *
+     * @param  Horde_Routes_Mapper
+     */
+    public function __construct($mapper)
+    {
+        $this->_mapper = $mapper;
+        $this->analyze();
+    }
+
+    /**
+     * Analyze the routes connected to the mapper.  If all of the possible
+     * controller names can be determined from the routes themselves, select
+     * the scanHardcodes() strategy that returns them collected from the
+     * routes.  If the possible controller names cannot be determined this
+     * way, select the scanFilesystem() strategy.
+     */
+    public function analyze()
+    {
+        $needScan = false;
+        $controllers = array();
+        foreach ($this->_mapper->matchList as $route) {
+            if (in_array('controller', $route->hardCoded)) {
+                $controllers[ $route->defaults['controller'] ] = true;
+            } else {
+                $needScan = true;
+                break;
+            }
+        }
+        $this->_controllers = array_keys($controllers);
+
+        if ($needScan || empty($this->_controllers)) {
+            $this->_callback = array($this, 'scanFilesystem');
+        } else {
+            $this->_callback = array($this, 'scanHardcodes');
+        }
+    }
+
+    /**
+     * Get the controllerScan callback stategy selected for this mapper.
+     *
+     * @return callback
+     */
+    public function getCallback()
+    {
+        return $this->_callback;
+    }
+
+    /**
+     * Scan a directory and return an array of the controllers it contains.
+     * The array is used by Horde_Routes to build its matching regexps.
+     *
+     * @param  string  $dirname  Controller directory
+     * @param  string  $prefix   Prefix controllers found with string
+     * @return array             Controller names
+     */
+    public function scanFilesystem($dirname = null, $prefix = '')
+    {
+        $controllers = array();
+
+        if ($dirname === null) {
+            return $controllers;
+        }
+
+        $baseregexp = preg_quote($dirname, '/');
+
+        foreach (new RecursiveIteratorIterator(
+                 new RecursiveDirectoryIterator($dirname)) as $entry) {
+
+            if ($entry->isFile()) {
+                // match .php files that don't start with an underscore
+                if (preg_match('/^[^_]{1,1}.*\.php$/', basename($entry->getFilename())) != 0) {
+                    // strip off base path: dirname/admin/users.php -> admin/users.php
+                    $controller = preg_replace("/^$baseregexp(.*)\.php/", '\\1', $entry->getPathname());
+
+                    // PrepareController -> prepare_controller -> prepare
+                    $controller = strtolower(preg_replace('/([a-z])([A-Z])/', "\${1}_\${2}", $controller));
+                    $controller = substr($controller, 0, -(strlen('_controller')));
+
+                    // add to controller list
+                    $controllers[] = $prefix . $controller;
+                }
+            }
+        }
+
+        $callback = array('Horde_Routes_Utils', 'longestFirst');
+        usort($controllers, $callback);
+
+        return $controllers;
+    }
+
+    /**
+     * Return an array of controller names that were collected from the
+     * hardcodes of the routes connected to this mapper.
+     *
+     * @param  string  $dirname  For method signature compatibility only
+     * @param  string  $prefix   Prefix controllers found with string
+     * @return array             Controller names
+     */
+    public function scanHardcodes($dirname = null, $prefix = null)
+    {
+        if ($prefix === null) {
+            $controllers = $this->_controllers;
+        } else {
+            $controllers = array();
+            foreach ($this->_controllers as $controller) {
+                $controllers[] = $prefix . $controller;
+            }
+        }
+
+        usort($controllers, 'Horde_Routes_Utils::longestFirst');
+        return $controllers;
+    }
+
+}
\ No newline at end of file
index 37cf420..b2cc149 100644 (file)
@@ -52,8 +52,9 @@ http://pear.php.net/dtd/package-2.0.xsd">
       <file name="Base.php" role="php" />
       <file name="Dispatcher.php" role="php" />
       <file name="Exception.php" role="php" />
-      <file name="UrlWriter.php" role="php" />
+      <file name="Scanner.php" role="php" />
       <file name="StatusCodes.php" role="php" />
+      <file name="UrlWriter.php" role="php" />
      </dir> <!-- /lib/Horde/Controller -->
     </dir> <!-- /lib/Horde -->
    </dir> <!-- /lib -->
@@ -74,8 +75,9 @@ http://pear.php.net/dtd/package-2.0.xsd">
    <install name="lib/Horde/Controller/Base.php" as="Horde/Controller/Base.php" />
    <install name="lib/Horde/Controller/Dispatcher.php" as="Horde/Controller/Dispatcher.php" />
    <install name="lib/Horde/Controller/Exception.php" as="Horde/Controller/Exception.php" />
-   <install name="lib/Horde/Controller/UrlWriter.php" as="Horde/Controller/UrlWriter.php" />
+   <install name="lib/Horde/Controller/Scanner.php" as="Horde/Controller/Scanner.php" />
    <install name="lib/Horde/Controller/StatusCodes.php" as="Horde/Controller/StatusCodes.php" />
+   <install name="lib/Horde/Controller/UrlWriter.php" as="Horde/Controller/UrlWriter.php" />
    <install name="lib/Horde/Controller/Mime/Type.php" as="Horde/Controller/Mime/Type.php" />
    <install name="lib/Horde/Controller/Request/Base.php" as="Horde/Controller/Request/Base.php" />
    <install name="lib/Horde/Controller/Request/Http.php" as="Horde/Controller/Request/Http.php" />