1<?php 2 3/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ 4 5/** 6 * Copyright (c) 2007-2009 Martin Jansen 7 * 8 * All rights reserved. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. The name of the author may not be used to endorse or promote products 19 * derived from this software without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 26 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 30 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 * 32 * @category Networking 33 * @package URI_Template 34 * @author Martin Jansen <mj@php.net> 35 * @license http://www.opensource.org/licenses/bsd-license.php BSD 36 * @version SVN: $Id: Template.php 294940 2010-02-12 00:11:48Z clockwerx $ 37 * @link http://pear.php.net/package/URI_Template 38 */ 39 40/** 41 * Parser for URI Templates 42 * 43 * This class implements parsing of URI Templates as defined in the IETF's 44 * URI Template draft. 45 * 46 * @category Networking 47 * @package URI_Template 48 * @author Martin Jansen <mj@php.net> 49 * @copyright 2007-2008 Martin Jansen 50 * @license http://www.opensource.org/licenses/bsd-license.php BSD 51 * @version Release: @package_version@ 52 * @link http://pear.php.net/package/URI_Template 53 * @since Class available since release 0.1.0 54 */ 55class URI_Template 56{ 57 /** 58 * The URI template string. 59 * 60 * @var string $template URI template string 61 */ 62 protected $template = ''; 63 64 /** 65 * The array containing the replacement variables. 66 * 67 * @var array $values Array of replacement variables 68 */ 69 protected $values = array(); 70 71 /** 72 * Constructor method 73 * 74 * @param string $template URI Template string 75 */ 76 public function __construct($template) 77 { 78 $this->template = $template; 79 } 80 81 /** 82 * Substitutes template expansions in the URI template. 83 * 84 * @param array $values Associative array with replacements for the 85 * variables in the expansions 86 * @param boolean $URLencode Boolean variable determing if the replacements 87 * should be encoded according to RFC 1738 or 88 * or not. Default is true, i.e. the replacements 89 * are encoded. 90 * 91 * @return string URI 92 */ 93 public function substitute($values, $URLencode = true) 94 { 95 /* We need to assign $values to an object member because it is needed 96 * in self::_substitute(). 97 */ 98 $this->values = $values; 99 100 /* Because it is common that a template contains several replacements, 101 * we do the URL encoding here instead of in _substitute. 102 */ 103 if ($URLencode) { 104 foreach ($this->values as &$value) { 105 if (is_array($value)) { 106 $value = array_map('rawurlencode', $value); 107 } else { 108 $value = rawurlencode($value); 109 } 110 } 111 } 112 113 return preg_replace_callback( 114 '~(\{[^\}]+\})~', 115 array($this, '_substitute'), 116 $this->template 117 ); 118 } 119 120 /** 121 * Return an array containing the template variables names. 122 * 123 * @return array Array of template variables names 124 */ 125 public function getTemplateVariables() 126 { 127 $variables = array(); 128 129 if (preg_match_all('~(\{[^\}]+\})~', $this->template, $matches)) { 130 foreach ($matches[0] as $match) { 131 $expansion = substr($match, 1, -1); 132 list( , , $vars) = $this->parseExpansion($expansion); 133 $variables = array_merge($variables, array_keys($vars)); 134 } 135 $variables = array_values(array_unique($variables)); 136 } 137 138 return $variables; 139 } 140 141 /** 142 * Callback method for handling a single replacement. 143 * 144 * @param array $matches an Array of matched elements 145 * 146 * @see substitute 147 * @return string 148 */ 149 protected function _substitute($matches) 150 { 151 $output = ''; 152 $expansion = substr($matches[0], 1, -1); 153 list($op, $arg, $variables) = $this->parseExpansion($expansion); 154 155 foreach (array_keys($variables) as $key) { 156 if (isset($this->values[$key])) { 157 $variables[$key] = $this->values[$key]; 158 } 159 } 160 161 if (!$op) { 162 $output = current($variables); 163 } else { 164 $opname = 'operation' . ucfirst(strtolower($op)); 165 if (in_array($opname, get_class_methods($this))) { 166 $output = $this->$opname($variables, $arg); 167 } 168 } 169 170 return $output; 171 } 172 173 /** 174 * Implements the 'prefix' operator. 175 * 176 * Adds the value of the second parameter to the beginning of the first 177 * element from the first parameter and returns the resulting string. 178 * The value of the second parameter may be an array. 179 * 180 * @param array $variables List of variables. Only the first element is 181 * used. 182 * @param string $arg Prefix string 183 * 184 * @return string 185 */ 186 protected function operationPrefix($variables, $arg) 187 { 188 $tmp = current($variables); 189 if (is_array($tmp)) { 190 if (count($tmp) > 0) { 191 $tmp = join($arg, $tmp); 192 } else { 193 $tmp = ''; 194 } 195 } 196 return (empty($tmp) ? '' : $arg . $tmp); 197 } 198 199 /** 200 * Implements the 'suffix' operator. 201 * 202 * Appends the value of the second parameter to the first element of the 203 * first parameter and returns the resulting string. The value of the 204 * second parameter may be an array. 205 * 206 * @param array $variables List of variables. Only the first element is 207 * used. 208 * @param string $arg String to append to the first element of 209 * $variables. 210 * 211 * @return string 212 */ 213 protected function operationSuffix($variables, $arg) 214 { 215 $tmp = current($variables); 216 if (is_array($tmp)) { 217 if (count($tmp) > 0) { 218 $tmp = join($arg, $tmp); 219 } else { 220 $tmp = ''; 221 } 222 } 223 return (empty($tmp) ? '' : $tmp . $arg); 224 } 225 226 /** 227 * Implements the 'join' operator. 228 * 229 * For each variable from the first parameter that is defined and 230 * non-empty create a keyvalue string that is the concatenation of the 231 * variable name, '=', and the variable value. All elements are in turn 232 * concatenated with the value of the second parameter. 233 * 234 * @param array $variables List of variables 235 * @param string $arg Join needle 236 * 237 * @return string 238 */ 239 protected function operationJoin($variables, $arg) 240 { 241 $tmp = array(); 242 ksort($variables); 243 foreach ($variables as $key => $value) { 244 if (empty($value)) { 245 continue; 246 } 247 248 $tmp[] = $key . '=' . $value; 249 } 250 251 return join($arg, $tmp); 252 } 253 254 /** 255 * Implements the 'list' operator. 256 * 257 * Joins the elements of the first element of the first parameter with the 258 * value of the second parameter. 259 * 260 * @param array $variables List of variables. Only the first element is 261 * used and this must be an array. 262 * @param string $arg Join needle 263 * 264 * @return string 265 */ 266 protected function operationList($variables, $arg) 267 { 268 $tmp = current($variables); 269 return (is_array($tmp) ? join($arg, $tmp) : ''); 270 } 271 272 /** 273 * Implements the 'opt' operator. 274 * 275 * If one or more variables from the first parameter are non-empty then 276 * this method returns the value of the second parameter. Otherwise an 277 * empty string is returned. 278 * 279 * @param array $variables List of variables 280 * @param string $arg Return value 281 * 282 * @return string 283 */ 284 protected function operationOpt($variables, $arg) 285 { 286 foreach ($variables as $value) { 287 $defined = (is_array($value) ? (count($value) > 0) : !empty($value)); 288 if ($defined) { 289 return $arg; 290 } 291 } 292 293 return ''; 294 } 295 296 /** 297 * Implements the 'neg' operator. 298 * 299 * If all the variables from the first parameter are empty then this method 300 * returns the value of the second parameter. Otherwise an empty string 301 * is returned. 302 * 303 * @param array $variables List of variables 304 * @param string $arg Return value 305 * 306 * @return string 307 */ 308 protected function operationNeg($variables, $arg) 309 { 310 $defined = false; 311 foreach ($variables as $value) { 312 $defined = $defined || (!empty($value)); 313 } 314 315 return (!$defined ? $arg : ''); 316 } 317 318 /** 319 * Parses an expansion into its components 320 * 321 * @param string $expansion Expansion 322 * 323 * @see Appendix A of the URI Templates Draft 324 * (http://bitworking.org/projects/URI-Templates/draft-gregorio-uritemplate-02.html#appendix_a) 325 * @return array Array with three elements containing the name of the 326 * operation, the operation argument and the variables from 327 * the expansion 328 */ 329 protected function parseExpansion($expansion) 330 { 331 if (strstr($expansion, '|')) { 332 list($op, $arg, $vars) = explode('|', $expansion); 333 $op = substr($op, 1); 334 } else { 335 $op = $arg = ''; 336 $vars = $expansion; 337 } 338 339 $vars = explode(',', $vars); 340 341 $variables = array(); 342 foreach ($vars as $var) { 343 if (strstr($var, '=')) { 344 list($varname, $vardefault) = explode('=', $var); 345 } else { 346 $varname = $var; 347 $vardefault = ''; 348 } 349 350 $variables[$varname] = $vardefault; 351 } 352 353 return array($op, $arg, $variables); 354 } 355} 356