forked from GNUsocial/gnu-social
		
	
		
			
				
	
	
		
			430 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			430 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | |
| /**
 | |
|  * URL parser and mapper
 | |
|  *
 | |
|  * PHP version 5
 | |
|  *
 | |
|  * LICENSE:
 | |
|  * 
 | |
|  * Copyright (c) 2006, Bertrand Mansion <golgote@mamasam.com>
 | |
|  * All rights reserved.
 | |
|  *
 | |
|  * Redistribution and use in source and binary forms, with or without
 | |
|  * modification, are permitted provided that the following conditions
 | |
|  * are met:
 | |
|  *
 | |
|  *    * Redistributions of source code must retain the above copyright
 | |
|  *      notice, this list of conditions and the following disclaimer.
 | |
|  *    * Redistributions in binary form must reproduce the above copyright
 | |
|  *      notice, this list of conditions and the following disclaimer in the 
 | |
|  *      documentation and/or other materials provided with the distribution.
 | |
|  *    * The names of the authors may not be used to endorse or promote products 
 | |
|  *      derived from this software without specific prior written permission.
 | |
|  *
 | |
|  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 | |
|  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 | |
|  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 | |
|  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 | |
|  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 | |
|  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 | |
|  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 | |
|  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 | |
|  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 | |
|  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 | |
|  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | |
|  *
 | |
|  * @category   Net
 | |
|  * @package    Net_URL_Mapper
 | |
|  * @author     Bertrand Mansion <golgote@mamasam.com>
 | |
|  * @license    http://opensource.org/licenses/bsd-license.php New BSD License
 | |
|  * @version    CVS: $Id: Path.php,v 1.1 2007/03/28 10:23:04 mansion Exp $
 | |
|  * @link       http://pear.php.net/package/Net_URL_Mapper
 | |
|  */
 | |
| 
 | |
| require_once 'Net/URL.php';
 | |
| require_once 'Net/URL/Mapper/Part/Dynamic.php';
 | |
| require_once 'Net/URL/Mapper/Part/Wildcard.php';
 | |
| require_once 'Net/URL/Mapper/Part/Fixed.php';
 | |
| 
 | |
| class Net_URL_Mapper_Path
 | |
| {
 | |
|     private $path = '';
 | |
|     private $N = 0;
 | |
|     public $token;
 | |
|     public $value;
 | |
|     private $line = 1;
 | |
|     private $state = 1;
 | |
| 
 | |
| 
 | |
|     protected $alias;
 | |
|     protected $rules = array();
 | |
|     protected $defaults = array();
 | |
|     protected $parts = array();
 | |
|     protected $rule;
 | |
|     protected $format;
 | |
|     protected $minKeys;
 | |
|     protected $maxKeys;
 | |
|     protected $fixed = true;
 | |
|     protected $required;
 | |
| 
 | |
|     public function __construct($path = '', $defaults = array(), $rules = array())
 | |
|     {
 | |
|         $this->path = '/'.trim(Net_URL::resolvePath($path), '/');
 | |
|         $this->setDefaults($defaults);
 | |
|         $this->setRules($rules);
 | |
| 
 | |
|         try {
 | |
|             $this->parsePath();
 | |
|         } catch (Exception $e) {
 | |
|             // The path could not be parsed correctly, treat it as fixed
 | |
|             $this->fixed = true;
 | |
|             $part = self::createPart(Net_URL_Mapper_Part::FIXED, $this->path, $this->path);
 | |
|             $this->parts = array($part);
 | |
|         }
 | |
|         $this->getRequired();
 | |
|     }
 | |
| 
 | |
|     public function getPath()
 | |
|     {
 | |
|         return $this->path;
 | |
|     }
 | |
| 
 | |
|     protected function parsePath()
 | |
|     {
 | |
|         while ($this->yylex()) { }
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|     * Get the path alias
 | |
|     * Path aliases can be used instead of full path
 | |
|     * @return null|string
 | |
|     */
 | |
|     public function getAlias()
 | |
|     {
 | |
|         return $this->alias;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|     * Set the path name
 | |
|     * @param string Set the path name
 | |
|     * @see getAlias()
 | |
|     */
 | |
|     public function setAlias($alias)
 | |
|     {
 | |
|         $this->alias = $alias;
 | |
|         return $this;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|     * Get the path parts default values
 | |
|     * @return null|array
 | |
|     */
 | |
|     public function getDefaults()
 | |
|     {
 | |
|         return $this->defaults;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|     * Set the path parts default values
 | |
|     * @param array  Associative array with format partname => value
 | |
|     */    
 | |
|     public function setDefaults($defaults)
 | |
|     {
 | |
|         if (is_array($defaults)) {
 | |
|             $this->defaults = $defaults;
 | |
|         } else {
 | |
|             $this->defaults = array();
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|     * Set the path parts default values
 | |
|     * @param array  Associative array with format partname => value
 | |
|     */    
 | |
|     public function setRules($rules)
 | |
|     {
 | |
|         if (is_array($rules)) {
 | |
|             $this->rules = $rules;   
 | |
|         } else {
 | |
|             $this->rules = array();
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|     * Returns the regular expression used to match this path
 | |
|     * @return string  PERL Regular expression
 | |
|     */ 
 | |
|     public function getRule()
 | |
|     {
 | |
|         if (is_null($this->rule)) {
 | |
|             $this->rule = '/^';
 | |
|             foreach ($this->parts as $path => $part) {
 | |
|                 $this->rule .= $part->getRule();
 | |
|             }
 | |
|             $this->rule .= '$/';
 | |
|         }
 | |
|         return $this->rule;
 | |
|     }
 | |
| 
 | |
|     public function getFormat()
 | |
|     {
 | |
|         if (is_null($this->format)) {
 | |
|             $this->format = '/^';
 | |
|             foreach ($this->parts as $path => $part) {
 | |
|                 $this->format .= $part->getFormat();
 | |
|             }
 | |
|             $this->format .= '$/';
 | |
|         }
 | |
|         return $this->format;
 | |
|     }
 | |
| 
 | |
|     protected function addPart($part)
 | |
|     {
 | |
|         if (array_key_exists($part->content, $this->defaults)) {
 | |
|             $part->setRequired(false);
 | |
|             $part->setDefaults($this->defaults[$part->content]);
 | |
|         }
 | |
|         if (isset($this->rules[$part->content])) {
 | |
|             $part->setRule($this->rules[$part->content]);
 | |
|         }
 | |
|         $this->rule = null;
 | |
|         if ($part->getType() != Net_URL_Mapper_Part::FIXED) {
 | |
|             $this->fixed = false;
 | |
|             $this->parts[$part->content] = $part;
 | |
|         } else {
 | |
|             $this->parts[] = $part;
 | |
|         }
 | |
|         return $part;
 | |
|     }
 | |
| 
 | |
|     public static function createPart($type, $content, $path)
 | |
|     {
 | |
|         switch ($type) {
 | |
|             case Net_URL_Mapper_Part::DYNAMIC:
 | |
|                 return new Net_URL_Mapper_Part_Dynamic($content, $path);
 | |
|                 break;
 | |
|             case Net_URL_Mapper_Part::WILDCARD:
 | |
|                 return new Net_URL_Mapper_Part_Wildcard($content, $path);
 | |
|                 break;
 | |
|             default:
 | |
|                 return new Net_URL_Mapper_Part_Fixed($content, $path);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|     * Checks whether the path contains the given part by name
 | |
|     * If value parameter is given, the part also checks if the 
 | |
|     * given value conforms to the part rule.
 | |
|     * @param string Part name
 | |
|     * @param mixed  The value to check against 
 | |
|     */
 | |
|     public function hasKey($partName, $value = null)
 | |
|     {
 | |
|         if (array_key_exists($partName, $this->parts)) {
 | |
|             if (!is_null($value) && $value !== false) {
 | |
|                 return $this->parts[$partName]->match($value);
 | |
|             } else {
 | |
|                 return true;
 | |
|             }
 | |
|         } elseif (array_key_exists($partName, $this->defaults) &&
 | |
|             $value == $this->defaults[$partName]) {
 | |
|             return true;
 | |
|         }
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     public function generate($values = array(), $qstring = array(), $anchor = '')
 | |
|     {
 | |
|         $path = '';
 | |
|         foreach ($this->parts as $part) {
 | |
|             $path .= $part->generate($values);
 | |
|         }
 | |
|         $path = '/'.trim(Net_URL::resolvePath($path), '/');
 | |
|         if (!empty($qstring)) {
 | |
|             $path .= '?'.http_build_query($qstring);
 | |
|         }
 | |
|         if (!empty($anchor)) {
 | |
|             $path .= '#'.ltrim($anchor, '#');
 | |
|         }
 | |
|         return $path;
 | |
|     }
 | |
| 
 | |
|     public function getRequired()
 | |
|     {
 | |
|         if (!isset($this->required)) {
 | |
|             $req = array();
 | |
|             foreach ($this->parts as $part) {
 | |
|                 if ($part->isRequired()) {
 | |
|                     $req[] = $part->content;
 | |
|                 }
 | |
|             }
 | |
|             $this->required = $req;
 | |
|         }
 | |
|         return $this->required;
 | |
|     }
 | |
| 
 | |
|     public function getMaxKeys()
 | |
|     {
 | |
|         if (is_null($this->maxKeys)) {
 | |
|             $this->maxKeys = count($this->required);
 | |
|             $this->maxKeys += count($this->defaults);
 | |
|         }
 | |
|         return $this->maxKeys;
 | |
|     }
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
|     private $_yy_state = 1;
 | |
|     private $_yy_stack = array();
 | |
| 
 | |
|     function yylex()
 | |
|     {
 | |
|         return $this->{'yylex' . $this->_yy_state}();
 | |
|     }
 | |
| 
 | |
|     function yypushstate($state)
 | |
|     {
 | |
|         array_push($this->_yy_stack, $this->_yy_state);
 | |
|         $this->_yy_state = $state;
 | |
|     }
 | |
| 
 | |
|     function yypopstate()
 | |
|     {
 | |
|         $this->_yy_state = array_pop($this->_yy_stack);
 | |
|     }
 | |
| 
 | |
|     function yybegin($state)
 | |
|     {
 | |
|         $this->_yy_state = $state;
 | |
|     }
 | |
| 
 | |
| 
 | |
| 
 | |
|     function yylex1()
 | |
|     {
 | |
|         $tokenMap = array (
 | |
|               1 => 1,
 | |
|               3 => 1,
 | |
|               5 => 1,
 | |
|               7 => 1,
 | |
|               9 => 1,
 | |
|             );
 | |
|         if ($this->N >= strlen($this->path)) {
 | |
|             return false; // end of input
 | |
|         }
 | |
|         $yy_global_pattern = "/^(\/?:\/?\\(([a-zA-Z0-9_]+)\\))|^(\/?\\*\/?\\(([a-zA-Z0-9_]+)\\))|^(\/?:([a-zA-Z0-9_]+))|^(\/?\\*([a-zA-Z0-9_]+))|^(\/?([^\/:*]+))/";
 | |
| 
 | |
|         do {
 | |
|             if (preg_match($yy_global_pattern, substr($this->path, $this->N), $yymatches)) {
 | |
|                 $yysubmatches = $yymatches;
 | |
|                 $yymatches = array_filter($yymatches, 'strlen'); // remove empty sub-patterns
 | |
|                 if (!count($yymatches)) {
 | |
|                     throw new Exception('Error: lexing failed because a rule matched' .
 | |
|                         'an empty string.  Input "' . substr($this->path,
 | |
|                         $this->N, 5) . '... state START');
 | |
|                 }
 | |
|                 next($yymatches); // skip global match
 | |
|                 $this->token = key($yymatches); // token number
 | |
|                 if ($tokenMap[$this->token]) {
 | |
|                     // extract sub-patterns for passing to lex function
 | |
|                     $yysubmatches = array_slice($yysubmatches, $this->token + 1,
 | |
|                         $tokenMap[$this->token]);
 | |
|                 } else {
 | |
|                     $yysubmatches = array();
 | |
|                 }
 | |
|                 $this->value = current($yymatches); // token value
 | |
|                 $r = $this->{'yy_r1_' . $this->token}($yysubmatches);
 | |
|                 if ($r === null) {
 | |
|                     $this->N += strlen($this->value);
 | |
|                     $this->line += substr_count("\n", $this->value);
 | |
|                     // accept this token
 | |
|                     return true;
 | |
|                 } elseif ($r === true) {
 | |
|                     // we have changed state
 | |
|                     // process this token in the new state
 | |
|                     return $this->yylex();
 | |
|                 } elseif ($r === false) {
 | |
|                     $this->N += strlen($this->value);
 | |
|                     $this->line += substr_count("\n", $this->value);
 | |
|                     if ($this->N >= strlen($this->path)) {
 | |
|                         return false; // end of input
 | |
|                     }
 | |
|                     // skip this token
 | |
|                     continue;
 | |
|                 } else {                    $yy_yymore_patterns = array(
 | |
|         1 => "^(\/?\\*\/?\\(([a-zA-Z0-9_]+)\\))|^(\/?:([a-zA-Z0-9_]+))|^(\/?\\*([a-zA-Z0-9_]+))|^(\/?([^\/:*]+))",
 | |
|         3 => "^(\/?:([a-zA-Z0-9_]+))|^(\/?\\*([a-zA-Z0-9_]+))|^(\/?([^\/:*]+))",
 | |
|         5 => "^(\/?\\*([a-zA-Z0-9_]+))|^(\/?([^\/:*]+))",
 | |
|         7 => "^(\/?([^\/:*]+))",
 | |
|         9 => "",
 | |
|     );
 | |
| 
 | |
|                     // yymore is needed
 | |
|                     do {
 | |
|                         if (!strlen($yy_yymore_patterns[$this->token])) {
 | |
|                             throw new Exception('cannot do yymore for the last token');
 | |
|                         }
 | |
|                         if (preg_match($yy_yymore_patterns[$this->token],
 | |
|                               substr($this->path, $this->N), $yymatches)) {
 | |
|                             $yymatches = array_filter($yymatches, 'strlen'); // remove empty sub-patterns
 | |
|                             next($yymatches); // skip global match
 | |
|                             $this->token = key($yymatches); // token number
 | |
|                             $this->value = current($yymatches); // token value
 | |
|                             $this->line = substr_count("\n", $this->value);
 | |
|                         }
 | |
|                     } while ($this->{'yy_r1_' . $this->token}() !== null);
 | |
|                     // accept
 | |
|                     $this->N += strlen($this->value);
 | |
|                     $this->line += substr_count("\n", $this->value);
 | |
|                     return true;
 | |
|                 }
 | |
|             } else {
 | |
|                 throw new Exception('Unexpected input at line' . $this->line .
 | |
|                     ': ' . $this->path[$this->N]);
 | |
|             }
 | |
|             break;
 | |
|         } while (true);
 | |
|     } // end function
 | |
| 
 | |
| 
 | |
|     const START = 1;
 | |
|     function yy_r1_1($yy_subpatterns)
 | |
|     {
 | |
| 
 | |
|     $c = $yy_subpatterns[0];
 | |
|     $part = self::createPart(Net_URL_Mapper_Part::DYNAMIC, $c, $this->value);
 | |
|     $this->addPart($part);
 | |
|     }
 | |
|     function yy_r1_3($yy_subpatterns)
 | |
|     {
 | |
| 
 | |
|     $c = $yy_subpatterns[0];
 | |
|     $part = self::createPart(Net_URL_Mapper_Part::WILDCARD, $c, $this->value);
 | |
|     $this->addPart($part);
 | |
|     }
 | |
|     function yy_r1_5($yy_subpatterns)
 | |
|     {
 | |
| 
 | |
|     $c = $yy_subpatterns[0];
 | |
|     $part = self::createPart(Net_URL_Mapper_Part::DYNAMIC, $c, $this->value);
 | |
|     $this->addPart($part);
 | |
|     }
 | |
|     function yy_r1_7($yy_subpatterns)
 | |
|     {
 | |
| 
 | |
|     $c = $yy_subpatterns[0];
 | |
|     $part = self::createPart(Net_URL_Mapper_Part::WILDCARD, $c, $this->value);
 | |
|     $this->addPart($part);
 | |
|     }
 | |
|     function yy_r1_9($yy_subpatterns)
 | |
|     {
 | |
| 
 | |
|     $c = $yy_subpatterns[0];
 | |
|     $part = self::createPart(Net_URL_Mapper_Part::FIXED, $c, $this->value);
 | |
|     $this->addPart($part);
 | |
|     }
 | |
| 
 | |
| }
 | |
| 
 | |
| ?>
 |