GuzzleHttpUriTemplate

Expands URI templates.

Defined (1)

The class is defined in the following location(s).

/lib/Azure/GuzzleHttp/UriTemplate.php  
  1. class UriTemplate 
  2. /** @var string URI template */ 
  3. private $template; 
  4.  
  5. /** @var array Variables to use in the template expansion */ 
  6. private $variables; 
  7.  
  8. /** @var array Hash for quick operator lookups */ 
  9. private static $operatorHash = [ 
  10. '' => ['prefix' => '', 'joiner' => ', ', 'query' => false],  
  11. '+' => ['prefix' => '', 'joiner' => ', ', 'query' => false],  
  12. '#' => ['prefix' => '#', 'joiner' => ', ', 'query' => false],  
  13. '.' => ['prefix' => '.', 'joiner' => '.', 'query' => false],  
  14. '/' => ['prefix' => '/', 'joiner' => '/', 'query' => false],  
  15. ';' => ['prefix' => ';', 'joiner' => ';', 'query' => true],  
  16. '?' => ['prefix' => '?', 'joiner' => '&', 'query' => true],  
  17. '&' => ['prefix' => '&', 'joiner' => '&', 'query' => true] 
  18. ]; 
  19.  
  20. /** @var array Delimiters */ 
  21. private static $delims = [':', '/', '?', '#', '[', ']', '@', '!', '$',  
  22. '&', '\'', '(', ')', '*', '+', ', ', ';', '=']; 
  23.  
  24. /** @var array Percent encoded delimiters */ 
  25. private static $delimsPct = ['%3A', '%2F', '%3F', '%23', '%5B', '%5D',  
  26. '%40', '%21', '%24', '%26', '%27', '%28', '%29', '%2A', '%2B', '%2C',  
  27. '%3B', '%3D']; 
  28.  
  29. public function expand($template, array $variables) 
  30. if (false === strpos($template, '{')) { 
  31. return $template; 
  32.  
  33. $this->template = $template; 
  34. $this->variables = $variables; 
  35.  
  36. return preg_replace_callback( 
  37. '/\{([^\}]+)\}/',  
  38. [$this, 'expandMatch'],  
  39. $this->template 
  40. ); 
  41.  
  42. /** 
  43. * Parse an expression into parts 
  44. * @param string $expression Expression to parse 
  45. * @return array Returns an associative array of parts 
  46. */ 
  47. private function parseExpression($expression) 
  48. $result = []; 
  49.  
  50. if (isset(self::$operatorHash[$expression[0]])) { 
  51. $result['operator'] = $expression[0]; 
  52. $expression = substr($expression, 1); 
  53. } else { 
  54. $result['operator'] = ''; 
  55.  
  56. foreach (explode(', ', $expression) as $value) { 
  57. $value = trim($value); 
  58. $varspec = []; 
  59. if ($colonPos = strpos($value, ':')) { 
  60. $varspec['value'] = substr($value, 0, $colonPos); 
  61. $varspec['modifier'] = ':'; 
  62. $varspec['position'] = (int) substr($value, $colonPos + 1); 
  63. } elseif (substr($value, -1) === '*') { 
  64. $varspec['modifier'] = '*'; 
  65. $varspec['value'] = substr($value, 0, -1); 
  66. } else { 
  67. $varspec['value'] = (string) $value; 
  68. $varspec['modifier'] = ''; 
  69. $result['values'][] = $varspec; 
  70.  
  71. return $result; 
  72.  
  73. /** 
  74. * Process an expansion 
  75. * @param array $matches Matches met in the preg_replace_callback 
  76. * @return string Returns the replacement string 
  77. */ 
  78. private function expandMatch(array $matches) 
  79. static $rfc1738to3986 = ['+' => '%20', '%7e' => '~']; 
  80.  
  81. $replacements = []; 
  82. $parsed = self::parseExpression($matches[1]); 
  83. $prefix = self::$operatorHash[$parsed['operator']]['prefix']; 
  84. $joiner = self::$operatorHash[$parsed['operator']]['joiner']; 
  85. $useQuery = self::$operatorHash[$parsed['operator']]['query']; 
  86.  
  87. foreach ($parsed['values'] as $value) { 
  88.  
  89. if (!isset($this->variables[$value['value']])) { 
  90. continue; 
  91.  
  92. $variable = $this->variables[$value['value']]; 
  93. $actuallyUseQuery = $useQuery; 
  94. $expanded = ''; 
  95.  
  96. if (is_array($variable)) { 
  97.  
  98. $isAssoc = $this->isAssoc($variable); 
  99. $kvp = []; 
  100. foreach ($variable as $key => $var) { 
  101.  
  102. if ($isAssoc) { 
  103. $key = rawurlencode($key); 
  104. $isNestedArray = is_array($var); 
  105. } else { 
  106. $isNestedArray = false; 
  107.  
  108. if (!$isNestedArray) { 
  109. $var = rawurlencode($var); 
  110. if ($parsed['operator'] === '+' || 
  111. $parsed['operator'] === '#' 
  112. ) { 
  113. $var = $this->decodeReserved($var); 
  114.  
  115. if ($value['modifier'] === '*') { 
  116. if ($isAssoc) { 
  117. if ($isNestedArray) { 
  118. // Nested arrays must allow for deeply nested 
  119. // structures. 
  120. $var = strtr( 
  121. http_build_query([$key => $var]),  
  122. $rfc1738to3986 
  123. ); 
  124. } else { 
  125. $var = $key . '=' . $var; 
  126. } elseif ($key > 0 && $actuallyUseQuery) { 
  127. $var = $value['value'] . '=' . $var; 
  128.  
  129. $kvp[$key] = $var; 
  130.  
  131. if (empty($variable)) { 
  132. $actuallyUseQuery = false; 
  133. } elseif ($value['modifier'] === '*') { 
  134. $expanded = implode($joiner, $kvp); 
  135. if ($isAssoc) { 
  136. // Don't prepend the value name when using the explode 
  137. // modifier with an associative array. 
  138. $actuallyUseQuery = false; 
  139. } else { 
  140. if ($isAssoc) { 
  141. // When an associative array is encountered and the 
  142. // explode modifier is not set, then the result must be 
  143. // a comma separated list of keys followed by their 
  144. // respective values. 
  145. foreach ($kvp as $k => &$v) { 
  146. $v = $k . ', ' . $v; 
  147. $expanded = implode(', ', $kvp); 
  148.  
  149. } else { 
  150. if ($value['modifier'] === ':') { 
  151. $variable = substr($variable, 0, $value['position']); 
  152. $expanded = rawurlencode($variable); 
  153. if ($parsed['operator'] === '+' || $parsed['operator'] === '#') { 
  154. $expanded = $this->decodeReserved($expanded); 
  155.  
  156. if ($actuallyUseQuery) { 
  157. if (!$expanded && $joiner !== '&') { 
  158. $expanded = $value['value']; 
  159. } else { 
  160. $expanded = $value['value'] . '=' . $expanded; 
  161.  
  162. $replacements[] = $expanded; 
  163.  
  164. $ret = implode($joiner, $replacements); 
  165. if ($ret && $prefix) { 
  166. return $prefix . $ret; 
  167.  
  168. return $ret; 
  169.  
  170. /** 
  171. * Determines if an array is associative. 
  172. * This makes the assumption that input arrays are sequences or hashes. 
  173. * This assumption is a tradeoff for accuracy in favor of speed, but it 
  174. * should work in almost every case where input is supplied for a URI 
  175. * template. 
  176. * @param array $array Array to check 
  177. * @return bool 
  178. */ 
  179. private function isAssoc(array $array) 
  180. return $array && array_keys($array)[0] !== 0; 
  181.  
  182. /** 
  183. * Removes percent encoding on reserved characters (used with + and # 
  184. * modifiers). 
  185. * @param string $string String to fix 
  186. * @return string 
  187. */ 
  188. private function decodeReserved($string) 
  189. return str_replace(self::$delimsPct, self::$delims, $string);