3 * URL parser and mapper
9 * Copyright (c) 2006, Bertrand Mansion <golgote@mamasam.com>
10 * All rights reserved.
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
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.
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.
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.plex 283937 2009-07-12 11:37:21Z mansion $
41 * @link http://pear.php.net/package/Net_URL_Mapper
44 require_once 'Net/URL/Mapper/Part/Dynamic.php';
45 require_once 'Net/URL/Mapper/Part/Wildcard.php';
46 require_once 'Net/URL/Mapper/Part/Fixed.php';
48 class Net_URL_Mapper_Path
59 protected $rules = array();
60 protected $defaults = array();
61 protected $parts = array();
66 protected $fixed = true;
69 public function __construct($path = '', $defaults = array(), $rules = array())
71 $this->path = '/'.trim(Net_URL::resolvePath($path), '/');
72 $this->setDefaults($defaults);
73 $this->setRules($rules);
77 } catch (Exception $e) {
78 // The path could not be parsed correctly, treat it as fixed
80 $part = self::createPart(Net_URL_Mapper_Part::FIXED, $this->path, $this->path);
81 $this->parts = array($part);
87 * Called when the object is serialized
88 * Make sure we do not store too much info when the object is serialized
89 * and call the regular expressions generator functions so that they will
90 * not need to be generated again on wakeup.
92 * @return array Name of properties to store when serialized
94 public function __sleep()
98 return array('alias', 'path', 'defaults', 'rule', 'format',
99 'parts', 'minKeys', 'maxKeys', 'fixed', 'required');
102 public function getPath()
107 protected function parsePath()
109 while ($this->yylex()) { }
114 * Path aliases can be used instead of full path
115 * @return null|string
117 public function getAlias()
124 * @param string Set the path name
127 public function setAlias($alias)
129 $this->alias = $alias;
134 * Get the path parts default values
137 public function getDefaults()
139 return $this->defaults;
143 * Set the path parts default values
144 * @param array Associative array with format partname => value
146 public function setDefaults($defaults)
148 if (is_array($defaults)) {
149 $this->defaults = $defaults;
151 $this->defaults = array();
156 * Set the path parts default values
157 * @param array Associative array with format partname => value
159 public function setRules($rules)
161 if (is_array($rules)) {
162 $this->rules = $rules;
164 $this->rules = array();
169 * Returns the regular expression used to match this path
170 * @return string PERL Regular expression
172 public function getRule()
174 if (is_null($this->rule)) {
176 foreach ($this->parts as $path => $part) {
177 $this->rule .= $part->getRule();
184 public function getFormat()
186 if (is_null($this->format)) {
187 $this->format = '/^';
188 foreach ($this->parts as $path => $part) {
189 $this->format .= $part->getFormat();
191 $this->format .= '$/';
193 return $this->format;
196 protected function addPart($part)
198 if (array_key_exists($part->content, $this->defaults)) {
199 $part->setRequired(false);
200 $part->setDefaults($this->defaults[$part->content]);
202 if (isset($this->rules[$part->content])) {
203 $part->setRule($this->rules[$part->content]);
206 if ($part->getType() != Net_URL_Mapper_Part::FIXED) {
207 $this->fixed = false;
208 $this->parts[$part->content] = $part;
210 $this->parts[] = $part;
215 public static function createPart($type, $content, $path)
218 case Net_URL_Mapper_Part::DYNAMIC:
219 return new Net_URL_Mapper_Part_Dynamic($content, $path);
221 case Net_URL_Mapper_Part::WILDCARD:
222 return new Net_URL_Mapper_Part_Wildcard($content, $path);
225 return new Net_URL_Mapper_Part_Fixed($content, $path);
230 * Checks whether the path contains the given part by name
231 * If value parameter is given, the part also checks if the
232 * given value conforms to the part rule.
233 * @param string Part name
234 * @param mixed The value to check against
236 public function hasKey($partName, $value = null)
238 if (array_key_exists($partName, $this->parts)) {
239 if (!is_null($value) && $value !== false) {
240 return $this->parts[$partName]->match($value);
244 } elseif (array_key_exists($partName, $this->defaults) &&
245 $value == $this->defaults[$partName]) {
251 public function generate($values = array(), $qstring = array(), $anchor = '')
254 foreach ($this->parts as $part) {
255 $path .= $part->generate($values);
257 $path = '/'.trim(Net_URL::resolvePath($path), '/');
258 if (!empty($qstring)) {
259 $path .= '?'.http_build_query($qstring);
261 if (!empty($anchor)) {
262 $path .= '#'.ltrim($anchor, '#');
267 public function getRequired()
269 if (!isset($this->required)) {
271 foreach ($this->parts as $part) {
272 if ($part->isRequired()) {
273 $req[] = $part->content;
276 $this->required = $req;
278 return $this->required;
281 public function getMaxKeys()
283 if (is_null($this->maxKeys)) {
284 $this->maxKeys = count($this->required);
285 $this->maxKeys += count($this->defaults);
287 return $this->maxKeys;
298 static = /\/?([^\/:\*]+)/
299 variable = /([a-zA-Z0-9_]+)/
302 grouping = /\/?\(([a-zA-Z0-9_]+)\)/
307 $c = $yy_subpatterns[0];
308 $part = self::createPart(Net_URL_Mapper_Part::DYNAMIC, $c, $this->value);
309 $this->addPart($part);
312 $c = $yy_subpatterns[0];
313 $part = self::createPart(Net_URL_Mapper_Part::WILDCARD, $c, $this->value);
314 $this->addPart($part);
317 $c = $yy_subpatterns[0];
318 $part = self::createPart(Net_URL_Mapper_Part::DYNAMIC, $c, $this->value);
319 $this->addPart($part);
322 $c = $yy_subpatterns[0];
323 $part = self::createPart(Net_URL_Mapper_Part::WILDCARD, $c, $this->value);
324 $this->addPart($part);
327 $c = $yy_subpatterns[0];
328 $part = self::createPart(Net_URL_Mapper_Part::FIXED, $c, $this->value);
329 $this->addPart($part);