]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - extlib/Net/URL/Mapper/Path.php
b541002c7af6a4473ec16b43b0b34eceac472f33
[quix0rs-gnu-social.git] / extlib / Net / URL / Mapper / Path.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: Path.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.php';
45 require_once 'Net/URL/Mapper/Part/Dynamic.php';
46 require_once 'Net/URL/Mapper/Part/Wildcard.php';
47 require_once 'Net/URL/Mapper/Part/Fixed.php';
48
49 class Net_URL_Mapper_Path
50 {
51     private $path = '';
52     private $N = 0;
53     public $token;
54     public $value;
55     private $line = 1;
56     private $state = 1;
57
58
59     protected $alias;
60     protected $rules = array();
61     protected $defaults = array();
62     protected $parts = array();
63     protected $rule;
64     protected $format;
65     protected $minKeys;
66     protected $maxKeys;
67     protected $fixed = true;
68     protected $required;
69
70     public function __construct($path = '', $defaults = array(), $rules = array())
71     {
72         $this->path = '/'.trim(Net_URL::resolvePath($path), '/');
73         $this->setDefaults($defaults);
74         $this->setRules($rules);
75
76         try {
77             $this->parsePath();
78         } catch (Exception $e) {
79             // The path could not be parsed correctly, treat it as fixed
80             $this->fixed = true;
81             $part = self::createPart(Net_URL_Mapper_Part::FIXED, $this->path, $this->path);
82             $this->parts = array($part);
83         }
84         $this->getRequired();
85     }
86
87     public function getPath()
88     {
89         return $this->path;
90     }
91
92     protected function parsePath()
93     {
94         while ($this->yylex()) { }
95     }
96
97     /**
98     * Get the path alias
99     * Path aliases can be used instead of full path
100     * @return null|string
101     */
102     public function getAlias()
103     {
104         return $this->alias;
105     }
106
107     /**
108     * Set the path name
109     * @param string Set the path name
110     * @see getAlias()
111     */
112     public function setAlias($alias)
113     {
114         $this->alias = $alias;
115         return $this;
116     }
117
118     /**
119     * Get the path parts default values
120     * @return null|array
121     */
122     public function getDefaults()
123     {
124         return $this->defaults;
125     }
126
127     /**
128     * Set the path parts default values
129     * @param array  Associative array with format partname => value
130     */    
131     public function setDefaults($defaults)
132     {
133         if (is_array($defaults)) {
134             $this->defaults = $defaults;
135         } else {
136             $this->defaults = array();
137         }
138     }
139
140     /**
141     * Set the path parts default values
142     * @param array  Associative array with format partname => value
143     */    
144     public function setRules($rules)
145     {
146         if (is_array($rules)) {
147             $this->rules = $rules;   
148         } else {
149             $this->rules = array();
150         }
151     }
152
153     /**
154     * Returns the regular expression used to match this path
155     * @return string  PERL Regular expression
156     */ 
157     public function getRule()
158     {
159         if (is_null($this->rule)) {
160             $this->rule = '/^';
161             foreach ($this->parts as $path => $part) {
162                 $this->rule .= $part->getRule();
163             }
164             $this->rule .= '$/';
165         }
166         return $this->rule;
167     }
168
169     public function getFormat()
170     {
171         if (is_null($this->format)) {
172             $this->format = '/^';
173             foreach ($this->parts as $path => $part) {
174                 $this->format .= $part->getFormat();
175             }
176             $this->format .= '$/';
177         }
178         return $this->format;
179     }
180
181     protected function addPart($part)
182     {
183         if (array_key_exists($part->content, $this->defaults)) {
184             $part->setRequired(false);
185             $part->setDefaults($this->defaults[$part->content]);
186         }
187         if (isset($this->rules[$part->content])) {
188             $part->setRule($this->rules[$part->content]);
189         }
190         $this->rule = null;
191         if ($part->getType() != Net_URL_Mapper_Part::FIXED) {
192             $this->fixed = false;
193             $this->parts[$part->content] = $part;
194         } else {
195             $this->parts[] = $part;
196         }
197         return $part;
198     }
199
200     public static function createPart($type, $content, $path)
201     {
202         switch ($type) {
203             case Net_URL_Mapper_Part::DYNAMIC:
204                 return new Net_URL_Mapper_Part_Dynamic($content, $path);
205                 break;
206             case Net_URL_Mapper_Part::WILDCARD:
207                 return new Net_URL_Mapper_Part_Wildcard($content, $path);
208                 break;
209             default:
210                 return new Net_URL_Mapper_Part_Fixed($content, $path);
211         }
212     }
213
214     /**
215     * Checks whether the path contains the given part by name
216     * If value parameter is given, the part also checks if the 
217     * given value conforms to the part rule.
218     * @param string Part name
219     * @param mixed  The value to check against 
220     */
221     public function hasKey($partName, $value = null)
222     {
223         if (array_key_exists($partName, $this->parts)) {
224             if (!is_null($value) && $value !== false) {
225                 return $this->parts[$partName]->match($value);
226             } else {
227                 return true;
228             }
229         } elseif (array_key_exists($partName, $this->defaults) &&
230             $value == $this->defaults[$partName]) {
231             return true;
232         }
233         return false;
234     }
235
236     public function generate($values = array(), $qstring = array(), $anchor = '')
237     {
238         $path = '';
239         foreach ($this->parts as $part) {
240             $path .= $part->generate($values);
241         }
242         $path = '/'.trim(Net_URL::resolvePath($path), '/');
243         if (!empty($qstring)) {
244             $path .= '?'.http_build_query($qstring);
245         }
246         if (!empty($anchor)) {
247             $path .= '#'.ltrim($anchor, '#');
248         }
249         return $path;
250     }
251
252     public function getRequired()
253     {
254         if (!isset($this->required)) {
255             $req = array();
256             foreach ($this->parts as $part) {
257                 if ($part->isRequired()) {
258                     $req[] = $part->content;
259                 }
260             }
261             $this->required = $req;
262         }
263         return $this->required;
264     }
265
266     public function getMaxKeys()
267     {
268         if (is_null($this->maxKeys)) {
269             $this->maxKeys = count($this->required);
270             $this->maxKeys += count($this->defaults);
271         }
272         return $this->maxKeys;
273     }
274
275
276
277
278     private $_yy_state = 1;
279     private $_yy_stack = array();
280
281     function yylex()
282     {
283         return $this->{'yylex' . $this->_yy_state}();
284     }
285
286     function yypushstate($state)
287     {
288         array_push($this->_yy_stack, $this->_yy_state);
289         $this->_yy_state = $state;
290     }
291
292     function yypopstate()
293     {
294         $this->_yy_state = array_pop($this->_yy_stack);
295     }
296
297     function yybegin($state)
298     {
299         $this->_yy_state = $state;
300     }
301
302
303
304     function yylex1()
305     {
306         $tokenMap = array (
307               1 => 1,
308               3 => 1,
309               5 => 1,
310               7 => 1,
311               9 => 1,
312             );
313         if ($this->N >= strlen($this->path)) {
314             return false; // end of input
315         }
316         $yy_global_pattern = "/^(\/?:\/?\\(([a-zA-Z0-9_]+)\\))|^(\/?\\*\/?\\(([a-zA-Z0-9_]+)\\))|^(\/?:([a-zA-Z0-9_]+))|^(\/?\\*([a-zA-Z0-9_]+))|^(\/?([^\/:*]+))/";
317
318         do {
319             if (preg_match($yy_global_pattern, substr($this->path, $this->N), $yymatches)) {
320                 $yysubmatches = $yymatches;
321                 $yymatches = array_filter($yymatches, 'strlen'); // remove empty sub-patterns
322                 if (!count($yymatches)) {
323                     throw new Exception('Error: lexing failed because a rule matched' .
324                         'an empty string.  Input "' . substr($this->path,
325                         $this->N, 5) . '... state START');
326                 }
327                 next($yymatches); // skip global match
328                 $this->token = key($yymatches); // token number
329                 if ($tokenMap[$this->token]) {
330                     // extract sub-patterns for passing to lex function
331                     $yysubmatches = array_slice($yysubmatches, $this->token + 1,
332                         $tokenMap[$this->token]);
333                 } else {
334                     $yysubmatches = array();
335                 }
336                 $this->value = current($yymatches); // token value
337                 $r = $this->{'yy_r1_' . $this->token}($yysubmatches);
338                 if ($r === null) {
339                     $this->N += strlen($this->value);
340                     $this->line += substr_count("\n", $this->value);
341                     // accept this token
342                     return true;
343                 } elseif ($r === true) {
344                     // we have changed state
345                     // process this token in the new state
346                     return $this->yylex();
347                 } elseif ($r === false) {
348                     $this->N += strlen($this->value);
349                     $this->line += substr_count("\n", $this->value);
350                     if ($this->N >= strlen($this->path)) {
351                         return false; // end of input
352                     }
353                     // skip this token
354                     continue;
355                 } else {                    $yy_yymore_patterns = array(
356         1 => "^(\/?\\*\/?\\(([a-zA-Z0-9_]+)\\))|^(\/?:([a-zA-Z0-9_]+))|^(\/?\\*([a-zA-Z0-9_]+))|^(\/?([^\/:*]+))",
357         3 => "^(\/?:([a-zA-Z0-9_]+))|^(\/?\\*([a-zA-Z0-9_]+))|^(\/?([^\/:*]+))",
358         5 => "^(\/?\\*([a-zA-Z0-9_]+))|^(\/?([^\/:*]+))",
359         7 => "^(\/?([^\/:*]+))",
360         9 => "",
361     );
362
363                     // yymore is needed
364                     do {
365                         if (!strlen($yy_yymore_patterns[$this->token])) {
366                             throw new Exception('cannot do yymore for the last token');
367                         }
368                         if (preg_match($yy_yymore_patterns[$this->token],
369                               substr($this->path, $this->N), $yymatches)) {
370                             $yymatches = array_filter($yymatches, 'strlen'); // remove empty sub-patterns
371                             next($yymatches); // skip global match
372                             $this->token = key($yymatches); // token number
373                             $this->value = current($yymatches); // token value
374                             $this->line = substr_count("\n", $this->value);
375                         }
376                     } while ($this->{'yy_r1_' . $this->token}() !== null);
377                     // accept
378                     $this->N += strlen($this->value);
379                     $this->line += substr_count("\n", $this->value);
380                     return true;
381                 }
382             } else {
383                 throw new Exception('Unexpected input at line' . $this->line .
384                     ': ' . $this->path[$this->N]);
385             }
386             break;
387         } while (true);
388     } // end function
389
390
391     const START = 1;
392     function yy_r1_1($yy_subpatterns)
393     {
394
395     $c = $yy_subpatterns[0];
396     $part = self::createPart(Net_URL_Mapper_Part::DYNAMIC, $c, $this->value);
397     $this->addPart($part);
398     }
399     function yy_r1_3($yy_subpatterns)
400     {
401
402     $c = $yy_subpatterns[0];
403     $part = self::createPart(Net_URL_Mapper_Part::WILDCARD, $c, $this->value);
404     $this->addPart($part);
405     }
406     function yy_r1_5($yy_subpatterns)
407     {
408
409     $c = $yy_subpatterns[0];
410     $part = self::createPart(Net_URL_Mapper_Part::DYNAMIC, $c, $this->value);
411     $this->addPart($part);
412     }
413     function yy_r1_7($yy_subpatterns)
414     {
415
416     $c = $yy_subpatterns[0];
417     $part = self::createPart(Net_URL_Mapper_Part::WILDCARD, $c, $this->value);
418     $this->addPart($part);
419     }
420     function yy_r1_9($yy_subpatterns)
421     {
422
423     $c = $yy_subpatterns[0];
424     $part = self::createPart(Net_URL_Mapper_Part::FIXED, $c, $this->value);
425     $this->addPart($part);
426     }
427
428 }
429
430 ?>