Overview

Namespaces

  • Contrib
    • Bundle
      • CoverallsBundle
        • Console
        • Entity
      • CoverallsV1Bundle
        • Api
        • Collector
        • Command
        • Config
        • Entity
          • Git
    • Component
      • File
      • Log
      • System
        • Git
  • Guzzle
    • Batch
      • Exception
    • Cache
    • Common
      • Exception
    • Http
      • Curl
      • Exception
      • Message
      • QueryAggregator
    • Inflection
    • Iterator
    • Log
    • Parser
      • Cookie
      • Message
      • UriTemplate
      • Url
    • Plugin
      • Async
      • Backoff
      • Cache
      • Cookie
        • CookieJar
        • Exception
      • CurlAuth
      • ErrorResponse
        • Exception
      • History
      • Log
      • Md5
      • Mock
      • Oauth
    • Service
      • Builder
      • Command
        • Factory
        • LocationVisitor
          • Request
          • Response
      • Description
      • Exception
      • Resource
    • Stream
  • PHP
  • Psr
    • Log
  • Symfony
    • Component
      • Config
        • Definition
          • Builder
          • Exception
        • Exception
        • Loader
        • Resource
        • Util
      • Console
        • Command
        • Formatter
        • Helper
        • Input
        • Output
        • Tester
      • EventDispatcher
        • Debug
      • Finder
        • Adapter
        • Comparator
        • Exception
        • Expression
        • Iterator
        • Shell
      • Stopwatch
      • Yaml
        • Exception

Classes

  • PeclUriTemplate
  • UriTemplate

Interfaces

  • UriTemplateInterface
  • Overview
  • Namespace
  • Class
  • Tree
  • Todo
  1: <?php
  2: 
  3: namespace Guzzle\Parser\UriTemplate;
  4: 
  5: /**
  6:  * Expands URI templates using an array of variables
  7:  *
  8:  * @link http://tools.ietf.org/html/draft-gregorio-uritemplate-08
  9:  */
 10: class UriTemplate implements UriTemplateInterface
 11: {
 12:     /**
 13:      * @var string URI template
 14:      */
 15:     private $template;
 16: 
 17:     /**
 18:      * @var array Variables to use in the template expansion
 19:      */
 20:     private $variables;
 21: 
 22:     /**
 23:      * @var string Regex used to parse expressions
 24:      */
 25:     private static $regex = '/\{([^\}]+)\}/';
 26: 
 27:     /**
 28:      * @var array Hash for quick operator lookups
 29:      */
 30:     private static $operatorHash = array(
 31:         '+' => true, '#' => true, '.' => true, '/' => true, ';' => true, '?' => true, '&' => true
 32:     );
 33: 
 34:     /**
 35:      * @var array Delimiters
 36:      */
 37:     private static $delims = array(
 38:         ':', '/', '?', '#', '[', ']', '@', '!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '='
 39:     );
 40: 
 41:     /**
 42:      * @var array Percent encoded delimiters
 43:      */
 44:     private static $delimsPct = array(
 45:         '%3A', '%2F', '%3F', '%23', '%5B', '%5D', '%40', '%21', '%24', '%26', '%27', '%28', '%29', '%2A', '%2B', '%2C',
 46:         '%3B', '%3D'
 47:     );
 48: 
 49:     /**
 50:      * {@inheritdoc}
 51:      */
 52:     public function expand($template, array $variables)
 53:     {
 54:         $this->template = $template;
 55:         $this->variables = $variables;
 56: 
 57:         // Check to ensure that the preg_* function is needed
 58:         if (false === strpos($this->template, '{')) {
 59:             return $this->template;
 60:         }
 61: 
 62:         return preg_replace_callback(self::$regex, array($this, 'expandMatch'), $this->template);
 63:     }
 64: 
 65:     /**
 66:      * Parse an expression into parts
 67:      *
 68:      * @param string $expression Expression to parse
 69:      *
 70:      * @return array Returns an associative array of parts
 71:      */
 72:     private function parseExpression($expression)
 73:     {
 74:         // Check for URI operators
 75:         $operator = '';
 76: 
 77:         if (isset(self::$operatorHash[$expression[0]])) {
 78:             $operator = $expression[0];
 79:             $expression = substr($expression, 1);
 80:         }
 81: 
 82:         $values = explode(',', $expression);
 83:         foreach ($values as &$value) {
 84:             $value = trim($value);
 85:             $varspec = array();
 86:             $substrPos = strpos($value, ':');
 87:             if ($substrPos) {
 88:                 $varspec['value'] = substr($value, 0, $substrPos);
 89:                 $varspec['modifier'] = ':';
 90:                 $varspec['position'] = (int) substr($value, $substrPos + 1);
 91:             } elseif (substr($value, -1) == '*') {
 92:                 $varspec['modifier'] = '*';
 93:                 $varspec['value'] = substr($value, 0, -1);
 94:             } else {
 95:                 $varspec['value'] = (string) $value;
 96:                 $varspec['modifier'] = '';
 97:             }
 98:             $value = $varspec;
 99:         }
100: 
101:         return array(
102:             'operator' => $operator,
103:             'values'   => $values
104:         );
105:     }
106: 
107:     /**
108:      * Process an expansion
109:      *
110:      * @param array $matches Matches met in the preg_replace_callback
111:      *
112:      * @return string Returns the replacement string
113:      */
114:     private function expandMatch(array $matches)
115:     {
116:         static $rfc1738to3986 = array(
117:             '+'   => '%20',
118:             '%7e' => '~'
119:         );
120: 
121:         $parsed = self::parseExpression($matches[1]);
122:         $replacements = array();
123: 
124:         $prefix = $parsed['operator'];
125:         $joiner = $parsed['operator'];
126:         $useQueryString = false;
127:         if ($parsed['operator'] == '?') {
128:             $joiner = '&';
129:             $useQueryString = true;
130:         } elseif ($parsed['operator'] == '&') {
131:             $useQueryString = true;
132:         } elseif ($parsed['operator'] == '#') {
133:             $joiner = ',';
134:         } elseif ($parsed['operator'] == ';') {
135:             $useQueryString = true;
136:         } elseif ($parsed['operator'] == '' || $parsed['operator'] == '+') {
137:             $joiner = ',';
138:             $prefix = '';
139:         }
140: 
141:         foreach ($parsed['values'] as $value) {
142: 
143:             if (!array_key_exists($value['value'], $this->variables) || $this->variables[$value['value']] === null) {
144:                 continue;
145:             }
146: 
147:             $variable = $this->variables[$value['value']];
148:             $actuallyUseQueryString = $useQueryString;
149:             $expanded = '';
150: 
151:             if (is_array($variable)) {
152: 
153:                 $isAssoc = $this->isAssoc($variable);
154:                 $kvp = array();
155:                 foreach ($variable as $key => $var) {
156: 
157:                     if ($isAssoc) {
158:                         $key = rawurlencode($key);
159:                         $isNestedArray = is_array($var);
160:                     } else {
161:                         $isNestedArray = false;
162:                     }
163: 
164:                     if (!$isNestedArray) {
165:                         $var = rawurlencode($var);
166:                         if ($parsed['operator'] == '+' || $parsed['operator'] == '#') {
167:                             $var = $this->decodeReserved($var);
168:                         }
169:                     }
170: 
171:                     if ($value['modifier'] == '*') {
172:                         if ($isAssoc) {
173:                             if ($isNestedArray) {
174:                                 // Nested arrays must allow for deeply nested structures
175:                                 $var = strtr(http_build_query(array($key => $var)), $rfc1738to3986);
176:                             } else {
177:                                 $var = $key . '=' . $var;
178:                             }
179:                         } elseif ($key > 0 && $actuallyUseQueryString) {
180:                             $var = $value['value'] . '=' . $var;
181:                         }
182:                     }
183: 
184:                     $kvp[$key] = $var;
185:                 }
186: 
187:                 if (empty($variable)) {
188:                     $actuallyUseQueryString = false;
189:                 } elseif ($value['modifier'] == '*') {
190:                     $expanded = implode($joiner, $kvp);
191:                     if ($isAssoc) {
192:                         // Don't prepend the value name when using the explode modifier with an associative array
193:                         $actuallyUseQueryString = false;
194:                     }
195:                 } else {
196:                     if ($isAssoc) {
197:                         // When an associative array is encountered and the explode modifier is not set, then the
198:                         // result must be a comma separated list of keys followed by their respective values.
199:                         foreach ($kvp as $k => &$v) {
200:                             $v = $k . ',' . $v;
201:                         }
202:                     }
203:                     $expanded = implode(',', $kvp);
204:                 }
205: 
206:             } else {
207:                 if ($value['modifier'] == ':') {
208:                     $variable = substr($variable, 0, $value['position']);
209:                 }
210:                 $expanded = rawurlencode($variable);
211:                 if ($parsed['operator'] == '+' || $parsed['operator'] == '#') {
212:                     $expanded = $this->decodeReserved($expanded);
213:                 }
214:             }
215: 
216:             if ($actuallyUseQueryString) {
217:                 if (!$expanded && $joiner != '&') {
218:                     $expanded = $value['value'];
219:                 } else {
220:                     $expanded = $value['value'] . '=' . $expanded;
221:                 }
222:             }
223: 
224:             $replacements[] = $expanded;
225:         }
226: 
227:         $ret = implode($joiner, $replacements);
228:         if ($ret && $prefix) {
229:             return $prefix . $ret;
230:         }
231: 
232:         return $ret;
233:     }
234: 
235:     /**
236:      * Determines if an array is associative
237:      *
238:      * @param array $array Array to check
239:      *
240:      * @return bool
241:      */
242:     private function isAssoc(array $array)
243:     {
244:         return (bool) count(array_filter(array_keys($array), 'is_string'));
245:     }
246: 
247:     /**
248:      * Removes percent encoding on reserved characters (used with + and # modifiers)
249:      *
250:      * @param string $string String to fix
251:      *
252:      * @return string
253:      */
254:     private function decodeReserved($string)
255:     {
256:         return str_replace(self::$delimsPct, self::$delims, $string);
257:     }
258: }
259: 
php-coveralls API documentation generated by ApiGen 2.8.0