]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - extlib/Net/URL/Mapper.php
Merge branch '0.8.x' of git@gitorious.org:statusnet/mainline into 0.9.x
[quix0rs-gnu-social.git] / extlib / Net / URL / Mapper.php
1 <?php
2 /**
3  * URL parser and mapper
4  *
5  * PHP version 5
6  *
7  * LICENSE:
8  * 
9  * Copyright (c) 2006, Bertrand Mansion <golgote@mamasam.com>
10  * All rights reserved.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  *
16  *    * Redistributions of source code must retain the above copyright
17  *      notice, this list of conditions and the following disclaimer.
18  *    * Redistributions in binary form must reproduce the above copyright
19  *      notice, this list of conditions and the following disclaimer in the 
20  *      documentation and/or other materials provided with the distribution.
21  *    * The names of the authors may not be used to endorse or promote products 
22  *      derived from this software without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
25  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
26  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
27  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
28  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
29  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
30  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
31  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
32  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
33  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
34  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35  *
36  * @category   Net
37  * @package    Net_URL_Mapper
38  * @author     Bertrand Mansion <golgote@mamasam.com>
39  * @license    http://opensource.org/licenses/bsd-license.php New BSD License
40  * @version    CVS: $Id: Mapper.php,v 1.1 2007/03/28 10:23:04 mansion Exp $
41  * @link       http://pear.php.net/package/Net_URL_Mapper
42  */
43
44 require_once 'Net/URL/Mapper/Path.php';
45 require_once 'Net/URL/Mapper/Exception.php';
46
47 /**
48  * URL parser and mapper class
49  *
50  * This class takes an URL and a configuration and returns formatted data
51  * about the request according to a configuration parameter
52  *
53  * @category   Net
54  * @package    Net_URL_Mapper
55  * @author     Bertrand Mansion <golgote@mamasam.com>
56  * @version    Release: @package_version@
57  */
58 class Net_URL_Mapper
59 {
60     /**
61     * Array of Net_URL_Mapper instances
62     * @var array
63     */
64     private static $instances = array();
65
66     /**
67     * Mapped paths collection
68     * @var array
69     */
70     protected $paths = array();
71
72     /**
73     * Prefix used for url mapping
74     * @var string
75     */
76     protected $prefix = '';
77
78     /**
79     * Optional scriptname if mod_rewrite is not available
80     * @var string
81     */
82     protected $scriptname = '';
83
84     /**
85     * Mapper instance id
86     * @var string
87     */
88     protected $id = '__default__';
89
90     /**
91     * Class constructor
92     * Constructor is private, you should use getInstance() instead.
93     */
94     private function __construct() { }
95
96     /**
97     * Returns a singleton object corresponding to the requested instance id
98     * @param  string    Requested instance name
99     * @return Object    Net_URL_Mapper Singleton
100     */
101     public static function getInstance($id = '__default__')
102     {
103         if (!isset(self::$instances[$id])) {
104             $m = new Net_URL_Mapper();
105             $m->id = $id;
106             self::$instances[$id] = $m;
107         }
108         return self::$instances[$id];
109     }
110
111     /**
112     * Returns the instance id
113     * @return   string  Mapper instance id
114     */
115     public function getId()
116     {
117         return $this->id;
118     }
119
120     /**
121     * Parses a path and creates a connection
122     * @param    string  The path to connect
123     * @param    array   Default values for path parts
124     * @param    array   Regular expressions for path parts
125     * @return   object  Net_URL_Mapper_Path
126     */
127     public function connect($path, $defaults = array(), $rules = array())
128     {
129         $pathObj = new Net_URL_Mapper_Path($path, $defaults, $rules);
130         $this->addPath($pathObj);
131         return $pathObj;
132     }
133
134     /**
135     * Set the url prefix if needed
136     *
137     * Example: using the prefix to differenciate mapper instances
138     * <code>
139     * $fr = Net_URL_Mapper::getInstance('fr');
140     * $fr->setPrefix('/fr');
141     * $en = Net_URL_Mapper::getInstance('en');
142     * $en->setPrefix('/en');
143     * </code>
144     *
145     * @param    string  URL prefix
146     */
147     public function setPrefix($prefix)
148     {
149         $this->prefix = '/'.trim($prefix, '/');
150     }
151
152     /**
153     * Set the scriptname if mod_rewrite not available
154     *
155     * Example: will match and generate url like
156     * - index.php/view/product/1
157     * <code>
158     * $m = Net_URL_Mapper::getInstance();
159     * $m->setScriptname('index.php');
160     * </code>
161     * @param    string  URL prefix
162     */
163     public function setScriptname($scriptname)
164     {
165         $this->scriptname = $scriptname;
166     }
167
168     /**
169     * Will attempt to match an url with a defined path
170     *
171     * If an url corresponds to a path, the resulting values are returned
172     * in an array. If none is found, null is returned. In case an url is
173     * matched but its content doesn't validate the path rules, an exception is
174     * thrown.
175     *
176     * @param    string  URL
177     * @return   array|null   array if match found, null otherwise
178     * @throws   Net_URL_Mapper_InvalidException
179     */
180     public function match($url)
181     {
182         $nurl = '/'.trim($url, '/');
183
184         // Remove scriptname if needed
185         
186         if (!empty($this->scriptname) &&
187             strpos($nurl, $this->scriptname) === 0) {
188             $nurl = substr($nurl, strlen($this->scriptname));
189             if (empty($nurl)) {
190                 $nurl = '/';
191             }
192         }
193
194         // Remove prefix
195         
196         if (!empty($this->prefix)) {
197             if (strpos($nurl, $this->prefix) !== 0) {
198                 return null;
199             }
200             $nurl = substr($nurl, strlen($this->prefix));
201             if (empty($nurl)) {
202                 $nurl = '/';
203             }
204         }
205         
206         // Remove query string
207         
208         if (($pos = strpos($nurl, '?')) !== false) {
209             $nurl = substr($nurl, 0, $pos);
210         }
211
212         $paths = array();
213         $values = null;
214
215         // Make a list of paths that conform to route format
216
217         foreach ($this->paths as $path) {
218             $regex = $path->getFormat();
219             if (preg_match($regex, $nurl)) {
220                 $paths[] = $path;
221             }   
222         }
223
224         // Make sure one of the paths found is valid
225
226         foreach ($paths as $path) {
227             $regex = $path->getRule();
228             if (preg_match($regex, $nurl, $matches)) {
229                 $values = $path->getDefaults();
230                 array_shift($matches);
231                 $clean = array();
232                 foreach ($matches as $k => $v) {
233                     $v = trim($v, '/');
234                     if (!is_int($k) && $v !== '') {
235                         $values[$k] = $v;
236                     }
237                 }
238                 break;
239             }
240         }
241
242         // A path conforms but does not validate
243
244         if (is_null($values) && !empty($paths)) {
245             $e = new Net_URL_Mapper_InvalidException('A path was found but is invalid.');
246             $e->setPath($paths[0]);
247             $e->setUrl($url);
248             throw $e;
249         }
250
251         return $values;
252     }
253
254     /**
255     * Generate an url based on given parameters
256     *
257     * Will attempt to find a path definition that matches the given parameters and
258     * will generate an url based on this path.
259     *
260     * @param    array   Values to be used for the url generation
261     * @param    array   Key/value pairs for query string if needed
262     * @param    string  Anchor (fragment) if needed
263     * @return   string|false    String if a rule was found, false otherwise
264     */
265     public function generate($values = array(), $qstring = array(), $anchor = '')
266     {
267         // Use root path if any
268
269         if (empty($values) && isset($this->paths['/'])) {
270             return $this->scriptname.$this->prefix.$this->paths['/']->generate($values, $qstring, $anchor);
271         }
272
273         foreach ($this->paths as $path) {
274             $set = array();
275             foreach ($values as $k => $v) {
276                 if ($path->hasKey($k, $v)) {
277                     $set[$k] = $v;
278                 }
279             }
280
281             if (count($set) == count($values) &&
282                 count($set) <= $path->getMaxKeys()) {
283
284                 $req = $path->getRequired();
285                 if (count(array_intersect(array_keys($set), $req)) != count($req)) {
286                     continue;
287                 }
288                 $gen = $path->generate($set, $qstring, $anchor);
289                 return $this->scriptname.$this->prefix.$gen;
290             }
291         }
292         return false;
293     }
294
295     /**
296     * Returns defined paths
297     * @return array     Array of paths
298     */
299     public function getPaths()
300     {
301         return $this->paths;
302     }
303
304     /**
305     * Reset all paths
306     * This is probably only useful for testing
307     */
308     public function reset()
309     {
310         $this->paths = array();
311         $this->prefix = '';
312     }
313
314     /**
315     * Add a new path to the mapper
316     * @param object     Net_URL_Mapper_Path object
317     */
318     public function addPath(Net_URL_Mapper_Path $path)
319     {
320         $this->paths[$path->getPath()] = $path;
321     }
322
323 }
324 ?>