#!/usr/bin/php -dphar.readonly=1 2, 'c' => 'text/plain', 'cc' => 'text/plain', 'cpp' => 'text/plain', 'c++' => 'text/plain', 'dtd' => 'text/plain', 'h' => 'text/plain', 'log' => 'text/plain', 'rng' => 'text/plain', 'txt' => 'text/plain', 'xsd' => 'text/plain', 'php' => 1, 'inc' => 1, 'avi' => 'video/avi', 'bmp' => 'image/bmp', 'css' => 'text/css', 'gif' => 'image/gif', 'htm' => 'text/html', 'html' => 'text/html', 'htmls' => 'text/html', 'ico' => 'image/x-ico', 'jpe' => 'image/jpeg', 'jpg' => 'image/jpeg', 'jpeg' => 'image/jpeg', 'js' => 'application/x-javascript', 'midi' => 'audio/midi', 'mid' => 'audio/midi', 'mod' => 'audio/mod', 'mov' => 'movie/quicktime', 'mp3' => 'audio/mp3', 'mpg' => 'video/mpeg', 'mpeg' => 'video/mpeg', 'pdf' => 'application/pdf', 'png' => 'image/png', 'swf' => 'application/shockwave-flash', 'tif' => 'image/tiff', 'tiff' => 'image/tiff', 'wav' => 'audio/wav', 'xbm' => 'image/xbm', 'xml' => 'text/xml', ); header("Cache-Control: no-cache, must-revalidate"); header("Pragma: no-cache"); $basename = basename(__FILE__); if (!strpos($_SERVER['REQUEST_URI'], $basename)) { chdir(Extract_Phar::$temp); include $web; return; } $pt = substr($_SERVER['REQUEST_URI'], strpos($_SERVER['REQUEST_URI'], $basename) + strlen($basename)); if (!$pt || $pt == '/') { $pt = $web; header('HTTP/1.1 301 Moved Permanently'); header('Location: ' . $_SERVER['REQUEST_URI'] . '/' . $pt); exit; } $a = realpath(Extract_Phar::$temp . DIRECTORY_SEPARATOR . $pt); if (!$a || strlen(dirname($a)) < strlen(Extract_Phar::$temp)) { header('HTTP/1.0 404 Not Found'); echo "\n \n File Not Found<title>\n </head>\n <body>\n <h1>404 - File ", $pt, " Not Found</h1>\n </body>\n</html>"; exit; } $b = pathinfo($a); if (!isset($b['extension'])) { header('Content-Type: text/plain'); header('Content-Length: ' . filesize($a)); readfile($a); exit; } if (isset($mimes[$b['extension']])) { if ($mimes[$b['extension']] === 1) { include $a; exit; } if ($mimes[$b['extension']] === 2) { highlight_file($a); exit; } header('Content-Type: ' .$mimes[$b['extension']]); header('Content-Length: ' . filesize($a)); readfile($a); exit; } } class Extract_Phar { static $temp; static $origdir; const GZ = 0x1000; const BZ2 = 0x2000; const MASK = 0x3000; const START = 'pharext_installer.php'; const LEN = 6697; static function go($return = false) { $fp = fopen(__FILE__, 'rb'); fseek($fp, self::LEN); $L = unpack('V', $a = (binary)fread($fp, 4)); $m = (binary)''; do { $read = 8192; if ($L[1] - strlen($m) < 8192) { $read = $L[1] - strlen($m); } $last = (binary)fread($fp, $read); $m .= $last; } while (strlen($last) && strlen($m) < $L[1]); if (strlen($m) < $L[1]) { die('ERROR: manifest length read was "' . strlen($m) .'" should be "' . $L[1] . '"'); } $info = self::_unpack($m); $f = $info['c']; if ($f & self::GZ) { if (!function_exists('gzinflate')) { die('Error: zlib extension is not enabled -' . ' gzinflate() function needed for zlib-compressed .phars'); } } if ($f & self::BZ2) { if (!function_exists('bzdecompress')) { die('Error: bzip2 extension is not enabled -' . ' bzdecompress() function needed for bz2-compressed .phars'); } } $temp = self::tmpdir(); if (!$temp || !is_writable($temp)) { $sessionpath = session_save_path(); if (strpos ($sessionpath, ";") !== false) $sessionpath = substr ($sessionpath, strpos ($sessionpath, ";")+1); if (!file_exists($sessionpath) || !is_dir($sessionpath)) { die('Could not locate temporary directory to extract phar'); } $temp = $sessionpath; } $temp .= '/pharextract/'.basename(__FILE__, '.phar'); self::$temp = $temp; self::$origdir = getcwd(); @mkdir($temp, 0777, true); $temp = realpath($temp); if (!file_exists($temp . DIRECTORY_SEPARATOR . md5_file(__FILE__))) { self::_removeTmpFiles($temp, getcwd()); @mkdir($temp, 0777, true); @file_put_contents($temp . '/' . md5_file(__FILE__), ''); foreach ($info['m'] as $path => $file) { $a = !file_exists(dirname($temp . '/' . $path)); @mkdir(dirname($temp . '/' . $path), 0777, true); clearstatcache(); if ($path[strlen($path) - 1] == '/') { @mkdir($temp . '/' . $path, 0777); } else { file_put_contents($temp . '/' . $path, self::extractFile($path, $file, $fp)); @chmod($temp . '/' . $path, 0666); } } } chdir($temp); if (!$return) { include self::START; } } static function tmpdir() { if (strpos(PHP_OS, 'WIN') !== false) { if ($var = getenv('TMP') ? getenv('TMP') : getenv('TEMP')) { return $var; } if (is_dir('/temp') || mkdir('/temp')) { return realpath('/temp'); } return false; } if ($var = getenv('TMPDIR')) { return $var; } return realpath('/tmp'); } static function _unpack($m) { $info = unpack('V', substr($m, 0, 4)); $l = unpack('V', substr($m, 10, 4)); $m = substr($m, 14 + $l[1]); $s = unpack('V', substr($m, 0, 4)); $o = 0; $start = 4 + $s[1]; $ret['c'] = 0; for ($i = 0; $i < $info[1]; $i++) { $len = unpack('V', substr($m, $start, 4)); $start += 4; $savepath = substr($m, $start, $len[1]); $start += $len[1]; $ret['m'][$savepath] = array_values(unpack('Va/Vb/Vc/Vd/Ve/Vf', substr($m, $start, 24))); $ret['m'][$savepath][3] = sprintf('%u', $ret['m'][$savepath][3] & 0xffffffff); $ret['m'][$savepath][7] = $o; $o += $ret['m'][$savepath][2]; $start += 24 + $ret['m'][$savepath][5]; $ret['c'] |= $ret['m'][$savepath][4] & self::MASK; } return $ret; } static function extractFile($path, $entry, $fp) { $data = ''; $c = $entry[2]; while ($c) { if ($c < 8192) { $data .= @fread($fp, $c); $c = 0; } else { $c -= 8192; $data .= @fread($fp, 8192); } } if ($entry[4] & self::GZ) { $data = gzinflate($data); } elseif ($entry[4] & self::BZ2) { $data = bzdecompress($data); } if (strlen($data) != $entry[0]) { die("Invalid internal .phar file (size error " . strlen($data) . " != " . $stat[7] . ")"); } if ($entry[3] != sprintf("%u", crc32((binary)$data) & 0xffffffff)) { die("Invalid internal .phar file (checksum error)"); } return $data; } static function _removeTmpFiles($temp, $origdir) { chdir($temp); foreach (glob('*') as $f) { if (file_exists($f)) { is_dir($f) ? @rmdir($f) : @unlink($f); if (file_exists($f) && is_dir($f)) { self::_removeTmpFiles($f, getcwd()); } } } @rmdir($temp); clearstatcache(); chdir($origdir); } } Extract_Phar::go(); __HALT_COMPILER(); ?> A%��{�����������À ��a:8:{s:6:"header";s:61:"pharext v@PHAREXT_VERSION@ (c) Michael Wallner <mike@php.net>";s:7:"version";s:17:"@PHAREXT_VERSION@";s:4:"name";s:12:"uri_template";s:4:"date";s:10:"2015-05-10";s:4:"stub";s:21:"pharext_installer.php";s:7:"license";s:3218:"-------------------------------------------------------------------- The PHP License, version 3.01 Copyright (c) 1999 - 2012 The PHP Group. All rights reserved. -------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without modification, is permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. 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. 3. The name "PHP" must not be used to endorse or promote products derived from this software without prior written permission. For written permission, please contact group@php.net. 4. Products derived from this software may not be called "PHP", nor may "PHP" appear in their name, without prior written permission from group@php.net. You may indicate that your software works in conjunction with PHP by saying "Foo for PHP" instead of calling it "PHP Foo" or "phpfoo" 5. The PHP Group may publish revised and/or new versions of the license from time to time. Each version will be given a distinguishing version number. Once covered code has been published under a particular version of the license, you may always continue to use it under the terms of that version. You may also choose to use such covered code under the terms of any subsequent version of the license published by the PHP Group. No one other than the PHP Group has the right to modify the terms applicable to covered code created under this License. 6. Redistributions of any form whatsoever must retain the following acknowledgment: "This product includes PHP software, freely available from <http://www.php.net/software/>". THIS SOFTWARE IS PROVIDED BY THE PHP DEVELOPMENT TEAM ``AS IS'' AND ANY EXPRESSED 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 PHP DEVELOPMENT TEAM OR ITS 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. -------------------------------------------------------------------- This software consists of voluntary contributions made by many individuals on behalf of the PHP Group. The PHP Group can be contacted via Email at group@php.net. For more information on the PHP Group and the PHP project, please see <http://www.php.net>. PHP includes the Zend Engine, freely available at <http://www.zend.com>. ";s:7:"release";s:3:"1.0";s:4:"type";s:9:"extension";}���pharext/Cli/Args.phpð��OUð�� “7¶���������pharext/Cli/Command.php��OU�� *—Û¶���������pharext/Command.php��OU��ætd\¶���������pharext/Exception.phpc��OUc��U†Ï{¶���������pharext/ExecCmd.php��OU��¹l”ʶ���������pharext/Installer.phpÒ��OUÒ��WäJ¶���������pharext/Openssl/PrivateKey.phpÁ��OUÁ��&æP¶���������pharext/Packager.php$"��OU$"��Ðá’E¶���������pharext/SourceDir/Basic.php¯��OU¯��zZ“¶���������pharext/SourceDir/Git.php��OU��b›åc¶���������pharext/SourceDir/Pecl.php��OU��¯–¶���������pharext/SourceDir.php]��OU]��S ‰¶���������pharext/Task/Activate.phpp ��OUp ��ŽEE¶���������pharext/Task/Askpass.phpU��OUU��‡*¶������ ���pharext/Task/BundleGenerator.php}��OU}�� ï`Y¶���������pharext/Task/Cleanup.php���OU���ÒZͶ���������pharext/Task/Configure.phpT��OUT��}Ëì¶���������pharext/Task/Extract.phpë��OUë��Éè›Õ¶���������pharext/Task/GitClone.phpm��OUm��óyµ@¶���������pharext/Task/Make.phpª��OUª��œç6 ¶���������pharext/Task/PaxFixup.php¬��OU¬��y⯶���������pharext/Task/PeclFixup.phpœ��OUœ��eùtš¶���������pharext/Task/PharBuild.phpí��OUí��£D垶���������pharext/Task/PharCompress.phpr��OUr�� â¶���������pharext/Task/PharRename.phpä��OUä��Š[Þ˶���������pharext/Task/PharSign.php¨��OU¨��Ûº¦i¶���������pharext/Task/Phpize.php��OU��ù 2Ѷ���������pharext/Task/StreamFetch.php��OU��ˆîs\¶���������pharext/Task.phpw���OUw��� ÄIǶ���������pharext/Tempdir.phpµ��OUµ��ë–,¶���������pharext/Tempfile.phpã��OUã��ï#¶���������pharext/Tempname.php`��OU`��<Np¶���������pharext/Version.php@���OU@���ÆC뵶���������pharext_installer.phpÝ���OUÝ���‹pDZ¶���������pharext_packager.phpÔ���OUÔ���Ñ1÷¶���������pharext_package.php2���OU2���vSTÒ¶������ ���package.xmll(��OUl(��§Se$¶���������LICENSE’ ��OU’ ��®\Ú¶������ ���README.mdx1��OUx1��W¨9¶������ ���config.m4Í��OUÍ��z0–¶������ ���config.w32M��OUM��^—p¶���������php_uri_template.há ��OUá ��*Ÿ{¶���������uri_template.c» ��OU» ��Rú–ù¶���������uri_template_string.ce��OUe��ÍŠÒʶ���������uri_template_processor.cb��OUb��,Ž½¶���������uri_template_parser.cM��OUM��Ú\-Ô¶���������uri_template_common.c’��OU’��#K›¶������*���examples/form_style_query_continuation.phpq��OUq��Ä2ಶ������+���examples/path_style_parameter_expansion.php��OU��F ¡¶������'���examples/form_style_query_expansion.phpQ��OUQ��ü9·¨¶���������examples/reserved_expansion.phpW��OUW��Ü¢ÇX¶���������examples/fragment_expansion.php³��OU³��†?t¶������$���examples/simple_string_expansion.php��OU�� ýK¶������,���examples/label_expansion_with_dot_prefix.php��OU��v;Þ"¶���������examples/variable_expansion.php ��OU ��Ó²3¶������#���examples/path_segment_expansion.php1��OU1��=üü¶���������tests/copyliterals1.phpt[��OU[��{IP¶���������tests/copyliterals2.phpt;��OU;��§rŒ2¶���������tests/copyliterals3.phptö��OUö��{´!2¶���������tests/copyliterals4.phptÞ���OUÞ���=!+¶���������tests/copyliterals5.phptè���OUè���ܦÁ¶���������tests/err1.phpt¢��OU¢��ë#¥f¶���������tests/err21.phpt†��OU†��_ÙY=¶���������tests/ex1a3.phpth��OUh��oi4 ¶���������tests/level1a1.phpt{��OU{��ê=ÏN¶���������tests/level4a3.phpt ��OU ��Û‹å¶���������tests/err10.phptx��OUx��Ú«ÌB¶���������tests/err22.phptW��OUW��X1B‹¶���������tests/ex1a4.phptA��OUA��±sˆå¶���������tests/level2a1.phptÑ��OUÑ��ÐLWB¶���������tests/level4a4.phpt��OU��—ºÔû¶���������tests/err11.phpt~��OU~��Ù0Hȶ���������tests/err23.phpt¾��OU¾��xúÏæ¶���������tests/expansion0.phptâ��OUâ��W?Rt¶���������tests/level2a2.phptž��OUž��ÁQÈÚ¶���������tests/level4a5.phptÃ��OUÃ��f|Mç¶���������tests/err12.phpt\��OU\��÷œÄ$¶���������tests/err24.phpt8��OU8���þ{¶���������tests/expansion1.phpt² ��OU² ��|''©¶���������tests/level3a1.phpt��OU��±í+4¶���������tests/level4a6.phptZ��OUZ��ß|”¶���������tests/err13.phptƒ��OUƒ��œñܶ���������tests/err3.phptu��OUu��<1‡}¶���������tests/expansion2.phptŒ ��OUŒ ��‘ð@˜¶���������tests/level3a2.phpt&��OU&��™LeÛ¶���������tests/level4a7.phptB��OUB��©\´Á¶���������tests/err14.phptÀ��OUÀ��Å·= ¶���������tests/err4.phpt��OU��u”jî¶���������tests/expansion3.phpt% ��OU% ��6âÝŒ¶���������tests/level3a3.phpt$��OU$��ó�íá¶���������tests/level4a8.phpt;��OU;��ë.J?¶���������tests/err15.phpt§��OU§��dÁÍö¶���������tests/err5.phpt†��OU†��F~p¶���������tests/expansion4.phptç ��OUç ��ëÚ9i¶���������tests/level3a4.phptÓ��OUÓ��ƒ9ɶ���������tests/mb1.phpt0��OU0��žá‹¶���������tests/err16.phptˆ��OUˆ��HËÝy¶���������tests/err6.phpt��OU��ÚÏض���������tests/expansion5.phptG ��OUG ��…Ò[¶���������tests/level3a5.phptù��OUù��ïjF¶���������tests/mb2.phptá��OUá��¥m««¶���������tests/err17.phptˆ��OUˆ��Ìcô¶���������tests/err7.phpt}��OU}��5‘P¶���������tests/expansion6.phpt ��OU ��†G`ɶ���������tests/level3a6.phpt��OU��Þv¶���������tests/var1braces.phpt˜��OU˜��€è(�¶���������tests/err18.phpt³��OU³�� à¶���������tests/err8.phpty��OUy��Yõ$Á¶���������tests/expansion7.phptQ ��OUQ ��Íi(¶���������tests/level3a7.phpt��OU��sqø*¶���������tests/var2empty.phpt÷���OU÷���P1¶���������tests/err19.phptŽ��OUŽ��Õ+­¶���������tests/err9.phptu��OUu��'¡€¶���������tests/expansion8.phpt€ ��OU€ ��ˆiáU¶���������tests/level3a8.phpt ��OU ��ðçD_¶���������tests/err2.phpt¢��OU¢��7'$ñ¶���������tests/ex1a1.phpt��OU��eg¥L¶���������tests/extended1a1.phpty��OUy��kY`(¶���������tests/level4a1.phptT��OUT�� Püµ¶���������tests/err20.phptŒ��OUŒ��Æ$Ã]¶���������tests/ex1a2.phptq��OUq��{£{4¶���������tests/extended1a2.phpt¾��OU¾��Ãtǽ¶���������tests/level4a2.phpt��OU��‰A˜}¶������<?php namespace pharext\Cli; /** * Command line arguments */ class Args implements \ArrayAccess { /** * Optional option */ const OPTIONAL = 0x000; /** * Required Option */ const REQUIRED = 0x001; /** * Only one value, even when used multiple times */ const SINGLE = 0x000; /** * Aggregate an array, when used multiple times */ const MULTI = 0x010; /** * Option takes no argument */ const NOARG = 0x000; /** * Option requires an argument */ const REQARG = 0x100; /** * Option takes an optional argument */ const OPTARG = 0x200; /** * Option halts processing */ const HALT = 0x10000000; /** * Original option spec * @var array */ private $orig = []; /** * Compiled spec * @var array */ private $spec = []; /** * Parsed args * @var array */ private $args = []; /** * Compile the original spec * @param array|Traversable $spec */ public function __construct($spec = null) { if (is_array($spec) || $spec instanceof Traversable) { $this->compile($spec); } } /** * Compile the original spec * @param array|Traversable $spec * @return pharext\CliArgs self */ public function compile($spec) { foreach ($spec as $arg) { if (isset($arg[0])) { $this->spec["-".$arg[0]] = $arg; } $this->spec["--".$arg[1]] = $arg; $this->orig[] = $arg; } return $this; } /** * Get original spec * @return array */ public function getSpec() { return $this->orig; } /** * Get compiled spec * @return array */ public function getCompiledSpec() { return $this->spec; } /** * Parse command line arguments according to the compiled spec. * * The Generator yields any parsing errors. * Parsing will stop when all arguments are processed or the first option * flagged CliArgs::HALT was encountered. * * @param int $argc * @param array $argv * @return Generator */ public function parse($argc, array $argv) { for ($i = 0; $i < $argc; ++$i) { $o = $argv[$i]; if ($o{0} === '-' && strlen($o) > 2 && $o{1} !== '-') { // multiple short opts, .e.g -vps $argc += strlen($o) - 2; array_splice($argv, $i, 1, array_map(function($s) { return "-$s"; }, str_split(substr($o, 1)))); $o = $argv[$i]; } elseif ($o{0} === '-' && strlen($o) > 2 && $o{1} === '-' && 0 < ($eq = strpos($o, "="))) { $argc++; array_splice($argv, $i, 1, [ substr($o, 0, $eq++), substr($o, $eq) ]); $o = $argv[$i]; } if (!isset($this->spec[$o])) { yield sprintf("Unknown option %s", $o); } elseif (!$this->optAcceptsArg($o)) { $this[$o] = true; } elseif ($i+1 < $argc && !isset($this->spec[$argv[$i+1]])) { $this[$o] = $argv[++$i]; } elseif ($this->optRequiresArg($o)) { yield sprintf("Option --%s requires an argument", $this->optLongName($o)); } else { // OPTARG $this[$o] = $this->optDefaultArg($o); } if ($this->optHalts($o)) { return; } } } /** * Validate that all required options were given. * * The Generator yields any validation errors. * * @return Generator */ public function validate() { $required = array_filter($this->orig, function($spec) { return $spec[3] & self::REQUIRED; }); foreach ($required as $req) { if (!strlen($this[$req[0]])) { yield sprintf("Option --%s is required", $req[1]); } } } public function toArray() { $args = []; foreach ($this->spec as $spec) { $opt = $this->opt($spec[1]); $args[$opt] = $this[$opt]; } return $args; } /** * Retreive the default argument of an option * @param string $o * @return mixed */ private function optDefaultArg($o) { $o = $this->opt($o); if (isset($this->spec[$o][4])) { return $this->spec[$o][4]; } return null; } /** * Retrieve the help message of an option * @param string $o * @return string */ private function optHelp($o) { $o = $this->opt($o); if (isset($this->spec[$o][2])) { return $this->spec[$o][2]; } return ""; } /** * Retrieve option's flags * @param string $o * @return int */ private function optFlags($o) { $o = $this->opt($o); if (isset($this->spec[$o])) { return $this->spec[$o][3]; } return null; } /** * Check whether an option is flagged for halting argument processing * @param string $o * @return boolean */ private function optHalts($o) { return $this->optFlags($o) & self::HALT; } /** * Check whether an option needs an argument * @param string $o * @return boolean */ private function optRequiresArg($o) { return $this->optFlags($o) & self::REQARG; } /** * Check wether an option accepts any argument * @param string $o * @return boolean */ private function optAcceptsArg($o) { return $this->optFlags($o) & 0xf00; } /** * Check whether an option can be used more than once * @param string $o * @return boolean */ private function optIsMulti($o) { return $this->optFlags($o) & self::MULTI; } /** * Retreive the long name of an option * @param string $o * @return string */ private function optLongName($o) { $o = $this->opt($o); return $this->spec[$o][1]; } /** * Retreive the short name of an option * @param string $o * @return string */ private function optShortName($o) { $o = $this->opt($o); return $this->spec[$o][0]; } /** * Retreive the canonical name (--long-name) of an option * @param string $o * @return string */ private function opt($o) { if ($o{0} !== '-') { if (strlen($o) > 1) { $o = "-$o"; } $o = "-$o"; } return $o; } /**@+ * Implements ArrayAccess and virtual properties */ function offsetExists($o) { $o = $this->opt($o); return isset($this->args[$o]); } function __isset($o) { return $this->offsetExists($o); } function offsetGet($o) { $o = $this->opt($o); if (isset($this->args[$o])) { return $this->args[$o]; } return $this->optDefaultArg($o); } function __get($o) { return $this->offsetGet($o); } function offsetSet($o, $v) { $osn = $this->optShortName($o); $oln = $this->optLongName($o); if ($this->optIsMulti($o)) { if (isset($osn)) { $this->args["-$osn"][] = $v; } $this->args["--$oln"][] = $v; } else { if (isset($osn)) { $this->args["-$osn"] = $v; } $this->args["--$oln"] = $v; } } function __set($o, $v) { $this->offsetSet($o, $v); } function offsetUnset($o) { unset($this->args["-".$this->optShortName($o)]); unset($this->args["--".$this->optLongName($o)]); } function __unset($o) { $this->offsetUnset($o); } /**@-*/ } <?php namespace pharext\Cli; use pharext\Cli\Args as CliArgs; use Phar; if (!function_exists("array_column")) { function array_column(array $array, $col, $idx = null) { $result = []; foreach ($array as $el) { if (isset($idx)) { $result[$el[$idx]] = $el[$col]; } else { $result[] = $el[$col]; } } return $result; } } trait Command { /** * Command line arguments * @var pharext\CliArgs */ private $args; /** * @inheritdoc * @see \pharext\Command::getArgs() */ public function getArgs() { return $this->args; } /** * Retrieve metadata of the currently running phar * @param string $key * @return mixed */ public function metadata($key = null) { $running = new Phar(Phar::running(false)); if ($key === "signature") { $sig = $running->getSignature(); return sprintf("%s signature of %s\n%s", $sig["hash_type"], $this->metadata("name"), chunk_split($sig["hash"], 64, "\n")); } $metadata = $running->getMetadata(); if (isset($key)) { return $metadata[$key]; } return $metadata; } /** * Output pharext vX.Y.Z header */ public function header() { if (!headers_sent()) { /* only display header, if we didn't generate any output yet */ printf("%s\n\n", $this->metadata("header")); } } /** * @inheritdoc * @see \pharext\Command::debug() */ public function debug($fmt) { if ($this->args->verbose) { vprintf($fmt, array_slice(func_get_args(), 1)); } } /** * @inheritdoc * @see \pharext\Command::info() */ public function info($fmt) { if (!$this->args->quiet) { vprintf($fmt, array_slice(func_get_args(), 1)); } } /** * @inheritdoc * @see \pharext\Command::warn() */ public function warn($fmt) { if (!$this->args->quiet) { if (!isset($fmt)) { $fmt = "%s\n"; $arg = error_get_last()["message"]; } else { $arg = array_slice(func_get_args(), 1); } vfprintf(STDERR, "Warning: $fmt", $arg); } } /** * @inheritdoc * @see \pharext\Command::error() */ public function error($fmt) { if (!isset($fmt)) { $fmt = "%s\n"; $arg = error_get_last()["message"]; } else { $arg = array_slice(func_get_args(), 1); } vfprintf(STDERR, "ERROR: $fmt", $arg); } /** * Output command line help message * @param string $prog */ public function help($prog) { printf("Usage:\n\n \$ %s", $prog); $flags = []; $required = []; $optional = []; foreach ($this->args->getSpec() as $spec) { if ($spec[3] & CliArgs::REQARG) { if ($spec[3] & CliArgs::REQUIRED) { $required[] = $spec; } else { $optional[] = $spec; } } else { $flags[] = $spec; } } if ($flags) { printf(" [-%s]", implode("", array_column($flags, 0))); } foreach ($required as $req) { printf(" -%s <arg>", $req[0]); } if ($optional) { printf(" [-%s <arg>]", implode("|-", array_column($optional, 0))); } printf("\n\n"); $spc = $this->args->getSpec(); $max = max(array_map("strlen", array_column($spc, 1))); $max += $max % 8 + 2; foreach ($spc as $spec) { if (isset($spec[0])) { printf(" -%s|", $spec[0]); } else { printf(" "); } printf("--%s ", $spec[1]); if ($spec[3] & CliArgs::REQARG) { printf("<arg> "); } elseif ($spec[3] & CliArgs::OPTARG) { printf("[<arg>]"); } else { printf(" "); } printf("%s%s", str_repeat(" ", $max-strlen($spec[1])+3*!isset($spec[0])), $spec[2]); if ($spec[3] & CliArgs::REQUIRED) { printf(" (REQUIRED)"); } if (isset($spec[4])) { printf(" [%s]", $spec[4]); } printf("\n"); } printf("\n"); } /** * Verbosity * @return boolean */ public function verbosity() { if ($this->args->verbose) { return true; } elseif ($this->args->quiet) { return false; } else { return null; } } } <?php namespace pharext; /** * Command interface */ interface Command { /** * Argument error */ const EARGS = 1; /** * Build error */ const EBUILD = 2; /** * Signature error */ const ESIGN = 3; /** * Extract/unpack error */ const EEXTRACT = 4; /** * Install error */ const EINSTALL = 5; /** * Retrieve command line arguments * @return pharext\CliArgs */ public function getArgs(); /** * Print debug message * @param string $fmt * @param string ...$args */ public function debug($fmt); /** * Print info * @param string $fmt * @param string ...$args */ public function info($fmt); /** * Print warning * @param string $fmt * @param string ...$args */ public function warn($fmt); /** * Print error * @param string $fmt * @param string ...$args */ public function error($fmt); /** * Execute the command * @param int $argc command line argument count * @param array $argv command line argument list */ public function run($argc, array $argv); } <?php namespace pharext; class Exception extends \Exception { public function __construct($message = null, $code = 0, $previous = null) { if (!isset($message)) { $last_error = error_get_last(); $message = $last_error["message"]; if (!$code) { $code = $last_error["type"]; } } parent::__construct($message, $code, $previous); } } <?php namespace pharext; /** * Execute system command */ class ExecCmd { /** * Sudo command, if the cmd needs escalated privileges * @var string */ private $sudo; /** * Executable of the cmd * @var string */ private $command; /** * Passthrough cmd output * @var bool */ private $verbose; /** * Output of cmd run * @var string */ private $output; /** * Return code of cmd run * @var int */ private $status; /** * @param string $command * @param bool verbose */ public function __construct($command, $verbose = false) { $this->command = $command; $this->verbose = $verbose; } /** * (Re-)set sudo command * @param string $sudo */ public function setSu($sudo = false) { $this->sudo = $sudo; } /** * Execute a program with escalated privileges handling interactive password prompt * @param string $command * @param bool $verbose * @return int exit status */ private function suExec($command, $verbose = null) { if (!($proc = proc_open($command, [STDIN,["pipe","w"],["pipe","w"]], $pipes))) { $this->status = -1; throw new Exception("Failed to run {$command}"); } $stdout = $pipes[1]; $passwd = 0; $checks = 10; while (!feof($stdout)) { $R = [$stdout]; $W = []; $E = []; if (!stream_select($R, $W, $E, null)) { continue; } $data = fread($stdout, 0x1000); /* only check a few times */ if ($passwd < $checks) { $passwd++; if (stristr($data, "password")) { $passwd = $checks + 1; printf("\n%s", $data); continue; } } elseif ($passwd > $checks) { /* new line after pw entry */ printf("\n"); $passwd = $checks; } if ($verbose === null) { print $this->progress($data, 0); } else { if ($verbose) { printf("%s", $data); } $this->output .= $data; } } if ($verbose === null) { $this->progress("", PHP_OUTPUT_HANDLER_FINAL); } return $this->status = proc_close($proc); } /** * Output handler that displays some progress while soaking output * @param string $string * @param int $flags * @return string */ private function progress($string, $flags) { static $counter = 0; static $symbols = ["\\","|","/","-"]; $this->output .= $string; if (false !== strpos($string, "\n")) { ++$counter; } return $flags & PHP_OUTPUT_HANDLER_FINAL ? " \r" : sprintf(" %s\r", $symbols[$counter % 4]); } /** * Run the command * @param array $args * @return \pharext\ExecCmd self * @throws \pharext\Exception */ public function run(array $args = null) { $exec = escapeshellcmd($this->command); if ($args) { $exec .= " ". implode(" ", array_map("escapeshellarg", (array) $args)); } if ($this->sudo) { $this->suExec(sprintf($this->sudo." 2>&1", $exec), $this->verbose); } elseif ($this->verbose) { ob_start(function($s) { $this->output .= $s; return $s; }, 1); passthru($exec, $this->status); ob_end_flush(); } elseif ($this->verbose !== false /* !quiet */) { ob_start([$this, "progress"], 1); passthru($exec . " 2>&1", $this->status); ob_end_flush(); } else { exec($exec ." 2>&1", $output, $this->status); $this->output = implode("\n", $output); } if ($this->status) { throw new Exception("Command {$exec} failed ({$this->status})"); } return $this; } /** * Retrieve exit code of cmd run * @return int */ public function getStatus() { return $this->status; } /** * Retrieve output of cmd run * @return string */ public function getOutput() { return $this->output; } } <?php namespace pharext; use pharext\Cli\Args as CliArgs; use pharext\Cli\Command as CliCommand; use Phar; use SplObjectStorage; /** * The extension install command executed by the extension phar */ class Installer implements Command { use CliCommand; /** * Cleanups * @var array */ private $cleanup = []; /** * Create the command */ public function __construct() { $this->args = new CliArgs([ ["h", "help", "Display help", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT], ["v", "verbose", "More output", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG], ["q", "quiet", "Less output", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG], ["p", "prefix", "PHP installation prefix if phpize is not in \$PATH, e.g. /opt/php7", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::REQARG], ["n", "common-name", "PHP common program name, e.g. php5 or zts-php", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::REQARG, "php"], ["c", "configure", "Additional extension configure flags, e.g. -c --with-flag", CliArgs::OPTIONAL|CliArgs::MULTI|CliArgs::REQARG], ["s", "sudo", "Installation might need increased privileges", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::OPTARG, "sudo -S %s"], ["i", "ini", "Activate in this php.ini instead of loaded default php.ini", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::REQARG], [null, "signature", "Show package signature", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT], [null, "license", "Show package license", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT], [null, "name", "Show package name", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT], [null, "date", "Show package release date", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT], [null, "release", "Show package release version", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT], [null, "version", "Show pharext version", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT], ]); } /** * Perform cleaniup */ function __destruct() { foreach ($this->cleanup as $cleanup) { $cleanup->run(); } } private function extract(Phar $phar) { $temp = (new Task\Extract($phar))->run($this->verbosity()); $this->cleanup[] = new Task\Cleanup($temp); return $temp; } private function hooks(SplObjectStorage $phars) { $hook = []; foreach ($phars as $phar) { if (isset($phar["pharext_package.php"])) { $sdir = include $phar["pharext_package.php"]; if ($sdir instanceof SourceDir) { $this->args->compile($sdir->getArgs()); $hook[] = $sdir; } } } return $hook; } private function load() { $list = new SplObjectStorage(); $phar = new Phar(Phar::running(false)); $temp = $this->extract($phar); foreach ($phar as $entry) { $dep_file = $entry->getBaseName(); if (fnmatch("*.ext.phar*", $dep_file)) { $dep_phar = new Phar("$temp/$dep_file"); $list[$dep_phar] = $this->extract($dep_phar); } } /* the actual ext.phar at last */ $list[$phar] = $temp; return $list; } /** * @inheritdoc * @see \pharext\Command::run() */ public function run($argc, array $argv) { try { /* load the phar(s) */ $list = $this->load(); /* installer hooks */ $hook = $this->hooks($list); } catch (\Exception $e) { $this->error("%s\n", $e->getMessage()); exit(self::EEXTRACT); } /* standard arg stuff */ $errs = []; $prog = array_shift($argv); foreach ($this->args->parse(--$argc, $argv) as $error) { $errs[] = $error; } if ($this->args["help"]) { $this->header(); $this->help($prog); exit; } try { foreach (["signature", "name", "date", "license", "release", "version"] as $opt) { if ($this->args[$opt]) { printf("%s\n", $this->metadata($opt)); exit; } } } catch (\Exception $e) { $this->error("%s\n", $e->getMessage()); exit(self::EARGS); } foreach ($this->args->validate() as $error) { $errs[] = $error; } if ($errs) { if (!$this->args["quiet"]) { $this->header(); } foreach ($errs as $err) { $this->error("%s\n", $err); } if (!$this->args["quiet"]) { $this->help($prog); } exit(self::EARGS); } try { /* post process hooks */ foreach ($hook as $sdir) { $sdir->setArgs($this->args); } } catch (\Exception $e) { $this->error("%s\n", $e->getMessage()); exit(self::EARGS); } /* install packages */ try { foreach ($list as $phar) { $this->info("Installing %s ...\n", basename($phar->getPath())); $this->install($list[$phar]); $this->activate($list[$phar]); $this->info("Successfully installed %s!\n", basename($phar->getPath())); } } catch (\Exception $e) { $this->error("%s\n", $e->getMessage()); exit(self::EINSTALL); } } /** * Phpize + trinity */ private function install($temp) { // phpize $phpize = new Task\Phpize($temp, $this->args->prefix, $this->args->{"common-name"}); $phpize->run($this->verbosity()); // configure $configure = new Task\Configure($temp, $this->args->configure, $this->args->prefix, $this->args{"common-name"}); $configure->run($this->verbosity()); // make $make = new Task\Make($temp); $make->run($this->verbosity()); // install $sudo = isset($this->args->sudo) ? $this->args->sudo : null; $install = new Task\Make($temp, ["install"], $sudo); $install->run($this->verbosity()); } private function activate($temp) { if ($this->args->ini) { $files = [realpath($this->args->ini)]; } else { $files = array_filter(array_map("trim", explode(",", php_ini_scanned_files()))); $files[] = php_ini_loaded_file(); } $sudo = isset($this->args->sudo) ? $this->args->sudo : null; $type = $this->metadata("type") ?: "extension"; $activate = new Task\Activate($temp, $files, $type, $this->args->prefix, $this->args{"common-name"}, $sudo); if (!$activate->run($this->verbosity())) { $this->info("Extension already activated ...\n"); } } } <?php namespace pharext\Openssl; use pharext\Exception; class PrivateKey { /** * Private key * @var string */ private $key; /** * Public key * @var string */ private $pub; /** * Read a private key * @param string $file * @param string $password * @throws \pharext\Exception */ function __construct($file, $password) { /* there appears to be a bug with refcount handling of this * resource; when the resource is stored as property, it cannot be * "coerced to a private key" on openssl_sign() later in another method */ $key = openssl_pkey_get_private("file://$file", $password); if (!is_resource($key)) { throw new Exception("Could not load private key"); } openssl_pkey_export($key, $this->key); $this->pub = openssl_pkey_get_details($key)["key"]; } /** * Sign the PHAR * @param \Phar $package */ function sign(\Phar $package) { $package->setSignatureAlgorithm(\Phar::OPENSSL, $this->key); } /** * Export the public key to a file * @param string $file * @throws \pharext\Exception */ function exportPublicKey($file) { if (!file_put_contents("$file.tmp", $this->pub) || !rename("$file.tmp", $file)) { throw new Exception; } } } <?php namespace pharext; use Phar; use pharext\Cli\Args as CliArgs; use pharext\Cli\Command as CliCommand; use pharext\Exception; /** * The extension packaging command executed by bin/pharext */ class Packager implements Command { use CliCommand; /** * Extension source directory * @var pharext\SourceDir */ private $source; /** * Cleanups * @var array */ private $cleanup = []; /** * Create the command */ public function __construct() { $this->args = new CliArgs([ ["h", "help", "Display this help", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT], ["v", "verbose", "More output", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG], ["q", "quiet", "Less output", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG], ["n", "name", "Extension name", CliArgs::REQUIRED|CliArgs::SINGLE|CliArgs::REQARG], ["r", "release", "Extension release version", CliArgs::REQUIRED|CliArgs::SINGLE|CliArgs::REQARG], ["s", "source", "Extension source directory", CliArgs::REQUIRED|CliArgs::SINGLE|CliArgs::REQARG], ["g", "git", "Use `git ls-tree` to determine file list", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG], ["b", "branch", "Checkout this tag/branch", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::REQARG], ["p", "pecl", "Use PECL package.xml to determine file list, name and release", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG], ["d", "dest", "Destination directory", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::REQARG, "."], ["z", "gzip", "Create additional PHAR compressed with gzip", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG], ["Z", "bzip", "Create additional PHAR compressed with bzip", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG], ["S", "sign", "Sign the PHAR with a private key", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::REQARG], ["E", "zend", "Mark as Zend Extension", CliArgs::OPTIONAL|CliARgs::SINGLE|CliArgs::NOARG], [null, "signature", "Show pharext signature", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT], [null, "license", "Show pharext license", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT], [null, "version", "Show pharext version", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT], ]); } /** * Perform cleaniup */ function __destruct() { foreach ($this->cleanup as $cleanup) { $cleanup->run(); } } /** * @inheritdoc * @see \pharext\Command::run() */ public function run($argc, array $argv) { $errs = []; $prog = array_shift($argv); foreach ($this->args->parse(--$argc, $argv) as $error) { $errs[] = $error; } if ($this->args["help"]) { $this->header(); $this->help($prog); exit; } try { foreach (["signature", "license", "version"] as $opt) { if ($this->args[$opt]) { printf("%s\n", $this->metadata($opt)); exit; } } } catch (\Exception $e) { $this->error("%s\n", $e->getMessage()); exit(self::EARGS); } try { /* source needs to be evaluated before CliArgs validation, * so e.g. name and version can be overriden and CliArgs * does not complain about missing arguments */ $this->loadSource(); } catch (\Exception $e) { $errs[] = $e->getMessage(); } foreach ($this->args->validate() as $error) { $errs[] = $error; } if ($errs) { if (!$this->args["quiet"]) { $this->header(); } foreach ($errs as $err) { $this->error("%s\n", $err); } printf("\n"); if (!$this->args["quiet"]) { $this->help($prog); } exit(self::EARGS); } $this->createPackage(); } /** * Download remote source * @param string $source * @return string local source */ private function download($source) { if ($this->args->git) { $task = new Task\GitClone($source, $this->args->branch); } else { /* print newline only once */ $done = false; $task = new Task\StreamFetch($source, function($bytes_pct) use(&$done) { if (!$done) { $this->info(" %3d%% [%s>%s] \r", floor($bytes_pct*100), str_repeat("=", round(50*$bytes_pct)), str_repeat(" ", round(50*(1-$bytes_pct))) ); if ($bytes_pct == 1) { $done = true; printf("\n"); } } }); } $local = $task->run($this->verbosity()); $this->cleanup[] = new Task\Cleanup($local); return $local; } /** * Extract local archive * @param stirng $source * @return string extracted directory */ private function extract($source) { try { $task = new Task\Extract($source); $dest = $task->run($this->verbosity()); } catch (\Exception $e) { if (false === strpos($e->getMessage(), "checksum mismatch")) { throw $e; } $dest = (new Task\PaxFixup($source))->run($this->verbosity()); } $this->cleanup[] = new Task\Cleanup($dest); return $dest; } /** * Localize a possibly remote source * @param string $source * @return string local source directory */ private function localize($source) { if (!stream_is_local($source) || ($this->args->git && isset($this->args->branch))) { $source = $this->download($source); $this->cleanup[] = new Task\Cleanup($source); } $source = realpath($source); if (!is_dir($source)) { $source = $this->extract($source); $this->cleanup[] = new Task\Cleanup($source); if (!$this->args->git) { $source = (new Task\PeclFixup($source))->run($this->verbosity()); } } return $source; } /** * Load the source dir * @throws \pharext\Exception */ private function loadSource(){ if ($this->args["source"]) { $source = $this->localize($this->args["source"]); if ($this->args["pecl"]) { $this->source = new SourceDir\Pecl($source); } elseif ($this->args["git"]) { $this->source = new SourceDir\Git($source); } elseif (is_file("$source/pharext_package.php")) { $this->source = include "$source/pharext_package.php"; } else { $this->source = new SourceDir\Basic($source); } if (!$this->source instanceof SourceDir) { throw new Exception("Unknown source dir $source"); } foreach ($this->source->getPackageInfo() as $key => $val) { $this->args->$key = $val; } } } /** * Creates the extension phar */ private function createPackage() { try { $meta = array_merge($this->metadata(), [ "date" => date("Y-m-d"), "name" => $this->args->name, "release" => $this->args->release, "license" => @file_get_contents(current(glob($this->source->getBaseDir()."/LICENSE*"))), "stub" => "pharext_installer.php", "type" => $this->args->zend ? "zend_extension" : "extension", ]); $file = (new Task\PharBuild($this->source, $meta))->run($this->verbosity()); } catch (\Exception $e) { $this->error("%s\n", $e->getMessage()); exit(self::EBUILD); } try { if ($this->args->sign) { $this->info("Using private key to sign phar ...\n"); $pass = (new Task\Askpass)->run($this->verbosity()); $sign = new Task\PharSign($file, $this->args->sign, $pass); $pkey = $sign->run($this->verbosity()); } } catch (\Exception $e) { $this->error("%s\n", $e->getMessage()); exit(self::ESIGN); } if ($this->args->gzip) { try { $gzip = (new Task\PharCompress($file, Phar::GZ))->run(); $move = new Task\PharRename($gzip, $this->args->dest, $this->args->name ."-". $this->args->release); $name = $move->run($this->verbosity()); $this->info("Created gzipped phar %s\n", $name); if ($this->args->sign) { $sign = new Task\PharSign($name, $this->args->sign, $pass); $sign->run($this->verbosity())->exportPublicKey($name.".pubkey"); } } catch (\Exception $e) { $this->warn("%s\n", $e->getMessage()); } } if ($this->args->bzip) { try { $bzip = (new Task\PharCompress($file, Phar::BZ2))->run(); $move = new Task\PharRename($bzip, $this->args->dest, $this->args->name ."-". $this->args->release); $name = $move->run($this->verbosity()); $this->info("Created bzipped phar %s\n", $name); if ($this->args->sign) { $sign = new Task\PharSign($name, $this->args->sign, $pass); $sign->run($this->verbosity())->exportPublicKey($name.".pubkey"); } } catch (\Exception $e) { $this->warn("%s\n", $e->getMessage()); } } try { $move = new Task\PharRename($file, $this->args->dest, $this->args->name ."-". $this->args->release); $name = $move->run($this->verbosity()); $this->info("Created executable phar %s\n", $name); if (isset($pkey)) { $pkey->exportPublicKey($name.".pubkey"); } } catch (\Exception $e) { $this->error("%s\n", $e->getMessage()); exit(self::EBUILD); } } } <?php namespace pharext\SourceDir; use pharext\Cli\Args; use pharext\SourceDir; use FilesystemIterator; use IteratorAggregate; use RecursiveCallbackFilterIterator; use RecursiveDirectoryIterator; use RecursiveIteratorIterator; class Basic implements IteratorAggregate, SourceDir { private $path; public function __construct($path) { $this->path = $path; } public function getBaseDir() { return $this->path; } public function getPackageInfo() { return []; } public function getArgs() { return []; } public function setArgs(Args $args) { } public function filter($current, $key, $iterator) { $sub = $current->getSubPath(); if ($sub === ".git" || $sub === ".hg" || $sub === ".svn") { return false; } return true; } public function getIterator() { $rdi = new RecursiveDirectoryIterator($this->path, FilesystemIterator::CURRENT_AS_SELF | // needed for 5.5 FilesystemIterator::KEY_AS_PATHNAME | FilesystemIterator::SKIP_DOTS); $rci = new RecursiveCallbackFilterIterator($rdi, [$this, "filter"]); $rii = new RecursiveIteratorIterator($rci); foreach ($rii as $path => $child) { if (!$child->isDir()) { yield $path; } } } } <?php namespace pharext\SourceDir; use pharext\Command; use pharext\Cli\Args; use pharext\SourceDir; /** * Extension source directory which is a git repo */ class Git implements \IteratorAggregate, SourceDir { /** * Base directory * @var string */ private $path; /** * @inheritdoc * @see \pharext\SourceDir::__construct() */ public function __construct($path) { $this->path = $path; } /** * @inheritdoc * @see \pharext\SourceDir::getBaseDir() */ public function getBaseDir() { return $this->path; } /** * @inheritdoc * @return array */ public function getPackageInfo() { return []; } /** * @inheritdoc * @return array */ public function getArgs() { return []; } /** * @inheritdoc */ public function setArgs(Args $args) { } /** * Generate a list of files by `git ls-files` * @return Generator */ private function generateFiles() { $pwd = getcwd(); chdir($this->path); if (($pipe = popen("git ls-tree -r --name-only HEAD", "r"))) { $path = realpath($this->path); while (!feof($pipe)) { if (strlen($file = trim(fgets($pipe)))) { /* there may be symlinks, so no realpath here */ yield "$path/$file"; } } pclose($pipe); } chdir($pwd); } /** * Implements IteratorAggregate * @see IteratorAggregate::getIterator() */ public function getIterator() { return $this->generateFiles(); } } <?php namespace pharext\SourceDir; use pharext\Cli\Args; use pharext\Exception; use pharext\SourceDir; use pharext\Tempfile; /** * A PECL extension source directory containing a v2 package.xml */ class Pecl implements \IteratorAggregate, SourceDir { /** * The package.xml * @var SimpleXmlElement */ private $sxe; /** * The base directory * @var string */ private $path; /** * The package.xml * @var string */ private $file; /** * @inheritdoc * @see \pharext\SourceDir::__construct() */ public function __construct($path) { if (is_file("$path/package2.xml")) { $sxe = simplexml_load_file($this->file = "$path/package2.xml"); } elseif (is_file("$path/package.xml")) { $sxe = simplexml_load_file($this->file = "$path/package.xml"); } else { throw new Exception("Missing package.xml in $path"); } $sxe->registerXPathNamespace("pecl", $sxe->getDocNamespaces()[""]); $this->sxe = $sxe; $this->path = realpath($path); } /** * @inheritdoc * @see \pharext\SourceDir::getBaseDir() */ public function getBaseDir() { return $this->path; } /** * Retrieve gathered package info * @return Generator */ public function getPackageInfo() { if (($name = $this->sxe->xpath("/pecl:package/pecl:name"))) { yield "name" => (string) $name[0]; } if (($release = $this->sxe->xpath("/pecl:package/pecl:version/pecl:release"))) { yield "release" => (string) $release[0]; } if ($this->sxe->xpath("/pecl:package/pecl:zendextsrcrelease")) { yield "zend" => true; } } /** * @inheritdoc * @see \pharext\SourceDir::getArgs() */ public function getArgs() { $configure = $this->sxe->xpath("/pecl:package/pecl:extsrcrelease/pecl:configureoption"); foreach ($configure as $cfg) { yield [null, $cfg["name"], ucfirst($cfg["prompt"]), Args::OPTARG, strlen($cfg["default"]) ? $cfg["default"] : null]; } $configure = $this->sxe->xpath("/pecl:package/pecl:zendextsrcrelease/pecl:configureoption"); foreach ($configure as $cfg) { yield [null, $cfg["name"], ucfirst($cfg["prompt"]), Args::OPTARG, strlen($cfg["default"]) ? $cfg["default"] : null]; } } /** * @inheritdoc * @see \pharext\SourceDir::setArgs() */ public function setArgs(Args $args) { $configure = $this->sxe->xpath("/pecl:package/pecl:extsrcrelease/pecl:configureoption"); foreach ($configure as $cfg) { if (isset($args[$cfg["name"]])) { $args->configure = "--{$cfg["name"]}={$args[$cfg["name"]]}"; } } $configure = $this->sxe->xpath("/pecl:package/pecl:zendextsrcrelease/pecl:configureoption"); foreach ($configure as $cfg) { if (isset($args[$cfg["name"]])) { $args->configure = "--{$cfg["name"]}={$args[$cfg["name"]]}"; } } } /** * Compute the path of a file by parent dir nodes * @param \SimpleXMLElement $ele * @return string */ private function dirOf($ele) { $path = ""; while (($ele = current($ele->xpath(".."))) && $ele->getName() == "dir") { $path = trim($ele["name"], "/") ."/". $path ; } return trim($path, "/"); } /** * Generate a list of files from the package.xml * @return Generator */ private function generateFiles() { /* hook */ $temp = tmpfile(); fprintf($temp, "<?php\nreturn new %s(__DIR__);\n", get_class($this)); rewind($temp); yield "pharext_package.php" => $temp; /* deps */ $dependencies = $this->sxe->xpath("/pecl:package/pecl:dependencies/pecl:required/pecl:package"); foreach ($dependencies as $key => $dep) { if (($glob = glob("{$this->path}/{$dep->name}-*.ext.phar*"))) { usort($glob, function($a, $b) { return version_compare( substr($a, strpos(".ext.phar", $a)), substr($b, strpos(".ext.phar", $b)) ); }); yield end($glob); } } /* files */ yield realpath($this->file); foreach ($this->sxe->xpath("//pecl:file") as $file) { yield realpath($this->path ."/". $this->dirOf($file) ."/". $file["name"]); } } /** * Implements IteratorAggregate * @see IteratorAggregate::getIterator() */ public function getIterator() { return $this->generateFiles(); } } <?php namespace pharext; /** * Source directory interface, which should yield file names to package on traversal */ interface SourceDir extends \Traversable { /** * Retrieve the base directory * @return string */ public function getBaseDir(); /** * Retrieve gathered package info * @return array|Traversable */ public function getPackageInfo(); /** * Provide installer command line args * @return array|Traversable */ public function getArgs(); /** * Process installer command line args * @param \pharext\Cli\Args $args */ public function setArgs(Cli\Args $args); } <?php namespace pharext\Task; use pharext\Exception; use pharext\ExecCmd; use pharext\Task; use pharext\Tempfile; /** * PHP INI activation */ class Activate implements Task { /** * @var string */ private $cwd; /** * @var array */ private $inis; /** * @var string */ private $type; /** * @var string */ private $php_config; /** * @var string */ private $sudo; /** * @param string $cwd working directory * @param array $inis custom INI or list of loaded/scanned INI files * @param string $type extension or zend_extension * @param string $prefix install prefix, e.g. /usr/local * @param string $common_name PHP programs common name, e.g. php5 * @param string $sudo sudo command * @throws \pharext\Exception */ public function __construct($cwd, array $inis, $type = "extension", $prefix = null, $common_name = "php", $sudo = null) { $this->cwd = $cwd; $this->type = $type; $this->sudo = $sudo; if (!$this->inis = $inis) { throw new Exception("No PHP INIs given"); } $cmd = $common_name . "-config"; if (isset($prefix)) { $cmd = $prefix . "/bin/" . $cmd; } $this->php_config = $cmd; } /** * @param bool $verbose * @return boolean false, if extension was already activated */ public function run($verbose = false) { if ($verbose !== false) { printf("Running INI activation ...\n"); } $extension = basename(current(glob("{$this->cwd}/modules/*.so"))); if ($this->type === "zend_extension") { $pattern = preg_quote((new ExecCmd($this->php_config))->run(["--extension-dir"])->getOutput() . "/$extension", "/"); } else { $pattern = preg_quote($extension, "/"); } foreach ($this->inis as $file) { if ($verbose) { printf("Checking %s ...\n", $file); } $temp = new Tempfile("phpini"); foreach (file($file) as $line) { if (preg_match("/^\s*{$this->type}\s*=\s*[\"']?{$pattern}[\"']?\s*(;.*)?\$/", $line)) { return false; } fwrite($temp->getStream(), $line); } } /* not found; append to last processed file, which is the main by default */ if ($verbose) { printf("Activating in %s ...\n", $file); } fprintf($temp->getStream(), $this->type . "=%s\n", $extension); $temp->closeStream(); $path = $temp->getPathname(); $stat = stat($file); // owner transfer $ugid = sprintf("%d:%d", $stat["uid"], $stat["gid"]); $cmd = new ExecCmd("chown", $verbose); if (isset($this->sudo)) { $cmd->setSu($this->sudo); } $cmd->run([$ugid, $path]); // permission transfer $perm = decoct($stat["mode"] & 0777); $cmd = new ExecCmd("chmod", $verbose); if (isset($this->sudo)) { $cmd->setSu($this->sudo); } $cmd->run([$perm, $path]); // rename $cmd = new ExecCmd("mv", $verbose); if (isset($this->sudo)) { $cmd->setSu($this->sudo); } $cmd->run([$path, $file]); if ($verbose) { printf("Replaced %s ...\n", $file); } return true; } } <?php namespace pharext\Task; use pharext\Task; /** * Ask password on console */ class Askpass implements Task { /** * @var string */ private $prompt; /** * @param string $prompt */ public function __construct($prompt = "Password:") { $this->prompt = $prompt; } /** * @param bool $verbose * @return string */ public function run($verbose = false) { system("stty -echo"); printf("%s ", $this->prompt); $pass = fgets(STDIN, 1024); printf("\n"); system("stty echo"); if (substr($pass, -1) == "\n") { $pass = substr($pass, 0, -1); } return $pass; } } <?php namespace pharext\Task; use pharext\Task; use RecursiveDirectoryIterator; use RecursiveIteratorIterator; /** * List all library files of pharext to bundle with a phar */ class BundleGenerator implements Task { /** * @param bool $verbose * @return Generator */ public function run($verbose = false) { if ($verbose) { printf("Packaging pharext ... \n"); } $rdi = new RecursiveDirectoryIterator(dirname(dirname(__DIR__))); $rii = new RecursiveIteratorIterator($rdi); for ($rii->rewind(); $rii->valid(); $rii->next()) { if (!$rii->isDot()) { yield $rii->getSubPathname() => $rii->key(); } } } } <?php namespace pharext\Task; use pharext\Task; use FilesystemIterator; use RecursiveDirectoryIterator; use RecursiveIteratorIterator; /** * Recursively cleanup FS entries */ class Cleanup implements Task { /** * @var string */ private $rm; public function __construct($rm) { $this->rm = $rm; } /** * @param bool $verbose */ public function run($verbose = false) { if ($verbose) { printf("Cleaning up %s ...\n", $this->rm); } if ($this->rm instanceof Tempfile) { unset($this->rm); } elseif (is_dir($this->rm)) { $rdi = new RecursiveDirectoryIterator($this->rm, FilesystemIterator::CURRENT_AS_SELF | // needed for 5.5 FilesystemIterator::KEY_AS_PATHNAME | FilesystemIterator::SKIP_DOTS); $rii = new RecursiveIteratorIterator($rdi, RecursiveIteratorIterator::CHILD_FIRST); foreach ($rii as $path => $child) { if ($child->isDir()) { rmdir($path); } else { unlink($path); } } rmdir($this->rm); } else { @unlink($this->rm); } } } <?php namespace pharext\Task; use pharext\Exception; use pharext\ExecCmd; use pharext\Task; /** * Runs extension's configure */ class Configure implements Task { /** * @var array */ private $args; /** * @var string */ private $cwd; /** * @param string $cwd working directory * @param array $args configure args * @param string $prefix install prefix, e.g. /usr/local * @param string $common_name PHP programs common name, e.g. php5 */ public function __construct($cwd, array $args = null, $prefix = null, $common_name = "php") { $this->cwd = $cwd; $cmd = $common_name . "-config"; if (isset($prefix)) { $cmd = $prefix . "/bin/" . $cmd; } $this->args = ["--with-php-config=$cmd"]; if ($args) { $this->args = array_merge($this->args, $args); } } public function run($verbose = false) { if ($verbose !== false) { printf("Running ./configure ...\n"); } $pwd = getcwd(); if (!chdir($this->cwd)) { throw new Exception; } try { $cmd = new ExecCmd("./configure", $verbose); $cmd->run($this->args); } finally { chdir($pwd); } } } <?php namespace pharext\Task; use pharext\Task; use pharext\Tempdir; use Phar; use PharData; /** * Extract a package archive */ class Extract implements Task { /** * @var Phar(Data) */ private $source; /** * @param mixed $source archive location */ public function __construct($source) { if ($source instanceof Phar || $source instanceof PharData) { $this->source = $source; } else { $this->source = new PharData($source); } } /** * @param bool $verbose * @return \pharext\Tempdir */ public function run($verbose = false) { if ($verbose) { printf("Extracting %s ...\n", basename($this->source->getPath())); } $dest = new Tempdir("extract"); $this->source->extractTo($dest); return $dest; } } <?php namespace pharext\Task; use pharext\ExecCmd; use pharext\Task; use pharext\Tempdir; /** * Clone a git repo */ class GitClone implements Task { /** * @var string */ private $source; /** * @var string */ private $branch; /** * @param string $source git repo location */ public function __construct($source, $branch = null) { $this->source = $source; $this->branch = $branch; } /** * @param bool $verbose * @return \pharext\Tempdir */ public function run($verbose = false) { if ($verbose !== false) { printf("Fetching %s ...\n", $this->source); } $local = new Tempdir("gitclone"); $cmd = new ExecCmd("git", $verbose); if (strlen($this->branch)) { $cmd->run(["clone", "--depth", 1, "--branch", $this->branch, $this->source, $local]); } else { $cmd->run(["clone", $this->source, $local]); } return $local; } } <?php namespace pharext\Task; use pharext\ExecCmd; use pharext\Exception; use pharext\Task; /** * Run make in the source dir */ class Make implements Task { /** * @var string */ private $cwd; /** * @var array */ private $args; /** * @var string */ private $sudo; /** * * @param string $cwd working directory * @param array $args make's arguments * @param string $sudo sudo command */ public function __construct($cwd, array $args = null, $sudo = null) { $this->cwd = $cwd; $this->sudo = $sudo; $this->args = $args; } /** * * @param bool $verbose * @throws \pharext\Exception */ public function run($verbose = false) { if ($verbose !== false) { printf("Running make"); if ($this->args) { foreach ($this->args as $arg) { printf(" %s", $arg); } } printf(" ...\n"); } $pwd = getcwd(); if (!chdir($this->cwd)) { throw new Exception; } try { $cmd = new ExecCmd("make", $verbose); if (isset($this->sudo)) { $cmd->setSu($this->sudo); } $args = $this->args; if (!$verbose) { $args = array_merge((array) $args, ["-s"]); } $cmd->run($args); } finally { chdir($pwd); } } } <?php namespace pharext\Task; use pharext\Exception; use pharext\Task; use pharext\Tempfile; class PaxFixup implements Task { private $source; public function __construct($source) { $this->source = $source; } private function openArchive($source) { $hdr = file_get_contents($source, false, null, 0, 3); if ($hdr === "\x1f\x8b\x08") { $fd = fopen("compress.zlib://$source", "r"); } elseif ($hdr === "BZh") { $fd = fopen("compress.bzip2://$source", "r"); } else { $fd = fopen($source, "r"); } if (!is_resource($fd)) { throw new Exception; } return $fd; } public function run($verbose = false) { if ($verbose !== false) { printf("Fixing up a tarball with global pax header ...\n"); } $temp = new Tempfile("paxfix"); stream_copy_to_stream($this->openArchive($this->source), $temp->getStream(), -1, 1024); $temp->closeStream(); return (new Extract((string) $temp))->run($verbose); } }<?php namespace pharext\Task; use pharext\Exception; use pharext\Task; /** * Fixup package.xml files in an extracted PECL dir */ class PeclFixup implements Task { /** * @var string */ private $source; /** * @param string $source source directory */ public function __construct($source) { $this->source = $source; } /** * @param bool $verbose * @return string sanitized source location * @throws \pahrext\Exception */ public function run($verbose = false) { if ($verbose !== false) { printf("Sanitizing PECL dir ...\n"); } $dirs = glob("{$this->source}/*", GLOB_ONLYDIR); $files = array_diff(glob("{$this->source}/*"), $dirs); $check = array_reduce($files, function($r, $v) { return $v && fnmatch("package*.xml", basename($v)); }, true); if (count($dirs) !== 1 || !$check) { throw new Exception("Does not look like an extracted PECL dir: {$this->source}"); } $dest = current($dirs); foreach ($files as $file) { if ($verbose) { printf("Moving %s into %s ...\n", basename($file), basename($dest)); } if (!rename($file, "$dest/" . basename($file))) { throw new Exception; } } return $dest; } } <?php namespace pharext\Task; use pharext\Exception; use pharext\SourceDir; use pharext\Task; use pharext\Tempname; use Phar; /** * Build phar */ class PharBuild implements Task { /** * @var \pharext\SourceDir */ private $source; /** * @var array */ private $meta; /** * @var bool */ private $readonly; /** * @param SourceDir $source extension source directory * @param array $meta phar meta data * @param bool $readonly whether the stub has -dphar.readonly=1 set */ public function __construct(SourceDir $source = null, array $meta = null, $readonly = true) { $this->source = $source; $this->meta = $meta; $this->readonly = $readonly; } /** * @param bool $verbose * @return \pharext\Tempname * @throws \pharext\Exception */ public function run($verbose = false) { /* Phar::compress() and ::convert*() use strtok("."), ugh! * so, be sure to not use any other dots in the filename * except for .phar */ $temp = new Tempname("", "-pharext.phar"); $phar = new Phar($temp); $phar->startBuffering(); if ($this->meta) { $phar->setMetadata($this->meta); if (isset($this->meta["stub"])) { $phar->setDefaultStub($this->meta["stub"]); $phar->setStub("#!/usr/bin/php -dphar.readonly=" . intval($this->readonly) ."\n". $phar->getStub()); } } $phar->buildFromIterator((new Task\BundleGenerator)->run()); if ($this->source) { if ($verbose) { $bdir = $this->source->getBaseDir(); $blen = strlen($bdir); foreach ($this->source as $index => $file) { if (is_resource($file)) { printf("Packaging %s ...\n", $index); $phar[$index] = $file; } else { printf("Packaging %s ...\n", $index = trim(substr($file, $blen), "/")); $phar->addFile($file, $index); } } } else { $phar->buildFromIterator($this->source, $this->source->getBaseDir()); } } $phar->stopBuffering(); if (!chmod($temp, fileperms($temp) | 0111)) { throw new Exception; } return $temp; } }<?php namespace pharext\Task; use pharext\Task; use Phar; /** * Clone a compressed copy of a phar */ class PharCompress implements Task { /** * @var string */ private $file; /** * @var Phar */ private $package; /** * @var int */ private $encoding; /** * @var string */ private $extension; /** * @param string $file path to the original phar * @param int $encoding Phar::GZ or Phar::BZ2 */ public function __construct($file, $encoding) { $this->file = $file; $this->package = new Phar($file); $this->encoding = $encoding; switch ($encoding) { case Phar::GZ: $this->extension = ".gz"; break; case Phar::BZ2: $this->extension = ".bz2"; break; } } /** * @param bool $verbose * @return string */ public function run($verbose = false) { if ($verbose) { printf("Compressing %s ...\n", basename($this->package->getPath())); } $phar = $this->package->compress($this->encoding); $meta = $phar->getMetadata(); if (isset($meta["stub"])) { /* drop shebang */ $phar->setDefaultStub($meta["stub"]); } return $this->file . $this->extension; } } <?php namespace pharext\Task; use pharext\Exception; use pharext\Task; /** * Rename the phar archive */ class PharRename implements Task { /** * @var string */ private $phar; /** * @var string */ private $dest; /** * @var string */ private $name; /** * @param string $phar path to phar * @param string $dest destination dir * @param string $name package name */ public function __construct($phar, $dest, $name) { $this->phar = $phar; $this->dest = $dest; $this->name = $name; } /** * @param bool $verbose * @return string path to renamed phar * @throws \pharext\Exception */ public function run($verbose = false) { $extension = substr(strstr($this->phar, "-pharext.phar"), 8); $name = sprintf("%s/%s.ext%s", $this->dest, $this->name, $extension); if ($verbose) { printf("Renaming %s to %s ...\n", basename($this->phar), basename($name)); } if (!rename($this->phar, $name)) { throw new Exception; } return $name; } } <?php namespace pharext\Task; use pharext\Openssl; use pharext\Task; use Phar; /** * Sign the phar with a private key */ class PharSign implements Task { /** * @var Phar */ private $phar; /** * @var \pharext\Openssl\PrivateKey */ private $pkey; /** * * @param mixed $phar phar instance or path to phar * @param string $pkey path to private key * @param string $pass password for the private key */ public function __construct($phar, $pkey, $pass) { if ($phar instanceof Phar || $phar instanceof PharData) { $this->phar = $phar; } else { $this->phar = new Phar($phar); } $this->pkey = new Openssl\PrivateKey($pkey, $pass); } /** * @param bool $verbose * @return \pharext\Openssl\PrivateKey */ public function run($verbose = false) { if ($verbose) { printf("Signing %s ...\n", basename($this->phar->getPath())); } $this->pkey->sign($this->phar); return $this->pkey; } } <?php namespace pharext\Task; use pharext\Exception; use pharext\ExecCmd; use pharext\Task; /** * Run phpize in the extension source directory */ class Phpize implements Task { /** * @var string */ private $phpize; /** * * @var string */ private $cwd; /** * @param string $cwd working directory * @param string $prefix install prefix, e.g. /usr/local * @param string $common_name PHP program common name, e.g. php5 */ public function __construct($cwd, $prefix = null, $common_name = "php") { $this->cwd = $cwd; $cmd = $common_name . "ize"; if (isset($prefix)) { $cmd = $prefix . "/bin/" . $cmd; } $this->phpize = $cmd; } /** * @param bool $verbose * @throws \pharext\Exception */ public function run($verbose = false) { if ($verbose !== false) { printf("Running %s ...\n", $this->phpize); } $pwd = getcwd(); if (!chdir($this->cwd)) { throw new Exception; } try { $cmd = new ExecCmd($this->phpize, $verbose); $cmd->run(); } finally { chdir($pwd); } } } <?php namespace pharext\Task; use pharext\Exception; use pharext\Task; use pharext\Tempfile; /** * Fetch a remote archive */ class StreamFetch implements Task { /** * @var string */ private $source; /** * @var callable */ private $progress; /** * @param string $source remote file location * @param callable $progress progress callback */ public function __construct($source, callable $progress) { $this->source = $source; $this->progress = $progress; } private function createStreamContext() { $progress = $this->progress; /* avoid bytes_max bug of older PHP versions */ $maxbytes = 0; return stream_context_create([],["notification" => function($notification, $severity, $message, $code, $bytes_cur, $bytes_max) use($progress, &$maxbytes) { if ($bytes_max > $maxbytes) { $maxbytes = $bytes_max; } switch ($notification) { case STREAM_NOTIFY_CONNECT: $progress(0); break; case STREAM_NOTIFY_PROGRESS: $progress($maxbytes > 0 ? $bytes_cur/$maxbytes : .5); break; case STREAM_NOTIFY_COMPLETED: /* this is sometimes not generated, why? */ $progress(1); break; } }]); } /** * @param bool $verbose * @return \pharext\Task\Tempfile * @throws \pharext\Exception */ public function run($verbose = false) { if ($verbose !== false) { printf("Fetching %s ...\n", $this->source); } $context = $this->createStreamContext(); if (!$remote = fopen($this->source, "r", false, $context)) { throw new Exception; } $local = new Tempfile("remote"); if (!stream_copy_to_stream($remote, $local->getStream())) { throw new Exception; } $local->closeStream(); /* STREAM_NOTIFY_COMPLETED is not generated, see above */ call_user_func($this->progress, 1); return $local; } } <?php namespace pharext; /** * Simple task interface */ interface Task { public function run($verbose = false); } <?php namespace pharext; /** * Create a temporary directory */ class Tempdir extends \SplFileInfo { /** * @param string $prefix prefix to uniqid() * @throws \pharext\Exception */ public function __construct($prefix) { $temp = new Tempname($prefix); if (!is_dir($temp) && !mkdir($temp, 0700, true)) { throw new Exception("Could not create tempdir: ".error_get_last()["message"]); } parent::__construct($temp); } } <?php namespace pharext; /** * Create a new temporary file */ class Tempfile extends \SplFileInfo { /** * @var resource */ private $handle; /** * @param string $prefix uniqid() prefix * @param string $suffix e.g. file extension * @throws \pharext\Exception */ public function __construct($prefix, $suffix = ".tmp") { $tries = 0; $omask = umask(077); do { $path = new Tempname($prefix, $suffix); $this->handle = fopen($path, "x"); } while (!is_resource($this->handle) && $tries++ < 10); umask($omask); if (!is_resource($this->handle)) { throw new Exception("Could not create temporary file"); } parent::__construct($path); } /** * Unlink the file */ public function __destruct() { @unlink($this->getPathname()); } /** * Close the stream */ public function closeStream() { fclose($this->handle); } /** * Retrieve the stream resource * @return resource */ public function getStream() { return $this->handle; } } <?php namespace pharext; use pharext\Exception; /** * A temporary file/directory name */ class Tempname { /** * @var string */ private $name; /** * @param string $prefix uniqid() prefix * @param string $suffix e.g. file extension */ public function __construct($prefix, $suffix = null) { $temp = sys_get_temp_dir() . "/pharext-" . posix_getlogin(); if (!is_dir($temp) && !mkdir($temp, 0700, true)) { throw new Exception; } $this->name = $temp ."/". uniqid($prefix) . $suffix; } /** * @return string */ public function __toString() { return (string) $this->name; } } <?php namespace pharext; const VERSION = "@PHAREXT_VERSION@"; <?php /** * The installer sub-stub for extension phars */ spl_autoload_register(function($c) { return include strtr($c, "\\_", "//") . ".php"; }); $installer = new pharext\Installer(); $installer->run($argc, $argv); <?php /** * The packager sub-stub for bin/pharext */ spl_autoload_register(function($c) { return include strtr($c, "\\_", "//") . ".php"; }); $packager = new pharext\Packager(); $packager->run($argc, $argv); <?php return new pharext\SourceDir\Pecl(__DIR__); <?xml version="1.0" encoding="UTF-8"?> <package packagerversion="1.9.0" version="2.0" xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0 http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.0 http://pear.php.net/dtd/package-2.0.xsd"> <name>uri_template</name> <channel>pecl.php.net</channel> <summary>uri_template extension</summary> <description>Implementation of URI Template(RFC6570) specification for PHP.</description> <lead> <name>Ioseb Dzmanashvili</name> <user>ioseb</user> <email>ioseb.dzmanashvili@gmail.com</email> <active>yes</active> </lead> <date>2013-10-15</date> <time>02:17:15</time> <version> <release>1.0</release> <api>1.0</api> </version> <stability> <release>stable</release> <api>stable</api> </stability> <license uri="http://www.php.net/license/3_01.txt">PHP License</license> <notes> - Fixed extended version: https://github.com/ioseb/uri-template/issues/6 - Package includes LICENSE and README.md files - Windows fix by Anatol Belski: https://github.com/ioseb/uri-template/pull/4 </notes> <contents> <dir name="/"> <file md5sum="5bb09683f8f09b15b7076f4b4c31d61f" name="LICENSE" role="doc" /> <file md5sum="fe5d989c11119c9fcd8076a6a24860f5" name="README.md" role="doc" /> <file md5sum="33edf24facfe26d6fcb820fe2591a952" name="config.m4" role="src" /> <file md5sum="5471648f454847f101daa6a21b69a6d7" name="config.w32" role="src" /> <file md5sum="ebb433359c6c41376e10312e8a9c39d6" name="php_uri_template.h" role="src" /> <file md5sum="4522151d14d6ec81d217d452c55a8f45" name="uri_template.c" role="src" /> <file md5sum="b8b23771c4477a5bb570971e62a4067c" name="uri_template_string.c" role="src" /> <file md5sum="97d5a8292fd9d64918203d4eb39b3927" name="uri_template_processor.c" role="src" /> <file md5sum="d9ea279a79adf98b9f0b531122d6f69d" name="uri_template_parser.c" role="src" /> <file md5sum="127bf90866dc654b46d2cc84faa3bdc5" name="uri_template_common.c" role="src" /> <file md5sum="71b45d35b495d88e3c0a373b213e8de4" name="examples/form_style_query_continuation.php" role="doc" /> <file md5sum="03dc0aeeba79ed509823b78ce0802957" name="examples/path_style_parameter_expansion.php" role="doc" /> <file md5sum="30804e7dcb339f51d7ff06fd39c661e4" name="examples/form_style_query_expansion.php" role="doc" /> <file md5sum="283915c45fc93a2c8a0e8aa73ca76f51" name="examples/reserved_expansion.php" role="doc" /> <file md5sum="0b067dd4f85ecbdcf0b029531de887c7" name="examples/fragment_expansion.php" role="doc" /> <file md5sum="58fe3069866026231894f40cd58152fd" name="examples/simple_string_expansion.php" role="doc" /> <file md5sum="ff23409d12ad58aafd01af7bf867352b" name="examples/label_expansion_with_dot_prefix.php" role="doc" /> <file md5sum="ca23d1377a69969d330cf8639bb0ccb8" name="examples/variable_expansion.php" role="doc" /> <file md5sum="7e7e0289e237157af3d9ee45da035a10" name="examples/path_segment_expansion.php" role="doc" /> <file md5sum="b223df76396a336003950a149a688aa6" name="tests/copyliterals1.phpt" role="test" /> <file md5sum="eead6cfd97b0f9bb102a5f76a8c3c76a" name="tests/copyliterals2.phpt" role="test" /> <file md5sum="3b2302d2f618aa29c565d8a64ef43bb6" name="tests/copyliterals3.phpt" role="test" /> <file md5sum="03d48c5c8df043665362ae84b6a1ac8e" name="tests/copyliterals4.phpt" role="test" /> <file md5sum="8f5d24b96ba753c7889c2a67c4d38448" name="tests/copyliterals5.phpt" role="test" /> <file md5sum="806986159ad931acccd02402e1191a19" name="tests/err1.phpt" role="test" /> <file md5sum="3417dec238532c3ef154ef11072a255a" name="tests/err21.phpt" role="test" /> <file md5sum="22c000dc827b31d25bdb348cd4a27721" name="tests/ex1a3.phpt" role="test" /> <file md5sum="02d5ce71ecaf066aa3c0d55697a132ac" name="tests/level1a1.phpt" role="test" /> <file md5sum="f37bd00d7306860a998b8aa2b5132d8c" name="tests/level4a3.phpt" role="test" /> <file md5sum="aab01679fb5cc98584f4bce1ac0e7353" name="tests/err10.phpt" role="test" /> <file md5sum="01d4d84f7883a6fc442c4f6e1369e4f4" name="tests/err22.phpt" role="test" /> <file md5sum="ae584c7534fb813233d3fbdfea191ca7" name="tests/ex1a4.phpt" role="test" /> <file md5sum="c9b3fcc04a77afc32c13ccaef5d6dd2f" name="tests/level2a1.phpt" role="test" /> <file md5sum="f1025ee85d9499e38db7dca6b505bffb" name="tests/level4a4.phpt" role="test" /> <file md5sum="84c3db854bb9dadde700f6dfee3c7466" name="tests/err11.phpt" role="test" /> <file md5sum="de2866c28c48459252f7764fab3b6550" name="tests/err23.phpt" role="test" /> <file md5sum="35aa69a87f790f1964f9a46f3e6cea10" name="tests/expansion0.phpt" role="test" /> <file md5sum="12b771716a18d5a7df24a8fffbc945b2" name="tests/level2a2.phpt" role="test" /> <file md5sum="6803686cc49872184ed743723af24323" name="tests/level4a5.phpt" role="test" /> <file md5sum="a9ca2c87bfe674fc50bb8ee3282d495a" name="tests/err12.phpt" role="test" /> <file md5sum="55f4244bb64742e4aff6122de4d907a6" name="tests/err24.phpt" role="test" /> <file md5sum="9fbc1f268a1ef27fceb1acce0ec07bc9" name="tests/expansion1.phpt" role="test" /> <file md5sum="059fa0f933b2b95c3b75a0e7a20dd204" name="tests/level3a1.phpt" role="test" /> <file md5sum="cc6e642be421c16ba7dcbe345ae3b7e0" name="tests/level4a6.phpt" role="test" /> <file md5sum="71e891d3bc305eea7343f59f298569cb" name="tests/err13.phpt" role="test" /> <file md5sum="184fbdb4ba9765da60aad19f9c8c2de9" name="tests/err3.phpt" role="test" /> <file md5sum="b24862426691033b85724243a25f4af4" name="tests/expansion2.phpt" role="test" /> <file md5sum="bb03bc36408a663cfa74abafdc9d731d" name="tests/level3a2.phpt" role="test" /> <file md5sum="f7375b5b2319e92dddc5b599a213a5a5" name="tests/level4a7.phpt" role="test" /> <file md5sum="5202418d153248b834f10daa649a6cca" name="tests/err14.phpt" role="test" /> <file md5sum="e0f09d64a1df560c8df3bc748bda298d" name="tests/err4.phpt" role="test" /> <file md5sum="9c7ea723f901e659c7949d873ba2be9a" name="tests/expansion3.phpt" role="test" /> <file md5sum="5fb5c42d25399303d277b46b3d292abd" name="tests/level3a3.phpt" role="test" /> <file md5sum="61caf10800a9c2b8651f95dba3a73bae" name="tests/level4a8.phpt" role="test" /> <file md5sum="e7b3e0e2582812908358f213be332559" name="tests/err15.phpt" role="test" /> <file md5sum="58b47a851d308887d72dbd234f29cfec" name="tests/err5.phpt" role="test" /> <file md5sum="2509d11a0dea2352238897d3faf4fb24" name="tests/expansion4.phpt" role="test" /> <file md5sum="ddddf86477de03953f918c36333eebff" name="tests/level3a4.phpt" role="test" /> <file md5sum="5e73619c1e092d00ac4e89c1d2290550" name="tests/mb1.phpt" role="test" /> <file md5sum="341341d30b80a89fd1d2073acf11b32a" name="tests/err16.phpt" role="test" /> <file md5sum="ea84a7fab65708f549d72dcd5397adbb" name="tests/err6.phpt" role="test" /> <file md5sum="88afa454ca4739f9c291984fea237adf" name="tests/expansion5.phpt" role="test" /> <file md5sum="b331e442a4aa616518bd1cfabb8a8c65" name="tests/level3a5.phpt" role="test" /> <file md5sum="78285a969f8745e60b6790ea05ca854d" name="tests/mb2.phpt" role="test" /> <file md5sum="e1fac236c3be0e80fdd29787f634b9ba" name="tests/err17.phpt" role="test" /> <file md5sum="ee91c390f4e973aabb07d944389b4834" name="tests/err7.phpt" role="test" /> <file md5sum="110c586c30f0999c3e855ddaac75de5c" name="tests/expansion6.phpt" role="test" /> <file md5sum="326d30eddd5f13bb12fe781bdf32b136" name="tests/level3a6.phpt" role="test" /> <file md5sum="6fe63ec3c6525338476328e785e15058" name="tests/var1braces.phpt" role="test" /> <file md5sum="6671c2af0bf20e9604efd7b13e16f30b" name="tests/err18.phpt" role="test" /> <file md5sum="766f8af80d84f443302e64e8e6bfbd59" name="tests/err8.phpt" role="test" /> <file md5sum="9be515db8dff91f146829bba1348e8f9" name="tests/expansion7.phpt" role="test" /> <file md5sum="900b686f1293eb61b2ba37be6fad7c4b" name="tests/level3a7.phpt" role="test" /> <file md5sum="bfe89ecd56775e928bb55e20e201abf6" name="tests/var2empty.phpt" role="test" /> <file md5sum="5c9870bc61852ac31ef4178dc20562cb" name="tests/err19.phpt" role="test" /> <file md5sum="ce0fba126943684e31fca7fcd435dddf" name="tests/err9.phpt" role="test" /> <file md5sum="9b7983394d4d7417a4ee50decdc8dd33" name="tests/expansion8.phpt" role="test" /> <file md5sum="57fd461ed44ae6cbb4c8de66685e02db" name="tests/level3a8.phpt" role="test" /> <file md5sum="7e186727ba05fb91fbb45aaf4769b1bd" name="tests/err2.phpt" role="test" /> <file md5sum="47ee2145a013698297ffe36bcce500b0" name="tests/ex1a1.phpt" role="test" /> <file md5sum="669b8f091d4ecb14012291f234dc14b7" name="tests/extended1a1.phpt" role="test" /> <file md5sum="89797a8602178561d127425bdf4800d3" name="tests/level4a1.phpt" role="test" /> <file md5sum="c6c0356706676d07307f71204ba784d0" name="tests/err20.phpt" role="test" /> <file md5sum="cb050a0a3517de327e4023fc13edb979" name="tests/ex1a2.phpt" role="test" /> <file md5sum="9e0a828121364cc10c321725f3119c0d" name="tests/extended1a2.phpt" role="test" /> <file md5sum="90766cad7c57b0a3d2b66f52c48ce6d1" name="tests/level4a2.phpt" role="test" /> </dir> </contents> <dependencies> <required> <php> <min>5.3.1</min> </php> <pearinstaller> <min>1.4.0b1</min> </pearinstaller> </required> </dependencies> <providesextension>uri_template</providesextension> <extsrcrelease /> <changelog> <release> <date>2012-10-31</date> <version> <release>0.99.2</release> <api>0.99.1</api> </version> <stability> <release>beta</release> <api>beta</api> </stability> <license uri="http://www.php.net/license/3_01.txt">PHP License</license> <notes> - Fixed bug: pct-encoded literals outside of varspecs must be copied directly, https://github.com/ioseb/uri-template/issues/1 - Fixed bug: encoding and copying of utf8 literals outside of varspecs </notes> </release> <release> <date>2012-07-31</date> <version> <release>0.99.1</release> <api>0.99.1</api> </version> <stability> <release>beta</release> <api>beta</api> </stability> <notes> - Initial release </notes> </release> </changelog> </package> -------------------------------------------------------------------- The PHP License, version 3.01 Copyright (c) 1999 - 2012 The PHP Group. All rights reserved. -------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without modification, is permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. 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. 3. The name "PHP" must not be used to endorse or promote products derived from this software without prior written permission. For written permission, please contact group@php.net. 4. Products derived from this software may not be called "PHP", nor may "PHP" appear in their name, without prior written permission from group@php.net. You may indicate that your software works in conjunction with PHP by saying "Foo for PHP" instead of calling it "PHP Foo" or "phpfoo" 5. The PHP Group may publish revised and/or new versions of the license from time to time. Each version will be given a distinguishing version number. Once covered code has been published under a particular version of the license, you may always continue to use it under the terms of that version. You may also choose to use such covered code under the terms of any subsequent version of the license published by the PHP Group. No one other than the PHP Group has the right to modify the terms applicable to covered code created under this License. 6. Redistributions of any form whatsoever must retain the following acknowledgment: "This product includes PHP software, freely available from <http://www.php.net/software/>". THIS SOFTWARE IS PROVIDED BY THE PHP DEVELOPMENT TEAM ``AS IS'' AND ANY EXPRESSED 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 PHP DEVELOPMENT TEAM OR ITS 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. -------------------------------------------------------------------- This software consists of voluntary contributions made by many individuals on behalf of the PHP Group. The PHP Group can be contacted via Email at group@php.net. For more information on the PHP Group and the PHP project, please see <http://www.php.net>. PHP includes the Zend Engine, freely available at <http://www.zend.com>. URI Template PHP Extension ============ PHP extension implementation of RFC-6570 in C - http://tools.ietf.org/html/rfc6570 Basic usage ----------- Substituting query parameters: ```php <?php $data = array( "query" => "mycelium", "number" => 100 ); $uri = uri_template('http://www.example.com/foo{?query,number}', $data); ?> ``` This will result to following URI: http://www.example.com/foo?query=mycelium&number=100 Capturing Execution State ------------------------- uri_template(...) function supports third optional parameter for capturing URI template parsing and processing state. Sometimes it's handy to see how template was parsed. Example below demonstrates usage of this parameter: ```php <?php $data = array( "id" => array("person","albums"), "token" => "12345", "fields" => array("id", "name", "picture"), ); $tpl = "{/id*}{?fields,token}"; uri_template($tpl, $data, $result); ?> ``` After executing example code above $result variable will have following structure: ```php <?php array ( 'result' => '/person/albums?fields=id,name,picture&token=12345', 'state' => 0, 'expressions' => array ( 0 => array ( 'op' => '/', 'sep' => '/', 'ifemp' => '', 'allow' => false, 'named' => false, 'error' => false, 'vars' => array ( 0 => array ( 'name' => 'id', 'length' => 0, 'explode' => true, ), ), ), 1 => array ( 'op' => '?', 'sep' => '&', 'ifemp' => '=', 'allow' => false, 'named' => true, 'error' => false, 'vars' => array ( 0 => array ( 'name' => 'fields', 'length' => 0, 'explode' => false, ), 1 => array ( 'name' => 'token', 'length' => 0, 'explode' => false, ), ), ), ), ) ?> ``` Detailed examples ----------------- All following subsections will rely on the data array shown below: ```php <?php $data = array( 'count' => array("one", "two", "three"), 'dom' => array("example", "com"), 'dub' => "me/too", 'hello' => "Hello World!", 'half' => "50%", 'var' => "value", 'who' => "fred", 'base' => "http://example.com/home/", 'path' => "/foo/bar", 'list' => array("red", "green", "blue"), 'keys' => array( "semi" => ";", "dot" => ".", "comma" => ",", ), 'v' => "6", 'x' => "1024", 'y' => "768", 'empty' => "", 'empty_keys' => array(), 'undef' => null, ); ?> ``` Variable expansion ------------------ For more details see [corresponding spec](http://tools.ietf.org/html/rfc6570#section-3.2.1). ```php <?php $templates = array( '{count}', '{count*}', '{/count}', '{/count*}', '{;count}', '{;count*}', '{?count}', '{?count*}', '{&count*}', ); $uris = array(); foreach ($templates as $template) { $uris[$template] = uri_template($template, $data); } var_export($uris); ?> ``` This will result to following URI array: ```php <?php array ( '{count}' => 'one,two,three', '{count*}' => 'one,two,three', '{/count}' => '/one,two,three', '{/count*}' => '/one/two/three', '{;count}' => ';count=one,two,three', '{;count*}' => ';count=one;count=two;count=three', '{?count}' => '?count=one,two,three', '{?count*}' => '?count=one&count=two&count=three', '{&count*}' => '&count=one&count=two&count=three', ) ?> ``` Simple String Expansion: {var} ------------------------------ For more details see [corresponding spec](http://tools.ietf.org/html/rfc6570#section-3.2.2). ```php <?php $templates = array( "{var}", "{hello}", "{half}", "O{empty}X", "O{undef}X", "{x,y}", "{x,hello,y}", "?{x,empty}", "?{x,undef}", "?{undef,y}", "{var:3}", "{var:30}", "{list}", "{list*}", "{keys}", "{keys*}" ); $uris = array(); foreach ($templates as $template) { $uris[$template] = uri_template($template, $data); } var_export($uris); ?> ``` This will result to following URI array: ```php <?php array ( '{var}' => 'value', '{hello}' => 'Hello%20World%21', '{half}' => '50%25', 'O{empty}X' => 'OX', 'O{undef}X' => 'OX', '{x,y}' => '1024,768', '{x,hello,y}' => '1024,Hello%20World%21,768', '?{x,empty}' => '?1024,', '?{x,undef}' => '?1024', '?{undef,y}' => '?768', '{var:3}' => 'val', '{var:30}' => 'value', '{list}' => 'red,green,blue', '{list*}' => 'red,green,blue', '{keys}' => 'semi,%3B,dot,.,comma,%2C', '{keys*}' => 'semi=%3B,dot=.,comma=%2C', ) ?> ``` Reserved Expansion: {+var} -------------------------- For more details see [corresponding spec](http://tools.ietf.org/html/rfc6570#section-3.2.3). ```php <?php $templates = array( "{+var}", "{+hello}", "{+half}", "{base}index", "{+base}index", "O{+empty}X", "O{+undef}X", "{+path}/here", "here?ref={+path}", "up{+path}{var}/here", "{+x,hello,y}", "{+path,x}/here", "{+path:6}/here", "{+list}", "{+list*}", "{+keys}", "{+keys*}" ); $uris = array(); foreach ($templates as $template) { $uris[$template] = uri_template($template, $data); } var_export($uris); ?> ``` This will result to following URI array: ```php <?php array ( '{+var}' => 'value', '{+hello}' => 'Hello%20World!', '{+half}' => '50%25', '{base}index' => 'http%3A%2F%2Fexample.com%2Fhome%2Findex', '{+base}index' => 'http://example.com/home/index', 'O{+empty}X' => 'OX', 'O{+undef}X' => 'OX', '{+path}/here' => '/foo/bar/here', 'here?ref={+path}' => 'here?ref=/foo/bar', 'up{+path}{var}/here' => 'up/foo/barvalue/here', '{+x,hello,y}' => '1024,Hello%20World!,768', '{+path,x}/here' => '/foo/bar,1024/here', '{+path:6}/here' => '/foo/b/here', '{+list}' => 'red,green,blue', '{+list*}' => 'red,green,blue', '{+keys}' => 'semi,;,dot,.,comma,,', '{+keys*}' => 'semi=;,dot=.,comma=,', ) ?> ``` Fragment Expansion: {#var} -------------------------- For more details see [corresponding spec](http://tools.ietf.org/html/rfc6570#section-3.2.4). ```php <?php $templates = array( "{#var}", "{#hello}", "{#half}", "foo{#empty}", "foo{#undef}", "{#x,hello,y}", "{#path,x}/here", "{#path:6}/here", "{#list}", "{#list*}", "{#keys}", "{#keys*}" ); $uris = array(); foreach ($templates as $template) { $uris[$template] = uri_template($template, $data); } var_export($uris); ?> ``` This will result to following URI array: ```php <?php array( "{#var}" => "#value", "{#hello}" => "#Hello%20World!", "{#half}" => "#50%25", "foo{#empty}" => "foo#", "foo{#undef}" => "foo", "{#x,hello,y}" => "#1024,Hello%20World!,768", "{#path,x}/here" => "#/foo/bar,1024/here", "{#path:6}/here" => "#/foo/b/here", "{#list}" => "#red,green,blue", "{#list*}" => "#red,green,blue", "{#keys}" => "#semi,;,dot,.,comma,,", "{#keys*}" => "#semi=;,dot=.,comma=," ) ?> ``` Label Expansion with Dot-Prefix: {.var} --------------------------------------- For more details see [corresponding spec](http://tools.ietf.org/html/rfc6570#section-3.2.5). ```php <?php $templates = array( "{.who}", "{.who,who}", "{.half,who}", "www{.dom*}", "X{.var}", "X{.empty}", "X{.undef}", "X{.var:3}", "X{.list}", "X{.list*}", "X{.keys}", "X{.keys*}", "X{.empty_keys}", "X{.empty_keys*}" ); $uris = array(); foreach ($templates as $template) { $uris[$template] = uri_template($template, $data); } var_export($uris); ?> ``` This will result to following URI array: ```php <?php array( "{.who}" => ".fred", "{.who,who}" => ".fred.fred", "{.half,who}" => ".50%25.fred", "www{.dom*}" => "www.example.com", "X{.var}" => "X.value", "X{.empty}" => "X.", "X{.undef}" => "X", "X{.var:3}" => "X.val", "X{.list}" => "X.red,green,blue", "X{.list*}" => "X.red.green.blue", "X{.keys}" => "X.semi,%3B,dot,.,comma,%2C", "X{.keys*}" => "X.semi=%3B.dot=..comma=%2C", "X{.empty_keys}" => "X", "X{.empty_keys*}" => "X" ) ?> ``` Path Segment Expansion: {/var} ------------------------------ For more details see [corresponding spec](http://tools.ietf.org/html/rfc6570#section-3.2.6). ```php <?php $templates = array( "{/who}", "{/who,who}", "{/half,who}", "{/who,dub}", "{/var}", "{/var,empty}", "{/var,undef}", "{/var,x}/here", "{/var:1,var}", "{/list}", "{/list*}", "{/list*,path:4}", "{/keys}", "{/keys*}" ); $uris = array(); foreach ($templates as $template) { $uris[$template] = uri_template($template, $data); } var_export($uris); ?> ``` This will result to following URI array: ```php <?php array( "{/who}" => "/fred", "{/who,who}" => "/fred/fred", "{/half,who}" => "/50%25/fred", "{/who,dub}" => "/fred/me%2Ftoo", "{/var}" => "/value", "{/var,empty}" => "/value/", "{/var,undef}" => "/value", "{/var,x}/here" => "/value/1024/here", "{/var:1,var}" => "/v/value", "{/list}" => "/red,green,blue", "{/list*}" => "/red/green/blue", "{/list*,path:4}" => "/red/green/blue/%2Ffoo", "{/keys}" => "/semi,%3B,dot,.,comma,%2C", "{/keys*}" => "/semi=%3B/dot=./comma=%2C" ) ?> ``` Path-Style Parameter Expansion: {;var} -------------------------------------- For more details see [corresponding spec](http://tools.ietf.org/html/rfc6570#section-3.2.7). ```php <?php $templates = array( "{;who}", "{;half}", "{;empty}", "{;v,empty,who}", "{;v,bar,who}", "{;x,y}", "{;x,y,empty}", "{;x,y,undef}", "{;hello:5}", "{;list}", "{;list*}", "{;keys}", "{;keys*}" ); $uris = array(); foreach ($templates as $template) { $uris[$template] = uri_template($template, $data); } var_export($uris); ?> ``` This will result to following URI array: ```php <?php array( "{;who}" => ";who=fred", "{;half}" => ";half=50%25", "{;empty}" => ";empty", "{;v,empty,who}" => ";v=6;empty;who=fred", "{;v,bar,who}" => ";v=6;who=fred", "{;x,y}" => ";x=1024;y=768", "{;x,y,empty}" => ";x=1024;y=768;empty", "{;x,y,undef}" => ";x=1024;y=768", "{;hello:5}" => ";hello=Hello", "{;list}" => ";list=red,green,blue", "{;list*}" => ";list=red;list=green;list=blue", "{;keys}" => ";keys=semi,%3B,dot,.,comma,%2C", "{;keys*}" => ";semi=%3B;dot=.;comma=%2C" ) ?> ``` Form-Style Query Expansion: {?var} ---------------------------------- For more details see [corresponding spec](http://tools.ietf.org/html/rfc6570#section-3.2.8). ```php <?php $templates = array( "{?who}", "{?half}", "{?x,y}", "{?x,y,empty}", "{?x,y,undef}", "{?var:3}", "{?list}", "{?list*}", "{?keys}", "{?keys*}" ); $uris = array(); foreach ($templates as $template) { $uris[$template] = uri_template($template, $data); } var_export($uris); ?> ``` This will result to following URI array: ```php <?php array( "{?who}" => "?who=fred", "{?half}" => "?half=50%25", "{?x,y}" => "?x=1024&y=768", "{?x,y,empty}" => "?x=1024&y=768&empty=", "{?x,y,undef}" => "?x=1024&y=768", "{?var:3}" => "?var=val", "{?list}" => "?list=red,green,blue", "{?list*}" => "?list=red&list=green&list=blue", "{?keys}" => "?keys=semi,%3B,dot,.,comma,%2C", "{?keys*}" => "?semi=%3B&dot=.&comma=%2C" ) ?> ``` Form-Style Query Continuation: {&var} ------------------------------------- For more details see [corresponding spec](http://tools.ietf.org/html/rfc6570#section-3.2.9). ```php <?php $templates = array( "{&who}", "{&half}", "?fixed=yes{&x}", "{&x,y,empty}", "{&x,y,undef}", "{&var:3}", "{&list}", "{&list*}", "{&keys}", "{&keys*}" ); $uris = array(); foreach ($templates as $template) { $uris[$template] = uri_template($template, $data); } var_export($uris); ?> ``` This will result to following URI array: ```php <?php array( "{&who}" => "&who=fred", "{&half}" => "&half=50%25", "?fixed=yes{&x}" => "?fixed=yes&x=1024", "{&x,y,empty}" => "&x=1024&y=768&empty=", "{&x,y,undef}" => "&x=1024&y=768", "{&var:3}" => "&var=val", "{&list}" => "&list=red,green,blue", "{&list*}" => "&list=red&list=green&list=blue", "{&keys}" => "&keys=semi,%3B,dot,.,comma,%2C", "{&keys*}" => "&semi=%3B&dot=.&comma=%2C" ) ?> ```dnl $Id$ dnl config.m4 for uri templates PHP_ARG_ENABLE(uri_template, Whether to enable the uri template extension, [ --enable-uri_template Enable "URI Template" extension]) if test "$PHP_URI_TEMPLATE" != "no"; then PHP_SUBST(URI_TEMPLATE_SHARED_LIBADD) PHP_NEW_EXTENSION(uri_template, uri_template.c uri_template_common.c uri_template_parser.c uri_template_processor.c uri_template_string.c, $ext_shared) CFLAGS="$CFLAGS -Wall -g" fi// $Id$ // vim:ft=javascript ARG_ENABLE("uri-template", "Whether to enable the uri template extension", "no"); if (PHP_URI_TEMPLATE != "no") { AC_DEFINE("HAVE_URI_TEMPLATE", 1); EXTENSION("uri_template", "uri_template.c uri_template_common.c uri_template_parser.c uri_template_processor.c uri_template_string.c"); } /* +----------------------------------------------------------------------+ | See LICENSE file for further copyright information | +----------------------------------------------------------------------+ | Authors: Ioseb Dzmanashvili <ioseb.dzmanashvili@gmail.com> | +----------------------------------------------------------------------+ */ #ifndef PHP_URI_TEMPLATE_H #define PHP_URI_TEMPLATE_H #define PHP_URI_TEMPLATE_EXTNAME "uri_template" #define PHP_URI_TEMPLATE_VERSION "1.0" #define URI_TEMPLATE_ERROR_NONE 0 #define URI_TEMPLATE_ERROR 1 #define URI_TEMPLATE_ERROR_SYNTAX 2 #define URI_TEMPLATE_ERROR_EXPRESSION 3 #define URI_TEMPLATE_ALLOW_UNRESERVED 0 #define URI_TEMPLATE_ALLOW_LITERALS 1 #define URI_TEMPLATE_ALLOW_RESERVED 2 #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "SAPI.h" #include "zend_API.h" #include "php.h" #include "php_ini.h" #include "ext/standard/info.h" #include "ext/standard/php_string.h" #include "ext/standard/php_smart_str.h" #include "ext/standard/html.h" #include "php_variables.h" extern zend_module_entry uri_template_module_entry; #define phpext_uri_template_ptr &uri_template_module_entry; #ifdef ZTS #include "TSRM.h" #endif PHP_FUNCTION(uri_template); typedef struct uri_template_var { struct uri_template_var *next; char *name; int length; int explode; } uri_template_var; typedef struct uri_template_vars { uri_template_var *first; uri_template_var *last; int count; } uri_template_vars; typedef struct uri_template_expr { char op; /* operator */ char first; /* result prefix */ char sep; /* variable separator */ char ifemp; /* if value is empty */ int allow; /* allow reserved chars */ int named; /* var is named */ int error; /* expression is malformed */ uri_template_vars *vars; /* list of expression vars */ } uri_template_expr; uri_template_vars *uri_template_vars_create(); uri_template_var *uri_template_var_create(); uri_template_expr *uri_template_expr_create(char operator); void uri_template_vars_free(uri_template_vars *list); void uri_template_var_free(uri_template_var *var); void uri_template_expr_add_var(uri_template_expr *expr, uri_template_var *var); void uri_template_expr_free(uri_template_expr *expr); void uri_template_parse(char *tpl, zval *return_value, zval *vars, zval *capture); void uri_template_process(uri_template_expr *expr, zval *vars, smart_str *result); void uri_template_substr_copy(smart_str *dest, char *source, size_t num, int allowed_chars); #ifdef ZTS #define IF_G(v) TSRMG(filter_globals_id, zend_filter_globals *, v) #else #define IF_G(v) (filter_globals.v) #endif #endif/* +----------------------------------------------------------------------+ | See LICENSE file for further copyright information | +----------------------------------------------------------------------+ | Authors: Ioseb Dzmanashvili <ioseb.dzmanashvili@gmail.com> | +----------------------------------------------------------------------+ */ #include "php_uri_template.h" /* {{{ proto mixed uri_template(constant template, array variables [, mixed result]) * Returns expanded URI template string. */ PHP_FUNCTION(uri_template) { char *tpl; int len; zval *vars; zval *result = NULL; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa|z", &tpl, &len, &vars, &result) == FAILURE) { RETURN_NULL(); } if (result != NULL) { zval_dtor(result); array_init(result); } uri_template_parse(tpl, return_value, vars, result); } /* }}} */ /* {{{ arginfo */ ZEND_BEGIN_ARG_INFO_EX(uri_template_arg_info, 0, 3, 2) ZEND_ARG_INFO(0, "template") ZEND_ARG_ARRAY_INFO(0, "variables", 0) ZEND_ARG_INFO(1, "result") ZEND_END_ARG_INFO() /* }}} */ /* {{{ PHP_MINFO_FUNCTION */ PHP_MINFO_FUNCTION(uri_template) { php_info_print_table_start(); php_info_print_table_header(2, "uri_template support", "enabled"); php_info_print_table_end(); } /* }}} */ /* {{{ PHP_MINIT_FUNCTION */ PHP_MINIT_FUNCTION(uri_template) { REGISTER_LONG_CONSTANT("URI_TEMPLATE_ERROR_NONE", URI_TEMPLATE_ERROR_NONE, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("URI_TEMPLATE_ERROR", URI_TEMPLATE_ERROR, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("URI_TEMPLATE_ERROR_SYNTAX", URI_TEMPLATE_ERROR_SYNTAX, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("URI_TEMPLATE_ERROR_EXPRESSION", URI_TEMPLATE_ERROR_EXPRESSION, CONST_CS | CONST_PERSISTENT); return SUCCESS; } /* {{{ uri_template_functions[] */ const zend_function_entry uri_template_functions[] = { PHP_FE(uri_template, uri_template_arg_info) {NULL, NULL, NULL} }; /* }}} */ /* {{{ uri_template_module_entry */ zend_module_entry uri_template_module_entry = { #if ZEND_MODULE_API_NO >= 20010901 STANDARD_MODULE_HEADER, #endif PHP_URI_TEMPLATE_EXTNAME, uri_template_functions, PHP_MINIT(uri_template), NULL, NULL, NULL, PHP_MINFO(uri_template), #if ZEND_MODULE_API_NO >= 20010901 PHP_URI_TEMPLATE_VERSION, #endif STANDARD_MODULE_PROPERTIES }; /* }}} */ #ifdef COMPILE_DL_URI_TEMPLATE ZEND_GET_MODULE(uri_template) #endif /* +----------------------------------------------------------------------+ | See LICENSE file for further copyright information | +----------------------------------------------------------------------+ | Authors: Ioseb Dzmanashvili <ioseb.dzmanashvili@gmail.com> | +----------------------------------------------------------------------+ */ #include "php_uri_template.h" #define MAX_ONE_BYTE_CHAR 0x7f #define MAX_TWO_BYTE_CHAR 0x7ff #define MAX_THREE_BYTE_CHAR 0xffff #define MAX_FOUR_BYTE_CHAR 0x1fffff static unsigned char hexchars[] = "0123456789ABCDEF"; static unsigned char badchar[] = {239, 191, 189}; static unsigned char urlchars[3][128] = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '-', '.', 0, '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 0, 0, 0, 0, 0, 0, 0, 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 0, 0, 0, 0, '_', 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, '~', 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '!', 0, '#', '$', 0, '&', 0, '(', ')', '*', '+', ',', '-', '.', '/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', 0, '=', 0, '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', 0, ']', 0, '_', 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, '~', 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '!', 0, '#', '$', 0, '&','\'', '(', ')', '*', '+', ',', '-', '.', '/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', 0, '=', 0, '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', 0, ']', 0, '_', 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, '~', 0 } }; inline static void append_encoded(smart_str *dest, const char *source, size_t num) { unsigned char c; int i; for (i = 0; i < num; i++) { c = *source++; smart_str_appendc(dest, '%'); smart_str_appendc(dest, hexchars[c >> 4]); smart_str_appendc(dest, hexchars[c & 15]); } } inline static void utf8_append_badchar(smart_str *dest) { int i; for (i = 0; i < 3; i++) { smart_str_appendc(dest, '%'); smart_str_appendc(dest, hexchars[badchar[i] >> 4]); smart_str_appendc(dest, hexchars[badchar[i] & 15]); } } static char *utf8_copy_char(smart_str *dest, char *source) { unsigned char first = *source; unsigned char second = *(source + 1) ^ 0x80; unsigned char third; unsigned char fourth; if (second & 0xC0) { utf8_append_badchar(dest); return ++source; } if (first < 0xE0) { if (first < 0xC0) { utf8_append_badchar(dest); return ++source; } if ((((first << 6) | second) & MAX_TWO_BYTE_CHAR) <= MAX_ONE_BYTE_CHAR) { utf8_append_badchar(dest); return ++source; } append_encoded(dest, source, 2); return source += 2; } third = *(source + 2) ^ 0x80; if (third & 0xC0) { utf8_append_badchar(dest); return ++source; } if (first < 0xF0) { if ((((((first << 6) | second) << 6) | third) & MAX_THREE_BYTE_CHAR) <= MAX_TWO_BYTE_CHAR) { utf8_append_badchar(dest); return ++source; } append_encoded(dest, source, 3); return source += 3; } fourth = *(source + 3) ^ 0x80; if (fourth & 0xC0) { utf8_append_badchar(dest); return ++source; } if (first < 0xF8) { if (((((((first << 6 | second) << 6) | third) << 6) | fourth) & MAX_FOUR_BYTE_CHAR) <= MAX_THREE_BYTE_CHAR) { utf8_append_badchar(dest); return ++source; } append_encoded(dest, source, 4); return source += 4; } return source; } void uri_template_substr_copy(smart_str *dest, char *source, size_t num, int allowed_chars) { unsigned char c; if (num <= 0) { return; } while (*source && num-- > 0) { c = *source; if (c > 127) { source = utf8_copy_char(dest, source); } else { if (urlchars[allowed_chars][c]) { smart_str_appendc(dest, *source); } else { append_encoded(dest, source, 1); } source++; } } } /* +----------------------------------------------------------------------+ | See LICENSE file for further copyright information | +----------------------------------------------------------------------+ | Authors: Ioseb Dzmanashvili <ioseb.dzmanashvili@gmail.com> | +----------------------------------------------------------------------+ */ #include "php_uri_template.h" #define URI_TEMPLATE_PROCESSING_ARGS \ uri_template_expr *expr, uri_template_var *var, zval *vars, smart_str *result #define ALLOWED_CHARS(expr) (expr->op == '+' || expr->op == '#' \ ? URI_TEMPLATE_ALLOW_RESERVED : URI_TEMPLATE_ALLOW_UNRESERVED) inline static void copy_var_valuel(smart_str *dest, zval *val, uri_template_expr *expr, uri_template_var *var) { size_t len = var->length && (var->length < Z_STRLEN_P(val)) ? var->length : Z_STRLEN_P(val); uri_template_substr_copy(dest, Z_STRVAL_P(val), len, ALLOWED_CHARS(expr)); } inline static void copy_var_value(smart_str *dest, zval *val, uri_template_expr *expr, uri_template_var *var) { uri_template_substr_copy(dest, Z_STRVAL_P(val), Z_STRLEN_P(val), ALLOWED_CHARS(expr)); } inline static void copy_var_name(smart_str *dest, uri_template_var *var) { uri_template_substr_copy(dest, var->name, strlen(var->name), URI_TEMPLATE_ALLOW_UNRESERVED); } inline static zend_bool array_is_assoc(zval *array) { HashPosition pos; ulong num_key; int key_type; uint key_len; char *str_key; for (zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(array), &pos); zend_hash_has_more_elements_ex(Z_ARRVAL_P(array), &pos) == SUCCESS; zend_hash_move_forward_ex(Z_ARRVAL_P(array), &pos)) { key_type = zend_hash_get_current_key_ex(Z_ARRVAL_P(array), &str_key, &key_len, &num_key, 0, &pos); if (key_type == HASH_KEY_IS_STRING) { return 1; } } return 0; } static void process_associative_array(URI_TEMPLATE_PROCESSING_ARGS) { uint key_len; char *str_key; char separator = var->explode ? expr->sep : ','; int i = 0; ulong num_key; HashPosition pos; zval **entry; for (zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(vars), &pos); zend_hash_has_more_elements_ex(Z_ARRVAL_P(vars), &pos) == SUCCESS; zend_hash_move_forward_ex(Z_ARRVAL_P(vars), &pos)) { if (zend_hash_get_current_data_ex(Z_ARRVAL_P(vars), (void**)&entry, &pos) == SUCCESS) { zend_hash_get_current_key_ex(Z_ARRVAL_P(vars), &str_key, &key_len, &num_key, 0, &pos); if (i > 0) { smart_str_appendc(result, separator); } convert_to_string_ex(entry); uri_template_substr_copy(result, str_key, key_len - 1, URI_TEMPLATE_ALLOW_UNRESERVED); if (var->explode) { if (!Z_STRLEN_PP(entry)) { if (expr->ifemp) { smart_str_appendc(result, expr->ifemp); } } else { smart_str_appendc(result, '='); } copy_var_value(result, *entry, expr, var); } else { if (Z_STRLEN_PP(entry)) { smart_str_appendc(result, ','); copy_var_value(result, *entry, expr, var); } } } i++; } } static void process_indexed_array(URI_TEMPLATE_PROCESSING_ARGS) { HashPosition pos; zval **entry; char separator = var->explode ? expr->sep : ','; int i = 0; for (zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(vars), &pos); zend_hash_has_more_elements_ex(Z_ARRVAL_P(vars), &pos) == SUCCESS; zend_hash_move_forward_ex(Z_ARRVAL_P(vars), &pos)) { if (zend_hash_get_current_data_ex(Z_ARRVAL_P(vars), (void**)&entry, &pos) == SUCCESS) { if (i > 0) { smart_str_appendc(result, separator); } convert_to_string_ex(entry); if (var->explode && expr->named) { copy_var_name(result, var); if (!Z_STRLEN_PP(entry)) { if (expr->ifemp) { smart_str_appendc(result, expr->ifemp); } } else { smart_str_appendc(result, '='); } } copy_var_value(result, *entry, expr, var); } i++; } } static void process_var_array(URI_TEMPLATE_PROCESSING_ARGS) { smart_str eval = {0}; if (array_is_assoc(vars)) { process_associative_array(expr, var, vars, &eval); } else { process_indexed_array(expr, var, vars, &eval); } smart_str_0(&eval); if (!var->explode) { if (eval.len) { if (expr->named) { copy_var_name(result, var); smart_str_appendc(result, '='); } smart_str_appendl(result, eval.c, eval.len); } else { if (expr->named) { copy_var_name(result, var); if (expr->ifemp) { smart_str_appendc(result, expr->ifemp); } } } } else { smart_str_appendl(result, eval.c, eval.len); } smart_str_free(&eval); } static zend_bool process_var(URI_TEMPLATE_PROCESSING_ARGS) { zval **entry; zend_bool found = zend_hash_find( Z_ARRVAL_P(vars), var->name, strlen(var->name) + 1, (void **)&entry ) == SUCCESS && Z_TYPE_PP(entry) != IS_NULL; if (found) { if (Z_TYPE_PP(entry) == IS_ARRAY) { process_var_array(expr, var, *entry, result); found = zend_hash_num_elements(Z_ARRVAL_PP(entry)) > 0; } else { convert_to_string_ex(entry); if (!expr->named) { copy_var_valuel(result, *entry, expr, var); } else { copy_var_name(result, var); if (!Z_STRLEN_PP(entry)) { if (expr->ifemp) { smart_str_appendc(result, expr->ifemp); } } else { smart_str_appendc(result, '='); copy_var_valuel(result, *entry, expr, var); } } } } return found; } void uri_template_process(uri_template_expr *expr, zval *vars, smart_str *result) { uri_template_var *var = expr->vars->first; zend_bool status = 0; zend_bool processed = 0; int i = 0; while (var != NULL) { smart_str eval = {0}; status = process_var(expr, var, vars, &eval); if (status) { smart_str_0(&eval); if (i == 0 && expr->first) { smart_str_appendc(result, expr->first); i++; } else if (processed) { smart_str_appendc(result, expr->sep); } smart_str_appendl(result, eval.c, eval.len); } smart_str_free(&eval); processed |= status; var = var->next; } }/* +----------------------------------------------------------------------+ | See LICENSE file for further copyright information | +----------------------------------------------------------------------+ | Authors: Ioseb Dzmanashvili <ioseb.dzmanashvili@gmail.com> | +----------------------------------------------------------------------+ */ #include "php_uri_template.h" #define SHIFT_BACK(var) (!var->explode ? var->length == 0 \ ? 0 : ((int) log10(var->length) + 1) + 1 : 1) #define IS_OPERATOR(c) c == '+' || c == '#' || c == '.' || \ c == '/' || c == ';' || c == '?' || c == '&' inline static int extract_num(char *str, int len); inline static void append_malformed_expr(smart_str *dest, char *tpl, int len); static void add_expr_to_array(zval *expressions, uri_template_expr *expr); static uri_template_expr *build_expr(char *tpl, int len); inline static int extract_num(char *str, int len) { char *buff; int ret; buff = emalloc(sizeof(char) * (len+1)); strncpy(buff, str, len); buff[len] = 0; ret = atoi(buff); efree(buff); return ret; } static uri_template_expr *build_expr(char *tpl, int len) { uri_template_expr *expr; uri_template_var *var = uri_template_var_create(); char *name = tpl, *start = tpl, *prefix; if (IS_OPERATOR(*tpl)) { expr = uri_template_expr_create(*tpl++); name = tpl; } else { expr = uri_template_expr_create(0); } while (tpl - start <= len) { switch(*tpl) { case '%': if (name + len - tpl > 2) { if (isxdigit(*(tpl + 1))) { if (isxdigit(*(++tpl + 1))) { tpl++; } else { expr->error = URI_TEMPLATE_ERROR; } } else { expr->error = URI_TEMPLATE_ERROR; } } else { expr->error = URI_TEMPLATE_ERROR; } break; case ':': prefix = ++tpl; if (*tpl >= '1' && *tpl++ <= '9') { while (isdigit(*tpl)) { tpl++; } if (*tpl == ',' || *tpl == '}') { if (tpl - prefix < 5) { var->length = extract_num(tpl - (tpl - prefix), tpl - prefix); } else { expr->error = URI_TEMPLATE_ERROR; } tpl--; } else { expr->error = URI_TEMPLATE_ERROR; } } else { expr->error = URI_TEMPLATE_ERROR; } break; case '*': if (*(tpl + 1) == '}' || *(tpl + 1) == ',') { var->explode = 1; } else { expr->error = URI_TEMPLATE_ERROR; } break; case ',': case '}': var->name = estrndup(name, tpl - name - SHIFT_BACK(var)); uri_template_expr_add_var(expr, var); if (*tpl == ',') { var = uri_template_var_create(); name = tpl + 1; } break; default: expr->error |= !(isalnum(*tpl) || *tpl == '_' || *tpl == '.'); break; } tpl++; } return expr; } inline static void append_malformed_expr(smart_str *dest, char *tpl, int len) { smart_str_appendc(dest, '{'); smart_str_appendl(dest, tpl, len); smart_str_appendc(dest, '}'); } static void add_expr_to_array(zval *expressions, uri_template_expr *expr) { uri_template_var *next; zval *result; zval *vars; char op[2] = {expr->op, 0}; char sep[2] = {expr->sep, 0}; char ifemp[2] = {expr->ifemp, 0}; MAKE_STD_ZVAL(result); array_init(result); add_assoc_string(result, "op", op, 1); add_assoc_string(result, "sep", sep, 1); add_assoc_string(result, "ifemp", ifemp, 1); add_assoc_bool(result, "allow", expr->allow); add_assoc_bool(result, "named", expr->named); add_assoc_bool(result, "error", expr->error); MAKE_STD_ZVAL(vars); array_init_size(vars, expr->vars->count); next = expr->vars->first; while (next != NULL) { zval *var; MAKE_STD_ZVAL(var); array_init(var); add_assoc_string(var, "name", next->name, 1); add_assoc_long(var, "length", next->length); add_assoc_bool(var, "explode", next->explode); add_next_index_zval(vars, var); next = next->next; } add_assoc_zval(result, "vars", vars); add_next_index_zval(expressions, result); } void uri_template_parse(char *tpl, zval *return_value, zval *vars, zval *capture) { smart_str result = {0}; zval *expressions = NULL; zval vars_ptr; unsigned char c; char *start; int state = URI_TEMPLATE_ERROR_NONE; if (capture != NULL) { MAKE_STD_ZVAL(expressions); array_init(expressions); } vars_ptr = *vars; zval_copy_ctor(&vars_ptr); while (*tpl) { if (*tpl == '{') { start = tpl + 1; while (*(tpl++) && *tpl != '}' && *tpl != '{'); if (*tpl == '}') { if (tpl - start > 0) { uri_template_expr *expr = build_expr(start, tpl - start); if (expr->error) { append_malformed_expr(&result, start, tpl - start); if (state == URI_TEMPLATE_ERROR_NONE) { state = URI_TEMPLATE_ERROR_EXPRESSION; } } else { uri_template_process(expr, &vars_ptr, &result); } if (expressions != NULL) { add_expr_to_array(expressions, expr); } uri_template_expr_free(expr); } else { smart_str_appends(&result, "{}"); } } else if (*tpl == '{') { smart_str_appendl(&result, start - 1, tpl - start + 1); state = URI_TEMPLATE_ERROR_SYNTAX; tpl--; } else { smart_str_appendc(&result, '{'); smart_str_appendl(&result, start, tpl - start); state = URI_TEMPLATE_ERROR_SYNTAX; } } else { c = *tpl; if (c == '}') { smart_str_appendc(&result, '}'); state = URI_TEMPLATE_ERROR_SYNTAX; } else if (c == '%' && isxdigit(*(tpl + 1)) && isxdigit(*(tpl + 2))) { smart_str_appendc(&result, '%'); smart_str_appendc(&result, *(++tpl)); smart_str_appendc(&result, *(++tpl)); } else { int result_len = result.len; int distance = 0; uri_template_substr_copy(&result, tpl, 1, URI_TEMPLATE_ALLOW_RESERVED); distance = result.len - result_len; tpl += (distance % 3 ? 1 : distance / 3) - 1; } } tpl++; } smart_str_0(&result); ZVAL_STRING(return_value, result.c ? result.c : "", 1); if (capture != NULL) { add_assoc_string(capture, "result", result.c ? result.c : "", 1); add_assoc_long(capture, "state", state); add_assoc_zval(capture, "expressions", expressions); } zval_dtor(&vars_ptr); smart_str_free(&result); } /* +----------------------------------------------------------------------+ | See LICENSE file for further copyright information | +----------------------------------------------------------------------+ | Authors: Ioseb Dzmanashvili <ioseb.dzmanashvili@gmail.com> | +----------------------------------------------------------------------+ */ #include "php_uri_template.h" uri_template_vars *uri_template_vars_create() { return ecalloc(1, sizeof(uri_template_vars)); } void uri_template_vars_free(uri_template_vars *list) { uri_template_var *first = list->first; uri_template_var *prev; while (first != NULL) { prev = first; first = first->next; uri_template_var_free(prev); } efree(list); } uri_template_var *uri_template_var_create() { return ecalloc(1, sizeof(uri_template_var)); } void uri_template_var_free(uri_template_var *var) { efree(var->name); efree(var); } uri_template_expr *uri_template_expr_create(char operator) { uri_template_expr *expr = ecalloc(1, sizeof(uri_template_expr)); expr->op = operator; expr->sep = ','; expr->vars = uri_template_vars_create(); switch(operator) { case '+': expr->allow = 1; break; case '.': case '/': case ';': expr->first = operator; expr->sep = operator; expr->named = operator == ';'; break; case '?': case '&': expr->first = operator; expr->sep = '&'; expr->named = 1; expr->ifemp = '='; break; case '#': expr->first = operator; expr->allow = 1; break; } return expr; } void uri_template_expr_add_var(uri_template_expr *expr, uri_template_var *var) { uri_template_vars *list = expr->vars; if (list->first == NULL) { list->first = var; list->last = var; } else { list->last->next = var; list->last = var; } list->count++; } void uri_template_expr_free(uri_template_expr *expr) { uri_template_vars_free(expr->vars); efree(expr); } <?php $data = array( 'count' => array("one", "two", "three"), 'dom' => array("example", "com"), 'dub' => "me/too", 'hello' => "Hello World!", 'half' => "50%", 'var' => "value", 'who' => "fred", 'base' => "http://example.com/home/", 'path' => "/foo/bar", 'list' => array("red", "green", "blue"), 'keys' => array( "semi" => ";", "dot" => ".", "comma" => ",", ), 'v' => "6", 'x' => "1024", 'y' => "768", 'empty' => "", 'empty_keys' => array(), 'undef' => null, ); $templates = array( "{&who}", "{&half}", "?fixed=yes{&x}", "{&x,y,empty}", "{&x,y,undef}", "{&var:3}", "{&list}", "{&list*}", "{&keys}", "{&keys*}" ); $uris = array(); foreach ($templates as $template) { $uris[$template] = uri_template($template, $data); } var_export($uris); /* Result */ /* array( "{&who}" => "&who=fred", "{&half}" => "&half=50%25", "?fixed=yes{&x}" => "?fixed=yes&x=1024", "{&x,y,empty}" => "&x=1024&y=768&empty=", "{&x,y,undef}" => "&x=1024&y=768", "{&var:3}" => "&var=val", "{&list}" => "&list=red,green,blue", "{&list*}" => "&list=red&list=green&list=blue", "{&keys}" => "&keys=semi,%3B,dot,.,comma,%2C", "{&keys*}" => "&semi=%3B&dot=.&comma=%2C" ) */ ?><?php $data = array( 'count' => array("one", "two", "three"), 'dom' => array("example", "com"), 'dub' => "me/too", 'hello' => "Hello World!", 'half' => "50%", 'var' => "value", 'who' => "fred", 'base' => "http://example.com/home/", 'path' => "/foo/bar", 'list' => array("red", "green", "blue"), 'keys' => array( "semi" => ";", "dot" => ".", "comma" => ",", ), 'v' => "6", 'x' => "1024", 'y' => "768", 'empty' => "", 'empty_keys' => array(), 'undef' => null, ); $templates = array( "{;who}", "{;half}", "{;empty}", "{;v,empty,who}", "{;v,bar,who}", "{;x,y}", "{;x,y,empty}", "{;x,y,undef}", "{;hello:5}", "{;list}", "{;list*}", "{;keys}", "{;keys*}" ); $uris = array(); foreach ($templates as $template) { $uris[$template] = uri_template($template, $data); } var_export($uris); /* Result */ /* array( "{;who}" => ";who=fred", "{;half}" => ";half=50%25", "{;empty}" => ";empty", "{;v,empty,who}" => ";v=6;empty;who=fred", "{;v,bar,who}" => ";v=6;who=fred", "{;x,y}" => ";x=1024;y=768", "{;x,y,empty}" => ";x=1024;y=768;empty", "{;x,y,undef}" => ";x=1024;y=768", "{;hello:5}" => ";hello=Hello", "{;list}" => ";list=red,green,blue", "{;list*}" => ";list=red;list=green;list=blue", "{;keys}" => ";keys=semi,%3B,dot,.,comma,%2C", "{;keys*}" => ";semi=%3B;dot=.;comma=%2C" ) */ ?><?php $data = array( 'count' => array("one", "two", "three"), 'dom' => array("example", "com"), 'dub' => "me/too", 'hello' => "Hello World!", 'half' => "50%", 'var' => "value", 'who' => "fred", 'base' => "http://example.com/home/", 'path' => "/foo/bar", 'list' => array("red", "green", "blue"), 'keys' => array( "semi" => ";", "dot" => ".", "comma" => ",", ), 'v' => "6", 'x' => "1024", 'y' => "768", 'empty' => "", 'empty_keys' => array(), 'undef' => null, ); $templates = array( "{?who}", "{?half}", "{?x,y}", "{?x,y,empty}", "{?x,y,undef}", "{?var:3}", "{?list}", "{?list*}", "{?keys}", "{?keys*}" ); $uris = array(); foreach ($templates as $template) { $uris[$template] = uri_template($template, $data); } var_export($uris); /* Result */ /* array( "{?who}" => "?who=fred", "{?half}" => "?half=50%25", "{?x,y}" => "?x=1024&y=768", "{?x,y,empty}" => "?x=1024&y=768&empty=", "{?x,y,undef}" => "?x=1024&y=768", "{?var:3}" => "?var=val", "{?list}" => "?list=red,green,blue", "{?list*}" => "?list=red&list=green&list=blue", "{?keys}" => "?keys=semi,%3B,dot,.,comma,%2C", "{?keys*}" => "?semi=%3B&dot=.&comma=%2C" ) */ ?><?php $data = array( 'count' => array("one", "two", "three"), 'dom' => array("example", "com"), 'dub' => "me/too", 'hello' => "Hello World!", 'half' => "50%", 'var' => "value", 'who' => "fred", 'base' => "http://example.com/home/", 'path' => "/foo/bar", 'list' => array("red", "green", "blue"), 'keys' => array( "semi" => ";", "dot" => ".", "comma" => ",", ), 'v' => "6", 'x' => "1024", 'y' => "768", 'empty' => "", 'empty_keys' => array(), 'undef' => null, ); $templates = array( "{+var}", "{+hello}", "{+half}", "{base}index", "{+base}index", "O{+empty}X", "O{+undef}X", "{+path}/here", "here?ref={+path}", "up{+path}{var}/here", "{+x,hello,y}", "{+path,x}/here", "{+path:6}/here", "{+list}", "{+list*}", "{+keys}", "{+keys*}" ); $uris = array(); foreach ($templates as $template) { $uris[$template] = uri_template($template, $data); } var_export($uris); /* Result */ /* array ( '{+var}' => 'value', '{+hello}' => 'Hello%20World!', '{+half}' => '50%25', '{base}index' => 'http%3A%2F%2Fexample.com%2Fhome%2Findex', '{+base}index' => 'http://example.com/home/index', 'O{+empty}X' => 'OX', 'O{+undef}X' => 'OX', '{+path}/here' => '/foo/bar/here', 'here?ref={+path}' => 'here?ref=/foo/bar', 'up{+path}{var}/here' => 'up/foo/barvalue/here', '{+x,hello,y}' => '1024,Hello%20World!,768', '{+path,x}/here' => '/foo/bar,1024/here', '{+path:6}/here' => '/foo/b/here', '{+list}' => 'red,green,blue', '{+list*}' => 'red,green,blue', '{+keys}' => 'semi,;,dot,.,comma,,', '{+keys*}' => 'semi=;,dot=.,comma=,', ) */ ?><?php $data = array( 'count' => array("one", "two", "three"), 'dom' => array("example", "com"), 'dub' => "me/too", 'hello' => "Hello World!", 'half' => "50%", 'var' => "value", 'who' => "fred", 'base' => "http://example.com/home/", 'path' => "/foo/bar", 'list' => array("red", "green", "blue"), 'keys' => array( "semi" => ";", "dot" => ".", "comma" => ",", ), 'v' => "6", 'x' => "1024", 'y' => "768", 'empty' => "", 'empty_keys' => array(), 'undef' => null, ); $templates = array( "{#var}", "{#hello}", "{#half}", "foo{#empty}", "foo{#undef}", "{#x,hello,y}", "{#path,x}/here", "{#path:6}/here", "{#list}", "{#list*}", "{#keys}", "{#keys*}" ); $uris = array(); foreach ($templates as $template) { $uris[$template] = uri_template($template, $data); } var_export($uris); /* Result */ /* array( "{#var}" => "#value", "{#hello}" => "#Hello%20World!", "{#half}" => "#50%25", "foo{#empty}" => "foo#", "foo{#undef}" => "foo", "{#x,hello,y}" => "#1024,Hello%20World!,768", "{#path,x}/here" => "#/foo/bar,1024/here", "{#path:6}/here" => "#/foo/b/here", "{#list}" => "#red,green,blue", "{#list*}" => "#red,green,blue", "{#keys}" => "#semi,;,dot,.,comma,,", "{#keys*}" => "#semi=;,dot=.,comma=," ) */ ?><?php $data = array( 'count' => array("one", "two", "three"), 'dom' => array("example", "com"), 'dub' => "me/too", 'hello' => "Hello World!", 'half' => "50%", 'var' => "value", 'who' => "fred", 'base' => "http://example.com/home/", 'path' => "/foo/bar", 'list' => array("red", "green", "blue"), 'keys' => array( "semi" => ";", "dot" => ".", "comma" => ",", ), 'v' => "6", 'x' => "1024", 'y' => "768", 'empty' => "", 'empty_keys' => array(), 'undef' => null, ); $templates = array( "{var}", "{hello}", "{half}", "O{empty}X", "O{undef}X", "{x,y}", "{x,hello,y}", "?{x,empty}", "?{x,undef}", "?{undef,y}", "{var:3}", "{var:30}", "{list}", "{list*}", "{keys}", "{keys*}" ); $uris = array(); foreach ($templates as $template) { $uris[$template] = uri_template($template, $data); } var_export($uris); /* Result */ /* array ( '{var}' => 'value', '{hello}' => 'Hello%20World%21', '{half}' => '50%25', 'O{empty}X' => 'OX', 'O{undef}X' => 'OX', '{x,y}' => '1024,768', '{x,hello,y}' => '1024,Hello%20World%21,768', '?{x,empty}' => '?1024,', '?{x,undef}' => '?1024', '?{undef,y}' => '?768', '{var:3}' => 'val', '{var:30}' => 'value', '{list}' => 'red,green,blue', '{list*}' => 'red,green,blue', '{keys}' => 'semi,%3B,dot,.,comma,%2C', '{keys*}' => 'semi=%3B,dot=.,comma=%2C', ) */ ?><?php $data = array( 'count' => array("one", "two", "three"), 'dom' => array("example", "com"), 'dub' => "me/too", 'hello' => "Hello World!", 'half' => "50%", 'var' => "value", 'who' => "fred", 'base' => "http://example.com/home/", 'path' => "/foo/bar", 'list' => array("red", "green", "blue"), 'keys' => array( "semi" => ";", "dot" => ".", "comma" => ",", ), 'v' => "6", 'x' => "1024", 'y' => "768", 'empty' => "", 'empty_keys' => array(), 'undef' => null, ); $templates = array( "{.who}", "{.who,who}", "{.half,who}", "www{.dom*}", "X{.var}", "X{.empty}", "X{.undef}", "X{.var:3}", "X{.list}", "X{.list*}", "X{.keys}", "X{.keys*}", "X{.empty_keys}", "X{.empty_keys*}" ); $uris = array(); foreach ($templates as $template) { $uris[$template] = uri_template($template, $data); } var_export($uris); /* Result */ /* array( "{.who}" => ".fred", "{.who,who}" => ".fred.fred", "{.half,who}" => ".50%25.fred", "www{.dom*}" => "www.example.com", "X{.var}" => "X.value", "X{.empty}" => "X.", "X{.undef}" => "X", "X{.var:3}" => "X.val", "X{.list}" => "X.red,green,blue", "X{.list*}" => "X.red.green.blue", "X{.keys}" => "X.semi,%3B,dot,.,comma,%2C", "X{.keys*}" => "X.semi=%3B.dot=..comma=%2C", "X{.empty_keys}" => "X", "X{.empty_keys*}" => "X" ) */ ?><?php $data = array( 'count' => array("one", "two", "three"), 'dom' => array("example", "com"), 'dub' => "me/too", 'hello' => "Hello World!", 'half' => "50%", 'var' => "value", 'who' => "fred", 'base' => "http://example.com/home/", 'path' => "/foo/bar", 'list' => array("red", "green", "blue"), 'keys' => array( "semi" => ";", "dot" => ".", "comma" => ",", ), 'v' => "6", 'x' => "1024", 'y' => "768", 'empty' => "", 'empty_keys' => array(), 'undef' => null, ); $templates = array( '{count}', '{count*}', '{/count}', '{/count*}', '{;count}', '{;count*}', '{?count}', '{?count*}', '{&count*}', ); $uris = array(); foreach ($templates as $template) { $uris[$template] = uri_template($template, $data); } var_export($uris); /* Result */ /* array ( '{count}' => 'one,two,three', '{count*}' => 'one,two,three', '{/count}' => '/one,two,three', '{/count*}' => '/one/two/three', '{;count}' => ';count=one,two,three', '{;count*}' => ';count=one;count=two;count=three', '{?count}' => '?count=one,two,three', '{?count*}' => '?count=one&count=two&count=three', '{&count*}' => '&count=one&count=two&count=three', ) */ ?><?php $data = array( 'count' => array("one", "two", "three"), 'dom' => array("example", "com"), 'dub' => "me/too", 'hello' => "Hello World!", 'half' => "50%", 'var' => "value", 'who' => "fred", 'base' => "http://example.com/home/", 'path' => "/foo/bar", 'list' => array("red", "green", "blue"), 'keys' => array( "semi" => ";", "dot" => ".", "comma" => ",", ), 'v' => "6", 'x' => "1024", 'y' => "768", 'empty' => "", 'empty_keys' => array(), 'undef' => null, ); $templates = array( "{/who}", "{/who,who}", "{/half,who}", "{/who,dub}", "{/var}", "{/var,empty}", "{/var,undef}", "{/var,x}/here", "{/var:1,var}", "{/list}", "{/list*}", "{/list*,path:4}", "{/keys}", "{/keys*}" ); $uris = array(); foreach ($templates as $template) { $uris[$template] = uri_template($template, $data); } var_export($uris); /* Result */ /* array( "{/who}" => "/fred", "{/who,who}" => "/fred/fred", "{/half,who}" => "/50%25/fred", "{/who,dub}" => "/fred/me%2Ftoo", "{/var}" => "/value", "{/var,empty}" => "/value/", "{/var,undef}" => "/value", "{/var,x}/here" => "/value/1024/here", "{/var:1,var}" => "/v/value", "{/list}" => "/red,green,blue", "{/list*}" => "/red/green/blue", "{/list*,path:4}" => "/red/green/blue/%2Ffoo", "{/keys}" => "/semi,%3B,dot,.,comma,%2C", "{/keys*}" => "/semi=%3B/dot=./comma=%2C" ) */ ?>--TEST-- uri_template() copy pct-encoded triplets --FILE-- <?php $input = '%25x21%20%2F%20%25x23-24%20%2F%20%25x26%20%2F%20%25x28-3B%20%2F%20%25x3D%20%2F%20%25x3F-5B'; $result = uri_template($input, array()); var_dump($result); ?> --EXPECT-- string(90) "%25x21%20%2F%20%25x23-24%20%2F%20%25x26%20%2F%20%25x28-3B%20%2F%20%25x3D%20%2F%20%25x3F-5B"--TEST-- uri_template() copy pct-encoded triplets and expand variables with utf8 strings --FILE-- <?php $input = '%25x21%20%2F%20%25x23-{utf8str}-24%20%2F%20%25x26%20%2F%20%25x28-3B%20%2F%20%25x3D%20%2F%20%25x3F-{utf8str}-5B'; $result = uri_template($input, array('utf8str' => 'სიმბáƒáƒšáƒ')); var_dump($result); ?> --EXPECT-- string(218) "%25x21%20%2F%20%25x23-%E1%83%A1%E1%83%98%E1%83%9B%E1%83%91%E1%83%9D%E1%83%9A%E1%83%9D-24%20%2F%20%25x26%20%2F%20%25x28-3B%20%2F%20%25x3D%20%2F%20%25x3F-%E1%83%A1%E1%83%98%E1%83%9B%E1%83%91%E1%83%9D%E1%83%9A%E1%83%9D-5B"--TEST-- uri_template() copy literals, compare encoded literal chars and encoded variable value --FILE-- <?php $utf8str = 'სიმბáƒáƒšáƒ'; $template = '{utf8str}'; $result = array( uri_template($utf8str, array()), uri_template($template, array('utf8str' => $utf8str)) ); var_dump($result); ?> --EXPECT-- array(2) { [0]=> string(63) "%E1%83%A1%E1%83%98%E1%83%9B%E1%83%91%E1%83%9D%E1%83%9A%E1%83%9D" [1]=> string(63) "%E1%83%A1%E1%83%98%E1%83%9B%E1%83%91%E1%83%9D%E1%83%9A%E1%83%9D" }--TEST-- uri_template() copy url - ignore ! char --FILE-- <?php $input = 'http://foo.com/baz?bar=bam_!'; $result = uri_template($input, array()); var_dump($result); ?> --EXPECT-- string(28) "http://foo.com/baz?bar=bam_!"--TEST-- uri_template() copy url - ignore %21 tripplet --FILE-- <?php $input = 'http://foo.com/baz?bar=bam_%21'; $result = uri_template($input, array()); var_dump($result); ?> --EXPECT-- string(30) "http://foo.com/baz?bar=bam_%21"--TEST-- uri_template() invalid expression - error 1 "{/id*" --FILE-- <?php $data = array( "id" => "thing", "var" => "value", "hello" => "Hello World!", "empty" => "", "path" => "/foo/bar", "x" => "1024", "y" => "768", "list" => array("red", "green", "blue"), "keys" => array( "semi" => ";", "dot" => ".", "comma" => "," ), "example" => "red", "searchTerms" => "uri templates" ); uri_template("{/id*", $data, $result); var_dump($result); ?> --EXPECT-- array(3) { ["result"]=> string(5) "{/id*" ["state"]=> int(2) ["expressions"]=> array(0) { } }--TEST-- uri_template() invalid expression - error 21 "{;keys:1*}" --FILE-- <?php $data = array( "id" => "thing", "var" => "value", "hello" => "Hello World!", "empty" => "", "path" => "/foo/bar", "x" => "1024", "y" => "768", "list" => array("red", "green", "blue"), "keys" => array( "semi" => ";", "dot" => ".", "comma" => "," ), "example" => "red", "searchTerms" => "uri templates" ); uri_template('{;keys:1*}', $data, $result); var_dump($result); ?> --EXPECT-- array(3) { ["result"]=> string(10) "{;keys:1*}" ["state"]=> int(3) ["expressions"]=> array(1) { [0]=> array(7) { ["op"]=> string(1) ";" ["sep"]=> string(1) ";" ["ifemp"]=> string(0) "" ["allow"]=> bool(false) ["named"]=> bool(true) ["error"]=> bool(true) ["vars"]=> array(1) { [0]=> array(3) { ["name"]=> string(7) "keys:1*" ["length"]=> int(0) ["explode"]=> bool(false) } } } } }--TEST-- uri_template() query parameters test - example 3 second parameter only --FILE-- <?php uri_template('http://www.example.com/foo{?query,number}', array( "number" => 100, ), $result); unset($result['expressions']); var_dump($result); ?> --EXPECT-- array(2) { ["result"]=> string(37) "http://www.example.com/foo?number=100" ["state"]=> int(0) }--TEST-- uri_template() level 1 expansion - simple expansion --FILE-- <?php $data = array( "var" => "value", "hello" => "Hello World!" ); $templates = array( "{var}" => "value", "{hello}" => "Hello%20World%21" ); $out = array(); foreach ($templates as $tpl => $expect) { $result = NULL; uri_template($tpl, $data, $result); unset($result['expressions']); $out[] = $result; } var_dump($out); ?> --EXPECT-- array(2) { [0]=> array(2) { ["result"]=> string(5) "value" ["state"]=> int(0) } [1]=> array(2) { ["result"]=> string(16) "Hello%20World%21" ["state"]=> int(0) } }--TEST-- uri_template() level 4 expansion - fragment expansion with value modifiers --FILE-- <?php $data = array( "var" => "value", "hello" => "Hello World!", "path" => "/foo/bar", "list" => array("red", "green", "blue"), "keys" => array( "semi" => ";", "dot" => ".", "comma" => "," ) ); $templates = array( "{#path:6}/here" => "#/foo/b/here", "{#list}" => "#red,green,blue", "{#list*}" => "#red,green,blue", "{#keys}" => "#semi,;,dot,.,comma,,", "{#keys*}" => "#semi=;,dot=.,comma=," ); $out = array(); foreach ($templates as $tpl => $expect) { $result = NULL; uri_template($tpl, $data, $result); unset($result['expressions']); $out[] = $result; } var_dump($out); ?> --EXPECT-- array(5) { [0]=> array(2) { ["result"]=> string(12) "#/foo/b/here" ["state"]=> int(0) } [1]=> array(2) { ["result"]=> string(15) "#red,green,blue" ["state"]=> int(0) } [2]=> array(2) { ["result"]=> string(15) "#red,green,blue" ["state"]=> int(0) } [3]=> array(2) { ["result"]=> string(21) "#semi,;,dot,.,comma,," ["state"]=> int(0) } [4]=> array(2) { ["result"]=> string(21) "#semi=;,dot=.,comma=," ["state"]=> int(0) } }--TEST-- uri_template() invalid expression - error 10 "{|var*}" --FILE-- <?php $data = array( "id" => "thing", "var" => "value", "hello" => "Hello World!", "empty" => "", "path" => "/foo/bar", "x" => "1024", "y" => "768", "list" => array("red", "green", "blue"), "keys" => array( "semi" => ";", "dot" => ".", "comma" => "," ), "example" => "red", "searchTerms" => "uri templates" ); uri_template('{|var*}', $data, $result); var_dump($result); ?> --EXPECT-- array(3) { ["result"]=> string(7) "{|var*}" ["state"]=> int(3) ["expressions"]=> array(1) { [0]=> array(7) { ["op"]=> string(0) "" ["sep"]=> string(1) "," ["ifemp"]=> string(0) "" ["allow"]=> bool(false) ["named"]=> bool(false) ["error"]=> bool(true) ["vars"]=> array(1) { [0]=> array(3) { ["name"]=> string(4) "|var" ["length"]=> int(0) ["explode"]=> bool(true) } } } } }--TEST-- uri_template() invalid expression - error 22 "?{-join|&|var,list}" --FILE-- <?php $data = array( "id" => "thing", "var" => "value", "hello" => "Hello World!", "empty" => "", "path" => "/foo/bar", "x" => "1024", "y" => "768", "list" => array("red", "green", "blue"), "keys" => array( "semi" => ";", "dot" => ".", "comma" => "," ), "example" => "red", "searchTerms" => "uri templates" ); uri_template('?{-join|&|var,list}', $data, $result); var_dump($result); ?> --EXPECT-- array(3) { ["result"]=> string(19) "?{-join|&|var,list}" ["state"]=> int(3) ["expressions"]=> array(1) { [0]=> array(7) { ["op"]=> string(0) "" ["sep"]=> string(1) "," ["ifemp"]=> string(0) "" ["allow"]=> bool(false) ["named"]=> bool(false) ["error"]=> bool(true) ["vars"]=> array(2) { [0]=> array(3) { ["name"]=> string(11) "-join|&|var" ["length"]=> int(0) ["explode"]=> bool(false) } [1]=> array(3) { ["name"]=> string(4) "list" ["length"]=> int(0) ["explode"]=> bool(false) } } } } }--TEST-- uri_template() query parameters test - example 4 no parameters --FILE-- <?php uri_template('http://www.example.com/foo{?query,number}', array(), $result); unset($result['expressions']); var_dump($result); ?> --EXPECT-- array(2) { ["result"]=> string(26) "http://www.example.com/foo" ["state"]=> int(0) }--TEST-- uri_template() level 2 expansion - reserved characters expansion --FILE-- <?php $data = array( "var" => "value", "hello" => "Hello World!", "path" => "/foo/bar" ); $templates = array( "{+var}" => "value", "{+hello}" => "Hello%20World!", "{+path}/here" => "/foo/bar/here", "here?ref={+path}" => "here?ref=/foo/bar" ); $out = array(); foreach ($templates as $tpl => $expect) { $result = NULL; uri_template($tpl, $data, $result); unset($result['expressions']); $out[] = $result; } var_dump($out); ?> --EXPECT-- array(4) { [0]=> array(2) { ["result"]=> string(5) "value" ["state"]=> int(0) } [1]=> array(2) { ["result"]=> string(14) "Hello%20World!" ["state"]=> int(0) } [2]=> array(2) { ["result"]=> string(13) "/foo/bar/here" ["state"]=> int(0) } [3]=> array(2) { ["result"]=> string(17) "here?ref=/foo/bar" ["state"]=> int(0) } }--TEST-- uri_template() level 4 expansion - dot prefixed label expansion with value modifiers --FILE-- <?php $data = array( "var" => "value", "hello" => "Hello World!", "path" => "/foo/bar", "list" => array("red", "green", "blue"), "keys" => array( "semi" => ";", "dot" => ".", "comma" => "," ) ); $templates = array( "X{.var:3}" => "X.val", "X{.list}" => "X.red,green,blue", "X{.list*}" => "X.red.green.blue", "X{.keys}" => "X.semi,%3B,dot,.,comma,%2C", "X{.keys*}" => "X.semi=%3B.dot=..comma=%2C" ); $out = array(); foreach ($templates as $tpl => $expect) { $result = NULL; uri_template($tpl, $data, $result); unset($result['expressions']); $out[] = $result; } var_dump($out); ?> --EXPECT-- array(5) { [0]=> array(2) { ["result"]=> string(5) "X.val" ["state"]=> int(0) } [1]=> array(2) { ["result"]=> string(16) "X.red,green,blue" ["state"]=> int(0) } [2]=> array(2) { ["result"]=> string(16) "X.red.green.blue" ["state"]=> int(0) } [3]=> array(2) { ["result"]=> string(26) "X.semi,%3B,dot,.,comma,%2C" ["state"]=> int(0) } [4]=> array(2) { ["result"]=> string(26) "X.semi=%3B.dot=..comma=%2C" ["state"]=> int(0) } }--TEST-- uri_template() invalid expression - error 11 "{*keys?}" --FILE-- <?php $data = array( "id" => "thing", "var" => "value", "hello" => "Hello World!", "empty" => "", "path" => "/foo/bar", "x" => "1024", "y" => "768", "list" => array("red", "green", "blue"), "keys" => array( "semi" => ";", "dot" => ".", "comma" => "," ), "example" => "red", "searchTerms" => "uri templates" ); uri_template('{*keys?}', $data, $result); var_dump($result); ?> --EXPECT-- array(3) { ["result"]=> string(8) "{*keys?}" ["state"]=> int(3) ["expressions"]=> array(1) { [0]=> array(7) { ["op"]=> string(0) "" ["sep"]=> string(1) "," ["ifemp"]=> string(0) "" ["allow"]=> bool(false) ["named"]=> bool(false) ["error"]=> bool(true) ["vars"]=> array(1) { [0]=> array(3) { ["name"]=> string(6) "*keys?" ["length"]=> int(0) ["explode"]=> bool(false) } } } } }--TEST-- uri_template() invalid expression - error 23 "?{-join|&|var,list}" --FILE-- <?php $data = array( "id" => "thing", "var" => "value", "hello" => "Hello World!", "empty" => "", "path" => "/foo/bar", "x" => "1024", "y" => "768", "list" => array("red", "green", "blue"), "keys" => array( "semi" => ";", "dot" => ".", "comma" => "," ), "example" => "red", "searchTerms" => "uri templates", "~thing" => "some-user", "query" => "PREFIX dc: <http://purl.org/dc/elements/1.1/> SELECT ?book ?who WHERE { ?book dc:creator ?who }", "default-graph-uri" => array("http://www.example/book/","http://www.example/papers/"), ); uri_template('/sparql{?query){&default-graph-uri*}', $data, $result); var_dump($result); ?> --EXPECT-- array(3) { ["result"]=> string(36) "/sparql{?query){&default-graph-uri*}" ["state"]=> int(2) ["expressions"]=> array(1) { [0]=> array(7) { ["op"]=> string(1) "&" ["sep"]=> string(1) "&" ["ifemp"]=> string(1) "=" ["allow"]=> bool(false) ["named"]=> bool(true) ["error"]=> bool(true) ["vars"]=> array(1) { [0]=> array(3) { ["name"]=> string(17) "default-graph-uri" ["length"]=> int(0) ["explode"]=> bool(true) } } } } }--TEST-- uri_template() expansion 0 - count variable test --FILE-- <?php $data = array( 'count' => array("one", "two", "three"), 'dom' => array("example", "com"), 'dub' => "me/too", 'hello' => "Hello World!", 'half' => "50%", 'var' => "value", 'who' => "fred", 'base' => "http://example.com/home/", 'path' => "/foo/bar", 'list' => array("red", "green", "blue"), 'keys' => array( "semi" => ";", "dot" => ".", "comma" => ",", ), 'v' => "6", 'x' => "1024", 'y' => "768", 'empty' => "", 'empty_keys' => array(), 'undef' => null, ); $templates = array( '{count}' => 'one,two,three', '{count*}' => 'one,two,three', '{/count}' => '/one,two,three', '{/count*}' => '/one/two/three', '{;count}' => ';count=one,two,three', '{;count*}' => ';count=one;count=two;count=three', '{?count}' => '?count=one,two,three', '{?count*}' => '?count=one&count=two&count=three', '{&count*}' => '&count=one&count=two&count=three', ); $out = array(); foreach ($templates as $tpl => $expect) { $result = NULL; uri_template($tpl, $data, $result); unset($result['expressions']); $out[] = $result; } var_dump($out); ?> --EXPECT-- array(9) { [0]=> array(2) { ["result"]=> string(13) "one,two,three" ["state"]=> int(0) } [1]=> array(2) { ["result"]=> string(13) "one,two,three" ["state"]=> int(0) } [2]=> array(2) { ["result"]=> string(14) "/one,two,three" ["state"]=> int(0) } [3]=> array(2) { ["result"]=> string(14) "/one/two/three" ["state"]=> int(0) } [4]=> array(2) { ["result"]=> string(20) ";count=one,two,three" ["state"]=> int(0) } [5]=> array(2) { ["result"]=> string(32) ";count=one;count=two;count=three" ["state"]=> int(0) } [6]=> array(2) { ["result"]=> string(20) "?count=one,two,three" ["state"]=> int(0) } [7]=> array(2) { ["result"]=> string(32) "?count=one&count=two&count=three" ["state"]=> int(0) } [8]=> array(2) { ["result"]=> string(32) "&count=one&count=two&count=three" ["state"]=> int(0) } }--TEST-- uri_template() level 2 expansion - fragment expansion --FILE-- <?php $data = array( "var" => "value", "hello" => "Hello World!", "path" => "/foo/bar" ); $templates = array( "X{#var}" => "X#value", "X{#hello}" => "X#Hello%20World!" ); $out = array(); foreach ($templates as $tpl => $expect) { $result = NULL; uri_template($tpl, $data, $result); unset($result['expressions']); $out[] = $result; } var_dump($out); ?> --EXPECT-- array(2) { [0]=> array(2) { ["result"]=> string(7) "X#value" ["state"]=> int(0) } [1]=> array(2) { ["result"]=> string(16) "X#Hello%20World!" ["state"]=> int(0) } }--TEST-- uri_template() level 4 expansion - path prefixed segments expansion with value modifiers --FILE-- <?php $data = array( "var" => "value", "hello" => "Hello World!", "path" => "/foo/bar", "list" => array("red", "green", "blue"), "keys" => array( "semi" => ";", "dot" => ".", "comma" => "," ) ); $templates = array( "{/var:1,var}" => "/v/value", "{/list}" => "/red,green,blue", "{/list*}" => "/red/green/blue", "{/list*,path:4}" => "/red/green/blue/%2Ffoo", "{/keys}" => "/semi,%3B,dot,.,comma,%2C", "{/keys*}" => "/semi=%3B/dot=./comma=%2C" ); $out = array(); foreach ($templates as $tpl => $expect) { $result = NULL; uri_template($tpl, $data, $result); unset($result['expressions']); $out[] = $result; } var_dump($out); ?> --EXPECT-- array(6) { [0]=> array(2) { ["result"]=> string(8) "/v/value" ["state"]=> int(0) } [1]=> array(2) { ["result"]=> string(15) "/red,green,blue" ["state"]=> int(0) } [2]=> array(2) { ["result"]=> string(15) "/red/green/blue" ["state"]=> int(0) } [3]=> array(2) { ["result"]=> string(22) "/red/green/blue/%2Ffoo" ["state"]=> int(0) } [4]=> array(2) { ["result"]=> string(25) "/semi,%3B,dot,.,comma,%2C" ["state"]=> int(0) } [5]=> array(2) { ["result"]=> string(25) "/semi=%3B/dot=./comma=%2C" ["state"]=> int(0) } }--TEST-- uri_template() invalid expression - error 12 "{?empty=default,var}" --FILE-- <?php $data = array( "id" => "thing", "var" => "value", "hello" => "Hello World!", "empty" => "", "path" => "/foo/bar", "x" => "1024", "y" => "768", "list" => array("red", "green", "blue"), "keys" => array( "semi" => ";", "dot" => ".", "comma" => "," ), "example" => "red", "searchTerms" => "uri templates" ); uri_template('{?empty=default,var}', $data, $result); var_dump($result); ?> --EXPECT-- array(3) { ["result"]=> string(20) "{?empty=default,var}" ["state"]=> int(3) ["expressions"]=> array(1) { [0]=> array(7) { ["op"]=> string(1) "?" ["sep"]=> string(1) "&" ["ifemp"]=> string(1) "=" ["allow"]=> bool(false) ["named"]=> bool(true) ["error"]=> bool(true) ["vars"]=> array(2) { [0]=> array(3) { ["name"]=> string(13) "empty=default" ["length"]=> int(0) ["explode"]=> bool(false) } [1]=> array(3) { ["name"]=> string(3) "var" ["length"]=> int(0) ["explode"]=> bool(false) } } } } }--TEST-- uri_template() invalid expression - error 24 "/resolution{?x, y}" --FILE-- <?php $data = array( "id" => "thing", "var" => "value", "hello" => "Hello World!", "empty" => "", "path" => "/foo/bar", "x" => "1024", "y" => "768", "list" => array("red", "green", "blue"), "keys" => array( "semi" => ";", "dot" => ".", "comma" => "," ), "example" => "red", "searchTerms" => "uri templates", "~thing" => "some-user", "query" => "PREFIX dc: <http://purl.org/dc/elements/1.1/> SELECT ?book ?who WHERE { ?book dc:creator ?who }", "default-graph-uri" => array("http://www.example/book/","http://www.example/papers/"), ); uri_template('/resolution{?x, y}', $data, $result); var_dump($result); ?> --EXPECT-- array(3) { ["result"]=> string(18) "/resolution{?x, y}" ["state"]=> int(3) ["expressions"]=> array(1) { [0]=> array(7) { ["op"]=> string(1) "?" ["sep"]=> string(1) "&" ["ifemp"]=> string(1) "=" ["allow"]=> bool(false) ["named"]=> bool(true) ["error"]=> bool(true) ["vars"]=> array(2) { [0]=> array(3) { ["name"]=> string(1) "x" ["length"]=> int(0) ["explode"]=> bool(false) } [1]=> array(3) { ["name"]=> string(2) " y" ["length"]=> int(0) ["explode"]=> bool(false) } } } } }--TEST-- uri_template() expansion 1 - basic tests --FILE-- <?php $data = array( 'count' => array("one", "two", "three"), 'dom' => array("example", "com"), 'dub' => "me/too", 'hello' => "Hello World!", 'half' => "50%", 'var' => "value", 'who' => "fred", 'base' => "http://example.com/home/", 'path' => "/foo/bar", 'list' => array("red", "green", "blue"), 'keys' => array( "semi" => ";", "dot" => ".", "comma" => ",", ), 'v' => "6", 'x' => "1024", 'y' => "768", 'empty' => "", 'empty_keys' => array(), 'undef' => null, ); $templates = array( "{var}" => "value", "{hello}" => "Hello%20World%21", "{half}" => "50%25", "O{empty}X" => "OX", "O{undef}X" => "OX", "{x,y}" => "1024,768", "{x,hello,y}" => "1024,Hello%20World%21,768", "?{x,empty}" => "?1024,", "?{x,undef}" => "?1024", "?{undef,y}" => "?768", "{var:3}" => "val", "{var:30}" => "value", "{list}" => "red,green,blue", "{list*}" => "red,green,blue", "{keys}" => "semi,%3B,dot,.,comma,%2C", "{keys*}" => "semi=%3B,dot=.,comma=%2C" ); $out = array(); foreach ($templates as $tpl => $expect) { $result = NULL; uri_template($tpl, $data, $result); unset($result['expressions']); $out[] = $result; } var_dump($out); ?> --EXPECT-- array(16) { [0]=> array(2) { ["result"]=> string(5) "value" ["state"]=> int(0) } [1]=> array(2) { ["result"]=> string(16) "Hello%20World%21" ["state"]=> int(0) } [2]=> array(2) { ["result"]=> string(5) "50%25" ["state"]=> int(0) } [3]=> array(2) { ["result"]=> string(2) "OX" ["state"]=> int(0) } [4]=> array(2) { ["result"]=> string(2) "OX" ["state"]=> int(0) } [5]=> array(2) { ["result"]=> string(8) "1024,768" ["state"]=> int(0) } [6]=> array(2) { ["result"]=> string(25) "1024,Hello%20World%21,768" ["state"]=> int(0) } [7]=> array(2) { ["result"]=> string(6) "?1024," ["state"]=> int(0) } [8]=> array(2) { ["result"]=> string(5) "?1024" ["state"]=> int(0) } [9]=> array(2) { ["result"]=> string(4) "?768" ["state"]=> int(0) } [10]=> array(2) { ["result"]=> string(3) "val" ["state"]=> int(0) } [11]=> array(2) { ["result"]=> string(5) "value" ["state"]=> int(0) } [12]=> array(2) { ["result"]=> string(14) "red,green,blue" ["state"]=> int(0) } [13]=> array(2) { ["result"]=> string(14) "red,green,blue" ["state"]=> int(0) } [14]=> array(2) { ["result"]=> string(24) "semi,%3B,dot,.,comma,%2C" ["state"]=> int(0) } [15]=> array(2) { ["result"]=> string(24) "semi=%3B,dot=.,comma=%2C" ["state"]=> int(0) } }--TEST-- uri_template() level 3 expansion - string expansions with multiple variables --FILE-- <?php $data = array( "var" => "value", "hello" => "Hello World!", "empty" => "", "path" => "/foo/bar", "x" => "1024", "y" => "768" ); $templates = array( "map?{x,y}" => "map?1024,768", "{x,hello,y}" => "1024,Hello%20World%21,768" ); $out = array(); foreach ($templates as $tpl => $expect) { $result = NULL; uri_template($tpl, $data, $result); unset($result['expressions']); $out[] = $result; } var_dump($out); ?> --EXPECT-- array(2) { [0]=> array(2) { ["result"]=> string(12) "map?1024,768" ["state"]=> int(0) } [1]=> array(2) { ["result"]=> string(25) "1024,Hello%20World%21,768" ["state"]=> int(0) } }--TEST-- uri_template() level 4 expansion - semi colon prefixed path style parameters expansion with value modifiers --FILE-- <?php $data = array( "var" => "value", "hello" => "Hello World!", "path" => "/foo/bar", "list" => array("red", "green", "blue"), "keys" => array( "semi" => ";", "dot" => ".", "comma" => "," ) ); $templates = array( "{;hello:5}" => ";hello=Hello", "{;list}" => ";list=red,green,blue", "{;list*}" => ";list=red;list=green;list=blue", "{;keys}" => ";keys=semi,%3B,dot,.,comma,%2C", "{;keys*}" => ";semi=%3B;dot=.;comma=%2C" ); $out = array(); foreach ($templates as $tpl => $expect) { $result = NULL; uri_template($tpl, $data, $result); unset($result['expressions']); $out[] = $result; } var_dump($out); ?> --EXPECT-- array(5) { [0]=> array(2) { ["result"]=> string(12) ";hello=Hello" ["state"]=> int(0) } [1]=> array(2) { ["result"]=> string(20) ";list=red,green,blue" ["state"]=> int(0) } [2]=> array(2) { ["result"]=> string(30) ";list=red;list=green;list=blue" ["state"]=> int(0) } [3]=> array(2) { ["result"]=> string(30) ";keys=semi,%3B,dot,.,comma,%2C" ["state"]=> int(0) } [4]=> array(2) { ["result"]=> string(25) ";semi=%3B;dot=.;comma=%2C" ["state"]=> int(0) } }--TEST-- uri_template() invalid expression - error 13 "{var}{-prefix|/-/|var}" --FILE-- <?php $data = array( "id" => "thing", "var" => "value", "hello" => "Hello World!", "empty" => "", "path" => "/foo/bar", "x" => "1024", "y" => "768", "list" => array("red", "green", "blue"), "keys" => array( "semi" => ";", "dot" => ".", "comma" => "," ), "example" => "red", "searchTerms" => "uri templates" ); uri_template('{var}{-prefix|/-/|var}', $data, $result); var_dump($result); ?> --EXPECT-- array(3) { ["result"]=> string(22) "value{-prefix|/-/|var}" ["state"]=> int(3) ["expressions"]=> array(2) { [0]=> array(7) { ["op"]=> string(0) "" ["sep"]=> string(1) "," ["ifemp"]=> string(0) "" ["allow"]=> bool(false) ["named"]=> bool(false) ["error"]=> bool(false) ["vars"]=> array(1) { [0]=> array(3) { ["name"]=> string(3) "var" ["length"]=> int(0) ["explode"]=> bool(false) } } } [1]=> array(7) { ["op"]=> string(0) "" ["sep"]=> string(1) "," ["ifemp"]=> string(0) "" ["allow"]=> bool(false) ["named"]=> bool(false) ["error"]=> bool(true) ["vars"]=> array(1) { [0]=> array(3) { ["name"]=> string(15) "-prefix|/-/|var" ["length"]=> int(0) ["explode"]=> bool(false) } } } } }--TEST-- uri_template() invalid expression - error 3 "{/?id}" --FILE-- <?php $data = array( "id" => "thing", "var" => "value", "hello" => "Hello World!", "empty" => "", "path" => "/foo/bar", "x" => "1024", "y" => "768", "list" => array("red", "green", "blue"), "keys" => array( "semi" => ";", "dot" => ".", "comma" => "," ), "example" => "red", "searchTerms" => "uri templates" ); uri_template("{/?id}", $data, $result); var_dump($result); ?> --EXPECT-- array(3) { ["result"]=> string(6) "{/?id}" ["state"]=> int(3) ["expressions"]=> array(1) { [0]=> array(7) { ["op"]=> string(1) "/" ["sep"]=> string(1) "/" ["ifemp"]=> string(0) "" ["allow"]=> bool(false) ["named"]=> bool(false) ["error"]=> bool(true) ["vars"]=> array(1) { [0]=> array(3) { ["name"]=> string(3) "?id" ["length"]=> int(0) ["explode"]=> bool(false) } } } } }--TEST-- uri_template() expansion 2 - reserved characters test --FILE-- <?php $data = array( 'count' => array("one", "two", "three"), 'dom' => array("example", "com"), 'dub' => "me/too", 'hello' => "Hello World!", 'half' => "50%", 'var' => "value", 'who' => "fred", 'base' => "http://example.com/home/", 'path' => "/foo/bar", 'list' => array("red", "green", "blue"), 'keys' => array( "semi" => ";", "dot" => ".", "comma" => ",", ), 'v' => "6", 'x' => "1024", 'y' => "768", 'empty' => "", 'empty_keys' => array(), 'undef' => null, ); $templates = array( "{+var}" => "value", "{+hello}" => "Hello%20World!", "{+half}" => "50%25", "{base}index" => "http%3A%2F%2Fexample.com%2Fhome%2Findex", "{+base}index" => "http://example.com/home/index", "O{+empty}X" => "OX", "O{+undef}X" => "OX", "{+path}/here" => "/foo/bar/here", "here?ref={+path}" => "here?ref=/foo/bar", "up{+path}{var}/here" => "up/foo/barvalue/here", "{+x,hello,y}" => "1024,Hello%20World!,768", "{+path,x}/here" => "/foo/bar,1024/here", "{+path:6}/here" => "/foo/b/here", "{+list}" => "red,green,blue", "{+list*}" => "red,green,blue", "{+keys}" => "semi,;,dot,.,comma,,", "{+keys*}" => "semi=;,dot=.,comma=," ); $out = array(); foreach ($templates as $tpl => $expect) { $result = NULL; uri_template($tpl, $data, $result); unset($result['expressions']); $out[] = $result; } var_dump($out); ?> --EXPECT-- array(17) { [0]=> array(2) { ["result"]=> string(5) "value" ["state"]=> int(0) } [1]=> array(2) { ["result"]=> string(14) "Hello%20World!" ["state"]=> int(0) } [2]=> array(2) { ["result"]=> string(5) "50%25" ["state"]=> int(0) } [3]=> array(2) { ["result"]=> string(39) "http%3A%2F%2Fexample.com%2Fhome%2Findex" ["state"]=> int(0) } [4]=> array(2) { ["result"]=> string(29) "http://example.com/home/index" ["state"]=> int(0) } [5]=> array(2) { ["result"]=> string(2) "OX" ["state"]=> int(0) } [6]=> array(2) { ["result"]=> string(2) "OX" ["state"]=> int(0) } [7]=> array(2) { ["result"]=> string(13) "/foo/bar/here" ["state"]=> int(0) } [8]=> array(2) { ["result"]=> string(17) "here?ref=/foo/bar" ["state"]=> int(0) } [9]=> array(2) { ["result"]=> string(20) "up/foo/barvalue/here" ["state"]=> int(0) } [10]=> array(2) { ["result"]=> string(23) "1024,Hello%20World!,768" ["state"]=> int(0) } [11]=> array(2) { ["result"]=> string(18) "/foo/bar,1024/here" ["state"]=> int(0) } [12]=> array(2) { ["result"]=> string(11) "/foo/b/here" ["state"]=> int(0) } [13]=> array(2) { ["result"]=> string(14) "red,green,blue" ["state"]=> int(0) } [14]=> array(2) { ["result"]=> string(14) "red,green,blue" ["state"]=> int(0) } [15]=> array(2) { ["result"]=> string(20) "semi,;,dot,.,comma,," ["state"]=> int(0) } [16]=> array(2) { ["result"]=> string(20) "semi=;,dot=.,comma=," ["state"]=> int(0) } }--TEST-- uri_template() level 3 expansion - reserved chars expansions with multiple variables --FILE-- <?php $data = array( "var" => "value", "hello" => "Hello World!", "empty" => "", "path" => "/foo/bar", "x" => "1024", "y" => "768" ); $templates = array( "{+x,hello,y}" => "1024,Hello%20World!,768", "{+path,x}/here" => "/foo/bar,1024/here" ); $out = array(); foreach ($templates as $tpl => $expect) { $result = NULL; uri_template($tpl, $data, $result); unset($result['expressions']); $out[] = $result; } var_dump($out); ?> --EXPECT-- array(2) { [0]=> array(2) { ["result"]=> string(23) "1024,Hello%20World!,768" ["state"]=> int(0) } [1]=> array(2) { ["result"]=> string(18) "/foo/bar,1024/here" ["state"]=> int(0) } }--TEST-- uri_template() level 4 expansion - form style query ampersand separated expansion with value modifiers --FILE-- <?php $data = array( "var" => "value", "hello" => "Hello World!", "path" => "/foo/bar", "list" => array("red", "green", "blue"), "keys" => array( "semi" => ";", "dot" => ".", "comma" => "," ) ); $templates = array( "{?var:3}" => "?var=val", "{?list}" => "?list=red,green,blue", "{?list*}" => "?list=red&list=green&list=blue", "{?keys}" => "?keys=semi,%3B,dot,.,comma,%2C", "{?keys*}" => "?semi=%3B&dot=.&comma=%2C" ); $out = array(); foreach ($templates as $tpl => $expect) { $result = NULL; uri_template($tpl, $data, $result); unset($result['expressions']); $out[] = $result; } var_dump($out); ?> --EXPECT-- array(5) { [0]=> array(2) { ["result"]=> string(8) "?var=val" ["state"]=> int(0) } [1]=> array(2) { ["result"]=> string(20) "?list=red,green,blue" ["state"]=> int(0) } [2]=> array(2) { ["result"]=> string(30) "?list=red&list=green&list=blue" ["state"]=> int(0) } [3]=> array(2) { ["result"]=> string(30) "?keys=semi,%3B,dot,.,comma,%2C" ["state"]=> int(0) } [4]=> array(2) { ["result"]=> string(25) "?semi=%3B&dot=.&comma=%2C" ["state"]=> int(0) } }--TEST-- uri_template() invalid expression - error 14 "?q={searchTerms}&c={example:color?}" --FILE-- <?php $data = array( "id" => "thing", "var" => "value", "hello" => "Hello World!", "empty" => "", "path" => "/foo/bar", "x" => "1024", "y" => "768", "list" => array("red", "green", "blue"), "keys" => array( "semi" => ";", "dot" => ".", "comma" => "," ), "example" => "red", "searchTerms" => "uri templates" ); uri_template('?q={searchTerms}&c={example:color?}', $data, $result); var_dump($result); ?> --EXPECT-- array(3) { ["result"]=> string(41) "?q=uri%20templates&c={example:color?}" ["state"]=> int(3) ["expressions"]=> array(2) { [0]=> array(7) { ["op"]=> string(0) "" ["sep"]=> string(1) "," ["ifemp"]=> string(0) "" ["allow"]=> bool(false) ["named"]=> bool(false) ["error"]=> bool(false) ["vars"]=> array(1) { [0]=> array(3) { ["name"]=> string(11) "searchTerms" ["length"]=> int(0) ["explode"]=> bool(false) } } } [1]=> array(7) { ["op"]=> string(0) "" ["sep"]=> string(1) "," ["ifemp"]=> string(0) "" ["allow"]=> bool(false) ["named"]=> bool(false) ["error"]=> bool(true) ["vars"]=> array(1) { [0]=> array(3) { ["name"]=> string(14) "example:color?" ["length"]=> int(0) ["explode"]=> bool(false) } } } } }--TEST-- uri_template() invalid expression - error 4 "{var:prefix}" --FILE-- <?php $data = array( "id" => "thing", "var" => "value", "hello" => "Hello World!", "empty" => "", "path" => "/foo/bar", "x" => "1024", "y" => "768", "list" => array("red", "green", "blue"), "keys" => array( "semi" => ";", "dot" => ".", "comma" => "," ), "example" => "red", "searchTerms" => "uri templates" ); uri_template("{var:prefix}", $data, $result); var_dump($result); ?> --EXPECT-- array(3) { ["result"]=> string(12) "{var:prefix}" ["state"]=> int(3) ["expressions"]=> array(1) { [0]=> array(7) { ["op"]=> string(0) "" ["sep"]=> string(1) "," ["ifemp"]=> string(0) "" ["allow"]=> bool(false) ["named"]=> bool(false) ["error"]=> bool(true) ["vars"]=> array(1) { [0]=> array(3) { ["name"]=> string(10) "var:prefix" ["length"]=> int(0) ["explode"]=> bool(false) } } } } }--TEST-- uri_template() expansion 3 - URL fragment tests --FILE-- <?php $data = array( 'count' => array("one", "two", "three"), 'dom' => array("example", "com"), 'dub' => "me/too", 'hello' => "Hello World!", 'half' => "50%", 'var' => "value", 'who' => "fred", 'base' => "http://example.com/home/", 'path' => "/foo/bar", 'list' => array("red", "green", "blue"), 'keys' => array( "semi" => ";", "dot" => ".", "comma" => ",", ), 'v' => "6", 'x' => "1024", 'y' => "768", 'empty' => "", 'empty_keys' => array(), 'undef' => null, ); $templates = array( "{#var}" => "#value", "{#hello}" => "#Hello%20World!", "{#half}" => "#50%25", "foo{#empty}" => "foo#", "foo{#undef}" => "foo", "{#x,hello,y}" => "#1024,Hello%20World!,768", "{#path,x}/here" => "#/foo/bar,1024/here", "{#path:6}/here" => "#/foo/b/here", "{#list}" => "#red,green,blue", "{#list*}" => "#red,green,blue", "{#keys}" => "#semi,;,dot,.,comma,,", "{#keys*}" => "#semi=;,dot=.,comma=," ); $out = array(); foreach ($templates as $tpl => $expect) { $result = NULL; uri_template($tpl, $data, $result); unset($result['expressions']); $out[] = $result; } var_dump($out); ?> --EXPECT-- array(12) { [0]=> array(2) { ["result"]=> string(6) "#value" ["state"]=> int(0) } [1]=> array(2) { ["result"]=> string(15) "#Hello%20World!" ["state"]=> int(0) } [2]=> array(2) { ["result"]=> string(6) "#50%25" ["state"]=> int(0) } [3]=> array(2) { ["result"]=> string(4) "foo#" ["state"]=> int(0) } [4]=> array(2) { ["result"]=> string(3) "foo" ["state"]=> int(0) } [5]=> array(2) { ["result"]=> string(24) "#1024,Hello%20World!,768" ["state"]=> int(0) } [6]=> array(2) { ["result"]=> string(19) "#/foo/bar,1024/here" ["state"]=> int(0) } [7]=> array(2) { ["result"]=> string(12) "#/foo/b/here" ["state"]=> int(0) } [8]=> array(2) { ["result"]=> string(15) "#red,green,blue" ["state"]=> int(0) } [9]=> array(2) { ["result"]=> string(15) "#red,green,blue" ["state"]=> int(0) } [10]=> array(2) { ["result"]=> string(21) "#semi,;,dot,.,comma,," ["state"]=> int(0) } [11]=> array(2) { ["result"]=> string(21) "#semi=;,dot=.,comma=," ["state"]=> int(0) } }--TEST-- uri_template() level 3 expansion - fragment expansions with multiple variables --FILE-- <?php $data = array( "var" => "value", "hello" => "Hello World!", "empty" => "", "path" => "/foo/bar", "x" => "1024", "y" => "768" ); $templates = array( "{#x,hello,y}" => "#1024,Hello%20World!,768", "{#path,x}/here" => "#/foo/bar,1024/here" ); $out = array(); foreach ($templates as $tpl => $expect) { $result = NULL; uri_template($tpl, $data, $result); unset($result['expressions']); $out[] = $result; } var_dump($out); ?> --EXPECT-- array(2) { [0]=> array(2) { ["result"]=> string(24) "#1024,Hello%20World!,768" ["state"]=> int(0) } [1]=> array(2) { ["result"]=> string(19) "#/foo/bar,1024/here" ["state"]=> int(0) } }--TEST-- uri_template() level 4 expansion - form style query continuation expansion with value modifiers --FILE-- <?php $data = array( "var" => "value", "hello" => "Hello World!", "path" => "/foo/bar", "list" => array("red", "green", "blue"), "keys" => array( "semi" => ";", "dot" => ".", "comma" => "," ) ); $templates = array( "{&var:3}" => "&var=val", "{&list}" => "&list=red,green,blue", "{&list*}" => "&list=red&list=green&list=blue", "{&keys}" => "&keys=semi,%3B,dot,.,comma,%2C", "{&keys*}" => "&semi=%3B&dot=.&comma=%2C" ); $out = array(); foreach ($templates as $tpl => $expect) { $result = NULL; uri_template($tpl, $data, $result); unset($result['expressions']); $out[] = $result; } var_dump($out); ?> --EXPECT-- array(5) { [0]=> array(2) { ["result"]=> string(8) "&var=val" ["state"]=> int(0) } [1]=> array(2) { ["result"]=> string(20) "&list=red,green,blue" ["state"]=> int(0) } [2]=> array(2) { ["result"]=> string(30) "&list=red&list=green&list=blue" ["state"]=> int(0) } [3]=> array(2) { ["result"]=> string(30) "&keys=semi,%3B,dot,.,comma,%2C" ["state"]=> int(0) } [4]=> array(2) { ["result"]=> string(25) "&semi=%3B&dot=.&comma=%2C" ["state"]=> int(0) } }--TEST-- uri_template() invalid expression - error 15 "x{?empty|foo=none}" --FILE-- <?php $data = array( "id" => "thing", "var" => "value", "hello" => "Hello World!", "empty" => "", "path" => "/foo/bar", "x" => "1024", "y" => "768", "list" => array("red", "green", "blue"), "keys" => array( "semi" => ";", "dot" => ".", "comma" => "," ), "example" => "red", "searchTerms" => "uri templates" ); uri_template('x{?empty|foo=none}', $data, $result); var_dump($result); ?> --EXPECT-- array(3) { ["result"]=> string(18) "x{?empty|foo=none}" ["state"]=> int(3) ["expressions"]=> array(1) { [0]=> array(7) { ["op"]=> string(1) "?" ["sep"]=> string(1) "&" ["ifemp"]=> string(1) "=" ["allow"]=> bool(false) ["named"]=> bool(true) ["error"]=> bool(true) ["vars"]=> array(1) { [0]=> array(3) { ["name"]=> string(14) "empty|foo=none" ["length"]=> int(0) ["explode"]=> bool(false) } } } } }--TEST-- uri_template() invalid expression - error 5 "{hello:2*}" --FILE-- <?php $data = array( "id" => "thing", "var" => "value", "hello" => "Hello World!", "empty" => "", "path" => "/foo/bar", "x" => "1024", "y" => "768", "list" => array("red", "green", "blue"), "keys" => array( "semi" => ";", "dot" => ".", "comma" => "," ), "example" => "red", "searchTerms" => "uri templates" ); uri_template("{hello:2*}", $data, $result); var_dump($result); ?> --EXPECT-- array(3) { ["result"]=> string(10) "{hello:2*}" ["state"]=> int(3) ["expressions"]=> array(1) { [0]=> array(7) { ["op"]=> string(0) "" ["sep"]=> string(1) "," ["ifemp"]=> string(0) "" ["allow"]=> bool(false) ["named"]=> bool(false) ["error"]=> bool(true) ["vars"]=> array(1) { [0]=> array(3) { ["name"]=> string(8) "hello:2*" ["length"]=> int(0) ["explode"]=> bool(false) } } } } }--TEST-- uri_template() expansion 4 - label tests --FILE-- <?php $data = array( 'count' => array("one", "two", "three"), 'dom' => array("example", "com"), 'dub' => "me/too", 'hello' => "Hello World!", 'half' => "50%", 'var' => "value", 'who' => "fred", 'base' => "http://example.com/home/", 'path' => "/foo/bar", 'list' => array("red", "green", "blue"), 'keys' => array( "semi" => ";", "dot" => ".", "comma" => ",", ), 'v' => "6", 'x' => "1024", 'y' => "768", 'empty' => "", 'empty_keys' => array(), 'undef' => null, ); $templates = array( "{.who}" => ".fred", "{.who,who}" => ".fred.fred", "{.half,who}" => ".50%25.fred", "www{.dom*}" => "www.example.com", "X{.var}" => "X.value", "X{.empty}" => "X.", "X{.undef}" => "X", "X{.var:3}" => "X.val", "X{.list}" => "X.red,green,blue", "X{.list*}" => "X.red.green.blue", "X{.keys}" => "X.semi,%3B,dot,.,comma,%2C", "X{.keys*}" => "X.semi=%3B.dot=..comma=%2C", "X{.empty_keys}" => "X", "X{.empty_keys*}" => "X" ); $out = array(); foreach ($templates as $tpl => $expect) { $result = NULL; uri_template($tpl, $data, $result); unset($result['expressions']); $out[] = $result; } var_dump($out); ?> --EXPECT-- array(14) { [0]=> array(2) { ["result"]=> string(5) ".fred" ["state"]=> int(0) } [1]=> array(2) { ["result"]=> string(10) ".fred.fred" ["state"]=> int(0) } [2]=> array(2) { ["result"]=> string(11) ".50%25.fred" ["state"]=> int(0) } [3]=> array(2) { ["result"]=> string(15) "www.example.com" ["state"]=> int(0) } [4]=> array(2) { ["result"]=> string(7) "X.value" ["state"]=> int(0) } [5]=> array(2) { ["result"]=> string(2) "X." ["state"]=> int(0) } [6]=> array(2) { ["result"]=> string(1) "X" ["state"]=> int(0) } [7]=> array(2) { ["result"]=> string(5) "X.val" ["state"]=> int(0) } [8]=> array(2) { ["result"]=> string(16) "X.red,green,blue" ["state"]=> int(0) } [9]=> array(2) { ["result"]=> string(16) "X.red.green.blue" ["state"]=> int(0) } [10]=> array(2) { ["result"]=> string(26) "X.semi,%3B,dot,.,comma,%2C" ["state"]=> int(0) } [11]=> array(2) { ["result"]=> string(26) "X.semi=%3B.dot=..comma=%2C" ["state"]=> int(0) } [12]=> array(2) { ["result"]=> string(1) "X" ["state"]=> int(0) } [13]=> array(2) { ["result"]=> string(1) "X" ["state"]=> int(0) } }--TEST-- uri_template() level 3 expansion - dot prefixed label expansions --FILE-- <?php $data = array( "var" => "value", "hello" => "Hello World!", "empty" => "", "path" => "/foo/bar", "x" => "1024", "y" => "768" ); $templates = array( "X{.var}" => "X.value", "X{.x,y}" => "X.1024.768" ); $out = array(); foreach ($templates as $tpl => $expect) { $result = NULL; uri_template($tpl, $data, $result); unset($result['expressions']); $out[] = $result; } var_dump($out); ?> --EXPECT-- array(2) { [0]=> array(2) { ["result"]=> string(7) "X.value" ["state"]=> int(0) } [1]=> array(2) { ["result"]=> string(10) "X.1024.768" ["state"]=> int(0) } }--TEST-- uri_template() multibyte string - test 1 "'å°åŒ—TáibÄ›i" --FILE-- <?php $data = array( 'var' => 'å°åŒ—TáibÄ›i' ); $templates = array( '{var}', '{var:1}', '{var:2}', '{var:3}', '{var:4}', '{var:5}', '{var:6}', '{var:8}', ); $out = array(); foreach ($templates as $tpl) { uri_template($tpl, $data, $result); unset($result['expressions']); $out[] = $result; } var_dump($out); ?> --EXPECT-- array(8) { [0]=> array(2) { ["result"]=> string(34) "%E5%8F%B0%E5%8C%97T%C3%A1ib%C4%9Bi" ["state"]=> int(0) } [1]=> array(2) { ["result"]=> string(9) "%E5%8F%B0" ["state"]=> int(0) } [2]=> array(2) { ["result"]=> string(18) "%E5%8F%B0%E5%8C%97" ["state"]=> int(0) } [3]=> array(2) { ["result"]=> string(19) "%E5%8F%B0%E5%8C%97T" ["state"]=> int(0) } [4]=> array(2) { ["result"]=> string(25) "%E5%8F%B0%E5%8C%97T%C3%A1" ["state"]=> int(0) } [5]=> array(2) { ["result"]=> string(26) "%E5%8F%B0%E5%8C%97T%C3%A1i" ["state"]=> int(0) } [6]=> array(2) { ["result"]=> string(27) "%E5%8F%B0%E5%8C%97T%C3%A1ib" ["state"]=> int(0) } [7]=> array(2) { ["result"]=> string(34) "%E5%8F%B0%E5%8C%97T%C3%A1ib%C4%9Bi" ["state"]=> int(0) } }--TEST-- uri_template() invalid expression - error 16 "/h{#hello+}" --FILE-- <?php $data = array( "id" => "thing", "var" => "value", "hello" => "Hello World!", "empty" => "", "path" => "/foo/bar", "x" => "1024", "y" => "768", "list" => array("red", "green", "blue"), "keys" => array( "semi" => ";", "dot" => ".", "comma" => "," ), "example" => "red", "searchTerms" => "uri templates" ); uri_template('/h{#hello+}', $data, $result); var_dump($result); ?> --EXPECT-- array(3) { ["result"]=> string(11) "/h{#hello+}" ["state"]=> int(3) ["expressions"]=> array(1) { [0]=> array(7) { ["op"]=> string(1) "#" ["sep"]=> string(1) "," ["ifemp"]=> string(0) "" ["allow"]=> bool(true) ["named"]=> bool(false) ["error"]=> bool(true) ["vars"]=> array(1) { [0]=> array(3) { ["name"]=> string(6) "hello+" ["length"]=> int(0) ["explode"]=> bool(false) } } } } }--TEST-- uri_template() invalid expression - error 6 "{??hello}" --FILE-- <?php $data = array( "id" => "thing", "var" => "value", "hello" => "Hello World!", "empty" => "", "path" => "/foo/bar", "x" => "1024", "y" => "768", "list" => array("red", "green", "blue"), "keys" => array( "semi" => ";", "dot" => ".", "comma" => "," ), "example" => "red", "searchTerms" => "uri templates" ); uri_template("{??hello}", $data, $result); var_dump($result); ?> --EXPECT-- array(3) { ["result"]=> string(9) "{??hello}" ["state"]=> int(3) ["expressions"]=> array(1) { [0]=> array(7) { ["op"]=> string(1) "?" ["sep"]=> string(1) "&" ["ifemp"]=> string(1) "=" ["allow"]=> bool(false) ["named"]=> bool(true) ["error"]=> bool(true) ["vars"]=> array(1) { [0]=> array(3) { ["name"]=> string(6) "?hello" ["length"]=> int(0) ["explode"]=> bool(false) } } } } }--TEST-- uri_template() expansion 5 - path segments tests --FILE-- <?php $data = array( 'count' => array("one", "two", "three"), 'dom' => array("example", "com"), 'dub' => "me/too", 'hello' => "Hello World!", 'half' => "50%", 'var' => "value", 'who' => "fred", 'base' => "http://example.com/home/", 'path' => "/foo/bar", 'list' => array("red", "green", "blue"), 'keys' => array( "semi" => ";", "dot" => ".", "comma" => ",", ), 'v' => "6", 'x' => "1024", 'y' => "768", 'empty' => "", 'empty_keys' => array(), 'undef' => null, ); $templates = array( "{/who}" => "/fred", "{/who,who}" => "/fred/fred", "{/half,who}" => "/50%25/fred", "{/who,dub}" => "/fred/me%2Ftoo", "{/var}" => "/value", "{/var,empty}" => "/value/", "{/var,undef}" => "/value", "{/var,x}/here" => "/value/1024/here", "{/var:1,var}" => "/v/value", "{/list}" => "/red,green,blue", "{/list*}" => "/red/green/blue", "{/list*,path:4}" => "/red/green/blue/%2Ffoo", "{/keys}" => "/semi,%3B,dot,.,comma,%2C", "{/keys*}" => "/semi=%3B/dot=./comma=%2C" ); $out = array(); foreach ($templates as $tpl => $expect) { $result = NULL; uri_template($tpl, $data, $result); unset($result['expressions']); $out[] = $result; } var_dump($out); ?> --EXPECT-- array(14) { [0]=> array(2) { ["result"]=> string(5) "/fred" ["state"]=> int(0) } [1]=> array(2) { ["result"]=> string(10) "/fred/fred" ["state"]=> int(0) } [2]=> array(2) { ["result"]=> string(11) "/50%25/fred" ["state"]=> int(0) } [3]=> array(2) { ["result"]=> string(14) "/fred/me%2Ftoo" ["state"]=> int(0) } [4]=> array(2) { ["result"]=> string(6) "/value" ["state"]=> int(0) } [5]=> array(2) { ["result"]=> string(7) "/value/" ["state"]=> int(0) } [6]=> array(2) { ["result"]=> string(6) "/value" ["state"]=> int(0) } [7]=> array(2) { ["result"]=> string(16) "/value/1024/here" ["state"]=> int(0) } [8]=> array(2) { ["result"]=> string(8) "/v/value" ["state"]=> int(0) } [9]=> array(2) { ["result"]=> string(15) "/red,green,blue" ["state"]=> int(0) } [10]=> array(2) { ["result"]=> string(15) "/red/green/blue" ["state"]=> int(0) } [11]=> array(2) { ["result"]=> string(22) "/red/green/blue/%2Ffoo" ["state"]=> int(0) } [12]=> array(2) { ["result"]=> string(25) "/semi,%3B,dot,.,comma,%2C" ["state"]=> int(0) } [13]=> array(2) { ["result"]=> string(25) "/semi=%3B/dot=./comma=%2C" ["state"]=> int(0) } }--TEST-- uri_template() level 3 expansion - path style segments slash prefixed expansions --FILE-- <?php $data = array( "var" => "value", "hello" => "Hello World!", "empty" => "", "path" => "/foo/bar", "x" => "1024", "y" => "768" ); $templates = array( "{/var}" => "/value", "{/var,x}/here" => "/value/1024/here" ); $out = array(); foreach ($templates as $tpl => $expect) { $result = NULL; uri_template($tpl, $data, $result); unset($result['expressions']); $out[] = $result; } var_dump($out); ?> --EXPECT-- array(2) { [0]=> array(2) { ["result"]=> string(6) "/value" ["state"]=> int(0) } [1]=> array(2) { ["result"]=> string(16) "/value/1024/here" ["state"]=> int(0) } }--TEST-- uri_template() multibyte string - string test damaged string "'å°åŒ—TáibÄ›i" --FILE-- <?php $s = 'å°åŒ—TáibÄ›i'; $s[0] = 'a'; $s[2] = 'b'; $s[4] = 'c'; $data = array( 'var' => $s ); $templates = array( '{var}', '{var:1}', '{var:2}', '{var:3}', '{var:4}', ); $out = array(); foreach ($templates as $tpl) { uri_template($tpl, $data, $result); unset($result['expressions']); $out[] = $result; } var_dump($out); ?> --EXPECT-- array(5) { [0]=> array(2) { ["result"]=> string(46) "a%EF%BF%BDb%EF%BF%BDc%EF%BF%BDT%C3%A1ib%C4%9Bi" ["state"]=> int(0) } [1]=> array(2) { ["result"]=> string(1) "a" ["state"]=> int(0) } [2]=> array(2) { ["result"]=> string(10) "a%EF%BF%BD" ["state"]=> int(0) } [3]=> array(2) { ["result"]=> string(11) "a%EF%BF%BDb" ["state"]=> int(0) } [4]=> array(2) { ["result"]=> string(20) "a%EF%BF%BDb%EF%BF%BD" ["state"]=> int(0) } }--TEST-- uri_template() invalid expression - error 17 "/h#{hello+}" --FILE-- <?php $data = array( "id" => "thing", "var" => "value", "hello" => "Hello World!", "empty" => "", "path" => "/foo/bar", "x" => "1024", "y" => "768", "list" => array("red", "green", "blue"), "keys" => array( "semi" => ";", "dot" => ".", "comma" => "," ), "example" => "red", "searchTerms" => "uri templates" ); uri_template('/h#{hello+}', $data, $result); var_dump($result); ?> --EXPECT-- array(3) { ["result"]=> string(11) "/h#{hello+}" ["state"]=> int(3) ["expressions"]=> array(1) { [0]=> array(7) { ["op"]=> string(0) "" ["sep"]=> string(1) "," ["ifemp"]=> string(0) "" ["allow"]=> bool(false) ["named"]=> bool(false) ["error"]=> bool(true) ["vars"]=> array(1) { [0]=> array(3) { ["name"]=> string(6) "hello+" ["length"]=> int(0) ["explode"]=> bool(false) } } } } }--TEST-- uri_template() invalid expression - error 7 "{!hello}" --FILE-- <?php $data = array( "id" => "thing", "var" => "value", "hello" => "Hello World!", "empty" => "", "path" => "/foo/bar", "x" => "1024", "y" => "768", "list" => array("red", "green", "blue"), "keys" => array( "semi" => ";", "dot" => ".", "comma" => "," ), "example" => "red", "searchTerms" => "uri templates" ); uri_template("{!hello}", $data, $result); var_dump($result); ?> --EXPECT-- array(3) { ["result"]=> string(8) "{!hello}" ["state"]=> int(3) ["expressions"]=> array(1) { [0]=> array(7) { ["op"]=> string(0) "" ["sep"]=> string(1) "," ["ifemp"]=> string(0) "" ["allow"]=> bool(false) ["named"]=> bool(false) ["error"]=> bool(true) ["vars"]=> array(1) { [0]=> array(3) { ["name"]=> string(6) "!hello" ["length"]=> int(0) ["explode"]=> bool(false) } } } } }--TEST-- uri_template() expansion 6 - path style tests --FILE-- <?php $data = array( 'count' => array("one", "two", "three"), 'dom' => array("example", "com"), 'dub' => "me/too", 'hello' => "Hello World!", 'half' => "50%", 'var' => "value", 'who' => "fred", 'base' => "http://example.com/home/", 'path' => "/foo/bar", 'list' => array("red", "green", "blue"), 'keys' => array( "semi" => ";", "dot" => ".", "comma" => ",", ), 'v' => "6", 'x' => "1024", 'y' => "768", 'empty' => "", 'empty_keys' => array(), 'undef' => null, ); $templates = array( "{;who}" => ";who=fred", "{;half}" => ";half=50%25", "{;empty}" => ";empty", "{;v,empty,who}" => ";v=6;empty;who=fred", "{;v,bar,who}" => ";v=6;who=fred", "{;x,y}" => ";x=1024;y=768", "{;x,y,empty}" => ";x=1024;y=768;empty", "{;x,y,undef}" => ";x=1024;y=768", "{;hello:5}" => ";hello=Hello", "{;list}" => ";list=red,green,blue", "{;list*}" => ";list=red;list=green;list=blue", "{;keys}" => ";keys=semi,%3B,dot,.,comma,%2C", "{;keys*}" => ";semi=%3B;dot=.;comma=%2C" ); $out = array(); foreach ($templates as $tpl => $expect) { $result = NULL; uri_template($tpl, $data, $result); unset($result['expressions']); $out[] = $result; } var_dump($out); ?> --EXPECT-- array(13) { [0]=> array(2) { ["result"]=> string(9) ";who=fred" ["state"]=> int(0) } [1]=> array(2) { ["result"]=> string(11) ";half=50%25" ["state"]=> int(0) } [2]=> array(2) { ["result"]=> string(6) ";empty" ["state"]=> int(0) } [3]=> array(2) { ["result"]=> string(19) ";v=6;empty;who=fred" ["state"]=> int(0) } [4]=> array(2) { ["result"]=> string(13) ";v=6;who=fred" ["state"]=> int(0) } [5]=> array(2) { ["result"]=> string(13) ";x=1024;y=768" ["state"]=> int(0) } [6]=> array(2) { ["result"]=> string(19) ";x=1024;y=768;empty" ["state"]=> int(0) } [7]=> array(2) { ["result"]=> string(13) ";x=1024;y=768" ["state"]=> int(0) } [8]=> array(2) { ["result"]=> string(12) ";hello=Hello" ["state"]=> int(0) } [9]=> array(2) { ["result"]=> string(20) ";list=red,green,blue" ["state"]=> int(0) } [10]=> array(2) { ["result"]=> string(30) ";list=red;list=green;list=blue" ["state"]=> int(0) } [11]=> array(2) { ["result"]=> string(30) ";keys=semi,%3B,dot,.,comma,%2C" ["state"]=> int(0) } [12]=> array(2) { ["result"]=> string(25) ";semi=%3B;dot=.;comma=%2C" ["state"]=> int(0) } }--TEST-- uri_template() level 3 expansion - path style parameters semi colon prefixed expansions --FILE-- <?php $data = array( "var" => "value", "hello" => "Hello World!", "empty" => "", "path" => "/foo/bar", "x" => "1024", "y" => "768" ); $templates = array( "{;x,y}" => ";x=1024;y=768", "{;x,y,empty}" => ";x=1024;y=768;empty" ); $out = array(); foreach ($templates as $tpl => $expect) { $result = NULL; uri_template($tpl, $data, $result); unset($result['expressions']); $out[] = $result; } var_dump($out); ?> --EXPECT-- array(2) { [0]=> array(2) { ["result"]=> string(13) ";x=1024;y=768" ["state"]=> int(0) } [1]=> array(2) { ["result"]=> string(19) ";x=1024;y=768;empty" ["state"]=> int(0) } }--TEST-- uri_template() invalid expression - braces test "{", "}", "{}" --FILE-- <?php $out = array(); foreach (array('{', '}', '{}') as $tpl) { $result = NULL; uri_template($tpl, array(), $result); $out[] = $result; } var_dump($out); ?> --EXPECT-- array(3) { [0]=> array(3) { ["result"]=> string(1) "{" ["state"]=> int(2) ["expressions"]=> array(0) { } } [1]=> array(3) { ["result"]=> string(1) "}" ["state"]=> int(2) ["expressions"]=> array(0) { } } [2]=> array(3) { ["result"]=> string(2) "{}" ["state"]=> int(0) ["expressions"]=> array(0) { } } }--TEST-- uri_template() invalid expression - error 18 "/vars/:var" --FILE-- <?php $data = array( "id" => "thing", "var" => "value", "hello" => "Hello World!", "empty" => "", "path" => "/foo/bar", "x" => "1024", "y" => "768", "list" => array("red", "green", "blue"), "keys" => array( "semi" => ";", "dot" => ".", "comma" => "," ), "example" => "red", "searchTerms" => "uri templates" ); uri_template('/vars/:var', $data, $result); var_dump($result); ?> --EXPECT-- array(3) { ["result"]=> string(10) "/vars/:var" ["state"]=> int(0) ["expressions"]=> array(0) { } }--TEST-- uri_template() invalid expression - error 8 "{=path}" --FILE-- <?php $data = array( "id" => "thing", "var" => "value", "hello" => "Hello World!", "empty" => "", "path" => "/foo/bar", "x" => "1024", "y" => "768", "list" => array("red", "green", "blue"), "keys" => array( "semi" => ";", "dot" => ".", "comma" => "," ), "example" => "red", "searchTerms" => "uri templates" ); uri_template("{=path}", $data, $result); var_dump($result); ?> --EXPECT-- array(3) { ["result"]=> string(7) "{=path}" ["state"]=> int(3) ["expressions"]=> array(1) { [0]=> array(7) { ["op"]=> string(0) "" ["sep"]=> string(1) "," ["ifemp"]=> string(0) "" ["allow"]=> bool(false) ["named"]=> bool(false) ["error"]=> bool(true) ["vars"]=> array(1) { [0]=> array(3) { ["name"]=> string(5) "=path" ["length"]=> int(0) ["explode"]=> bool(false) } } } } }--TEST-- uri_template() expansion 7 - form style tests --FILE-- <?php $data = array( 'count' => array("one", "two", "three"), 'dom' => array("example", "com"), 'dub' => "me/too", 'hello' => "Hello World!", 'half' => "50%", 'var' => "value", 'who' => "fred", 'base' => "http://example.com/home/", 'path' => "/foo/bar", 'list' => array("red", "green", "blue"), 'keys' => array( "semi" => ";", "dot" => ".", "comma" => ",", ), 'v' => "6", 'x' => "1024", 'y' => "768", 'empty' => "", 'empty_keys' => array(), 'undef' => null, ); $templates = array( "{?who}" => "?who=fred", "{?half}" => "?half=50%25", "{?x,y}" => "?x=1024&y=768", "{?x,y,empty}" => "?x=1024&y=768&empty=", "{?x,y,undef}" => "?x=1024&y=768", "{?var:3}" => "?var=val", "{?list}" => "?list=red,green,blue", "{?list*}" => "?list=red&list=green&list=blue", "{?keys}" => "?keys=semi,%3B,dot,.,comma,%2C", "{?keys*}" => "?semi=%3B&dot=.&comma=%2C" ); $out = array(); foreach ($templates as $tpl => $expect) { $result = NULL; uri_template($tpl, $data, $result); unset($result['expressions']); $out[] = $result; } var_dump($out); ?> --EXPECT-- array(10) { [0]=> array(2) { ["result"]=> string(9) "?who=fred" ["state"]=> int(0) } [1]=> array(2) { ["result"]=> string(11) "?half=50%25" ["state"]=> int(0) } [2]=> array(2) { ["result"]=> string(13) "?x=1024&y=768" ["state"]=> int(0) } [3]=> array(2) { ["result"]=> string(20) "?x=1024&y=768&empty=" ["state"]=> int(0) } [4]=> array(2) { ["result"]=> string(13) "?x=1024&y=768" ["state"]=> int(0) } [5]=> array(2) { ["result"]=> string(8) "?var=val" ["state"]=> int(0) } [6]=> array(2) { ["result"]=> string(20) "?list=red,green,blue" ["state"]=> int(0) } [7]=> array(2) { ["result"]=> string(30) "?list=red&list=green&list=blue" ["state"]=> int(0) } [8]=> array(2) { ["result"]=> string(30) "?keys=semi,%3B,dot,.,comma,%2C" ["state"]=> int(0) } [9]=> array(2) { ["result"]=> string(25) "?semi=%3B&dot=.&comma=%2C" ["state"]=> int(0) } }--TEST-- uri_template() level 3 expansion - form style query ampersand separated --FILE-- <?php $data = array( "var" => "value", "hello" => "Hello World!", "empty" => "", "path" => "/foo/bar", "x" => "1024", "y" => "768" ); $templates = array( "{?x,y}" => "?x=1024&y=768", "{?x,y,empty}" => "?x=1024&y=768&empty=" ); $out = array(); foreach ($templates as $tpl => $expect) { $result = NULL; uri_template($tpl, $data, $result); unset($result['expressions']); $out[] = $result; } var_dump($out); ?> --EXPECT-- array(2) { [0]=> array(2) { ["result"]=> string(13) "?x=1024&y=768" ["state"]=> int(0) } [1]=> array(2) { ["result"]=> string(20) "?x=1024&y=768&empty=" ["state"]=> int(0) } }--TEST-- uri_template() invalid expression - empty template --FILE-- <?php uri_template('', array(), $result); var_dump($result); ?> --EXPECT-- array(3) { ["result"]=> string(0) "" ["state"]=> int(0) ["expressions"]=> array(0) { } }--TEST-- uri_template() invalid expression - error 19 "{keys:1}" --FILE-- <?php $data = array( "id" => "thing", "var" => "value", "hello" => "Hello World!", "empty" => "", "path" => "/foo/bar", "x" => "1024", "y" => "768", "list" => array("red", "green", "blue"), "keys" => array( "semi" => ";", "dot" => ".", "comma" => "," ), "example" => "red", "searchTerms" => "uri templates" ); uri_template('{keys:1}', $data, $result); var_dump($result); ?> --EXPECT-- array(3) { ["result"]=> string(24) "semi,%3B,dot,.,comma,%2C" ["state"]=> int(0) ["expressions"]=> array(1) { [0]=> array(7) { ["op"]=> string(0) "" ["sep"]=> string(1) "," ["ifemp"]=> string(0) "" ["allow"]=> bool(false) ["named"]=> bool(false) ["error"]=> bool(false) ["vars"]=> array(1) { [0]=> array(3) { ["name"]=> string(4) "keys" ["length"]=> int(1) ["explode"]=> bool(false) } } } } }--TEST-- uri_template() invalid expression - error 9 "{$var}" --FILE-- <?php $data = array( "id" => "thing", "var" => "value", "hello" => "Hello World!", "empty" => "", "path" => "/foo/bar", "x" => "1024", "y" => "768", "list" => array("red", "green", "blue"), "keys" => array( "semi" => ";", "dot" => ".", "comma" => "," ), "example" => "red", "searchTerms" => "uri templates" ); uri_template('{$var}', $data, $result); var_dump($result); ?> --EXPECT-- array(3) { ["result"]=> string(6) "{$var}" ["state"]=> int(3) ["expressions"]=> array(1) { [0]=> array(7) { ["op"]=> string(0) "" ["sep"]=> string(1) "," ["ifemp"]=> string(0) "" ["allow"]=> bool(false) ["named"]=> bool(false) ["error"]=> bool(true) ["vars"]=> array(1) { [0]=> array(3) { ["name"]=> string(4) "$var" ["length"]=> int(0) ["explode"]=> bool(false) } } } } }--TEST-- uri_template() expansion 8 - form style query continuation tests --FILE-- <?php $data = array( 'count' => array("one", "two", "three"), 'dom' => array("example", "com"), 'dub' => "me/too", 'hello' => "Hello World!", 'half' => "50%", 'var' => "value", 'who' => "fred", 'base' => "http://example.com/home/", 'path' => "/foo/bar", 'list' => array("red", "green", "blue"), 'keys' => array( "semi" => ";", "dot" => ".", "comma" => ",", ), 'v' => "6", 'x' => "1024", 'y' => "768", 'empty' => "", 'empty_keys' => array(), 'undef' => null, ); $templates = array( "{&who}" => "&who=fred", "{&half}" => "&half=50%25", "?fixed=yes{&x}" => "?fixed=yes&x=1024", "{&x,y,empty}" => "&x=1024&y=768&empty=", "{&x,y,undef}" => "&x=1024&y=768", "{&var:3}" => "&var=val", "{&list}" => "&list=red,green,blue", "{&list*}" => "&list=red&list=green&list=blue", "{&keys}" => "&keys=semi,%3B,dot,.,comma,%2C", "{&keys*}" => "&semi=%3B&dot=.&comma=%2C" ); $out = array(); foreach ($templates as $tpl => $expect) { $result = NULL; uri_template($tpl, $data, $result); unset($result['expressions']); $out[] = $result; } var_dump($out); ?> --EXPECT-- array(10) { [0]=> array(2) { ["result"]=> string(9) "&who=fred" ["state"]=> int(0) } [1]=> array(2) { ["result"]=> string(11) "&half=50%25" ["state"]=> int(0) } [2]=> array(2) { ["result"]=> string(17) "?fixed=yes&x=1024" ["state"]=> int(0) } [3]=> array(2) { ["result"]=> string(20) "&x=1024&y=768&empty=" ["state"]=> int(0) } [4]=> array(2) { ["result"]=> string(13) "&x=1024&y=768" ["state"]=> int(0) } [5]=> array(2) { ["result"]=> string(8) "&var=val" ["state"]=> int(0) } [6]=> array(2) { ["result"]=> string(20) "&list=red,green,blue" ["state"]=> int(0) } [7]=> array(2) { ["result"]=> string(30) "&list=red&list=green&list=blue" ["state"]=> int(0) } [8]=> array(2) { ["result"]=> string(30) "&keys=semi,%3B,dot,.,comma,%2C" ["state"]=> int(0) } [9]=> array(2) { ["result"]=> string(25) "&semi=%3B&dot=.&comma=%2C" ["state"]=> int(0) } }--TEST-- uri_template() level 3 expansion - form style query continuation --FILE-- <?php $data = array( "var" => "value", "hello" => "Hello World!", "empty" => "", "path" => "/foo/bar", "x" => "1024", "y" => "768" ); $templates = array( "?fixed=yes{&x}" => "?fixed=yes&x=1024", "{&x,y,empty}" => "&x=1024&y=768&empty=" ); $out = array(); foreach ($templates as $tpl => $expect) { $result = NULL; uri_template($tpl, $data, $result); unset($result['expressions']); $out[] = $result; } var_dump($out); ?> --EXPECT-- array(2) { [0]=> array(2) { ["result"]=> string(17) "?fixed=yes&x=1024" ["state"]=> int(0) } [1]=> array(2) { ["result"]=> string(20) "&x=1024&y=768&empty=" ["state"]=> int(0) } }--TEST-- uri_template() invalid expression - error 2 "/id*}" --FILE-- <?php $data = array( "id" => "thing", "var" => "value", "hello" => "Hello World!", "empty" => "", "path" => "/foo/bar", "x" => "1024", "y" => "768", "list" => array("red", "green", "blue"), "keys" => array( "semi" => ";", "dot" => ".", "comma" => "," ), "example" => "red", "searchTerms" => "uri templates" ); uri_template("/id*}", $data, $result); var_dump($result); ?> --EXPECT-- array(3) { ["result"]=> string(5) "/id*}" ["state"]=> int(2) ["expressions"]=> array(0) { } }--TEST-- uri_template() query parameters test - example 1 two query parameters --FILE-- <?php uri_template('http://www.example.com/foo{?query,number}', array( "query" => "mycelium", "number" => 100 ), $result); unset($result['expressions']); var_dump($result); ?> --EXPECT-- array(2) { ["result"]=> string(52) "http://www.example.com/foo?query=mycelium&number=100" ["state"]=> int(0) }--TEST-- uri_template() level 4 expansion - Additional Examples 1 --FILE-- <?php $data = array( "id" => "person", "token" => "12345", "fields" => array("id", "name", "picture"), "format" => "json", "q" => "URI Templates", "page" => "5", "lang" => "en", "geocode" => array("37.76","-122.427"), "first_name" => "John", "last.name" => "Doe", "Some%20Thing" => "foo", "number" => 6, "long" => 37.76, "lat" => -122.427 ); $templates = array( "{/id*}" => "/person", "{/id*}{?fields,first_name,last.name,token}" => "/person?fields=id,name,picture&first_name=John&last.name=Doe&token=12345", "/search.{format}{?q,geocode,lang,locale,page,result_type}" => "/search.json?q=URI%20Templates&geocode=37.76,-122.427&lang=en&page=5", "/test{/Some%20Thing}" => "/test/foo", "/set{?number}" => "/set?number=6", "/loc{?long,lat}" => "/loc?long=37.76&lat=-122.427", ); $out = array(); foreach ($templates as $tpl => $expect) { $result = NULL; uri_template($tpl, $data, $result); unset($result['expressions']); $out[] = $result; } var_dump($out); ?> --EXPECT-- array(6) { [0]=> array(2) { ["result"]=> string(7) "/person" ["state"]=> int(0) } [1]=> array(2) { ["result"]=> string(72) "/person?fields=id,name,picture&first_name=John&last.name=Doe&token=12345" ["state"]=> int(0) } [2]=> array(2) { ["result"]=> string(68) "/search.json?q=URI%20Templates&geocode=37.76,-122.427&lang=en&page=5" ["state"]=> int(0) } [3]=> array(2) { ["result"]=> string(9) "/test/foo" ["state"]=> int(0) } [4]=> array(2) { ["result"]=> string(13) "/set?number=6" ["state"]=> int(0) } [5]=> array(2) { ["result"]=> string(28) "/loc?long=37.76&lat=-122.427" ["state"]=> int(0) } }--TEST-- uri_template() level 4 expansion - string expansion with value modifiers --FILE-- <?php $data = array( "var" => "value", "hello" => "Hello World!", "path" => "/foo/bar", "list" => array("red", "green", "blue"), "keys" => array( "semi" => ";", "dot" => ".", "comma" => "," ) ); $templates = array( "{var:3}" => "val", "{var:30}" => "value", "{list}" => "red,green,blue", "{list*}" => "red,green,blue", "{keys}" => "semi,%3B,dot,.,comma,%2C", "{keys*}" => "semi=%3B,dot=.,comma=%2C" ); $out = array(); foreach ($templates as $tpl => $expect) { $result = NULL; uri_template($tpl, $data, $result); unset($result['expressions']); $out[] = $result; } var_dump($out); ?> --EXPECT-- array(6) { [0]=> array(2) { ["result"]=> string(3) "val" ["state"]=> int(0) } [1]=> array(2) { ["result"]=> string(5) "value" ["state"]=> int(0) } [2]=> array(2) { ["result"]=> string(14) "red,green,blue" ["state"]=> int(0) } [3]=> array(2) { ["result"]=> string(14) "red,green,blue" ["state"]=> int(0) } [4]=> array(2) { ["result"]=> string(24) "semi,%3B,dot,.,comma,%2C" ["state"]=> int(0) } [5]=> array(2) { ["result"]=> string(24) "semi=%3B,dot=.,comma=%2C" ["state"]=> int(0) } }--TEST-- uri_template() invalid expression - error 20 "{+keys:1}" --FILE-- <?php $data = array( "id" => "thing", "var" => "value", "hello" => "Hello World!", "empty" => "", "path" => "/foo/bar", "x" => "1024", "y" => "768", "list" => array("red", "green", "blue"), "keys" => array( "semi" => ";", "dot" => ".", "comma" => "," ), "example" => "red", "searchTerms" => "uri templates" ); uri_template('{+keys:1}', $data, $result); var_dump($result); ?> --EXPECT-- array(3) { ["result"]=> string(20) "semi,;,dot,.,comma,," ["state"]=> int(0) ["expressions"]=> array(1) { [0]=> array(7) { ["op"]=> string(1) "+" ["sep"]=> string(1) "," ["ifemp"]=> string(0) "" ["allow"]=> bool(true) ["named"]=> bool(false) ["error"]=> bool(false) ["vars"]=> array(1) { [0]=> array(3) { ["name"]=> string(4) "keys" ["length"]=> int(1) ["explode"]=> bool(false) } } } } }--TEST-- uri_template() query parameters test - example 2 first parameter only --FILE-- <?php uri_template('http://www.example.com/foo{?query,number}', array( "query" => "mycelium" ), $result); unset($result['expressions']); var_dump($result); ?> --EXPECT-- array(2) { ["result"]=> string(41) "http://www.example.com/foo?query=mycelium" ["state"]=> int(0) }--TEST-- uri_template() level 4 expansion - Additional Examples 2 --FILE-- <?php $data = array( "id" => array("person","albums"), "token" => "12345", "fields" => array("id", "name", "picture"), "format" => "atom", "q" => "URI Templates", "page" => "10", "start" => "5", "lang" => "en", "geocode" => array("37.76","-122.427") ); $templates = array( "{/id*}" => "/person/albums", "{/id*}{?fields,token}" => "/person/albums?fields=id,name,picture&token=12345" ); $out = array(); foreach ($templates as $tpl => $expect) { $result = NULL; uri_template($tpl, $data, $result); unset($result['expressions']); $out[] = $result; } var_dump($out); ?> --EXPECT-- array(2) { [0]=> array(2) { ["result"]=> string(14) "/person/albums" ["state"]=> int(0) } [1]=> array(2) { ["result"]=> string(49) "/person/albums?fields=id,name,picture&token=12345" ["state"]=> int(0) } }--TEST-- uri_template() level 4 expansion - plus operator and reserved chars with value modifiers --FILE-- <?php $data = array( "var" => "value", "hello" => "Hello World!", "path" => "/foo/bar", "list" => array("red", "green", "blue"), "keys" => array( "semi" => ";", "dot" => ".", "comma" => "," ) ); $templates = array( "{+path:6}/here" => "/foo/b/here", "{+list}" => "red,green,blue", "{+list*}" => "red,green,blue", "{+keys}" => "semi,;,dot,.,comma,,", "{+keys*}" => "semi=;,dot=.,comma=," ); $out = array(); foreach ($templates as $tpl => $expect) { $result = NULL; uri_template($tpl, $data, $result); unset($result['expressions']); $out[] = $result; } var_dump($out); ?> --EXPECT-- array(5) { [0]=> array(2) { ["result"]=> string(11) "/foo/b/here" ["state"]=> int(0) } [1]=> array(2) { ["result"]=> string(14) "red,green,blue" ["state"]=> int(0) } [2]=> array(2) { ["result"]=> string(14) "red,green,blue" ["state"]=> int(0) } [3]=> array(2) { ["result"]=> string(20) "semi,;,dot,.,comma,," ["state"]=> int(0) } [4]=> array(2) { ["result"]=> string(20) "semi=;,dot=.,comma=," ["state"]=> int(0) } }§¥}ć… ‰e'°ÃK"NÚ$Œ���GBMB