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