]> git.mxchange.org Git - quix0rs-gnu-social.git/commitdiff
new URLMapper without Net_URL_Mapper
authorEvan Prodromou <evan@status.net>
Wed, 14 Sep 2011 16:15:56 +0000 (12:15 -0400)
committerEvan Prodromou <evan@status.net>
Wed, 14 Sep 2011 16:15:56 +0000 (12:15 -0400)
lib/router.php
lib/urlmapper.php [new file with mode: 0644]
lib/util.php

index 180d8f791b97d3bc351984ad9effbf17f1ca2715..8fbb6eb1f43ff7ae4478e6b7dc686e5ddf6f5f5a 100644 (file)
@@ -31,73 +31,6 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
     exit(1);
 }
 
-require_once 'Net/URL/Mapper.php';
-
-class StatusNet_URL_Mapper extends Net_URL_Mapper
-{
-    private static $_singleton = null;
-    private $_actionToPath = array();
-
-    private function __construct()
-    {
-    }
-
-    public static function getInstance($id = '__default__')
-    {
-        if (empty(self::$_singleton)) {
-            self::$_singleton = new StatusNet_URL_Mapper();
-        }
-        return self::$_singleton;
-    }
-
-    public function connect($path, $defaults = array(), $rules = array())
-    {
-        $result = null;
-        if (Event::handle('StartConnectPath', array(&$path, &$defaults, &$rules, &$result))) {
-            $result = parent::connect($path, $defaults, $rules);
-            if (array_key_exists('action', $defaults)) {
-                $action = $defaults['action'];
-            } elseif (array_key_exists('action', $rules)) {
-                $action = $rules['action'];
-            } else {
-                $action = null;
-            }
-            $this->_mapAction($action, $result);
-            Event::handle('EndConnectPath', array($path, $defaults, $rules, $result));
-        }
-        return $result;
-    }
-
-    protected function _mapAction($action, $path)
-    {
-        if (!array_key_exists($action, $this->_actionToPath)) {
-            $this->_actionToPath[$action] = array();
-        }
-        $this->_actionToPath[$action][] = $path;
-        return;
-    }
-
-    public function generate($values = array(), $qstring = array(), $anchor = '')
-    {
-        if (!array_key_exists('action', $values)) {
-            return parent::generate($values, $qstring, $anchor);
-        }
-
-        $action = $values['action'];
-
-        if (!array_key_exists($action, $this->_actionToPath)) {
-            return parent::generate($values, $qstring, $anchor);
-        }
-
-        $oldPaths    = $this->paths;
-        $this->paths = $this->_actionToPath[$action];
-        $result      = parent::generate($values, $qstring, $anchor);
-        $this->paths = $oldPaths;
-
-        return $result;
-    }
-}
-
 /**
  * URL Router
  *
@@ -159,8 +92,8 @@ class Router
      * you're running and the plugins that are enabled. To avoid having bad routes
      * get stuck in the cache, the key includes a list of plugins and the software
      * version.
-     *
-     * There can still be problems with a) differences in versions of the plugins and
+     * 
+    * There can still be problems with a) differences in versions of the plugins and
      * b) people running code between official versions, but these tend to be more
      * sophisticated users who can grok what's going on and clear their caches.
      *
@@ -183,7 +116,7 @@ class Router
 
     function initialize()
     {
-        $m = StatusNet_URL_Mapper::getInstance();
+        $m = new URLMapper();
 
         if (Event::handle('StartInitializeRouter', array(&$m))) {
 
@@ -1139,7 +1072,7 @@ class Router
     {
         try {
             $match = $this->m->match($path);
-        } catch (Net_URL_Mapper_InvalidException $e) {
+        } catch (Exception $e) {
             common_log(LOG_ERR, "Problem getting route for $path - " .
                        $e->getMessage());
             // TRANS: Client error on action trying to visit a non-existing page.
@@ -1161,7 +1094,6 @@ class Router
         }
 
         $url = $this->m->generate($args, $params, $fragment);
-
         // Due to a bug in the Net_URL_Mapper code, the returned URL may
         // contain a malformed query of the form ?p1=v1?p2=v2?p3=v3. We
         // repair that here rather than modifying the upstream code...
diff --git a/lib/urlmapper.php b/lib/urlmapper.php
new file mode 100644 (file)
index 0000000..dffb32c
--- /dev/null
@@ -0,0 +1,246 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * URL mapper
+ * 
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Cache
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * URL mapper
+ *
+ * Converts a path into a set of parameters, and vice versa
+ *
+ * We used to use Net_URL_Mapper, so there's a wrapper class at Router, q.v.
+ * 
+ * NUM's vagaries are the main reason we have weirdnesses here.
+ *
+ * @category  URL
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+class URLMapper
+{
+    const ACTION = 'action';
+
+    protected $statics = array();
+    protected $variables = array();
+    protected $reverse = array();
+
+    function connect($path, $args, $paramPatterns=null)
+    {
+        if (!array_key_exists(self::ACTION, $args)) {
+            throw new Exception(sprintf("Can't connect %s; path has no action.", $path));
+        }
+
+        $action = $args[self::ACTION];
+
+        $paramNames = $this->getParamNames($path);
+
+        if (empty($paramNames)) {
+            $this->statics[$path] = $args;
+            if (array_key_exists($action, $this->reverse)) {
+                $this->reverse[$args[self::ACTION]][] = array($args, $path);
+            } else {
+                $this->reverse[$args[self::ACTION]] = array(array($args, $path));
+            }
+        } else {
+
+            // Eff if I understand why some go here and some go there.
+            // Anyways, fixup my preconceptions
+
+            foreach ($paramNames as $name) {
+                if (!array_key_exists($name, $paramPatterns) &&
+                    array_key_exists($name, $args)) {
+                    $paramPatterns[$name] = $args[$name];
+                    unset($args[$name]);
+                }
+            }
+
+            $regex = $this->makeRegex($path, $paramPatterns);
+
+            $this->variables[] = array($args, $regex, $paramNames);
+
+            $format = $this->makeFormat($path, $paramPatterns);
+
+            if (array_key_exists($action, $this->reverse)) {
+                $this->reverse[$args[self::ACTION]][] = array($args, $format, $paramNames);
+            } else {
+                $this->reverse[$args[self::ACTION]] = array(array($args, $format, $paramNames));
+            }
+        }
+    }
+
+    function match($path)
+    {
+        if (array_key_exists($path, $this->statics)) {
+            return $this->statics[$path];
+        }
+
+        foreach ($this->variables as $pattern) {
+            list($args, $regex, $paramNames) = $pattern;
+            if (preg_match($regex, $path, $match)) {
+                $results = $args;
+                foreach ($paramNames as $name) {
+                    $results[$name] = $match[$name];
+                }
+                return $results;
+            }
+        }
+        
+        throw new Exception(sprintf('No match for path "%s"', $path));
+    }
+
+    function generate($args, $qstring, $fragment)
+    {
+        if (!array_key_exists(self::ACTION, $args)) {
+            throw new Exception("Every path needs an action.");
+        }
+
+        $action = $args[self::ACTION];
+
+        if (!array_key_exists($action, $this->reverse)) {
+            throw new Exception(sprintf('No candidate paths for action "%s"', $action));
+        }
+
+        $candidates = $this->reverse[$action];
+
+        foreach ($candidates as $candidate) {
+            if (count($candidate) == 2) { // static
+                list($tryArgs, $tryPath) = $candidate;
+                foreach ($tryArgs as $key => $value) {
+                    if (!array_key_exists($key, $args) || $args[$key] != $value) {
+                        // next candidate
+                        continue 2;
+                    }
+                }
+                // success
+                return $tryPath;
+            } else {
+                list($tryArgs, $format, $paramNames) = $candidate;
+
+                foreach ($tryArgs as $key => $value) {
+                    if (!array_key_exists($key, $args) || $args[$key] != $value) {
+                        // next candidate
+                        continue 2;
+                    }
+                }
+
+                // success
+
+                $toFormat = array();
+
+                foreach ($paramNames as $name) {
+                    if (!array_key_exists($name, $args)) {
+                        // next candidate
+                        continue 2;
+                    }
+                    $toFormat[] = $args[$name];
+                }
+
+                $path = vsprintf($format, $toFormat);
+
+                if (!empty($qstring)) {
+                    $path .= '?' . http_build_query($qstring);
+                }
+
+                return $path;
+            }
+        }
+
+        unset($args['action']);
+
+        if (empty($args)) {
+            throw new Exception(sprintf('No matches for action "%s"', $action));
+        }
+
+        $argstring = '';
+
+        foreach ($args as $key => $value) {
+            $argstring .= "$key=$value ";
+        }
+
+        throw new Exception(sprintf('No matches for action "%s" with arguments "%s"', $action, $argstring));
+    }
+
+    protected function getParamNames($path)
+    {
+        preg_match_all('/:(?P<name>\w+)/', $path, $match);
+        return $match['name'];
+    }
+
+    protected function makeRegex($path, $paramPatterns)
+    {
+        $pr = new PatternReplacer($paramPatterns);
+
+        $regex = preg_replace_callback('/:(\w+)/',
+                                       array($pr, 'toPattern'),
+                                       $path);
+
+        $regex = '#' . str_replace('#', '\#', $regex) . '#';
+
+        return $regex;
+    }
+
+    protected function makeFormat($path, $paramPatterns)
+    {
+        $format = preg_replace('/(:\w+)/', '%s', $path);
+
+        return $format;
+    }
+}
+
+class PatternReplacer
+{
+    private $patterns;
+
+    function __construct($patterns)
+    {
+        $this->patterns = $patterns;
+    }
+
+    function toPattern($matches)
+    {
+        // trim out the :
+        $name = $matches[1];
+        if (array_key_exists($name, $this->patterns)) {
+            $pattern = $this->patterns[$name];
+        } else {
+            // ???
+            $pattern = '\w+';
+        }
+        return '(?P<'.$name.'>'.$pattern.')';
+    }
+}
index ba705eb7fa1823650db3f048a5e3f0cce1984a74..3658e3ceea2e6a3ad19a12ed8723ee9b6df21ac0 100644 (file)
@@ -1237,12 +1237,12 @@ function common_local_url($action, $args=null, $params=null, $fragment=null, $ad
         $ssl = common_is_sensitive($action);
 
         if (common_config('site','fancy')) {
-            $url = common_path(mb_substr($path, 1), $ssl, $addSession);
+            $url = common_path($path, $ssl, $addSession);
         } else {
             if (mb_strpos($path, '/index.php') === 0) {
-                $url = common_path(mb_substr($path, 1), $ssl, $addSession);
+                $url = common_path($path, $ssl, $addSession);
             } else {
-                $url = common_path('index.php'.$path, $ssl, $addSession);
+                $url = common_path('index.php/'.$path, $ssl, $addSession);
             }
         }
         Event::handle('EndLocalURL', array(&$action, &$params, &$fragment, &$addSession, &$url));