anxietypb
8 years ago
169 changed files with 2 additions and 16633 deletions
-
3.gitignore
-
7vendor/autoload.php
-
415vendor/composer/ClassLoader.php
-
21vendor/composer/LICENSE
-
9vendor/composer/autoload_classmap.php
-
10vendor/composer/autoload_files.php
-
10vendor/composer/autoload_namespaces.php
-
13vendor/composer/autoload_psr4.php
-
70vendor/composer/autoload_real.php
-
70vendor/composer/autoload_static.php
-
248vendor/composer/installed.json
-
3vendor/container-interop/container-interop/.gitignore
-
20vendor/container-interop/container-interop/LICENSE
-
85vendor/container-interop/container-interop/README.md
-
11vendor/container-interop/container-interop/composer.json
-
114vendor/container-interop/container-interop/docs/ContainerInterface-meta.md
-
153vendor/container-interop/container-interop/docs/ContainerInterface.md
-
259vendor/container-interop/container-interop/docs/Delegate-lookup-meta.md
-
60vendor/container-interop/container-interop/docs/Delegate-lookup.md
-
BINvendor/container-interop/container-interop/docs/images/interoperating_containers.png
-
BINvendor/container-interop/container-interop/docs/images/priority.png
-
BINvendor/container-interop/container-interop/docs/images/side_by_side_containers.png
-
37vendor/container-interop/container-interop/src/Interop/Container/ContainerInterface.php
-
13vendor/container-interop/container-interop/src/Interop/Container/Exception/ContainerException.php
-
13vendor/container-interop/container-interop/src/Interop/Container/Exception/NotFoundException.php
-
1vendor/nikic/fast-route/.hhconfig
-
12vendor/nikic/fast-route/.travis.yml
-
126vendor/nikic/fast-route/FastRoute.hhi
-
31vendor/nikic/fast-route/LICENSE
-
273vendor/nikic/fast-route/README.md
-
21vendor/nikic/fast-route/composer.json
-
24vendor/nikic/fast-route/phpunit.xml
-
6vendor/nikic/fast-route/src/BadRouteException.php
-
25vendor/nikic/fast-route/src/DataGenerator.php
-
28vendor/nikic/fast-route/src/DataGenerator/CharCountBased.php
-
28vendor/nikic/fast-route/src/DataGenerator/GroupCountBased.php
-
25vendor/nikic/fast-route/src/DataGenerator/GroupPosBased.php
-
25vendor/nikic/fast-route/src/DataGenerator/MarkBased.php
-
144vendor/nikic/fast-route/src/DataGenerator/RegexBasedAbstract.php
-
25vendor/nikic/fast-route/src/Dispatcher.php
-
28vendor/nikic/fast-route/src/Dispatcher/CharCountBased.php
-
28vendor/nikic/fast-route/src/Dispatcher/GroupCountBased.php
-
30vendor/nikic/fast-route/src/Dispatcher/GroupPosBased.php
-
28vendor/nikic/fast-route/src/Dispatcher/MarkBased.php
-
80vendor/nikic/fast-route/src/Dispatcher/RegexBasedAbstract.php
-
38vendor/nikic/fast-route/src/Route.php
-
46vendor/nikic/fast-route/src/RouteCollector.php
-
36vendor/nikic/fast-route/src/RouteParser.php
-
81vendor/nikic/fast-route/src/RouteParser/Std.php
-
12vendor/nikic/fast-route/src/bootstrap.php
-
70vendor/nikic/fast-route/src/functions.php
-
13vendor/nikic/fast-route/test/Dispatcher/CharCountBasedTest.php
-
561vendor/nikic/fast-route/test/Dispatcher/DispatcherTest.php
-
13vendor/nikic/fast-route/test/Dispatcher/GroupCountBasedTest.php
-
13vendor/nikic/fast-route/test/Dispatcher/GroupPosBasedTest.php
-
20vendor/nikic/fast-route/test/Dispatcher/MarkBasedTest.php
-
39vendor/nikic/fast-route/test/HackTypechecker/HackTypecheckerTest.php
-
29vendor/nikic/fast-route/test/HackTypechecker/fixtures/all_options.php
-
11vendor/nikic/fast-route/test/HackTypechecker/fixtures/empty_options.php
-
11vendor/nikic/fast-route/test/HackTypechecker/fixtures/no_options.php
-
147vendor/nikic/fast-route/test/RouteParser/StdTest.php
-
11vendor/nikic/fast-route/test/bootstrap.php
-
3vendor/pimple/pimple/.gitignore
-
32vendor/pimple/pimple/.travis.yml
-
35vendor/pimple/pimple/CHANGELOG
-
19vendor/pimple/pimple/LICENSE
-
201vendor/pimple/pimple/README.rst
-
25vendor/pimple/pimple/composer.json
-
30vendor/pimple/pimple/ext/pimple/.gitignore
-
12vendor/pimple/pimple/ext/pimple/README.md
-
63vendor/pimple/pimple/ext/pimple/config.m4
-
13vendor/pimple/pimple/ext/pimple/config.w32
-
121vendor/pimple/pimple/ext/pimple/php_pimple.h
-
922vendor/pimple/pimple/ext/pimple/pimple.c
-
81vendor/pimple/pimple/ext/pimple/pimple_compat.h
-
45vendor/pimple/pimple/ext/pimple/tests/001.phpt
-
15vendor/pimple/pimple/ext/pimple/tests/002.phpt
-
16vendor/pimple/pimple/ext/pimple/tests/003.phpt
-
30vendor/pimple/pimple/ext/pimple/tests/004.phpt
-
27vendor/pimple/pimple/ext/pimple/tests/005.phpt
-
51vendor/pimple/pimple/ext/pimple/tests/006.phpt
-
22vendor/pimple/pimple/ext/pimple/tests/007.phpt
-
29vendor/pimple/pimple/ext/pimple/tests/008.phpt
-
13vendor/pimple/pimple/ext/pimple/tests/009.phpt
-
45vendor/pimple/pimple/ext/pimple/tests/010.phpt
-
19vendor/pimple/pimple/ext/pimple/tests/011.phpt
-
28vendor/pimple/pimple/ext/pimple/tests/012.phpt
-
33vendor/pimple/pimple/ext/pimple/tests/013.phpt
-
30vendor/pimple/pimple/ext/pimple/tests/014.phpt
-
17vendor/pimple/pimple/ext/pimple/tests/015.phpt
-
24vendor/pimple/pimple/ext/pimple/tests/016.phpt
-
17vendor/pimple/pimple/ext/pimple/tests/017.phpt
-
17vendor/pimple/pimple/ext/pimple/tests/017_1.phpt
-
23vendor/pimple/pimple/ext/pimple/tests/018.phpt
-
18vendor/pimple/pimple/ext/pimple/tests/019.phpt
-
51vendor/pimple/pimple/ext/pimple/tests/bench.phpb
-
25vendor/pimple/pimple/ext/pimple/tests/bench_shared.phpb
-
14vendor/pimple/pimple/phpunit.xml.dist
-
282vendor/pimple/pimple/src/Pimple/Container.php
-
46vendor/pimple/pimple/src/Pimple/ServiceProviderInterface.php
@ -1,7 +0,0 @@ |
|||
<?php |
|||
|
|||
// autoload.php @generated by Composer
|
|||
|
|||
require_once __DIR__ . '/composer' . '/autoload_real.php'; |
|||
|
|||
return ComposerAutoloaderInitd54f12fb5bd1b34598360c61119b8df4::getLoader(); |
@ -1,415 +0,0 @@ |
|||
<?php |
|||
|
|||
/* |
|||
* This file is part of Composer. |
|||
* |
|||
* (c) Nils Adermann <naderman@naderman.de> |
|||
* Jordi Boggiano <j.boggiano@seld.be> |
|||
* |
|||
* For the full copyright and license information, please view the LICENSE |
|||
* file that was distributed with this source code. |
|||
*/ |
|||
|
|||
namespace Composer\Autoload; |
|||
|
|||
/** |
|||
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader. |
|||
* |
|||
* $loader = new \Composer\Autoload\ClassLoader(); |
|||
* |
|||
* // register classes with namespaces
|
|||
* $loader->add('Symfony\Component', __DIR__.'/component'); |
|||
* $loader->add('Symfony', __DIR__.'/framework'); |
|||
* |
|||
* // activate the autoloader
|
|||
* $loader->register(); |
|||
* |
|||
* // to enable searching the include path (eg. for PEAR packages)
|
|||
* $loader->setUseIncludePath(true); |
|||
* |
|||
* In this example, if you try to use a class in the Symfony\Component |
|||
* namespace or one of its children (Symfony\Component\Console for instance), |
|||
* the autoloader will first look for the class under the component/ |
|||
* directory, and it will then fallback to the framework/ directory if not |
|||
* found before giving up. |
|||
* |
|||
* This class is loosely based on the Symfony UniversalClassLoader. |
|||
* |
|||
* @author Fabien Potencier <fabien@symfony.com> |
|||
* @author Jordi Boggiano <j.boggiano@seld.be> |
|||
* @see http://www.php-fig.org/psr/psr-0/ |
|||
* @see http://www.php-fig.org/psr/psr-4/ |
|||
*/ |
|||
class ClassLoader |
|||
{ |
|||
// PSR-4
|
|||
private $prefixLengthsPsr4 = array(); |
|||
private $prefixDirsPsr4 = array(); |
|||
private $fallbackDirsPsr4 = array(); |
|||
|
|||
// PSR-0
|
|||
private $prefixesPsr0 = array(); |
|||
private $fallbackDirsPsr0 = array(); |
|||
|
|||
private $useIncludePath = false; |
|||
private $classMap = array(); |
|||
private $classMapAuthoritative = false; |
|||
private $missingClasses = array(); |
|||
|
|||
public function getPrefixes() |
|||
{ |
|||
if (!empty($this->prefixesPsr0)) { |
|||
return call_user_func_array('array_merge', $this->prefixesPsr0); |
|||
} |
|||
|
|||
return array(); |
|||
} |
|||
|
|||
public function getPrefixesPsr4() |
|||
{ |
|||
return $this->prefixDirsPsr4; |
|||
} |
|||
|
|||
public function getFallbackDirs() |
|||
{ |
|||
return $this->fallbackDirsPsr0; |
|||
} |
|||
|
|||
public function getFallbackDirsPsr4() |
|||
{ |
|||
return $this->fallbackDirsPsr4; |
|||
} |
|||
|
|||
public function getClassMap() |
|||
{ |
|||
return $this->classMap; |
|||
} |
|||
|
|||
/** |
|||
* @param array $classMap Class to filename map |
|||
*/ |
|||
public function addClassMap(array $classMap) |
|||
{ |
|||
if ($this->classMap) { |
|||
$this->classMap = array_merge($this->classMap, $classMap); |
|||
} else { |
|||
$this->classMap = $classMap; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Registers a set of PSR-0 directories for a given prefix, either |
|||
* appending or prepending to the ones previously set for this prefix. |
|||
* |
|||
* @param string $prefix The prefix |
|||
* @param array|string $paths The PSR-0 root directories |
|||
* @param bool $prepend Whether to prepend the directories |
|||
*/ |
|||
public function add($prefix, $paths, $prepend = false) |
|||
{ |
|||
if (!$prefix) { |
|||
if ($prepend) { |
|||
$this->fallbackDirsPsr0 = array_merge( |
|||
(array) $paths, |
|||
$this->fallbackDirsPsr0 |
|||
); |
|||
} else { |
|||
$this->fallbackDirsPsr0 = array_merge( |
|||
$this->fallbackDirsPsr0, |
|||
(array) $paths |
|||
); |
|||
} |
|||
|
|||
return; |
|||
} |
|||
|
|||
$first = $prefix[0]; |
|||
if (!isset($this->prefixesPsr0[$first][$prefix])) { |
|||
$this->prefixesPsr0[$first][$prefix] = (array) $paths; |
|||
|
|||
return; |
|||
} |
|||
if ($prepend) { |
|||
$this->prefixesPsr0[$first][$prefix] = array_merge( |
|||
(array) $paths, |
|||
$this->prefixesPsr0[$first][$prefix] |
|||
); |
|||
} else { |
|||
$this->prefixesPsr0[$first][$prefix] = array_merge( |
|||
$this->prefixesPsr0[$first][$prefix], |
|||
(array) $paths |
|||
); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Registers a set of PSR-4 directories for a given namespace, either |
|||
* appending or prepending to the ones previously set for this namespace. |
|||
* |
|||
* @param string $prefix The prefix/namespace, with trailing '\\' |
|||
* @param array|string $paths The PSR-4 base directories |
|||
* @param bool $prepend Whether to prepend the directories |
|||
* |
|||
* @throws \InvalidArgumentException |
|||
*/ |
|||
public function addPsr4($prefix, $paths, $prepend = false) |
|||
{ |
|||
if (!$prefix) { |
|||
// Register directories for the root namespace.
|
|||
if ($prepend) { |
|||
$this->fallbackDirsPsr4 = array_merge( |
|||
(array) $paths, |
|||
$this->fallbackDirsPsr4 |
|||
); |
|||
} else { |
|||
$this->fallbackDirsPsr4 = array_merge( |
|||
$this->fallbackDirsPsr4, |
|||
(array) $paths |
|||
); |
|||
} |
|||
} elseif (!isset($this->prefixDirsPsr4[$prefix])) { |
|||
// Register directories for a new namespace.
|
|||
$length = strlen($prefix); |
|||
if ('\\' !== $prefix[$length - 1]) { |
|||
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); |
|||
} |
|||
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; |
|||
$this->prefixDirsPsr4[$prefix] = (array) $paths; |
|||
} elseif ($prepend) { |
|||
// Prepend directories for an already registered namespace.
|
|||
$this->prefixDirsPsr4[$prefix] = array_merge( |
|||
(array) $paths, |
|||
$this->prefixDirsPsr4[$prefix] |
|||
); |
|||
} else { |
|||
// Append directories for an already registered namespace.
|
|||
$this->prefixDirsPsr4[$prefix] = array_merge( |
|||
$this->prefixDirsPsr4[$prefix], |
|||
(array) $paths |
|||
); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Registers a set of PSR-0 directories for a given prefix, |
|||
* replacing any others previously set for this prefix. |
|||
* |
|||
* @param string $prefix The prefix |
|||
* @param array|string $paths The PSR-0 base directories |
|||
*/ |
|||
public function set($prefix, $paths) |
|||
{ |
|||
if (!$prefix) { |
|||
$this->fallbackDirsPsr0 = (array) $paths; |
|||
} else { |
|||
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Registers a set of PSR-4 directories for a given namespace, |
|||
* replacing any others previously set for this namespace. |
|||
* |
|||
* @param string $prefix The prefix/namespace, with trailing '\\' |
|||
* @param array|string $paths The PSR-4 base directories |
|||
* |
|||
* @throws \InvalidArgumentException |
|||
*/ |
|||
public function setPsr4($prefix, $paths) |
|||
{ |
|||
if (!$prefix) { |
|||
$this->fallbackDirsPsr4 = (array) $paths; |
|||
} else { |
|||
$length = strlen($prefix); |
|||
if ('\\' !== $prefix[$length - 1]) { |
|||
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); |
|||
} |
|||
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; |
|||
$this->prefixDirsPsr4[$prefix] = (array) $paths; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Turns on searching the include path for class files. |
|||
* |
|||
* @param bool $useIncludePath |
|||
*/ |
|||
public function setUseIncludePath($useIncludePath) |
|||
{ |
|||
$this->useIncludePath = $useIncludePath; |
|||
} |
|||
|
|||
/** |
|||
* Can be used to check if the autoloader uses the include path to check |
|||
* for classes. |
|||
* |
|||
* @return bool |
|||
*/ |
|||
public function getUseIncludePath() |
|||
{ |
|||
return $this->useIncludePath; |
|||
} |
|||
|
|||
/** |
|||
* Turns off searching the prefix and fallback directories for classes |
|||
* that have not been registered with the class map. |
|||
* |
|||
* @param bool $classMapAuthoritative |
|||
*/ |
|||
public function setClassMapAuthoritative($classMapAuthoritative) |
|||
{ |
|||
$this->classMapAuthoritative = $classMapAuthoritative; |
|||
} |
|||
|
|||
/** |
|||
* Should class lookup fail if not found in the current class map? |
|||
* |
|||
* @return bool |
|||
*/ |
|||
public function isClassMapAuthoritative() |
|||
{ |
|||
return $this->classMapAuthoritative; |
|||
} |
|||
|
|||
/** |
|||
* Registers this instance as an autoloader. |
|||
* |
|||
* @param bool $prepend Whether to prepend the autoloader or not |
|||
*/ |
|||
public function register($prepend = false) |
|||
{ |
|||
spl_autoload_register(array($this, 'loadClass'), true, $prepend); |
|||
} |
|||
|
|||
/** |
|||
* Unregisters this instance as an autoloader. |
|||
*/ |
|||
public function unregister() |
|||
{ |
|||
spl_autoload_unregister(array($this, 'loadClass')); |
|||
} |
|||
|
|||
/** |
|||
* Loads the given class or interface. |
|||
* |
|||
* @param string $class The name of the class |
|||
* @return bool|null True if loaded, null otherwise |
|||
*/ |
|||
public function loadClass($class) |
|||
{ |
|||
if ($file = $this->findFile($class)) { |
|||
includeFile($file); |
|||
|
|||
return true; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Finds the path to the file where the class is defined. |
|||
* |
|||
* @param string $class The name of the class |
|||
* |
|||
* @return string|false The path if found, false otherwise |
|||
*/ |
|||
public function findFile($class) |
|||
{ |
|||
// work around for PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731
|
|||
if ('\\' == $class[0]) { |
|||
$class = substr($class, 1); |
|||
} |
|||
|
|||
// class map lookup
|
|||
if (isset($this->classMap[$class])) { |
|||
return $this->classMap[$class]; |
|||
} |
|||
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) { |
|||
return false; |
|||
} |
|||
|
|||
$file = $this->findFileWithExtension($class, '.php'); |
|||
|
|||
// Search for Hack files if we are running on HHVM
|
|||
if (false === $file && defined('HHVM_VERSION')) { |
|||
$file = $this->findFileWithExtension($class, '.hh'); |
|||
} |
|||
|
|||
if (false === $file) { |
|||
// Remember that this class does not exist.
|
|||
$this->missingClasses[$class] = true; |
|||
} |
|||
|
|||
return $file; |
|||
} |
|||
|
|||
private function findFileWithExtension($class, $ext) |
|||
{ |
|||
// PSR-4 lookup
|
|||
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; |
|||
|
|||
$first = $class[0]; |
|||
if (isset($this->prefixLengthsPsr4[$first])) { |
|||
foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) { |
|||
if (0 === strpos($class, $prefix)) { |
|||
foreach ($this->prefixDirsPsr4[$prefix] as $dir) { |
|||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) { |
|||
return $file; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
// PSR-4 fallback dirs
|
|||
foreach ($this->fallbackDirsPsr4 as $dir) { |
|||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { |
|||
return $file; |
|||
} |
|||
} |
|||
|
|||
// PSR-0 lookup
|
|||
if (false !== $pos = strrpos($class, '\\')) { |
|||
// namespaced class name
|
|||
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) |
|||
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); |
|||
} else { |
|||
// PEAR-like class name
|
|||
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; |
|||
} |
|||
|
|||
if (isset($this->prefixesPsr0[$first])) { |
|||
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { |
|||
if (0 === strpos($class, $prefix)) { |
|||
foreach ($dirs as $dir) { |
|||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { |
|||
return $file; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
// PSR-0 fallback dirs
|
|||
foreach ($this->fallbackDirsPsr0 as $dir) { |
|||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { |
|||
return $file; |
|||
} |
|||
} |
|||
|
|||
// PSR-0 include paths.
|
|||
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { |
|||
return $file; |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Scope isolated include. |
|||
* |
|||
* Prevents access to $this/self from included files. |
|||
*/ |
|||
function includeFile($file) |
|||
{ |
|||
include $file; |
|||
} |
@ -1,21 +0,0 @@ |
|||
|
|||
Copyright (c) 2016 Nils Adermann, Jordi Boggiano |
|||
|
|||
Permission is hereby granted, free of charge, to any person obtaining a copy |
|||
of this software and associated documentation files (the "Software"), to deal |
|||
in the Software without restriction, including without limitation the rights |
|||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|||
copies of the Software, and to permit persons to whom the Software is furnished |
|||
to do so, subject to the following conditions: |
|||
|
|||
The above copyright notice and this permission notice shall be included in all |
|||
copies or substantial portions of the Software. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
|||
THE SOFTWARE. |
|||
|
@ -1,9 +0,0 @@ |
|||
<?php |
|||
|
|||
// autoload_classmap.php @generated by Composer
|
|||
|
|||
$vendorDir = dirname(dirname(__FILE__)); |
|||
$baseDir = dirname($vendorDir); |
|||
|
|||
return array( |
|||
); |
@ -1,10 +0,0 @@ |
|||
<?php |
|||
|
|||
// autoload_files.php @generated by Composer
|
|||
|
|||
$vendorDir = dirname(dirname(__FILE__)); |
|||
$baseDir = dirname($vendorDir); |
|||
|
|||
return array( |
|||
'253c157292f75eb38082b5acb06f3f01' => $vendorDir . '/nikic/fast-route/src/functions.php', |
|||
); |
@ -1,10 +0,0 @@ |
|||
<?php |
|||
|
|||
// autoload_namespaces.php @generated by Composer
|
|||
|
|||
$vendorDir = dirname(dirname(__FILE__)); |
|||
$baseDir = dirname($vendorDir); |
|||
|
|||
return array( |
|||
'Pimple' => array($vendorDir . '/pimple/pimple/src'), |
|||
); |
@ -1,13 +0,0 @@ |
|||
<?php |
|||
|
|||
// autoload_psr4.php @generated by Composer
|
|||
|
|||
$vendorDir = dirname(dirname(__FILE__)); |
|||
$baseDir = dirname($vendorDir); |
|||
|
|||
return array( |
|||
'Slim\\' => array($vendorDir . '/slim/slim/Slim'), |
|||
'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-message/src'), |
|||
'Interop\\Container\\' => array($vendorDir . '/container-interop/container-interop/src/Interop/Container'), |
|||
'FastRoute\\' => array($vendorDir . '/nikic/fast-route/src'), |
|||
); |
@ -1,70 +0,0 @@ |
|||
<?php |
|||
|
|||
// autoload_real.php @generated by Composer
|
|||
|
|||
class ComposerAutoloaderInitd54f12fb5bd1b34598360c61119b8df4 |
|||
{ |
|||
private static $loader; |
|||
|
|||
public static function loadClassLoader($class) |
|||
{ |
|||
if ('Composer\Autoload\ClassLoader' === $class) { |
|||
require __DIR__ . '/ClassLoader.php'; |
|||
} |
|||
} |
|||
|
|||
public static function getLoader() |
|||
{ |
|||
if (null !== self::$loader) { |
|||
return self::$loader; |
|||
} |
|||
|
|||
spl_autoload_register(array('ComposerAutoloaderInitd54f12fb5bd1b34598360c61119b8df4', 'loadClassLoader'), true, true); |
|||
self::$loader = $loader = new \Composer\Autoload\ClassLoader(); |
|||
spl_autoload_unregister(array('ComposerAutoloaderInitd54f12fb5bd1b34598360c61119b8df4', 'loadClassLoader')); |
|||
|
|||
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION'); |
|||
if ($useStaticLoader) { |
|||
require_once __DIR__ . '/autoload_static.php'; |
|||
|
|||
call_user_func(\Composer\Autoload\ComposerStaticInitd54f12fb5bd1b34598360c61119b8df4::getInitializer($loader)); |
|||
} else { |
|||
$map = require __DIR__ . '/autoload_namespaces.php'; |
|||
foreach ($map as $namespace => $path) { |
|||
$loader->set($namespace, $path); |
|||
} |
|||
|
|||
$map = require __DIR__ . '/autoload_psr4.php'; |
|||
foreach ($map as $namespace => $path) { |
|||
$loader->setPsr4($namespace, $path); |
|||
} |
|||
|
|||
$classMap = require __DIR__ . '/autoload_classmap.php'; |
|||
if ($classMap) { |
|||
$loader->addClassMap($classMap); |
|||
} |
|||
} |
|||
|
|||
$loader->register(true); |
|||
|
|||
if ($useStaticLoader) { |
|||
$includeFiles = Composer\Autoload\ComposerStaticInitd54f12fb5bd1b34598360c61119b8df4::$files; |
|||
} else { |
|||
$includeFiles = require __DIR__ . '/autoload_files.php'; |
|||
} |
|||
foreach ($includeFiles as $fileIdentifier => $file) { |
|||
composerRequired54f12fb5bd1b34598360c61119b8df4($fileIdentifier, $file); |
|||
} |
|||
|
|||
return $loader; |
|||
} |
|||
} |
|||
|
|||
function composerRequired54f12fb5bd1b34598360c61119b8df4($fileIdentifier, $file) |
|||
{ |
|||
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { |
|||
require $file; |
|||
|
|||
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true; |
|||
} |
|||
} |
@ -1,70 +0,0 @@ |
|||
<?php |
|||
|
|||
// autoload_static.php @generated by Composer
|
|||
|
|||
namespace Composer\Autoload; |
|||
|
|||
class ComposerStaticInitd54f12fb5bd1b34598360c61119b8df4 |
|||
{ |
|||
public static $files = array ( |
|||
'253c157292f75eb38082b5acb06f3f01' => __DIR__ . '/..' . '/nikic/fast-route/src/functions.php', |
|||
); |
|||
|
|||
public static $prefixLengthsPsr4 = array ( |
|||
'S' => |
|||
array ( |
|||
'Slim\\' => 5, |
|||
), |
|||
'P' => |
|||
array ( |
|||
'Psr\\Http\\Message\\' => 17, |
|||
), |
|||
'I' => |
|||
array ( |
|||
'Interop\\Container\\' => 18, |
|||
), |
|||
'F' => |
|||
array ( |
|||
'FastRoute\\' => 10, |
|||
), |
|||
); |
|||
|
|||
public static $prefixDirsPsr4 = array ( |
|||
'Slim\\' => |
|||
array ( |
|||
0 => __DIR__ . '/..' . '/slim/slim/Slim', |
|||
), |
|||
'Psr\\Http\\Message\\' => |
|||
array ( |
|||
0 => __DIR__ . '/..' . '/psr/http-message/src', |
|||
), |
|||
'Interop\\Container\\' => |
|||
array ( |
|||
0 => __DIR__ . '/..' . '/container-interop/container-interop/src/Interop/Container', |
|||
), |
|||
'FastRoute\\' => |
|||
array ( |
|||
0 => __DIR__ . '/..' . '/nikic/fast-route/src', |
|||
), |
|||
); |
|||
|
|||
public static $prefixesPsr0 = array ( |
|||
'P' => |
|||
array ( |
|||
'Pimple' => |
|||
array ( |
|||
0 => __DIR__ . '/..' . '/pimple/pimple/src', |
|||
), |
|||
), |
|||
); |
|||
|
|||
public static function getInitializer(ClassLoader $loader) |
|||
{ |
|||
return \Closure::bind(function () use ($loader) { |
|||
$loader->prefixLengthsPsr4 = ComposerStaticInitd54f12fb5bd1b34598360c61119b8df4::$prefixLengthsPsr4; |
|||
$loader->prefixDirsPsr4 = ComposerStaticInitd54f12fb5bd1b34598360c61119b8df4::$prefixDirsPsr4; |
|||
$loader->prefixesPsr0 = ComposerStaticInitd54f12fb5bd1b34598360c61119b8df4::$prefixesPsr0; |
|||
|
|||
}, null, ClassLoader::class); |
|||
} |
|||
} |
@ -1,248 +0,0 @@ |
|||
[ |
|||
{ |
|||
"name": "container-interop/container-interop", |
|||
"version": "1.1.0", |
|||
"version_normalized": "1.1.0.0", |
|||
"source": { |
|||
"type": "git", |
|||
"url": "https://github.com/container-interop/container-interop.git", |
|||
"reference": "fc08354828f8fd3245f77a66b9e23a6bca48297e" |
|||
}, |
|||
"dist": { |
|||
"type": "zip", |
|||
"url": "https://api.github.com/repos/container-interop/container-interop/zipball/fc08354828f8fd3245f77a66b9e23a6bca48297e", |
|||
"reference": "fc08354828f8fd3245f77a66b9e23a6bca48297e", |
|||
"shasum": "" |
|||
}, |
|||
"time": "2014-12-30 15:22:37", |
|||
"type": "library", |
|||
"installation-source": "dist", |
|||
"autoload": { |
|||
"psr-4": { |
|||
"Interop\\Container\\": "src/Interop/Container/" |
|||
} |
|||
}, |
|||
"notification-url": "https://packagist.org/downloads/", |
|||
"license": [ |
|||
"MIT" |
|||
], |
|||
"description": "Promoting the interoperability of container objects (DIC, SL, etc.)" |
|||
}, |
|||
{ |
|||
"name": "nikic/fast-route", |
|||
"version": "v1.0.1", |
|||
"version_normalized": "1.0.1.0", |
|||
"source": { |
|||
"type": "git", |
|||
"url": "https://github.com/nikic/FastRoute.git", |
|||
"reference": "8ea928195fa9b907f0d6e48312d323c1a13cc2af" |
|||
}, |
|||
"dist": { |
|||
"type": "zip", |
|||
"url": "https://api.github.com/repos/nikic/FastRoute/zipball/8ea928195fa9b907f0d6e48312d323c1a13cc2af", |
|||
"reference": "8ea928195fa9b907f0d6e48312d323c1a13cc2af", |
|||
"shasum": "" |
|||
}, |
|||
"require": { |
|||
"php": ">=5.4.0" |
|||
}, |
|||
"time": "2016-06-12 19:08:51", |
|||
"type": "library", |
|||
"installation-source": "dist", |
|||
"autoload": { |
|||
"psr-4": { |
|||
"FastRoute\\": "src/" |
|||
}, |
|||
"files": [ |
|||
"src/functions.php" |
|||
] |
|||
}, |
|||
"notification-url": "https://packagist.org/downloads/", |
|||
"license": [ |
|||
"BSD-3-Clause" |
|||
], |
|||
"authors": [ |
|||
{ |
|||
"name": "Nikita Popov", |
|||
"email": "nikic@php.net" |
|||
} |
|||
], |
|||
"description": "Fast request router for PHP", |
|||
"keywords": [ |
|||
"router", |
|||
"routing" |
|||
] |
|||
}, |
|||
{ |
|||
"name": "psr/http-message", |
|||
"version": "1.0.1", |
|||
"version_normalized": "1.0.1.0", |
|||
"source": { |
|||
"type": "git", |
|||
"url": "https://github.com/php-fig/http-message.git", |
|||
"reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" |
|||
}, |
|||
"dist": { |
|||
"type": "zip", |
|||
"url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", |
|||
"reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", |
|||
"shasum": "" |
|||
}, |
|||
"require": { |
|||
"php": ">=5.3.0" |
|||
}, |
|||
"time": "2016-08-06 14:39:51", |
|||
"type": "library", |
|||
"extra": { |
|||
"branch-alias": { |
|||
"dev-master": "1.0.x-dev" |
|||
} |
|||
}, |
|||
"installation-source": "dist", |
|||
"autoload": { |
|||
"psr-4": { |
|||
"Psr\\Http\\Message\\": "src/" |
|||
} |
|||
}, |
|||
"notification-url": "https://packagist.org/downloads/", |
|||
"license": [ |
|||
"MIT" |
|||
], |
|||
"authors": [ |
|||
{ |
|||
"name": "PHP-FIG", |
|||
"homepage": "http://www.php-fig.org/" |
|||
} |
|||
], |
|||
"description": "Common interface for HTTP messages", |
|||
"homepage": "https://github.com/php-fig/http-message", |
|||
"keywords": [ |
|||
"http", |
|||
"http-message", |
|||
"psr", |
|||
"psr-7", |
|||
"request", |
|||
"response" |
|||
] |
|||
}, |
|||
{ |
|||
"name": "pimple/pimple", |
|||
"version": "v3.0.2", |
|||
"version_normalized": "3.0.2.0", |
|||
"source": { |
|||
"type": "git", |
|||
"url": "https://github.com/silexphp/Pimple.git", |
|||
"reference": "a30f7d6e57565a2e1a316e1baf2a483f788b258a" |
|||
}, |
|||
"dist": { |
|||
"type": "zip", |
|||
"url": "https://api.github.com/repos/silexphp/Pimple/zipball/a30f7d6e57565a2e1a316e1baf2a483f788b258a", |
|||
"reference": "a30f7d6e57565a2e1a316e1baf2a483f788b258a", |
|||
"shasum": "" |
|||
}, |
|||
"require": { |
|||
"php": ">=5.3.0" |
|||
}, |
|||
"time": "2015-09-11 15:10:35", |
|||
"type": "library", |
|||
"extra": { |
|||
"branch-alias": { |
|||
"dev-master": "3.0.x-dev" |
|||
} |
|||
}, |
|||
"installation-source": "dist", |
|||
"autoload": { |
|||
"psr-0": { |
|||
"Pimple": "src/" |
|||
} |
|||
}, |
|||
"notification-url": "https://packagist.org/downloads/", |
|||
"license": [ |
|||
"MIT" |
|||
], |
|||
"authors": [ |
|||
{ |
|||
"name": "Fabien Potencier", |
|||
"email": "fabien@symfony.com" |
|||
} |
|||
], |
|||
"description": "Pimple, a simple Dependency Injection Container", |
|||
"homepage": "http://pimple.sensiolabs.org", |
|||
"keywords": [ |
|||
"container", |
|||
"dependency injection" |
|||
] |
|||
}, |
|||
{ |
|||
"name": "slim/slim", |
|||
"version": "3.5.0", |
|||
"version_normalized": "3.5.0.0", |
|||
"source": { |
|||
"type": "git", |
|||
"url": "https://github.com/slimphp/Slim.git", |
|||
"reference": "184352bc1913d7ba552ab4131d62f4730ddb0893" |
|||
}, |
|||
"dist": { |
|||
"type": "zip", |
|||
"url": "https://api.github.com/repos/slimphp/Slim/zipball/184352bc1913d7ba552ab4131d62f4730ddb0893", |
|||
"reference": "184352bc1913d7ba552ab4131d62f4730ddb0893", |
|||
"shasum": "" |
|||
}, |
|||
"require": { |
|||
"container-interop/container-interop": "^1.1", |
|||
"nikic/fast-route": "^1.0", |
|||
"php": ">=5.5.0", |
|||
"pimple/pimple": "^3.0", |
|||
"psr/http-message": "^1.0" |
|||
}, |
|||
"provide": { |
|||
"psr/http-message-implementation": "1.0" |
|||
}, |
|||
"require-dev": { |
|||
"phpunit/phpunit": "^4.0", |
|||
"squizlabs/php_codesniffer": "^2.5" |
|||
}, |
|||
"time": "2016-07-26 15:12:13", |
|||
"type": "library", |
|||
"installation-source": "dist", |
|||
"autoload": { |
|||
"psr-4": { |
|||
"Slim\\": "Slim" |
|||
} |
|||
}, |
|||
"notification-url": "https://packagist.org/downloads/", |
|||
"license": [ |
|||
"MIT" |
|||
], |
|||
"authors": [ |
|||
{ |
|||
"name": "Rob Allen", |
|||
"email": "rob@akrabat.com", |
|||
"homepage": "http://akrabat.com" |
|||
}, |
|||
{ |
|||
"name": "Josh Lockhart", |
|||
"email": "hello@joshlockhart.com", |
|||
"homepage": "https://joshlockhart.com" |
|||
}, |
|||
{ |
|||
"name": "Gabriel Manricks", |
|||
"email": "gmanricks@me.com", |
|||
"homepage": "http://gabrielmanricks.com" |
|||
}, |
|||
{ |
|||
"name": "Andrew Smith", |
|||
"email": "a.smith@silentworks.co.uk", |
|||
"homepage": "http://silentworks.co.uk" |
|||
} |
|||
], |
|||
"description": "Slim is a PHP micro framework that helps you quickly write simple yet powerful web applications and APIs", |
|||
"homepage": "http://slimframework.com", |
|||
"keywords": [ |
|||
"api", |
|||
"framework", |
|||
"micro", |
|||
"router" |
|||
] |
|||
} |
|||
] |
@ -1,3 +0,0 @@ |
|||
composer.lock |
|||
composer.phar |
|||
/vendor/ |
@ -1,20 +0,0 @@ |
|||
The MIT License (MIT) |
|||
|
|||
Copyright (c) 2013 container-interop |
|||
|
|||
Permission is hereby granted, free of charge, to any person obtaining a copy of |
|||
this software and associated documentation files (the "Software"), to deal in |
|||
the Software without restriction, including without limitation the rights to |
|||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of |
|||
the Software, and to permit persons to whom the Software is furnished to do so, |
|||
subject to the following conditions: |
|||
|
|||
The above copyright notice and this permission notice shall be included in all |
|||
copies or substantial portions of the Software. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS |
|||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR |
|||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER |
|||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
|||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
@ -1,85 +0,0 @@ |
|||
# Container Interoperability |
|||
|
|||
[![Latest Stable Version](https://poser.pugx.org/container-interop/container-interop/v/stable.png)](https://packagist.org/packages/container-interop/container-interop) |
|||
|
|||
*container-interop* tries to identify and standardize features in *container* objects (service locators, |
|||
dependency injection containers, etc.) to achieve interopererability. |
|||
|
|||
Through discussions and trials, we try to create a standard, made of common interfaces but also recommendations. |
|||
|
|||
If PHP projects that provide container implementations begin to adopt these common standards, then PHP |
|||
applications and projects that use containers can depend on the common interfaces instead of specific |
|||
implementations. This facilitates a high-level of interoperability and flexibility that allows users to consume |
|||
*any* container implementation that can be adapted to these interfaces. |
|||
|
|||
The work done in this project is not officially endorsed by the [PHP-FIG](http://www.php-fig.org/), but it is being |
|||
worked on by members of PHP-FIG and other good developers. We adhere to the spirit and ideals of PHP-FIG, and hope |
|||
this project will pave the way for one or more future PSRs. |
|||
|
|||
|
|||
## Installation |
|||
|
|||
You can install this package through Composer: |
|||
|
|||
```json |
|||
{ |
|||
"require": { |
|||
"container-interop/container-interop": "~1.0" |
|||
} |
|||
} |
|||
``` |
|||
|
|||
The packages adheres to the [SemVer](http://semver.org/) specification, and there will be full backward compatibility |
|||
between minor versions. |
|||
|
|||
## Standards |
|||
|
|||
### Available |
|||
|
|||
- [`ContainerInterface`](src/Interop/Container/ContainerInterface.php). |
|||
[Description](docs/ContainerInterface.md) [Meta Document](docs/ContainerInterface-meta.md). |
|||
Describes the interface of a container that exposes methods to read its entries. |
|||
- [*Delegate lookup feature*](docs/Delegate-lookup.md). |
|||
[Meta Document](docs/Delegate-lookup-meta.md). |
|||
Describes the ability for a container to delegate the lookup of its dependencies to a third-party container. This |
|||
feature lets several containers work together in a single application. |
|||
|
|||
### Proposed |
|||
|
|||
View open [request for comments](https://github.com/container-interop/container-interop/labels/RFC) |
|||
|
|||
## Compatible projects |
|||
|
|||
### Projects implementing `ContainerInterface` |
|||
|
|||
- [Acclimate](https://github.com/jeremeamia/acclimate-container) |
|||
- [dcp-di](https://github.com/estelsmith/dcp-di) |
|||
- [Mouf](http://mouf-php.com) |
|||
- [Njasm Container](https://github.com/njasm/container) |
|||
- [PHP-DI](http://php-di.org) |
|||
- [PimpleInterop](https://github.com/moufmouf/pimple-interop) |
|||
- [XStatic](https://github.com/jeremeamia/xstatic) |
|||
|
|||
### Projects implementing the *delegate lookup* feature |
|||
|
|||
- [Mouf](http://mouf-php.com) |
|||
- [PHP-DI](http://php-di.org) |
|||
- [PimpleInterop](https://github.com/moufmouf/pimple-interop) |
|||
|
|||
## Workflow |
|||
|
|||
Everyone is welcome to join and contribute. |
|||
|
|||
The general workflow looks like this: |
|||
|
|||
1. Someone opens a discussion (GitHub issue) to suggest an interface |
|||
1. Feedback is gathered |
|||
1. The interface is added to a development branch |
|||
1. We release alpha versions so that the interface can be experimented with |
|||
1. Discussions and edits ensue until the interface is deemed stable by a general consensus |
|||
1. A new minor version of the package is released |
|||
|
|||
We try to not break BC by creating new interfaces instead of editing existing ones. |
|||
|
|||
While we currently work on interfaces, we are open to anything that might help towards interoperability, may that |
|||
be code, best practices, etc. |
@ -1,11 +0,0 @@ |
|||
{ |
|||
"name": "container-interop/container-interop", |
|||
"type": "library", |
|||
"description": "Promoting the interoperability of container objects (DIC, SL, etc.)", |
|||
"license": "MIT", |
|||
"autoload": { |
|||
"psr-4": { |
|||
"Interop\\Container\\": "src/Interop/Container/" |
|||
} |
|||
} |
|||
} |
@ -1,114 +0,0 @@ |
|||
# ContainerInterface Meta Document |
|||
|
|||
## Introduction |
|||
|
|||
This document describes the process and discussions that lead to the `ContainerInterface`. |
|||
Its goal is to explain the reasons behind each decision. |
|||
|
|||
## Goal |
|||
|
|||
The goal set by `ContainerInterface` is to standardize how frameworks and libraries make use of a |
|||
container to obtain objects and parameters. |
|||
|
|||
By standardizing such a behavior, frameworks and libraries using the `ContainerInterface` |
|||
could work with any compatible container. |
|||
That would allow end users to choose their own container based on their own preferences. |
|||
|
|||
It is important to distinguish the two usages of a container: |
|||
|
|||
- configuring entries |
|||
- fetching entries |
|||
|
|||
Most of the time, those two sides are not used by the same party. |
|||
While it is often end users who tend to configure entries, it is generally the framework that fetch |
|||
entries to build the application. |
|||
|
|||
This is why this interface focuses only on how entries can be fetched from a container. |
|||
|
|||
## Interface name |
|||
|
|||
The interface name has been thoroughly discussed and was decided by a vote. |
|||
|
|||
The list of options considered with their respective votes are: |
|||
|
|||
- `ContainerInterface`: +8 |
|||
- `ProviderInterface`: +2 |
|||
- `LocatorInterface`: 0 |
|||
- `ReadableContainerInterface`: -5 |
|||
- `ServiceLocatorInterface`: -6 |
|||
- `ObjectFactory`: -6 |
|||
- `ObjectStore`: -8 |
|||
- `ConsumerInterface`: -9 |
|||
|
|||
[Full results of the vote](https://github.com/container-interop/container-interop/wiki/%231-interface-name:-Vote) |
|||
|
|||
The complete discussion can be read in [the issue #1](https://github.com/container-interop/container-interop/issues/1). |
|||
|
|||
## Interface methods |
|||
|
|||
The choice of which methods the interface would contain was made after a statistical analysis of existing containers. |
|||
The results of this analysis are available [in this document](https://gist.github.com/mnapoli/6159681). |
|||
|
|||
The summary of the analysis showed that: |
|||
|
|||
- all containers offer a method to get an entry by its id |
|||
- a large majority name such method `get()` |
|||
- for all containers, the `get()` method has 1 mandatory parameter of type string |
|||
- some containers have an optional additional argument for `get()`, but it doesn't same the same purpose between containers |
|||
- a large majority of the containers offer a method to test if it can return an entry by its id |
|||
- a majority name such method `has()` |
|||
- for all containers offering `has()`, the method has exactly 1 parameter of type string |
|||
- a large majority of the containers throw an exception rather than returning null when an entry is not found in `get()` |
|||
- a large majority of the containers don't implement `ArrayAccess` |
|||
|
|||
The question of whether to include methods to define entries has been discussed in |
|||
[issue #1](https://github.com/container-interop/container-interop/issues/1). |
|||
It has been judged that such methods do not belong in the interface described here because it is out of its scope |
|||
(see the "Goal" section). |
|||
|
|||
As a result, the `ContainerInterface` contains two methods: |
|||
|
|||
- `get()`, returning anything, with one mandatory string parameter. Should throw an exception if the entry is not found. |
|||
- `has()`, returning a boolean, with one mandatory string parameter. |
|||
|
|||
### Number of parameters in `get()` method |
|||
|
|||
While `ContainerInterface` only defines one mandatory parameter in `get()`, it is not incompatible with |
|||
existing containers that have additional optional parameters. PHP allows an implementation to offer more parameters |
|||
as long as they are optional, because the implementation *does* satisfy the interface. |
|||
|
|||
This issue has been discussed in [issue #6](https://github.com/container-interop/container-interop/issues/6). |
|||
|
|||
### Type of the `$id` parameter |
|||
|
|||
The type of the `$id` parameter in `get()` and `has()` has been discussed in |
|||
[issue #6](https://github.com/container-interop/container-interop/issues/6). |
|||
While `string` is used in all the containers that were analyzed, it was suggested that allowing |
|||
anything (such as objects) could allow containers to offer a more advanced query API. |
|||
|
|||
An example given was to use the container as an object builder. The `$id` parameter would then be an |
|||
object that would describe how to create an instance. |
|||
|
|||
The conclusion of the discussion was that this was beyond the scope of getting entries from a container without |
|||
knowing how the container provided them, and it was more fit for a factory. |
|||
|
|||
## Contributors |
|||
|
|||
Are listed here all people that contributed in the discussions or votes, by alphabetical order: |
|||
|
|||
- [Amy Stephen](https://github.com/AmyStephen) |
|||
- [David Négrier](https://github.com/moufmouf) |
|||
- [Don Gilbert](https://github.com/dongilbert) |
|||
- [Jason Judge](https://github.com/judgej) |
|||
- [Jeremy Lindblom](https://github.com/jeremeamia) |
|||
- [Marco Pivetta](https://github.com/Ocramius) |
|||
- [Matthieu Napoli](https://github.com/mnapoli) |
|||
- [Paul M. Jones](https://github.com/pmjones) |
|||
- [Stephan Hochdörfer](https://github.com/shochdoerfer) |
|||
- [Taylor Otwell](https://github.com/taylorotwell) |
|||
|
|||
## Relevant links |
|||
|
|||
- [`ContainerInterface.php`](https://github.com/container-interop/container-interop/blob/master/src/Interop/Container/ContainerInterface.php) |
|||
- [List of all issues](https://github.com/container-interop/container-interop/issues?labels=ContainerInterface&milestone=&page=1&state=closed) |
|||
- [Vote for the interface name](https://github.com/container-interop/container-interop/wiki/%231-interface-name:-Vote) |
@ -1,153 +0,0 @@ |
|||
Container interface |
|||
=================== |
|||
|
|||
This document describes a common interface for dependency injection containers. |
|||
|
|||
The goal set by `ContainerInterface` is to standardize how frameworks and libraries make use of a |
|||
container to obtain objects and parameters (called *entries* in the rest of this document). |
|||
|
|||
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", |
|||
"SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be |
|||
interpreted as described in [RFC 2119][]. |
|||
|
|||
The word `implementor` in this document is to be interpreted as someone |
|||
implementing the `ContainerInterface` in a depency injection-related library or framework. |
|||
Users of dependency injections containers (DIC) are refered to as `user`. |
|||
|
|||
[RFC 2119]: http://tools.ietf.org/html/rfc2119 |
|||
|
|||
1. Specification |
|||
----------------- |
|||
|
|||
### 1.1 Basics |
|||
|
|||
- The `Interop\Container\ContainerInterface` exposes two methods : `get` and `has`. |
|||
|
|||
- `get` takes one mandatory parameter: an entry identifier. It MUST be a string. |
|||
A call to `get` can return anything (a *mixed* value), or throws an exception if the identifier |
|||
is not known to the container. Two successive calls to `get` with the same |
|||
identifier SHOULD return the same value. However, depending on the `implementor` |
|||
design and/or `user` configuration, different values might be returned, so |
|||
`user` SHOULD NOT rely on getting the same value on 2 successive calls. |
|||
While `ContainerInterface` only defines one mandatory parameter in `get()`, implementations |
|||
MAY accept additional optional parameters. |
|||
|
|||
- `has` takes one unique parameter: an entry identifier. It MUST return `true` |
|||
if an entry identifier is known to the container and `false` if it is not. |
|||
|
|||
### 1.2 Exceptions |
|||
|
|||
Exceptions directly thrown by the container MUST implement the |
|||
[`Interop\Container\Exception\ContainerException`](../src/Interop/Container/Exception/ContainerException.php). |
|||
|
|||
A call to the `get` method with a non-existing id should throw a |
|||
[`Interop\Container\Exception\NotFoundException`](../src/Interop/Container/Exception/NotFoundException.php). |
|||
|
|||
### 1.3 Additional features |
|||
|
|||
This section describes additional features that MAY be added to a container. Containers are not |
|||
required to implement these features to respect the ContainerInterface. |
|||
|
|||
#### 1.3.1 Delegate lookup feature |
|||
|
|||
The goal of the *delegate lookup* feature is to allow several containers to share entries. |
|||
Containers implementing this feature can perform dependency lookups in other containers. |
|||
|
|||
Containers implementing this feature will offer a greater lever of interoperability |
|||
with other containers. Implementation of this feature is therefore RECOMMENDED. |
|||
|
|||
A container implementing this feature: |
|||
|
|||
- MUST implement the `ContainerInterface` |
|||
- MUST provide a way to register a delegate container (using a constructor parameter, or a setter, |
|||
or any possible way). The delegate container MUST implement the `ContainerInterface`. |
|||
|
|||
When a container is configured to use a delegate container for dependencies: |
|||
|
|||
- Calls to the `get` method should only return an entry if the entry is part of the container. |
|||
If the entry is not part of the container, an exception should be thrown |
|||
(as requested by the `ContainerInterface`). |
|||
- Calls to the `has` method should only return `true` if the entry is part of the container. |
|||
If the entry is not part of the container, `false` should be returned. |
|||
- If the fetched entry has dependencies, **instead** of performing |
|||
the dependency lookup in the container, the lookup is performed on the *delegate container*. |
|||
|
|||
Important! By default, the lookup SHOULD be performed on the delegate container **only**, not on the container itself. |
|||
|
|||
It is however allowed for containers to provide exception cases for special entries, and a way to lookup |
|||
into the same container (or another container) instead of the delegate container. |
|||
|
|||
2. Package |
|||
---------- |
|||
|
|||
The interfaces and classes described as well as relevant exception are provided as part of the |
|||
[container-interop/container-interop](https://packagist.org/packages/container-interop/container-interop) package. |
|||
|
|||
3. `Interop\Container\ContainerInterface` |
|||
----------------------------------------- |
|||
|
|||
```php |
|||
<?php |
|||
namespace Interop\Container; |
|||
|
|||
use Interop\Container\Exception\ContainerException; |
|||
use Interop\Container\Exception\NotFoundException; |
|||
|
|||
/** |
|||
* Describes the interface of a container that exposes methods to read its entries. |
|||
*/ |
|||
interface ContainerInterface |
|||
{ |
|||
/** |
|||
* Finds an entry of the container by its identifier and returns it. |
|||
* |
|||
* @param string $id Identifier of the entry to look for. |
|||
* |
|||
* @throws NotFoundException No entry was found for this identifier. |
|||
* @throws ContainerException Error while retrieving the entry. |
|||
* |
|||
* @return mixed Entry. |
|||
*/ |
|||
public function get($id); |
|||
|
|||
/** |
|||
* Returns true if the container can return an entry for the given identifier. |
|||
* Returns false otherwise. |
|||
* |
|||
* @param string $id Identifier of the entry to look for. |
|||
* |
|||
* @return boolean |
|||
*/ |
|||
public function has($id); |
|||
} |
|||
``` |
|||
|
|||
4. `Interop\Container\Exception\ContainerException` |
|||
--------------------------------------------------- |
|||
|
|||
```php |
|||
<?php |
|||
namespace Interop\Container\Exception; |
|||
|
|||
/** |
|||
* Base interface representing a generic exception in a container. |
|||
*/ |
|||
interface ContainerException |
|||
{ |
|||
} |
|||
``` |
|||
|
|||
5. `Interop\Container\Exception\NotFoundException` |
|||
--------------------------------------------------- |
|||
|
|||
```php |
|||
<?php |
|||
namespace Interop\Container\Exception; |
|||
|
|||
/** |
|||
* No entry was found in the container. |
|||
*/ |
|||
interface NotFoundException extends ContainerException |
|||
{ |
|||
} |
|||
``` |
@ -1,259 +0,0 @@ |
|||
Delegate lookup feature Meta Document |
|||
===================================== |
|||
|
|||
1. Summary |
|||
---------- |
|||
|
|||
This document describes the *delegate lookup feature*. |
|||
Containers are not required to implement this feature to respect the `ContainerInterface`. |
|||
However, containers implementing this feature will offer a greater lever of interoperability |
|||
with other containers, allowing multiple containers to share entries in the same application. |
|||
Implementation of this feature is therefore recommanded. |
|||
|
|||
2. Why Bother? |
|||
-------------- |
|||
|
|||
The [`ContainerInterface`](../src/Interop/Container/ContainerInterface.php) ([meta doc](ContainerInterface.md)) |
|||
standardizes how frameworks and libraries make use of a container to obtain objects and parameters. |
|||
|
|||
By standardizing such a behavior, frameworks and libraries relying on the `ContainerInterface` |
|||
could work with any compatible container. |
|||
That would allow end users to choose their own container based on their own preferences. |
|||
|
|||
The `ContainerInterface` is also enough if we want to have several containers side-by-side in the same |
|||
application. For instance, this is what the [CompositeContainer](https://github.com/jeremeamia/acclimate-container/blob/master/src/CompositeContainer.php) |
|||
class of [Acclimate](https://github.com/jeremeamia/acclimate-container) is designed for: |
|||
|
|||
![Side by side containers](images/side_by_side_containers.png) |
|||
|
|||
However, an instance in container 1 cannot reference an instance in container 2. |
|||
|
|||
It would be better if an instance of container 1 could reference an instance in container 2, |
|||
and the opposite should be true. |
|||
|
|||
![Interoperating containers](images/interoperating_containers.png) |
|||
|
|||
In the sample above, entry 1 in container 1 is referencing entry 3 in container 2. |
|||
|
|||
3. Scope |
|||
-------- |
|||
|
|||
### 3.1 Goals |
|||
|
|||
The goal of the *delegate lookup* feature is to allow several containers to share entries. |
|||
|
|||
4. Approaches |
|||
------------- |
|||
|
|||
### 4.1 Chosen Approach |
|||
|
|||
Containers implementing this feature can perform dependency lookups in other containers. |
|||
|
|||
A container implementing this feature: |
|||
|
|||
- must implement the `ContainerInterface` |
|||
- must provide a way to register a *delegate container* (using a constructor parameter, or a setter, or any |
|||
possible way). The *delegate container* must implement the `ContainerInterface`. |
|||
|
|||
When a *delegate container* is configured on a container: |
|||
|
|||
- Calls to the `get` method should only return an entry if the entry is part of the container. |
|||
If the entry is not part of the container, an exception should be thrown (as required in the `ContainerInterface`). |
|||
- Calls to the `has` method should only return *true* if the entry is part of the container. |
|||
If the entry is not part of the container, *false* should be returned. |
|||
- Finally, the important part: if the entry we are fetching has dependencies, |
|||
**instead** of perfoming the dependency lookup in the container, the lookup is performed on the *delegate container*. |
|||
|
|||
Important! By default, the lookup should be performed on the delegate container **only**, not on the container itself. |
|||
|
|||
It is however allowed for containers to provide exception cases for special entries, and a way to lookup into |
|||
the same container (or another container) instead of the delegate container. |
|||
|
|||
### 4.2 Typical usage |
|||
|
|||
The *delegate container* will usually be a composite container. A composite container is a container that |
|||
contains several other containers. When performing a lookup on a composite container, the inner containers are |
|||
queried until one container returns an entry. |
|||
An inner container implementing the *delegate lookup feature* will return entries it contains, but if these |
|||
entries have dependencies, the dependencies lookup calls will be performed on the composite container, giving |
|||
a chance to all containers to answer. |
|||
|
|||
Interestingly enough, the order in which containers are added in the composite container matters. Indeed, |
|||
the first containers to be added in the composite container can "override" the entries of containers with |
|||
lower priority. |
|||
|
|||
![Containers priority](images/priority.png) |
|||
|
|||
In the example above, "container 2" contains a controller "myController" and the controller is referencing an |
|||
"entityManager" entry. "Container 1" contains also an entry named "entityManager". |
|||
Without the *delegate lookup* feature, when requesting the "myController" instance to container 2, it would take |
|||
in charge the instanciation of both entries. |
|||
|
|||
However, using the *delegate lookup* feature, here is what happens when we ask the composite controller for the |
|||
"myController" instance: |
|||
|
|||
- The composite controller asks container 1 if if contains the "myController" instance. The answer is no. |
|||
- The composite controller asks container 2 if if contains the "myController" instance. The answer is yes. |
|||
- The composite controller performs a `get` call on container 2 for the "myController" instance. |
|||
- Container 2 sees that "myController" has a dependency on "entityManager". |
|||
- Container 2 delegates the lookup of "entityManager" to the composite controller. |
|||
- The composite controller asks container 1 if if contains the "entityManager" instance. The answer is yes. |
|||
- The composite controller performs a `get` call on container 1 for the "entityManager" instance. |
|||
|
|||
In the end, we get a controller instanciated by container 2 that references an entityManager instanciated |
|||
by container 1. |
|||
|
|||
### 4.3 Alternative: the fallback strategy |
|||
|
|||
The first proposed approach we tried was to perform all the lookups in the "local" container, |
|||
and if a lookup fails in the container, to use the delegate container. In this scenario, the |
|||
delegate container is used in "fallback" mode. |
|||
|
|||
This strategy has been described in @moufmouf blog post: http://mouf-php.com/container-interop-whats-next (solution 1). |
|||
It was also discussed [here](https://github.com/container-interop/container-interop/pull/8#issuecomment-33570697) and |
|||
[here](https://github.com/container-interop/container-interop/pull/20#issuecomment-56599631). |
|||
|
|||
Problems with this strategy: |
|||
|
|||
- Heavy problem regarding infinite loops |
|||
- Unable to overload a container entry with the delegate container entry |
|||
|
|||
### 4.4 Alternative: force implementing an interface |
|||
|
|||
The first proposed approach was to develop a `ParentAwareContainerInterface` interface. |
|||
It was proposed here: https://github.com/container-interop/container-interop/pull/8 |
|||
|
|||
The interface would have had the behaviour of the delegate lookup feature but would have forced the addition of |
|||
a `setParentContainter` method: |
|||
|
|||
```php |
|||
interface ParentAwareContainerInterface extends ReadableContainerInterface { |
|||
/** |
|||
* Sets the parent container associated to that container. This container will call |
|||
* the parent container to fetch dependencies. |
|||
* |
|||
* @param ContainerInterface $container |
|||
*/ |
|||
public function setParentContainer(ContainerInterface $container); |
|||
} |
|||
``` |
|||
|
|||
The interface idea was first questioned by @Ocramius [here](https://github.com/container-interop/container-interop/pull/8#issuecomment-51721777). |
|||
@Ocramius expressed the idea that an interface should not contain setters, otherwise, it is forcing implementation |
|||
details on the class implementing the interface. |
|||
Then @mnapoli made a proposal for a "convention" [here](https://github.com/container-interop/container-interop/pull/8#issuecomment-51841079), |
|||
this idea was further discussed until all participants in the discussion agreed to remove the interface idea |
|||
and replace it with a "standard" feature. |
|||
|
|||
**Pros:** |
|||
|
|||
If we had had an interface, we could have delegated the registration of the delegate/composite container to the |
|||
the delegate/composite container itself. |
|||
For instance: |
|||
|
|||
```php |
|||
$containerA = new ContainerA(); |
|||
$containerB = new ContainerB(); |
|||
|
|||
$compositeContainer = new CompositeContainer([$containerA, $containerB]); |
|||
|
|||
// The call to 'setParentContainer' is delegated to the CompositeContainer |
|||
// It is not the responsibility of the user anymore. |
|||
class CompositeContainer { |
|||
... |
|||
|
|||
public function __construct($containers) { |
|||
foreach ($containers as $container) { |
|||
if ($container instanceof ParentAwareContainerInterface) { |
|||
$container->setParentContainer($this); |
|||
} |
|||
} |
|||
... |
|||
} |
|||
} |
|||
|
|||
``` |
|||
|
|||
**Cons:** |
|||
|
|||
Cons have been extensively discussed [here](https://github.com/container-interop/container-interop/pull/8#issuecomment-51721777). |
|||
Basically, forcing a setter into an interface is a bad idea. Setters are similar to constructor arguments, |
|||
and it's a bad idea to standardize a constructor: how the delegate container is configured into a container is an implementation detail. This outweights the benefits of the interface. |
|||
|
|||
### 4.4 Alternative: no exception case for delegate lookups |
|||
|
|||
Originally, the proposed wording for delegate lookup calls was: |
|||
|
|||
> Important! The lookup MUST be performed on the delegate container **only**, not on the container itself. |
|||
|
|||
This was later replaced by: |
|||
|
|||
> Important! By default, the lookup SHOULD be performed on the delegate container **only**, not on the container itself. |
|||
> |
|||
> It is however allowed for containers to provide exception cases for special entries, and a way to lookup |
|||
> into the same container (or another container) instead of the delegate container. |
|||
|
|||
Exception cases have been allowed to avoid breaking dependencies with some services that must be provided |
|||
by the container (on @njasm proposal). This was proposed here: https://github.com/container-interop/container-interop/pull/20#issuecomment-56597235 |
|||
|
|||
### 4.5 Alternative: having one of the containers act as the composite container |
|||
|
|||
In real-life scenarios, we usually have a big framework (Symfony 2, Zend Framework 2, etc...) and we want to |
|||
add another DI container to this container. Most of the time, the "big" framework will be responsible for |
|||
creating the controller's instances, using it's own DI container. Until *container-interop* is fully adopted, |
|||
the "big" framework will not be aware of the existence of a composite container that it should use instead |
|||
of its own container. |
|||
|
|||
For this real-life use cases, @mnapoli and @moufmouf proposed to extend the "big" framework's DI container |
|||
to make it act as a composite container. |
|||
|
|||
This has been discussed [here](https://github.com/container-interop/container-interop/pull/8#issuecomment-40367194) |
|||
and [here](http://mouf-php.com/container-interop-whats-next#solution4). |
|||
|
|||
This was implemented in Symfony 2 using: |
|||
|
|||
- [interop.symfony.di](https://github.com/thecodingmachine/interop.symfony.di/tree/v0.1.0) |
|||
- [framework interop](https://github.com/mnapoli/framework-interop/) |
|||
|
|||
This was implemented in Silex using: |
|||
|
|||
- [interop.silex.di](https://github.com/thecodingmachine/interop.silex.di) |
|||
|
|||
Having a container act as the composite container is not part of the delegate lookup standard because it is |
|||
simply a temporary design pattern used to make existing frameworks that do not support yet ContainerInterop |
|||
play nice with other DI containers. |
|||
|
|||
|
|||
5. Implementations |
|||
------------------ |
|||
|
|||
The following projects already implement the delegate lookup feature: |
|||
|
|||
- [Mouf](http://mouf-php.com), through the [`setDelegateLookupContainer` method](https://github.com/thecodingmachine/mouf/blob/2.0/src/Mouf/MoufManager.php#L2120) |
|||
- [PHP-DI](http://php-di.org/), through the [`$wrapperContainer` parameter of the constructor](https://github.com/mnapoli/PHP-DI/blob/master/src/DI/Container.php#L72) |
|||
- [pimple-interop](https://github.com/moufmouf/pimple-interop), through the [`$container` parameter of the constructor](https://github.com/moufmouf/pimple-interop/blob/master/src/Interop/Container/Pimple/PimpleInterop.php#L62) |
|||
|
|||
6. People |
|||
--------- |
|||
|
|||
Are listed here all people that contributed in the discussions, by alphabetical order: |
|||
|
|||
- [Alexandru Pătrănescu](https://github.com/drealecs) |
|||
- [Ben Peachey](https://github.com/potherca) |
|||
- [David Négrier](https://github.com/moufmouf) |
|||
- [Jeremy Lindblom](https://github.com/jeremeamia) |
|||
- [Marco Pivetta](https://github.com/Ocramius) |
|||
- [Matthieu Napoli](https://github.com/mnapoli) |
|||
- [Nelson J Morais](https://github.com/njasm) |
|||
- [Phil Sturgeon](https://github.com/philsturgeon) |
|||
- [Stephan Hochdörfer](https://github.com/shochdoerfer) |
|||
|
|||
7. Relevant Links |
|||
----------------- |
|||
|
|||
_**Note:** Order descending chronologically._ |
|||
|
|||
- [Pull request on the delegate lookup feature](https://github.com/container-interop/container-interop/pull/20) |
|||
- [Pull request on the interface idea](https://github.com/container-interop/container-interop/pull/8) |
|||
- [Original article exposing the delegate lookup idea along many others](http://mouf-php.com/container-interop-whats-next) |
|||
|
@ -1,60 +0,0 @@ |
|||
Delegate lookup feature |
|||
======================= |
|||
|
|||
This document describes a standard for dependency injection containers. |
|||
|
|||
The goal set by the *delegate lookup* feature is to allow several containers to share entries. |
|||
Containers implementing this feature can perform dependency lookups in other containers. |
|||
|
|||
Containers implementing this feature will offer a greater lever of interoperability |
|||
with other containers. Implementation of this feature is therefore RECOMMENDED. |
|||
|
|||
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", |
|||
"SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be |
|||
interpreted as described in [RFC 2119][]. |
|||
|
|||
The word `implementor` in this document is to be interpreted as someone |
|||
implementing the delegate lookup feature in a dependency injection-related library or framework. |
|||
Users of dependency injections containers (DIC) are refered to as `user`. |
|||
|
|||
[RFC 2119]: http://tools.ietf.org/html/rfc2119 |
|||
|
|||
1. Vocabulary |
|||
------------- |
|||
|
|||
In a dependency injection container, the container is used to fetch entries. |
|||
Entries can have dependencies on other entries. Usually, these other entries are fetched by the container. |
|||
|
|||
The *delegate lookup* feature is the ability for a container to fetch dependencies in |
|||
another container. In the rest of the document, the word "container" will reference the container |
|||
implemented by the implementor. The word "delegate container" will reference the container we are |
|||
fetching the dependencies from. |
|||
|
|||
2. Specification |
|||
---------------- |
|||
|
|||
A container implementing the *delegate lookup* feature: |
|||
|
|||
- MUST implement the [`ContainerInterface`](ContainerInterface.md) |
|||
- MUST provide a way to register a delegate container (using a constructor parameter, or a setter, |
|||
or any possible way). The delegate container MUST implement the [`ContainerInterface`](ContainerInterface.md). |
|||
|
|||
When a container is configured to use a delegate container for dependencies: |
|||
|
|||
- Calls to the `get` method should only return an entry if the entry is part of the container. |
|||
If the entry is not part of the container, an exception should be thrown |
|||
(as requested by the [`ContainerInterface`](ContainerInterface.md)). |
|||
- Calls to the `has` method should only return `true` if the entry is part of the container. |
|||
If the entry is not part of the container, `false` should be returned. |
|||
- If the fetched entry has dependencies, **instead** of performing |
|||
the dependency lookup in the container, the lookup is performed on the *delegate container*. |
|||
|
|||
Important: By default, the dependency lookups SHOULD be performed on the delegate container **only**, not on the container itself. |
|||
|
|||
It is however allowed for containers to provide exception cases for special entries, and a way to lookup |
|||
into the same container (or another container) instead of the delegate container. |
|||
|
|||
3. Package / Interface |
|||
---------------------- |
|||
|
|||
This feature is not tied to any code, interface or package. |
Before Width: 603 | Height: 548 | Size: 35 KiB |
Before Width: 595 | Height: 383 | Size: 22 KiB |
Before Width: 594 | Height: 382 | Size: 22 KiB |
@ -1,37 +0,0 @@ |
|||
<?php |
|||
/** |
|||
* @license http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file) |
|||
*/ |
|||
|
|||
namespace Interop\Container; |
|||
|
|||
use Interop\Container\Exception\ContainerException; |
|||
use Interop\Container\Exception\NotFoundException; |
|||
|
|||
/** |
|||
* Describes the interface of a container that exposes methods to read its entries. |
|||
*/ |
|||
interface ContainerInterface |
|||
{ |
|||
/** |
|||
* Finds an entry of the container by its identifier and returns it. |
|||
* |
|||
* @param string $id Identifier of the entry to look for. |
|||
* |
|||
* @throws NotFoundException No entry was found for this identifier. |
|||
* @throws ContainerException Error while retrieving the entry. |
|||
* |
|||
* @return mixed Entry. |
|||
*/ |
|||
public function get($id); |
|||
|
|||
/** |
|||
* Returns true if the container can return an entry for the given identifier. |
|||
* Returns false otherwise. |
|||
* |
|||
* @param string $id Identifier of the entry to look for. |
|||
* |
|||
* @return boolean |
|||
*/ |
|||
public function has($id); |
|||
} |
@ -1,13 +0,0 @@ |
|||
<?php |
|||
/** |
|||
* @license http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file) |
|||
*/ |
|||
|
|||
namespace Interop\Container\Exception; |
|||
|
|||
/** |
|||
* Base interface representing a generic exception in a container. |
|||
*/ |
|||
interface ContainerException |
|||
{ |
|||
} |
@ -1,13 +0,0 @@ |
|||
<?php |
|||
/** |
|||
* @license http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file) |
|||
*/ |
|||
|
|||
namespace Interop\Container\Exception; |
|||
|
|||
/** |
|||
* No entry was found in the container. |
|||
*/ |
|||
interface NotFoundException extends ContainerException |
|||
{ |
|||
} |
@ -1 +0,0 @@ |
|||
assume_php=false |
@ -1,12 +0,0 @@ |
|||
language: php |
|||
|
|||
php: |
|||
- 5.4 |
|||
- 5.5 |
|||
- 5.6 |
|||
- 7.0 |
|||
- hhvm |
|||
|
|||
matrix: |
|||
allow_failures: |
|||
- php: 7.0 |
@ -1,126 +0,0 @@ |
|||
<?hh // decl |
|||
|
|||
namespace FastRoute { |
|||
class BadRouteException extends \LogicException { |
|||
} |
|||
|
|||
interface RouteParser { |
|||
public function parse(string $route): array<array>; |
|||
} |
|||
|
|||
class RouteCollector { |
|||
public function __construct(RouteParser $routeParser, DataGenerator $dataGenerator); |
|||
public function addRoute(mixed $httpMethod, string $route, mixed $handler): void; |
|||
public function getData(): array; |
|||
} |
|||
|
|||
class Route { |
|||
public function __construct(string $httpMethod, mixed $handler, string $regex, array $variables); |
|||
public function matches(string $str): bool; |
|||
} |
|||
|
|||
interface DataGenerator { |
|||
public function addRoute(string $httpMethod, array $routeData, mixed $handler); |
|||
public function getData(): array; |
|||
} |
|||
|
|||
interface Dispatcher { |
|||
const int NOT_FOUND = 0; |
|||
const int FOUND = 1; |
|||
const int METHOD_NOT_ALLOWED = 2; |
|||
public function dispatch(string $httpMethod, string $uri): array; |
|||
} |
|||
|
|||
function simpleDispatcher( |
|||
(function(RouteCollector): void) $routeDefinitionCallback, |
|||
shape( |
|||
'routeParser' => ?classname<RouteParser>, |
|||
'dataGenerator' => ?classname<DataGenerator>, |
|||
'dispatcher' => ?classname<Dispatcher>, |
|||
'routeCollector' => ?classname<RouteCollector>, |
|||
) $options = shape()): Dispatcher; |
|||
|
|||
function cachedDispatcher( |
|||
(function(RouteCollector): void) $routeDefinitionCallback, |
|||
shape( |
|||
'routeParser' => ?classname<RouteParser>, |
|||
'dataGenerator' => ?classname<DataGenerator>, |
|||
'dispatcher' => ?classname<Dispatcher>, |
|||
'routeCollector' => ?classname<RouteCollector>, |
|||
'cacheDisabled' => ?bool, |
|||
'cacheFile' => ?string, |
|||
) $options = shape()): Dispatcher; |
|||
} |
|||
|
|||
namespace FastRoute\DataGenerator { |
|||
abstract class RegexBasedAbstract implements \FastRoute\DataGenerator { |
|||
protected abstract function getApproxChunkSize(); |
|||
protected abstract function processChunk($regexToRoutesMap); |
|||
|
|||
public function addRoute(string $httpMethod, array $routeData, mixed $handler): void; |
|||
public function getData(): array; |
|||
} |
|||
|
|||
class CharCountBased extends RegexBasedAbstract { |
|||
protected function getApproxChunkSize(): int; |
|||
protected function processChunk(array<string, string> $regexToRoutesMap): array<string, mixed>; |
|||
} |
|||
|
|||
class GroupCountBased extends RegexBasedAbstract { |
|||
protected function getApproxChunkSize(): int; |
|||
protected function processChunk(array<string, string> $regexToRoutesMap): array<string, mixed>; |
|||
} |
|||
|
|||
class GroupPosBased extends RegexBasedAbstract { |
|||
protected function getApproxChunkSize(): int; |
|||
protected function processChunk(array<string, string> $regexToRoutesMap): array<string, mixed>; |
|||
} |
|||
|
|||
class MarkBased extends RegexBasedAbstract { |
|||
protected function getApproxChunkSize(): int; |
|||
protected function processChunk(array<string, string> $regexToRoutesMap): array<string, mixed>; |
|||
} |
|||
} |
|||
|
|||
namespace FastRoute\Dispatcher { |
|||
abstract class RegexBasedAbstract implements \FastRoute\Dispatcher { |
|||
protected abstract function dispatchVariableRoute(array<array> $routeData, string $uri): array; |
|||
|
|||
public function dispatch(string $httpMethod, string $uri): array; |
|||
} |
|||
|
|||
class GroupPosBased extends RegexBasedAbstract { |
|||
public function __construct(array $data); |
|||
protected function dispatchVariableRoute(array<array> $routeData, string $uri): array; |
|||
} |
|||
|
|||
class GroupCountBased extends RegexBasedAbstract { |
|||
public function __construct(array $data); |
|||
protected function dispatchVariableRoute(array<array> $routeData, string $uri): array; |
|||
} |
|||
|
|||
class CharCountBased extends RegexBasedAbstract { |
|||
public function __construct(array $data); |
|||
protected function dispatchVariableRoute(array<array> $routeData, string $uri): array; |
|||
} |
|||
|
|||
class MarkBased extends RegexBasedAbstract { |
|||
public function __construct(array $data); |
|||
protected function dispatchVariableRoute(array<array> $routeData, string $uri): array; |
|||
} |
|||
} |
|||
|
|||
namespace FastRoute\RouteParser { |
|||
class Std implements \FastRoute\RouteParser { |
|||
const string VARIABLE_REGEX = <<<'REGEX' |
|||
\{ |
|||
\s* ([a-zA-Z][a-zA-Z0-9_]*) \s* |
|||
(?: |
|||
: \s* ([^{}]*(?:\{(?-1)\}[^{}]*)*) |
|||
)? |
|||
\} |
|||
REGEX; |
|||
const string DEFAULT_DISPATCH_REGEX = '[^/]+'; |
|||
public function parse(string $route): array<array>; |
|||
} |
|||
} |
@ -1,31 +0,0 @@ |
|||
Copyright (c) 2013 by Nikita Popov. |
|||
|
|||
Some rights reserved. |
|||
|
|||
Redistribution and use in source and binary forms, with or without |
|||
modification, are permitted provided that the following conditions are |
|||
met: |
|||
|
|||
* Redistributions of source code must retain the above copyright |
|||
notice, this list of conditions and the following disclaimer. |
|||
|
|||
* 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. |
|||
|
|||
* The names of the contributors may not be used to endorse or |
|||
promote products derived from this software without specific |
|||
prior written permission. |
|||
|
|||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|||
"AS IS" AND ANY EXPRESS 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 COPYRIGHT |
|||
OWNER OR 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. |
@ -1,273 +0,0 @@ |
|||
FastRoute - Fast request router for PHP |
|||
======================================= |
|||
|
|||
This library provides a fast implementation of a regular expression based router. [Blog post explaining how the |
|||
implementation works and why it is fast.][blog_post] |
|||
|
|||
Install |
|||
------- |
|||
|
|||
To install with composer: |
|||
|
|||
```sh |
|||
composer require nikic/fast-route |
|||
``` |
|||
|
|||
Requires PHP 5.4 or newer. |
|||
|
|||
Usage |
|||
----- |
|||
|
|||
Here's a basic usage example: |
|||
|
|||
```php |
|||
<?php |
|||
|
|||
require '/path/to/vendor/autoload.php'; |
|||
|
|||
$dispatcher = FastRoute\simpleDispatcher(function(FastRoute\RouteCollector $r) { |
|||
$r->addRoute('GET', '/users', 'get_all_users_handler'); |
|||
// {id} must be a number (\d+) |
|||
$r->addRoute('GET', '/user/{id:\d+}', 'get_user_handler'); |
|||
// The /{title} suffix is optional |
|||
$r->addRoute('GET', '/articles/{id:\d+}[/{title}]', 'get_article_handler'); |
|||
}); |
|||
|
|||
// Fetch method and URI from somewhere |
|||
$httpMethod = $_SERVER['REQUEST_METHOD']; |
|||
$uri = $_SERVER['REQUEST_URI']; |
|||
|
|||
// Strip query string (?foo=bar) and decode URI |
|||
if (false !== $pos = strpos($uri, '?')) { |
|||
$uri = substr($uri, 0, $pos); |
|||
} |
|||
$uri = rawurldecode($uri); |
|||
|
|||
$routeInfo = $dispatcher->dispatch($httpMethod, $uri); |
|||
switch ($routeInfo[0]) { |
|||
case FastRoute\Dispatcher::NOT_FOUND: |
|||
// ... 404 Not Found |
|||
break; |
|||
case FastRoute\Dispatcher::METHOD_NOT_ALLOWED: |
|||
$allowedMethods = $routeInfo[1]; |
|||
// ... 405 Method Not Allowed |
|||
break; |
|||
case FastRoute\Dispatcher::FOUND: |
|||
$handler = $routeInfo[1]; |
|||
$vars = $routeInfo[2]; |
|||
// ... call $handler with $vars |
|||
break; |
|||
} |
|||
``` |
|||
|
|||
### Defining routes |
|||
|
|||
The routes are defined by calling the `FastRoute\simpleDispatcher()` function, which accepts |
|||
a callable taking a `FastRoute\RouteCollector` instance. The routes are added by calling |
|||
`addRoute()` on the collector instance: |
|||
|
|||
```php |
|||
$r->addRoute($method, $routePattern, $handler); |
|||
``` |
|||
|
|||
The `$method` is an uppercase HTTP method string for which a certain route should match. It |
|||
is possible to specify multiple valid methods using an array: |
|||
|
|||
```php |
|||
// These two calls |
|||
$r->addRoute('GET', '/test', 'handler'); |
|||
$r->addRoute('POST', '/test', 'handler'); |
|||
// Are equivalent to this one call |
|||
$r->addRoute(['GET', 'POST'], '/test', 'handler'); |
|||
``` |
|||
|
|||
By default the `$routePattern` uses a syntax where `{foo}` specifies a placeholder with name `foo` |
|||
and matching the regex `[^/]+`. To adjust the pattern the placeholder matches, you can specify |
|||
a custom pattern by writing `{bar:[0-9]+}`. Some examples: |
|||
|
|||
```php |
|||
// Matches /user/42, but not /user/xyz |
|||
$r->addRoute('GET', '/user/{id:\d+}', 'handler'); |
|||
|
|||
// Matches /user/foobar, but not /user/foo/bar |
|||
$r->addRoute('GET', '/user/{name}', 'handler'); |
|||
|
|||
// Matches /user/foo/bar as well |
|||
$r->addRoute('GET', '/user/{name:.+}', 'handler'); |
|||
``` |
|||
|
|||
Custom patterns for route placeholders cannot use capturing groups. For example `{lang:(en|de)}` |
|||
is not a valid placeholder, because `()` is a capturing group. Instead you can use either |
|||
`{lang:en|de}` or `{lang:(?:en|de)}`. |
|||
|
|||
Furthermore parts of the route enclosed in `[...]` are considered optional, so that `/foo[bar]` |
|||
will match both `/foo` and `/foobar`. Optional parts are only supported in a trailing position, |
|||
not in the middle of a route. |
|||
|
|||
```php |
|||
// This route |
|||
$r->addRoute('GET', '/user/{id:\d+}[/{name}]', 'handler'); |
|||
// Is equivalent to these two routes |
|||
$r->addRoute('GET', '/user/{id:\d+}', 'handler'); |
|||
$r->addRoute('GET', '/user/{id:\d+}/{name}', 'handler'); |
|||
|
|||
// Multiple nested optional parts are possible as well |
|||
$r->addRoute('GET', '/user[/{id:\d+}[/{name}]]', 'handler'); |
|||
|
|||
// This route is NOT valid, because optional parts can only occur at the end |
|||
$r->addRoute('GET', '/user[/{id:\d+}]/{name}', 'handler'); |
|||
``` |
|||
|
|||
The `$handler` parameter does not necessarily have to be a callback, it could also be a controller |
|||
class name or any other kind of data you wish to associate with the route. FastRoute only tells you |
|||
which handler corresponds to your URI, how you interpret it is up to you. |
|||
|
|||
### Caching |
|||
|
|||
The reason `simpleDispatcher` accepts a callback for defining the routes is to allow seamless |
|||
caching. By using `cachedDispatcher` instead of `simpleDispatcher` you can cache the generated |
|||
routing data and construct the dispatcher from the cached information: |
|||
|
|||
```php |
|||
<?php |
|||
|
|||
$dispatcher = FastRoute\cachedDispatcher(function(FastRoute\RouteCollector $r) { |
|||
$r->addRoute('GET', '/user/{name}/{id:[0-9]+}', 'handler0'); |
|||
$r->addRoute('GET', '/user/{id:[0-9]+}', 'handler1'); |
|||
$r->addRoute('GET', '/user/{name}', 'handler2'); |
|||
}, [ |
|||
'cacheFile' => __DIR__ . '/route.cache', /* required */ |
|||
'cacheDisabled' => IS_DEBUG_ENABLED, /* optional, enabled by default */ |
|||
]); |
|||
``` |
|||
|
|||
The second parameter to the function is an options array, which can be used to specify the cache |
|||
file location, among other things. |
|||
|
|||
### Dispatching a URI |
|||
|
|||
A URI is dispatched by calling the `dispatch()` method of the created dispatcher. This method |
|||
accepts the HTTP method and a URI. Getting those two bits of information (and normalizing them |
|||
appropriately) is your job - this library is not bound to the PHP web SAPIs. |
|||
|
|||
The `dispatch()` method returns an array whose first element contains a status code. It is one |
|||
of `Dispatcher::NOT_FOUND`, `Dispatcher::METHOD_NOT_ALLOWED` and `Dispatcher::FOUND`. For the |
|||
method not allowed status the second array element contains a list of HTTP methods allowed for |
|||
the supplied URI. For example: |
|||
|
|||
[FastRoute\Dispatcher::METHOD_NOT_ALLOWED, ['GET', 'POST']] |
|||
|
|||
> **NOTE:** The HTTP specification requires that a `405 Method Not Allowed` response include the |
|||
`Allow:` header to detail available methods for the requested resource. Applications using FastRoute |
|||
should use the second array element to add this header when relaying a 405 response. |
|||
|
|||
For the found status the second array element is the handler that was associated with the route |
|||
and the third array element is a dictionary of placeholder names to their values. For example: |
|||
|
|||
/* Routing against GET /user/nikic/42 */ |
|||
|
|||
[FastRoute\Dispatcher::FOUND, 'handler0', ['name' => 'nikic', 'id' => '42']] |
|||
|
|||
### Overriding the route parser and dispatcher |
|||
|
|||
The routing process makes use of three components: A route parser, a data generator and a |
|||
dispatcher. The three components adhere to the following interfaces: |
|||
|
|||
```php |
|||
<?php |
|||
|
|||
namespace FastRoute; |
|||
|
|||
interface RouteParser { |
|||
public function parse($route); |
|||
} |
|||
|
|||
interface DataGenerator { |
|||
public function addRoute($httpMethod, $routeData, $handler); |
|||
public function getData(); |
|||
} |
|||
|
|||
interface Dispatcher { |
|||
const NOT_FOUND = 0, FOUND = 1, METHOD_NOT_ALLOWED = 2; |
|||
|
|||
public function dispatch($httpMethod, $uri); |
|||
} |
|||
``` |
|||
|
|||
The route parser takes a route pattern string and converts it into an array of route infos, where |
|||
each route info is again an array of it's parts. The structure is best understood using an example: |
|||
|
|||
/* The route /user/{id:\d+}[/{name}] converts to the following array: */ |
|||
[ |
|||
[ |
|||
'/user/', |
|||
['id', '\d+'], |
|||
], |
|||
[ |
|||
'/user/', |
|||
['id', '\d+'], |
|||
'/', |
|||
['name', '[^/]+'], |
|||
], |
|||
] |
|||
|
|||
This array can then be passed to the `addRoute()` method of a data generator. After all routes have |
|||
been added the `getData()` of the generator is invoked, which returns all the routing data required |
|||
by the dispatcher. The format of this data is not further specified - it is tightly coupled to |
|||
the corresponding dispatcher. |
|||
|
|||
The dispatcher accepts the routing data via a constructor and provides a `dispatch()` method, which |
|||
you're already familiar with. |
|||
|
|||
The route parser can be overwritten individually (to make use of some different pattern syntax), |
|||
however the data generator and dispatcher should always be changed as a pair, as the output from |
|||
the former is tightly coupled to the input of the latter. The reason the generator and the |
|||
dispatcher are separate is that only the latter is needed when using caching (as the output of |
|||
the former is what is being cached.) |
|||
|
|||
When using the `simpleDispatcher` / `cachedDispatcher` functions from above the override happens |
|||
through the options array: |
|||
|
|||
```php |
|||
<?php |
|||
|
|||
$dispatcher = FastRoute\simpleDispatcher(function(FastRoute\RouteCollector $r) { |
|||
/* ... */ |
|||
}, [ |
|||
'routeParser' => 'FastRoute\\RouteParser\\Std', |
|||
'dataGenerator' => 'FastRoute\\DataGenerator\\GroupCountBased', |
|||
'dispatcher' => 'FastRoute\\Dispatcher\\GroupCountBased', |
|||
]); |
|||
``` |
|||
|
|||
The above options array corresponds to the defaults. By replacing `GroupCountBased` by |
|||
`GroupPosBased` you could switch to a different dispatching strategy. |
|||
|
|||
### A Note on HEAD Requests |
|||
|
|||
The HTTP spec requires servers to [support both GET and HEAD methods][2616-511]: |
|||
|
|||
> The methods GET and HEAD MUST be supported by all general-purpose servers |
|||
|
|||
To avoid forcing users to manually register HEAD routes for each resource we fallback to matching an |
|||
available GET route for a given resource. The PHP web SAPI transparently removes the entity body |
|||
from HEAD responses so this behavior has no effect on the vast majority of users. |
|||
|
|||
However, implementers using FastRoute outside the web SAPI environment (e.g. a custom server) MUST |
|||
NOT send entity bodies generated in response to HEAD requests. If you are a non-SAPI user this is |
|||
*your responsibility*; FastRoute has no purview to prevent you from breaking HTTP in such cases. |
|||
|
|||
Finally, note that applications MAY always specify their own HEAD method route for a given |
|||
resource to bypass this behavior entirely. |
|||
|
|||
### Credits |
|||
|
|||
This library is based on a router that [Levi Morrison][levi] implemented for the Aerys server. |
|||
|
|||
A large number of tests, as well as HTTP compliance considerations, were provided by [Daniel Lowrey][rdlowrey]. |
|||
|
|||
|
|||
[2616-511]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.1 "RFC 2616 Section 5.1.1" |
|||
[blog_post]: http://nikic.github.io/2014/02/18/Fast-request-routing-using-regular-expressions.html |
|||
[levi]: https://github.com/morrisonlevi |
|||
[rdlowrey]: https://github.com/rdlowrey |
@ -1,21 +0,0 @@ |
|||
{ |
|||
"name": "nikic/fast-route", |
|||
"description": "Fast request router for PHP", |
|||
"keywords": ["routing", "router"], |
|||
"license": "BSD-3-Clause", |
|||
"authors": [ |
|||
{ |
|||
"name": "Nikita Popov", |
|||
"email": "nikic@php.net" |
|||
} |
|||
], |
|||
"require": { |
|||
"php": ">=5.4.0" |
|||
}, |
|||
"autoload": { |
|||
"psr-4": { |
|||
"FastRoute\\": "src/" |
|||
}, |
|||
"files": ["src/functions.php"] |
|||
} |
|||
} |
@ -1,24 +0,0 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
|
|||
<phpunit backupGlobals="false" |
|||
backupStaticAttributes="false" |
|||
colors="true" |
|||
convertErrorsToExceptions="true" |
|||
convertNoticesToExceptions="true" |
|||
convertWarningsToExceptions="true" |
|||
processIsolation="false" |
|||
syntaxCheck="false" |
|||
bootstrap="test/bootstrap.php" |
|||
> |
|||
<testsuites> |
|||
<testsuite name="FastRoute Tests"> |
|||
<directory>./test/</directory> |
|||
</testsuite> |
|||
</testsuites> |
|||
|
|||
<filter> |
|||
<whitelist> |
|||
<directory>./src/</directory> |
|||
</whitelist> |
|||
</filter> |
|||
</phpunit> |
@ -1,6 +0,0 @@ |
|||
<?php |
|||
|
|||
namespace FastRoute; |
|||
|
|||
class BadRouteException extends \LogicException { |
|||
} |
@ -1,25 +0,0 @@ |
|||
<?php |
|||
|
|||
namespace FastRoute; |
|||
|
|||
interface DataGenerator { |
|||
/** |
|||
* Adds a route to the data generator. The route data uses the |
|||
* same format that is returned by RouterParser::parser(). |
|||
* |
|||
* The handler doesn't necessarily need to be a callable, it |
|||
* can be arbitrary data that will be returned when the route |
|||
* matches. |
|||
* |
|||
* @param string $httpMethod |
|||
* @param array $routeData |
|||
* @param mixed $handler |
|||
*/ |
|||
public function addRoute($httpMethod, $routeData, $handler); |
|||
|
|||
/** |
|||
* Returns dispatcher data in some unspecified format, which |
|||
* depends on the used method of dispatch. |
|||
*/ |
|||
public function getData(); |
|||
} |
@ -1,28 +0,0 @@ |
|||
<?php |
|||
|
|||
namespace FastRoute\DataGenerator; |
|||
|
|||
class CharCountBased extends RegexBasedAbstract { |
|||
protected function getApproxChunkSize() { |
|||
return 30; |
|||
} |
|||
|
|||
protected function processChunk($regexToRoutesMap) { |
|||
$routeMap = []; |
|||
$regexes = []; |
|||
|
|||
$suffixLen = 0; |
|||
$suffix = ''; |
|||
$count = count($regexToRoutesMap); |
|||
foreach ($regexToRoutesMap as $regex => $route) { |
|||
$suffixLen++; |
|||
$suffix .= "\t"; |
|||
|
|||
$regexes[] = '(?:' . $regex . '/(\t{' . $suffixLen . '})\t{' . ($count - $suffixLen) . '})'; |
|||
$routeMap[$suffix] = [$route->handler, $route->variables]; |
|||
} |
|||
|
|||
$regex = '~^(?|' . implode('|', $regexes) . ')$~'; |
|||
return ['regex' => $regex, 'suffix' => '/' . $suffix, 'routeMap' => $routeMap]; |
|||
} |
|||
} |
@ -1,28 +0,0 @@ |
|||
<?php |
|||
|
|||
namespace FastRoute\DataGenerator; |
|||
|
|||
class GroupCountBased extends RegexBasedAbstract { |
|||
protected function getApproxChunkSize() { |
|||
return 10; |
|||
} |
|||
|
|||
protected function processChunk($regexToRoutesMap) { |
|||
$routeMap = []; |
|||
$regexes = []; |
|||
$numGroups = 0; |
|||
foreach ($regexToRoutesMap as $regex => $route) { |
|||
$numVariables = count($route->variables); |
|||
$numGroups = max($numGroups, $numVariables); |
|||
|
|||
$regexes[] = $regex . str_repeat('()', $numGroups - $numVariables); |
|||
$routeMap[$numGroups + 1] = [$route->handler, $route->variables]; |
|||
|
|||
++$numGroups; |
|||
} |
|||
|
|||
$regex = '~^(?|' . implode('|', $regexes) . ')$~'; |
|||
return ['regex' => $regex, 'routeMap' => $routeMap]; |
|||
} |
|||
} |
|||
|
@ -1,25 +0,0 @@ |
|||
<?php |
|||
|
|||
namespace FastRoute\DataGenerator; |
|||
|
|||
class GroupPosBased extends RegexBasedAbstract { |
|||
protected function getApproxChunkSize() { |
|||
return 10; |
|||
} |
|||
|
|||
protected function processChunk($regexToRoutesMap) { |
|||
$routeMap = []; |
|||
$regexes = []; |
|||
$offset = 1; |
|||
foreach ($regexToRoutesMap as $regex => $route) { |
|||
$regexes[] = $regex; |
|||
$routeMap[$offset] = [$route->handler, $route->variables]; |
|||
|
|||
$offset += count($route->variables); |
|||
} |
|||
|
|||
$regex = '~^(?:' . implode('|', $regexes) . ')$~'; |
|||
return ['regex' => $regex, 'routeMap' => $routeMap]; |
|||
} |
|||
} |
|||
|
@ -1,25 +0,0 @@ |
|||
<?php |
|||
|
|||
namespace FastRoute\DataGenerator; |
|||
|
|||
class MarkBased extends RegexBasedAbstract { |
|||
protected function getApproxChunkSize() { |
|||
return 30; |
|||
} |
|||
|
|||
protected function processChunk($regexToRoutesMap) { |
|||
$routeMap = []; |
|||
$regexes = []; |
|||
$markName = 'a'; |
|||
foreach ($regexToRoutesMap as $regex => $route) { |
|||
$regexes[] = $regex . '(*MARK:' . $markName . ')'; |
|||
$routeMap[$markName] = [$route->handler, $route->variables]; |
|||
|
|||
++$markName; |
|||
} |
|||
|
|||
$regex = '~^(?|' . implode('|', $regexes) . ')$~'; |
|||
return ['regex' => $regex, 'routeMap' => $routeMap]; |
|||
} |
|||
} |
|||
|
@ -1,144 +0,0 @@ |
|||
<?php |
|||
|
|||
namespace FastRoute\DataGenerator; |
|||
|
|||
use FastRoute\DataGenerator; |
|||
use FastRoute\BadRouteException; |
|||
use FastRoute\Route; |
|||
|
|||
abstract class RegexBasedAbstract implements DataGenerator { |
|||
protected $staticRoutes = []; |
|||
protected $methodToRegexToRoutesMap = []; |
|||
|
|||
protected abstract function getApproxChunkSize(); |
|||
protected abstract function processChunk($regexToRoutesMap); |
|||
|
|||
public function addRoute($httpMethod, $routeData, $handler) { |
|||
if ($this->isStaticRoute($routeData)) { |
|||
$this->addStaticRoute($httpMethod, $routeData, $handler); |
|||
} else { |
|||
$this->addVariableRoute($httpMethod, $routeData, $handler); |
|||
} |
|||
} |
|||
|
|||
public function getData() { |
|||
if (empty($this->methodToRegexToRoutesMap)) { |
|||
return [$this->staticRoutes, []]; |
|||
} |
|||
|
|||
return [$this->staticRoutes, $this->generateVariableRouteData()]; |
|||
} |
|||
|
|||
private function generateVariableRouteData() { |
|||
$data = []; |
|||
foreach ($this->methodToRegexToRoutesMap as $method => $regexToRoutesMap) { |
|||
$chunkSize = $this->computeChunkSize(count($regexToRoutesMap)); |
|||
$chunks = array_chunk($regexToRoutesMap, $chunkSize, true); |
|||
$data[$method] = array_map([$this, 'processChunk'], $chunks); |
|||
} |
|||
return $data; |
|||
} |
|||
|
|||
private function computeChunkSize($count) { |
|||
$numParts = max(1, round($count / $this->getApproxChunkSize())); |
|||
return ceil($count / $numParts); |
|||
} |
|||
|
|||
private function isStaticRoute($routeData) { |
|||
return count($routeData) === 1 && is_string($routeData[0]); |
|||
} |
|||
|
|||
private function addStaticRoute($httpMethod, $routeData, $handler) { |
|||
$routeStr = $routeData[0]; |
|||
|
|||
if (isset($this->staticRoutes[$httpMethod][$routeStr])) { |
|||
throw new BadRouteException(sprintf( |
|||
'Cannot register two routes matching "%s" for method "%s"', |
|||
$routeStr, $httpMethod |
|||
)); |
|||
} |
|||
|
|||
if (isset($this->methodToRegexToRoutesMap[$httpMethod])) { |
|||
foreach ($this->methodToRegexToRoutesMap[$httpMethod] as $route) { |
|||
if ($route->matches($routeStr)) { |
|||
throw new BadRouteException(sprintf( |
|||
'Static route "%s" is shadowed by previously defined variable route "%s" for method "%s"', |
|||
$routeStr, $route->regex, $httpMethod |
|||
)); |
|||
} |
|||
} |
|||
} |
|||
|
|||
$this->staticRoutes[$httpMethod][$routeStr] = $handler; |
|||
} |
|||
|
|||
private function addVariableRoute($httpMethod, $routeData, $handler) { |
|||
list($regex, $variables) = $this->buildRegexForRoute($routeData); |
|||
|
|||
if (isset($this->methodToRegexToRoutesMap[$httpMethod][$regex])) { |
|||
throw new BadRouteException(sprintf( |
|||
'Cannot register two routes matching "%s" for method "%s"', |
|||
$regex, $httpMethod |
|||
)); |
|||
} |
|||
|
|||
$this->methodToRegexToRoutesMap[$httpMethod][$regex] = new Route( |
|||
$httpMethod, $handler, $regex, $variables |
|||
); |
|||
} |
|||
|
|||
private function buildRegexForRoute($routeData) { |
|||
$regex = ''; |
|||
$variables = []; |
|||
foreach ($routeData as $part) { |
|||
if (is_string($part)) { |
|||
$regex .= preg_quote($part, '~'); |
|||
continue; |
|||
} |
|||
|
|||
list($varName, $regexPart) = $part; |
|||
|
|||
if (isset($variables[$varName])) { |
|||
throw new BadRouteException(sprintf( |
|||
'Cannot use the same placeholder "%s" twice', $varName |
|||
)); |
|||
} |
|||
|
|||
if ($this->regexHasCapturingGroups($regexPart)) { |
|||
throw new BadRouteException(sprintf( |
|||
'Regex "%s" for parameter "%s" contains a capturing group', |
|||
$regexPart, $varName |
|||
)); |
|||
} |
|||
|
|||
$variables[$varName] = $varName; |
|||
$regex .= '(' . $regexPart . ')'; |
|||
} |
|||
|
|||
return [$regex, $variables]; |
|||
} |
|||
|
|||
private function regexHasCapturingGroups($regex) { |
|||
if (false === strpos($regex, '(')) { |
|||
// Needs to have at least a ( to contain a capturing group
|
|||
return false; |
|||
} |
|||
|
|||
// Semi-accurate detection for capturing groups
|
|||
return preg_match( |
|||
'~ |
|||
(?: |
|||
\(\?\( |
|||
| \[ [^\]\\\\]* (?: \\\\ . [^\]\\\\]* )* \] |
|||
| \\\\ . |
|||
) (*SKIP)(*FAIL) | |
|||
\( |
|||
(?! |
|||
\? (?! <(?![!=]) | P< | \' ) |
|||
| \* |
|||
) |
|||
~x', |
|||
$regex |
|||
); |
|||
} |
|||
} |
@ -1,25 +0,0 @@ |
|||
<?php |
|||
|
|||
namespace FastRoute; |
|||
|
|||
interface Dispatcher { |
|||
const NOT_FOUND = 0; |
|||
const FOUND = 1; |
|||
const METHOD_NOT_ALLOWED = 2; |
|||
|
|||
/** |
|||
* Dispatches against the provided HTTP method verb and URI. |
|||
* |
|||
* Returns array with one of the following formats: |
|||
* |
|||
* [self::NOT_FOUND] |
|||
* [self::METHOD_NOT_ALLOWED, ['GET', 'OTHER_ALLOWED_METHODS']] |
|||
* [self::FOUND, $handler, ['varName' => 'value', ...]] |
|||
* |
|||
* @param string $httpMethod |
|||
* @param string $uri |
|||
* |
|||
* @return array |
|||
*/ |
|||
public function dispatch($httpMethod, $uri); |
|||
} |
@ -1,28 +0,0 @@ |
|||
<?php |
|||
|
|||
namespace FastRoute\Dispatcher; |
|||
|
|||
class CharCountBased extends RegexBasedAbstract { |
|||
public function __construct($data) { |
|||
list($this->staticRouteMap, $this->variableRouteData) = $data; |
|||
} |
|||
|
|||
protected function dispatchVariableRoute($routeData, $uri) { |
|||
foreach ($routeData as $data) { |
|||
if (!preg_match($data['regex'], $uri . $data['suffix'], $matches)) { |
|||
continue; |
|||
} |
|||
|
|||
list($handler, $varNames) = $data['routeMap'][end($matches)]; |
|||
|
|||
$vars = []; |
|||
$i = 0; |
|||
foreach ($varNames as $varName) { |
|||
$vars[$varName] = $matches[++$i]; |
|||
} |
|||
return [self::FOUND, $handler, $vars]; |
|||
} |
|||
|
|||
return [self::NOT_FOUND]; |
|||
} |
|||
} |
@ -1,28 +0,0 @@ |
|||
<?php |
|||
|
|||
namespace FastRoute\Dispatcher; |
|||
|
|||
class GroupCountBased extends RegexBasedAbstract { |
|||
public function __construct($data) { |
|||
list($this->staticRouteMap, $this->variableRouteData) = $data; |
|||
} |
|||
|
|||
protected function dispatchVariableRoute($routeData, $uri) { |
|||
foreach ($routeData as $data) { |
|||
if (!preg_match($data['regex'], $uri, $matches)) { |
|||
continue; |
|||
} |
|||
|
|||
list($handler, $varNames) = $data['routeMap'][count($matches)]; |
|||
|
|||
$vars = []; |
|||
$i = 0; |
|||
foreach ($varNames as $varName) { |
|||
$vars[$varName] = $matches[++$i]; |
|||
} |
|||
return [self::FOUND, $handler, $vars]; |
|||
} |
|||
|
|||
return [self::NOT_FOUND]; |
|||
} |
|||
} |
@ -1,30 +0,0 @@ |
|||
<?php |
|||
|
|||
namespace FastRoute\Dispatcher; |
|||
|
|||
class GroupPosBased extends RegexBasedAbstract { |
|||
public function __construct($data) { |
|||
list($this->staticRouteMap, $this->variableRouteData) = $data; |
|||
} |
|||
|
|||
protected function dispatchVariableRoute($routeData, $uri) { |
|||
foreach ($routeData as $data) { |
|||
if (!preg_match($data['regex'], $uri, $matches)) { |
|||
continue; |
|||
} |
|||
|
|||
// find first non-empty match
|
|||
for ($i = 1; '' === $matches[$i]; ++$i); |
|||
|
|||
list($handler, $varNames) = $data['routeMap'][$i]; |
|||
|
|||
$vars = []; |
|||
foreach ($varNames as $varName) { |
|||
$vars[$varName] = $matches[$i++]; |
|||
} |
|||
return [self::FOUND, $handler, $vars]; |
|||
} |
|||
|
|||
return [self::NOT_FOUND]; |
|||
} |
|||
} |
@ -1,28 +0,0 @@ |
|||
<?php |
|||
|
|||
namespace FastRoute\Dispatcher; |
|||
|
|||
class MarkBased extends RegexBasedAbstract { |
|||
public function __construct($data) { |
|||
list($this->staticRouteMap, $this->variableRouteData) = $data; |
|||
} |
|||
|
|||
protected function dispatchVariableRoute($routeData, $uri) { |
|||
foreach ($routeData as $data) { |
|||
if (!preg_match($data['regex'], $uri, $matches)) { |
|||
continue; |
|||
} |
|||
|
|||
list($handler, $varNames) = $data['routeMap'][$matches['MARK']]; |
|||
|
|||
$vars = []; |
|||
$i = 0; |
|||
foreach ($varNames as $varName) { |
|||
$vars[$varName] = $matches[++$i]; |
|||
} |
|||
return [self::FOUND, $handler, $vars]; |
|||
} |
|||
|
|||
return [self::NOT_FOUND]; |
|||
} |
|||
} |
@ -1,80 +0,0 @@ |
|||
<?php |
|||
|
|||
namespace FastRoute\Dispatcher; |
|||
|
|||
use FastRoute\Dispatcher; |
|||
|
|||
abstract class RegexBasedAbstract implements Dispatcher { |
|||
protected $staticRouteMap; |
|||
protected $variableRouteData; |
|||
|
|||
protected abstract function dispatchVariableRoute($routeData, $uri); |
|||
|
|||
public function dispatch($httpMethod, $uri) { |
|||
if (isset($this->staticRouteMap[$httpMethod][$uri])) { |
|||
$handler = $this->staticRouteMap[$httpMethod][$uri]; |
|||
return [self::FOUND, $handler, []]; |
|||
} |
|||
|
|||
$varRouteData = $this->variableRouteData; |
|||
if (isset($varRouteData[$httpMethod])) { |
|||
$result = $this->dispatchVariableRoute($varRouteData[$httpMethod], $uri); |
|||
if ($result[0] === self::FOUND) { |
|||
return $result; |
|||
} |
|||
} |
|||
|
|||
// For HEAD requests, attempt fallback to GET
|
|||
if ($httpMethod === 'HEAD') { |
|||
if (isset($this->staticRouteMap['GET'][$uri])) { |
|||
$handler = $this->staticRouteMap['GET'][$uri]; |
|||
return [self::FOUND, $handler, []]; |
|||
} |
|||
if (isset($varRouteData['GET'])) { |
|||
$result = $this->dispatchVariableRoute($varRouteData['GET'], $uri); |
|||
if ($result[0] === self::FOUND) { |
|||
return $result; |
|||
} |
|||
} |
|||
} |
|||
|
|||
// If nothing else matches, try fallback routes
|
|||
if (isset($this->staticRouteMap['*'][$uri])) { |
|||
$handler = $this->staticRouteMap['*'][$uri]; |
|||
return [self::FOUND, $handler, []]; |
|||
} |
|||
if (isset($varRouteData['*'])) { |
|||
$result = $this->dispatchVariableRoute($varRouteData['*'], $uri); |
|||
if ($result[0] === self::FOUND) { |
|||
return $result; |
|||
} |
|||
} |
|||
|
|||
// Find allowed methods for this URI by matching against all other HTTP methods as well
|
|||
$allowedMethods = []; |
|||
|
|||
foreach ($this->staticRouteMap as $method => $uriMap) { |
|||
if ($method !== $httpMethod && isset($uriMap[$uri])) { |
|||
$allowedMethods[] = $method; |
|||
} |
|||
} |
|||
|
|||
foreach ($varRouteData as $method => $routeData) { |
|||
if ($method === $httpMethod) { |
|||
continue; |
|||
} |
|||
|
|||
$result = $this->dispatchVariableRoute($routeData, $uri); |
|||
if ($result[0] === self::FOUND) { |
|||
$allowedMethods[] = $method; |
|||
} |
|||
} |
|||
|
|||
// If there are no allowed methods the route simply does not exist
|
|||
if ($allowedMethods) { |
|||
return [self::METHOD_NOT_ALLOWED, $allowedMethods]; |
|||
} else { |
|||
return [self::NOT_FOUND]; |
|||
} |
|||
} |
|||
} |
@ -1,38 +0,0 @@ |
|||
<?php |
|||
|
|||
namespace FastRoute; |
|||
|
|||
class Route { |
|||
public $httpMethod; |
|||
public $regex; |
|||
public $variables; |
|||
public $handler; |
|||
|
|||
/** |
|||
* Constructs a route (value object). |
|||
* |
|||
* @param string $httpMethod |
|||
* @param mixed $handler |
|||
* @param string $regex |
|||
* @param array $variables |
|||
*/ |
|||
public function __construct($httpMethod, $handler, $regex, $variables) { |
|||
$this->httpMethod = $httpMethod; |
|||
$this->handler = $handler; |
|||
$this->regex = $regex; |
|||
$this->variables = $variables; |
|||
} |
|||
|
|||
/** |
|||
* Tests whether this route matches the given string. |
|||
* |
|||
* @param string $str |
|||
* |
|||
* @return bool |
|||
*/ |
|||
public function matches($str) { |
|||
$regex = '~^' . $this->regex . '$~'; |
|||
return (bool) preg_match($regex, $str); |
|||
} |
|||
} |
|||
|
@ -1,46 +0,0 @@ |
|||
<?php |
|||
|
|||
namespace FastRoute; |
|||
|
|||
class RouteCollector { |
|||
private $routeParser; |
|||
private $dataGenerator; |
|||
|
|||
/** |
|||
* Constructs a route collector. |
|||
* |
|||
* @param RouteParser $routeParser |
|||
* @param DataGenerator $dataGenerator |
|||
*/ |
|||
public function __construct(RouteParser $routeParser, DataGenerator $dataGenerator) { |
|||
$this->routeParser = $routeParser; |
|||
$this->dataGenerator = $dataGenerator; |
|||
} |
|||
|
|||
/** |
|||
* Adds a route to the collection. |
|||
* |
|||
* The syntax used in the $route string depends on the used route parser. |
|||
* |
|||
* @param string|string[] $httpMethod |
|||
* @param string $route |
|||
* @param mixed $handler |
|||
*/ |
|||
public function addRoute($httpMethod, $route, $handler) { |
|||
$routeDatas = $this->routeParser->parse($route); |
|||
foreach ((array) $httpMethod as $method) { |
|||
foreach ($routeDatas as $routeData) { |
|||
$this->dataGenerator->addRoute($method, $routeData, $handler); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Returns the collected route data, as provided by the data generator. |
|||
* |
|||
* @return array |
|||
*/ |
|||
public function getData() { |
|||
return $this->dataGenerator->getData(); |
|||
} |
|||
} |
@ -1,36 +0,0 @@ |
|||
<?php |
|||
|
|||
namespace FastRoute; |
|||
|
|||
interface RouteParser { |
|||
/** |
|||
* Parses a route string into multiple route data arrays. |
|||
* |
|||
* The expected output is defined using an example: |
|||
* |
|||
* For the route string "/fixedRoutePart/{varName}[/moreFixed/{varName2:\d+}]", if {varName} is interpreted as |
|||
* a placeholder and [...] is interpreted as an optional route part, the expected result is: |
|||
* |
|||
* [ |
|||
* // first route: without optional part
|
|||
* [ |
|||
* "/fixedRoutePart/", |
|||
* ["varName", "[^/]+"], |
|||
* ], |
|||
* // second route: with optional part
|
|||
* [ |
|||
* "/fixedRoutePart/", |
|||
* ["varName", "[^/]+"], |
|||
* "/moreFixed/", |
|||
* ["varName2", [0-9]+"],
|
|||
* ], |
|||
* ] |
|||
* |
|||
* Here one route string was converted into two route data arrays. |
|||
* |
|||
* @param string $route Route string to parse |
|||
* |
|||
* @return mixed[][] Array of route data arrays |
|||
*/ |
|||
public function parse($route); |
|||
} |
@ -1,81 +0,0 @@ |
|||
<?php |
|||
|
|||
namespace FastRoute\RouteParser; |
|||
|
|||
use FastRoute\BadRouteException; |
|||
use FastRoute\RouteParser; |
|||
|
|||
/** |
|||
* Parses route strings of the following form: |
|||
* |
|||
* "/user/{name}[/{id:[0-9]+}]" |
|||
*/ |
|||
class Std implements RouteParser { |
|||
const VARIABLE_REGEX = <<<'REGEX' |
|||
\{ |
|||
\s* ([a-zA-Z_][a-zA-Z0-9_-]*) \s* |
|||
(?: |
|||
: \s* ([^{}]*(?:\{(?-1)\}[^{}]*)*) |
|||
)? |
|||
\} |
|||
REGEX; |
|||
const DEFAULT_DISPATCH_REGEX = '[^/]+'; |
|||
|
|||
public function parse($route) { |
|||
$routeWithoutClosingOptionals = rtrim($route, ']'); |
|||
$numOptionals = strlen($route) - strlen($routeWithoutClosingOptionals); |
|||
|
|||
// Split on [ while skipping placeholders
|
|||
$segments = preg_split('~' . self::VARIABLE_REGEX . '(*SKIP)(*F) | \[~x', $routeWithoutClosingOptionals); |
|||
if ($numOptionals !== count($segments) - 1) { |
|||
// If there are any ] in the middle of the route, throw a more specific error message
|
|||
if (preg_match('~' . self::VARIABLE_REGEX . '(*SKIP)(*F) | \]~x', $routeWithoutClosingOptionals)) { |
|||
throw new BadRouteException("Optional segments can only occur at the end of a route"); |
|||
} |
|||
throw new BadRouteException("Number of opening '[' and closing ']' does not match"); |
|||
} |
|||
|
|||
$currentRoute = ''; |
|||
$routeDatas = []; |
|||
foreach ($segments as $n => $segment) { |
|||
if ($segment === '' && $n !== 0) { |
|||
throw new BadRouteException("Empty optional part"); |
|||
} |
|||
|
|||
$currentRoute .= $segment; |
|||
$routeDatas[] = $this->parsePlaceholders($currentRoute); |
|||
} |
|||
return $routeDatas; |
|||
} |
|||
|
|||
/** |
|||
* Parses a route string that does not contain optional segments. |
|||
*/ |
|||
private function parsePlaceholders($route) { |
|||
if (!preg_match_all( |
|||
'~' . self::VARIABLE_REGEX . '~x', $route, $matches, |
|||
PREG_OFFSET_CAPTURE | PREG_SET_ORDER |
|||
)) { |
|||
return [$route]; |
|||
} |
|||
|
|||
$offset = 0; |
|||
$routeData = []; |
|||
foreach ($matches as $set) { |
|||
if ($set[0][1] > $offset) { |
|||
$routeData[] = substr($route, $offset, $set[0][1] - $offset); |
|||
} |
|||
$routeData[] = [ |
|||
$set[1][0], |
|||
isset($set[2]) ? trim($set[2][0]) : self::DEFAULT_DISPATCH_REGEX |
|||
]; |
|||
$offset = $set[0][1] + strlen($set[0][0]); |
|||
} |
|||
|
|||
if ($offset != strlen($route)) { |
|||
$routeData[] = substr($route, $offset); |
|||
} |
|||
|
|||
return $routeData; |
|||
} |
|||
} |
@ -1,12 +0,0 @@ |
|||
<?php |
|||
|
|||
namespace FastRoute; |
|||
|
|||
require __DIR__ . '/functions.php'; |
|||
|
|||
spl_autoload_register(function($class) { |
|||
if (strpos($class, 'FastRoute\\') === 0) { |
|||
$name = substr($class, strlen('FastRoute')); |
|||
require __DIR__ . strtr($name, '\\', DIRECTORY_SEPARATOR) . '.php'; |
|||
} |
|||
}); |
@ -1,70 +0,0 @@ |
|||
<?php |
|||
|
|||
namespace FastRoute; |
|||
|
|||
if (!function_exists('FastRoute\simpleDispatcher')) { |
|||
/** |
|||
* @param callable $routeDefinitionCallback |
|||
* @param array $options |
|||
* |
|||
* @return Dispatcher |
|||
*/ |
|||
function simpleDispatcher(callable $routeDefinitionCallback, array $options = []) { |
|||
$options += [ |
|||
'routeParser' => 'FastRoute\\RouteParser\\Std', |
|||
'dataGenerator' => 'FastRoute\\DataGenerator\\GroupCountBased', |
|||
'dispatcher' => 'FastRoute\\Dispatcher\\GroupCountBased', |
|||
'routeCollector' => 'FastRoute\\RouteCollector', |
|||
]; |
|||
|
|||
/** @var RouteCollector $routeCollector */ |
|||
$routeCollector = new $options['routeCollector']( |
|||
new $options['routeParser'], new $options['dataGenerator'] |
|||
); |
|||
$routeDefinitionCallback($routeCollector); |
|||
|
|||
return new $options['dispatcher']($routeCollector->getData()); |
|||
} |
|||
|
|||
/** |
|||
* @param callable $routeDefinitionCallback |
|||
* @param array $options |
|||
* |
|||
* @return Dispatcher |
|||
*/ |
|||
function cachedDispatcher(callable $routeDefinitionCallback, array $options = []) { |
|||
$options += [ |
|||
'routeParser' => 'FastRoute\\RouteParser\\Std', |
|||
'dataGenerator' => 'FastRoute\\DataGenerator\\GroupCountBased', |
|||
'dispatcher' => 'FastRoute\\Dispatcher\\GroupCountBased', |
|||
'routeCollector' => 'FastRoute\\RouteCollector', |
|||
'cacheDisabled' => false, |
|||
]; |
|||
|
|||
if (!isset($options['cacheFile'])) { |
|||
throw new \LogicException('Must specify "cacheFile" option'); |
|||
} |
|||
|
|||
if (!$options['cacheDisabled'] && file_exists($options['cacheFile'])) { |
|||
$dispatchData = require $options['cacheFile']; |
|||
if (!is_array($dispatchData)) { |
|||
throw new \RuntimeException('Invalid cache file "' . $options['cacheFile'] . '"'); |
|||
} |
|||
return new $options['dispatcher']($dispatchData); |
|||
} |
|||
|
|||
$routeCollector = new $options['routeCollector']( |
|||
new $options['routeParser'], new $options['dataGenerator'] |
|||
); |
|||
$routeDefinitionCallback($routeCollector); |
|||
|
|||
/** @var RouteCollector $routeCollector */ |
|||
$dispatchData = $routeCollector->getData(); |
|||
file_put_contents( |
|||
$options['cacheFile'], |
|||
'<?php return ' . var_export($dispatchData, true) . ';' |
|||
); |
|||
|
|||
return new $options['dispatcher']($dispatchData); |
|||
} |
|||
} |
@ -1,13 +0,0 @@ |
|||
<?php |
|||
|
|||
namespace FastRoute\Dispatcher; |
|||
|
|||
class CharCountBasedTest extends DispatcherTest { |
|||
protected function getDispatcherClass() { |
|||
return 'FastRoute\\Dispatcher\\CharCountBased'; |
|||
} |
|||
|
|||
protected function getDataGeneratorClass() { |
|||
return 'FastRoute\\DataGenerator\\CharCountBased'; |
|||
} |
|||
} |
@ -1,561 +0,0 @@ |
|||
<?php |
|||
|
|||
namespace FastRoute\Dispatcher; |
|||
|
|||
use FastRoute\RouteCollector; |
|||
|
|||
abstract class DispatcherTest extends \PHPUnit_Framework_TestCase { |
|||
|
|||
/** |
|||
* Delegate dispatcher selection to child test classes |
|||
*/ |
|||
abstract protected function getDispatcherClass(); |
|||
|
|||
/** |
|||
* Delegate dataGenerator selection to child test classes |
|||
*/ |
|||
abstract protected function getDataGeneratorClass(); |
|||
|
|||
/** |
|||
* Set appropriate options for the specific Dispatcher class we're testing |
|||
*/ |
|||
private function generateDispatcherOptions() { |
|||
return [ |
|||
'dataGenerator' => $this->getDataGeneratorClass(), |
|||
'dispatcher' => $this->getDispatcherClass() |
|||
]; |
|||
} |
|||
|
|||
/** |
|||
* @dataProvider provideFoundDispatchCases |
|||
*/ |
|||
public function testFoundDispatches($method, $uri, $callback, $handler, $argDict) { |
|||
$dispatcher = \FastRoute\simpleDispatcher($callback, $this->generateDispatcherOptions()); |
|||
$info = $dispatcher->dispatch($method, $uri); |
|||
$this->assertSame($dispatcher::FOUND, $info[0]); |
|||
$this->assertSame($handler, $info[1]); |
|||
$this->assertSame($argDict, $info[2]); |
|||
} |
|||
|
|||
/** |
|||
* @dataProvider provideNotFoundDispatchCases |
|||
*/ |
|||
public function testNotFoundDispatches($method, $uri, $callback) { |
|||
$dispatcher = \FastRoute\simpleDispatcher($callback, $this->generateDispatcherOptions()); |
|||
$routeInfo = $dispatcher->dispatch($method, $uri); |
|||
$this->assertFalse(isset($routeInfo[1]), |
|||
"NOT_FOUND result must only contain a single element in the returned info array" |
|||
); |
|||
$this->assertSame($dispatcher::NOT_FOUND, $routeInfo[0]); |
|||
} |
|||
|
|||
/** |
|||
* @dataProvider provideMethodNotAllowedDispatchCases |
|||
*/ |
|||
public function testMethodNotAllowedDispatches($method, $uri, $callback, $availableMethods) { |
|||
$dispatcher = \FastRoute\simpleDispatcher($callback, $this->generateDispatcherOptions()); |
|||
$routeInfo = $dispatcher->dispatch($method, $uri); |
|||
$this->assertTrue(isset($routeInfo[1]), |
|||
"METHOD_NOT_ALLOWED result must return an array of allowed methods at index 1" |
|||
); |
|||
|
|||
list($routedStatus, $methodArray) = $dispatcher->dispatch($method, $uri); |
|||
$this->assertSame($dispatcher::METHOD_NOT_ALLOWED, $routedStatus); |
|||
$this->assertSame($availableMethods, $methodArray); |
|||
} |
|||
|
|||
/** |
|||
* @expectedException \FastRoute\BadRouteException |
|||
* @expectedExceptionMessage Cannot use the same placeholder "test" twice |
|||
*/ |
|||
public function testDuplicateVariableNameError() { |
|||
\FastRoute\simpleDispatcher(function(RouteCollector $r) { |
|||
$r->addRoute('GET', '/foo/{test}/{test:\d+}', 'handler0'); |
|||
}, $this->generateDispatcherOptions()); |
|||
} |
|||
|
|||
/** |
|||
* @expectedException \FastRoute\BadRouteException |
|||
* @expectedExceptionMessage Cannot register two routes matching "/user/([^/]+)" for method "GET" |
|||
*/ |
|||
public function testDuplicateVariableRoute() { |
|||
\FastRoute\simpleDispatcher(function(RouteCollector $r) { |
|||
$r->addRoute('GET', '/user/{id}', 'handler0'); // oops, forgot \d+ restriction ;)
|
|||
$r->addRoute('GET', '/user/{name}', 'handler1'); |
|||
}, $this->generateDispatcherOptions()); |
|||
} |
|||
|
|||
/** |
|||
* @expectedException \FastRoute\BadRouteException |
|||
* @expectedExceptionMessage Cannot register two routes matching "/user" for method "GET" |
|||
*/ |
|||
public function testDuplicateStaticRoute() { |
|||
\FastRoute\simpleDispatcher(function(RouteCollector $r) { |
|||
$r->addRoute('GET', '/user', 'handler0'); |
|||
$r->addRoute('GET', '/user', 'handler1'); |
|||
}, $this->generateDispatcherOptions()); |
|||
} |
|||
|
|||
/** |
|||
* @expectedException \FastRoute\BadRouteException |
|||
* @expectedExceptionMessage Static route "/user/nikic" is shadowed by previously defined variable route "/user/([^/]+)" for method "GET" |
|||
*/ |
|||
public function testShadowedStaticRoute() { |
|||
\FastRoute\simpleDispatcher(function(RouteCollector $r) { |
|||
$r->addRoute('GET', '/user/{name}', 'handler0'); |
|||
$r->addRoute('GET', '/user/nikic', 'handler1'); |
|||
}, $this->generateDispatcherOptions()); |
|||
} |
|||
|
|||
/** |
|||
* @expectedException \FastRoute\BadRouteException |
|||
* @expectedExceptionMessage Regex "(en|de)" for parameter "lang" contains a capturing group |
|||
*/ |
|||
public function testCapturing() { |
|||
\FastRoute\simpleDispatcher(function(RouteCollector $r) { |
|||
$r->addRoute('GET', '/{lang:(en|de)}', 'handler0'); |
|||
}, $this->generateDispatcherOptions()); |
|||
} |
|||
|
|||
public function provideFoundDispatchCases() { |
|||
$cases = []; |
|||
|
|||
// 0 -------------------------------------------------------------------------------------->
|
|||
|
|||
$callback = function(RouteCollector $r) { |
|||
$r->addRoute('GET', '/resource/123/456', 'handler0'); |
|||
}; |
|||
|
|||
$method = 'GET'; |
|||
$uri = '/resource/123/456'; |
|||
$handler = 'handler0'; |
|||
$argDict = []; |
|||
|
|||
$cases[] = [$method, $uri, $callback, $handler, $argDict]; |
|||
|
|||
// 1 -------------------------------------------------------------------------------------->
|
|||
|
|||
$callback = function(RouteCollector $r) { |
|||
$r->addRoute('GET', '/handler0', 'handler0'); |
|||
$r->addRoute('GET', '/handler1', 'handler1'); |
|||
$r->addRoute('GET', '/handler2', 'handler2'); |
|||
}; |
|||
|
|||
$method = 'GET'; |
|||
$uri = '/handler2'; |
|||
$handler = 'handler2'; |
|||
$argDict = []; |
|||
|
|||
$cases[] = [$method, $uri, $callback, $handler, $argDict]; |
|||
|
|||
// 2 -------------------------------------------------------------------------------------->
|
|||
|
|||
$callback = function(RouteCollector $r) { |
|||
$r->addRoute('GET', '/user/{name}/{id:[0-9]+}', 'handler0'); |
|||
$r->addRoute('GET', '/user/{id:[0-9]+}', 'handler1'); |
|||
$r->addRoute('GET', '/user/{name}', 'handler2'); |
|||
}; |
|||
|
|||
$method = 'GET'; |
|||
$uri = '/user/rdlowrey'; |
|||
$handler = 'handler2'; |
|||
$argDict = ['name' => 'rdlowrey']; |
|||
|
|||
$cases[] = [$method, $uri, $callback, $handler, $argDict]; |
|||
|
|||
// 3 -------------------------------------------------------------------------------------->
|
|||
|
|||
// reuse $callback from #2
|
|||
|
|||
$method = 'GET'; |
|||
$uri = '/user/12345'; |
|||
$handler = 'handler1'; |
|||
$argDict = ['id' => '12345']; |
|||
|
|||
$cases[] = [$method, $uri, $callback, $handler, $argDict]; |
|||
|
|||
// 4 -------------------------------------------------------------------------------------->
|
|||
|
|||
// reuse $callback from #3
|
|||
|
|||
$method = 'GET'; |
|||
$uri = '/user/NaN'; |
|||
$handler = 'handler2'; |
|||
$argDict = ['name' => 'NaN']; |
|||
|
|||
$cases[] = [$method, $uri, $callback, $handler, $argDict]; |
|||
|
|||
// 5 -------------------------------------------------------------------------------------->
|
|||
|
|||
// reuse $callback from #4
|
|||
|
|||
$method = 'GET'; |
|||
$uri = '/user/rdlowrey/12345'; |
|||
$handler = 'handler0'; |
|||
$argDict = ['name' => 'rdlowrey', 'id' => '12345']; |
|||
|
|||
$cases[] = [$method, $uri, $callback, $handler, $argDict]; |
|||
|
|||
// 6 -------------------------------------------------------------------------------------->
|
|||
|
|||
$callback = function(RouteCollector $r) { |
|||
$r->addRoute('GET', '/user/{id:[0-9]+}', 'handler0'); |
|||
$r->addRoute('GET', '/user/12345/extension', 'handler1'); |
|||
$r->addRoute('GET', '/user/{id:[0-9]+}.{extension}', 'handler2'); |
|||
|
|||
}; |
|||
|
|||
$method = 'GET'; |
|||
$uri = '/user/12345.svg'; |
|||
$handler = 'handler2'; |
|||
$argDict = ['id' => '12345', 'extension' => 'svg']; |
|||
|
|||
$cases[] = [$method, $uri, $callback, $handler, $argDict]; |
|||
|
|||
// 7 ----- Test GET method fallback on HEAD route miss ------------------------------------>
|
|||
|
|||
$callback = function(RouteCollector $r) { |
|||
$r->addRoute('GET', '/user/{name}', 'handler0'); |
|||
$r->addRoute('GET', '/user/{name}/{id:[0-9]+}', 'handler1'); |
|||
$r->addRoute('GET', '/static0', 'handler2'); |
|||
$r->addRoute('GET', '/static1', 'handler3'); |
|||
$r->addRoute('HEAD', '/static1', 'handler4'); |
|||
}; |
|||
|
|||
$method = 'HEAD'; |
|||
$uri = '/user/rdlowrey'; |
|||
$handler = 'handler0'; |
|||
$argDict = ['name' => 'rdlowrey']; |
|||
|
|||
$cases[] = [$method, $uri, $callback, $handler, $argDict]; |
|||
|
|||
// 8 ----- Test GET method fallback on HEAD route miss ------------------------------------>
|
|||
|
|||
// reuse $callback from #7
|
|||
|
|||
$method = 'HEAD'; |
|||
$uri = '/user/rdlowrey/1234'; |
|||
$handler = 'handler1'; |
|||
$argDict = ['name' => 'rdlowrey', 'id' => '1234']; |
|||
|
|||
$cases[] = [$method, $uri, $callback, $handler, $argDict]; |
|||
|
|||
// 9 ----- Test GET method fallback on HEAD route miss ------------------------------------>
|
|||
|
|||
// reuse $callback from #8
|
|||
|
|||
$method = 'HEAD'; |
|||
$uri = '/static0'; |
|||
$handler = 'handler2'; |
|||
$argDict = []; |
|||
|
|||
$cases[] = [$method, $uri, $callback, $handler, $argDict]; |
|||
|
|||
// 10 ---- Test existing HEAD route used if available (no fallback) ----------------------->
|
|||
|
|||
// reuse $callback from #9
|
|||
|
|||
$method = 'HEAD'; |
|||
$uri = '/static1'; |
|||
$handler = 'handler4'; |
|||
$argDict = []; |
|||
|
|||
$cases[] = [$method, $uri, $callback, $handler, $argDict]; |
|||
|
|||
// 11 ---- More specified routes are not shadowed by less specific of another method ------>
|
|||
|
|||
$callback = function(RouteCollector $r) { |
|||
$r->addRoute('GET', '/user/{name}', 'handler0'); |
|||
$r->addRoute('POST', '/user/{name:[a-z]+}', 'handler1'); |
|||
}; |
|||
|
|||
$method = 'POST'; |
|||
$uri = '/user/rdlowrey'; |
|||
$handler = 'handler1'; |
|||
$argDict = ['name' => 'rdlowrey']; |
|||
|
|||
$cases[] = [$method, $uri, $callback, $handler, $argDict]; |
|||
|
|||
// 12 ---- Handler of more specific routes is used, if it occurs first -------------------->
|
|||
|
|||
$callback = function(RouteCollector $r) { |
|||
$r->addRoute('GET', '/user/{name}', 'handler0'); |
|||
$r->addRoute('POST', '/user/{name:[a-z]+}', 'handler1'); |
|||
$r->addRoute('POST', '/user/{name}', 'handler2'); |
|||
}; |
|||
|
|||
$method = 'POST'; |
|||
$uri = '/user/rdlowrey'; |
|||
$handler = 'handler1'; |
|||
$argDict = ['name' => 'rdlowrey']; |
|||
|
|||
$cases[] = [$method, $uri, $callback, $handler, $argDict]; |
|||
|
|||
// 13 ---- Route with constant suffix ----------------------------------------------------->
|
|||
|
|||
$callback = function(RouteCollector $r) { |
|||
$r->addRoute('GET', '/user/{name}', 'handler0'); |
|||
$r->addRoute('GET', '/user/{name}/edit', 'handler1'); |
|||
}; |
|||
|
|||
$method = 'GET'; |
|||
$uri = '/user/rdlowrey/edit'; |
|||
$handler = 'handler1'; |
|||
$argDict = ['name' => 'rdlowrey']; |
|||
|
|||
$cases[] = [$method, $uri, $callback, $handler, $argDict]; |
|||
|
|||
// 14 ---- Handle multiple methods with the same handler ---------------------------------->
|
|||
|
|||
$callback = function(RouteCollector $r) { |
|||
$r->addRoute(['GET', 'POST'], '/user', 'handlerGetPost'); |
|||
$r->addRoute(['DELETE'], '/user', 'handlerDelete'); |
|||
$r->addRoute([], '/user', 'handlerNone'); |
|||
}; |
|||
|
|||
$argDict = []; |
|||
$cases[] = ['GET', '/user', $callback, 'handlerGetPost', $argDict]; |
|||
$cases[] = ['POST', '/user', $callback, 'handlerGetPost', $argDict]; |
|||
$cases[] = ['DELETE', '/user', $callback, 'handlerDelete', $argDict]; |
|||
|
|||
// 15 ----
|
|||
|
|||
$callback = function(RouteCollector $r) { |
|||
$r->addRoute('POST', '/user.json', 'handler0'); |
|||
$r->addRoute('GET', '/{entity}.json', 'handler1'); |
|||
}; |
|||
|
|||
$cases[] = ['GET', '/user.json', $callback, 'handler1', ['entity' => 'user']]; |
|||
|
|||
// 16 ----
|
|||
|
|||
$callback = function(RouteCollector $r) { |
|||
$r->addRoute('GET', '', 'handler0'); |
|||
}; |
|||
|
|||
$cases[] = ['GET', '', $callback, 'handler0', []]; |
|||
|
|||
// 17 ----
|
|||
|
|||
$callback = function(RouteCollector $r) { |
|||
$r->addRoute('HEAD', '/a/{foo}', 'handler0'); |
|||
$r->addRoute('GET', '/b/{foo}', 'handler1'); |
|||
}; |
|||
|
|||
$cases[] = ['HEAD', '/b/bar', $callback, 'handler1', ['foo' => 'bar']]; |
|||
|
|||
// 18 ----
|
|||
|
|||
$callback = function(RouteCollector $r) { |
|||
$r->addRoute('HEAD', '/a', 'handler0'); |
|||
$r->addRoute('GET', '/b', 'handler1'); |
|||
}; |
|||
|
|||
$cases[] = ['HEAD', '/b', $callback, 'handler1', []]; |
|||
|
|||
// 19 ----
|
|||
|
|||
$callback = function(RouteCollector $r) { |
|||
$r->addRoute('GET', '/foo', 'handler0'); |
|||
$r->addRoute('HEAD', '/{bar}', 'handler1'); |
|||
}; |
|||
|
|||
$cases[] = ['HEAD', '/foo', $callback, 'handler1', ['bar' => 'foo']]; |
|||
|
|||
// 20 ----
|
|||
|
|||
$callback = function(RouteCollector $r) { |
|||
$r->addRoute('*', '/user', 'handler0'); |
|||
$r->addRoute('*', '/{user}', 'handler1'); |
|||
$r->addRoute('GET', '/user', 'handler2'); |
|||
}; |
|||
|
|||
$cases[] = ['GET', '/user', $callback, 'handler2', []]; |
|||
|
|||
// 21 ----
|
|||
|
|||
$callback = function(RouteCollector $r) { |
|||
$r->addRoute('*', '/user', 'handler0'); |
|||
$r->addRoute('GET', '/user', 'handler1'); |
|||
}; |
|||
|
|||
$cases[] = ['POST', '/user', $callback, 'handler0', []]; |
|||
|
|||
// 22 ----
|
|||
|
|||
$cases[] = ['HEAD', '/user', $callback, 'handler1', []]; |
|||
|
|||
// 23 ----
|
|||
|
|||
$callback = function(RouteCollector $r) { |
|||
$r->addRoute('GET', '/{bar}', 'handler0'); |
|||
$r->addRoute('*', '/foo', 'handler1'); |
|||
}; |
|||
|
|||
$cases[] = ['GET', '/foo', $callback, 'handler0', ['bar' => 'foo']]; |
|||
|
|||
// x -------------------------------------------------------------------------------------->
|
|||
|
|||
return $cases; |
|||
} |
|||
|
|||
public function provideNotFoundDispatchCases() { |
|||
$cases = []; |
|||
|
|||
// 0 -------------------------------------------------------------------------------------->
|
|||
|
|||
$callback = function(RouteCollector $r) { |
|||
$r->addRoute('GET', '/resource/123/456', 'handler0'); |
|||
}; |
|||
|
|||
$method = 'GET'; |
|||
$uri = '/not-found'; |
|||
|
|||
$cases[] = [$method, $uri, $callback]; |
|||
|
|||
// 1 -------------------------------------------------------------------------------------->
|
|||
|
|||
// reuse callback from #0
|
|||
$method = 'POST'; |
|||
$uri = '/not-found'; |
|||
|
|||
$cases[] = [$method, $uri, $callback]; |
|||
|
|||
// 2 -------------------------------------------------------------------------------------->
|
|||
|
|||
// reuse callback from #1
|
|||
$method = 'PUT'; |
|||
$uri = '/not-found'; |
|||
|
|||
$cases[] = [$method, $uri, $callback]; |
|||
|
|||
// 3 -------------------------------------------------------------------------------------->
|
|||
|
|||
$callback = function(RouteCollector $r) { |
|||
$r->addRoute('GET', '/handler0', 'handler0'); |
|||
$r->addRoute('GET', '/handler1', 'handler1'); |
|||
$r->addRoute('GET', '/handler2', 'handler2'); |
|||
}; |
|||
|
|||
$method = 'GET'; |
|||
$uri = '/not-found'; |
|||
|
|||
$cases[] = [$method, $uri, $callback]; |
|||
|
|||
// 4 -------------------------------------------------------------------------------------->
|
|||
|
|||
$callback = function(RouteCollector $r) { |
|||
$r->addRoute('GET', '/user/{name}/{id:[0-9]+}', 'handler0'); |
|||
$r->addRoute('GET', '/user/{id:[0-9]+}', 'handler1'); |
|||
$r->addRoute('GET', '/user/{name}', 'handler2'); |
|||
}; |
|||
|
|||
$method = 'GET'; |
|||
$uri = '/not-found'; |
|||
|
|||
$cases[] = [$method, $uri, $callback]; |
|||
|
|||
// 5 -------------------------------------------------------------------------------------->
|
|||
|
|||
// reuse callback from #4
|
|||
$method = 'GET'; |
|||
$uri = '/user/rdlowrey/12345/not-found'; |
|||
|
|||
$cases[] = [$method, $uri, $callback]; |
|||
|
|||
// 6 -------------------------------------------------------------------------------------->
|
|||
|
|||
// reuse callback from #5
|
|||
$method = 'HEAD'; |
|||
|
|||
$cases[] = array($method, $uri, $callback); |
|||
|
|||
// x -------------------------------------------------------------------------------------->
|
|||
|
|||
return $cases; |
|||
} |
|||
|
|||
public function provideMethodNotAllowedDispatchCases() { |
|||
$cases = []; |
|||
|
|||
// 0 -------------------------------------------------------------------------------------->
|
|||
|
|||
$callback = function(RouteCollector $r) { |
|||
$r->addRoute('GET', '/resource/123/456', 'handler0'); |
|||
}; |
|||
|
|||
$method = 'POST'; |
|||
$uri = '/resource/123/456'; |
|||
$allowedMethods = ['GET']; |
|||
|
|||
$cases[] = [$method, $uri, $callback, $allowedMethods]; |
|||
|
|||
// 1 -------------------------------------------------------------------------------------->
|
|||
|
|||
$callback = function(RouteCollector $r) { |
|||
$r->addRoute('GET', '/resource/123/456', 'handler0'); |
|||
$r->addRoute('POST', '/resource/123/456', 'handler1'); |
|||
$r->addRoute('PUT', '/resource/123/456', 'handler2'); |
|||
$r->addRoute('*', '/', 'handler3'); |
|||
}; |
|||
|
|||
$method = 'DELETE'; |
|||
$uri = '/resource/123/456'; |
|||
$allowedMethods = ['GET', 'POST', 'PUT']; |
|||
|
|||
$cases[] = [$method, $uri, $callback, $allowedMethods]; |
|||
|
|||
// 2 -------------------------------------------------------------------------------------->
|
|||
|
|||
$callback = function(RouteCollector $r) { |
|||
$r->addRoute('GET', '/user/{name}/{id:[0-9]+}', 'handler0'); |
|||
$r->addRoute('POST', '/user/{name}/{id:[0-9]+}', 'handler1'); |
|||
$r->addRoute('PUT', '/user/{name}/{id:[0-9]+}', 'handler2'); |
|||
$r->addRoute('PATCH', '/user/{name}/{id:[0-9]+}', 'handler3'); |
|||
}; |
|||
|
|||
$method = 'DELETE'; |
|||
$uri = '/user/rdlowrey/42'; |
|||
$allowedMethods = ['GET', 'POST', 'PUT', 'PATCH']; |
|||
|
|||
$cases[] = [$method, $uri, $callback, $allowedMethods]; |
|||
|
|||
// 3 -------------------------------------------------------------------------------------->
|
|||
|
|||
$callback = function(RouteCollector $r) { |
|||
$r->addRoute('POST', '/user/{name}', 'handler1'); |
|||
$r->addRoute('PUT', '/user/{name:[a-z]+}', 'handler2'); |
|||
$r->addRoute('PATCH', '/user/{name:[a-z]+}', 'handler3'); |
|||
}; |
|||
|
|||
$method = 'GET'; |
|||
$uri = '/user/rdlowrey'; |
|||
$allowedMethods = ['POST', 'PUT', 'PATCH']; |
|||
|
|||
$cases[] = [$method, $uri, $callback, $allowedMethods]; |
|||
|
|||
// 4 -------------------------------------------------------------------------------------->
|
|||
|
|||
$callback = function(RouteCollector $r) { |
|||
$r->addRoute(['GET', 'POST'], '/user', 'handlerGetPost'); |
|||
$r->addRoute(['DELETE'], '/user', 'handlerDelete'); |
|||
$r->addRoute([], '/user', 'handlerNone'); |
|||
}; |
|||
|
|||
$cases[] = ['PUT', '/user', $callback, ['GET', 'POST', 'DELETE']]; |
|||
|
|||
// 5
|
|||
|
|||
$callback = function(RouteCollector $r) { |
|||
$r->addRoute('POST', '/user.json', 'handler0'); |
|||
$r->addRoute('GET', '/{entity}.json', 'handler1'); |
|||
}; |
|||
|
|||
$cases[] = ['PUT', '/user.json', $callback, ['POST', 'GET']]; |
|||
|
|||
// x -------------------------------------------------------------------------------------->
|
|||
|
|||
return $cases; |
|||
} |
|||
|
|||
} |
@ -1,13 +0,0 @@ |
|||
<?php |
|||
|
|||
namespace FastRoute\Dispatcher; |
|||
|
|||
class GroupCountBasedTest extends DispatcherTest { |
|||
protected function getDispatcherClass() { |
|||
return 'FastRoute\\Dispatcher\\GroupCountBased'; |
|||
} |
|||
|
|||
protected function getDataGeneratorClass() { |
|||
return 'FastRoute\\DataGenerator\\GroupCountBased'; |
|||
} |
|||
} |
@ -1,13 +0,0 @@ |
|||
<?php |
|||
|
|||
namespace FastRoute\Dispatcher; |
|||
|
|||
class GroupPosBasedTest extends DispatcherTest { |
|||
protected function getDispatcherClass() { |
|||
return 'FastRoute\\Dispatcher\\GroupPosBased'; |
|||
} |
|||
|
|||
protected function getDataGeneratorClass() { |
|||
return 'FastRoute\\DataGenerator\\GroupPosBased'; |
|||
} |
|||
} |
@ -1,20 +0,0 @@ |
|||
<?php |
|||
|
|||
namespace FastRoute\Dispatcher; |
|||
|
|||
class MarkBasedTest extends DispatcherTest { |
|||
public function setUp() { |
|||
preg_match('/(*MARK:A)a/', 'a', $matches); |
|||
if (!isset($matches['MARK'])) { |
|||
$this->markTestSkipped('PHP 5.6 required for MARK support'); |
|||
} |
|||
} |
|||
|
|||
protected function getDispatcherClass() { |
|||
return 'FastRoute\\Dispatcher\\MarkBased'; |
|||
} |
|||
|
|||
protected function getDataGeneratorClass() { |
|||
return 'FastRoute\\DataGenerator\\MarkBased'; |
|||
} |
|||
} |
@ -1,39 +0,0 @@ |
|||
<?php |
|||
|
|||
namespace FastRoute; |
|||
|
|||
class HackTypecheckerTest extends \PhpUnit_Framework_TestCase { |
|||
const SERVER_ALREADY_RUNNING_CODE = 77; |
|||
public function testTypechecks($recurse = true) { |
|||
if (!defined('HHVM_VERSION')) { |
|||
$this->markTestSkipped("HHVM only"); |
|||
} |
|||
if (!version_compare(HHVM_VERSION, '3.9.0', '>=')) { |
|||
$this->markTestSkipped('classname<T> requires HHVM 3.9+'); |
|||
} |
|||
|
|||
// The typechecker recurses the whole tree, so it makes sure
|
|||
// that everything in fixtures/ is valid when this runs.
|
|||
|
|||
$output = array(); |
|||
$exit_code = null; |
|||
exec( |
|||
'hh_server --check '.escapeshellarg(__DIR__.'/../../').' 2>&1', |
|||
$output, |
|||
$exit_code |
|||
); |
|||
if ($exit_code === self::SERVER_ALREADY_RUNNING_CODE) { |
|||
$this->assertTrue( |
|||
$recurse, |
|||
"Typechecker still running after running hh_client stop" |
|||
); |
|||
// Server already running - 3.10 => 3.11 regression:
|
|||
// https://github.com/facebook/hhvm/issues/6646
|
|||
exec('hh_client stop 2>/dev/null'); |
|||
$this->testTypechecks(/* recurse = */ false); |
|||
return; |
|||
|
|||
} |
|||
$this->assertSame(0, $exit_code, implode("\n", $output)); |
|||
} |
|||
} |
@ -1,29 +0,0 @@ |
|||
<?hh |
|||
|
|||
namespace FastRoute\TestFixtures; |
|||
|
|||
function all_options_simple(): \FastRoute\Dispatcher { |
|||
return \FastRoute\simpleDispatcher( |
|||
$collector ==> {}, |
|||
shape( |
|||
'routeParser' => \FastRoute\RouteParser\Std::class, |
|||
'dataGenerator' => \FastRoute\DataGenerator\GroupCountBased::class, |
|||
'dispatcher' => \FastRoute\Dispatcher\GroupCountBased::class, |
|||
'routeCollector' => \FastRoute\RouteCollector::class, |
|||
), |
|||
); |
|||
} |
|||
|
|||
function all_options_cached(): \FastRoute\Dispatcher { |
|||
return \FastRoute\cachedDispatcher( |
|||
$collector ==> {}, |
|||
shape( |
|||
'routeParser' => \FastRoute\RouteParser\Std::class, |
|||
'dataGenerator' => \FastRoute\DataGenerator\GroupCountBased::class, |
|||
'dispatcher' => \FastRoute\Dispatcher\GroupCountBased::class, |
|||
'routeCollector' => \FastRoute\RouteCollector::class, |
|||
'cacheFile' => '/dev/null', |
|||
'cacheDisabled' => false, |
|||
), |
|||
); |
|||
} |
@ -1,11 +0,0 @@ |
|||
<?hh |
|||
|
|||
namespace FastRoute\TestFixtures; |
|||
|
|||
function empty_options_simple(): \FastRoute\Dispatcher { |
|||
return \FastRoute\simpleDispatcher($collector ==> {}, shape()); |
|||
} |
|||
|
|||
function empty_options_cached(): \FastRoute\Dispatcher { |
|||
return \FastRoute\cachedDispatcher($collector ==> {}, shape()); |
|||
} |
@ -1,11 +0,0 @@ |
|||
<?hh |
|||
|
|||
namespace FastRoute\TestFixtures; |
|||
|
|||
function no_options_simple(): \FastRoute\Dispatcher { |
|||
return \FastRoute\simpleDispatcher($collector ==> {}); |
|||
} |
|||
|
|||
function no_options_cached(): \FastRoute\Dispatcher { |
|||
return \FastRoute\cachedDispatcher($collector ==> {}); |
|||
} |
@ -1,147 +0,0 @@ |
|||
<?php |
|||
|
|||
namespace FastRoute\RouteParser; |
|||
|
|||
class StdTest extends \PhpUnit_Framework_TestCase { |
|||
/** @dataProvider provideTestParse */ |
|||
public function testParse($routeString, $expectedRouteDatas) { |
|||
$parser = new Std(); |
|||
$routeDatas = $parser->parse($routeString); |
|||
$this->assertSame($expectedRouteDatas, $routeDatas); |
|||
} |
|||
|
|||
/** @dataProvider provideTestParseError */ |
|||
public function testParseError($routeString, $expectedExceptionMessage) { |
|||
$parser = new Std(); |
|||
$this->setExpectedException('FastRoute\\BadRouteException', $expectedExceptionMessage); |
|||
$parser->parse($routeString); |
|||
} |
|||
|
|||
public function provideTestParse() { |
|||
return [ |
|||
[ |
|||
'/test', |
|||
[ |
|||
['/test'], |
|||
] |
|||
], |
|||
[ |
|||
'/test/{param}', |
|||
[ |
|||
['/test/', ['param', '[^/]+']], |
|||
] |
|||
], |
|||
[ |
|||
'/te{ param }st', |
|||
[ |
|||
['/te', ['param', '[^/]+'], 'st'] |
|||
] |
|||
], |
|||
[ |
|||
'/test/{param1}/test2/{param2}', |
|||
[ |
|||
['/test/', ['param1', '[^/]+'], '/test2/', ['param2', '[^/]+']] |
|||
] |
|||
], |
|||
[ |
|||
'/test/{param:\d+}', |
|||
[ |
|||
['/test/', ['param', '\d+']] |
|||
] |
|||
], |
|||
[ |
|||
'/test/{ param : \d{1,9} }', |
|||
[ |
|||
['/test/', ['param', '\d{1,9}']] |
|||
] |
|||
], |
|||
[ |
|||
'/test[opt]', |
|||
[ |
|||
['/test'], |
|||
['/testopt'], |
|||
] |
|||
], |
|||
[ |
|||
'/test[/{param}]', |
|||
[ |
|||
['/test'], |
|||
['/test/', ['param', '[^/]+']], |
|||
] |
|||
], |
|||
[ |
|||
'/{param}[opt]', |
|||
[ |
|||
['/', ['param', '[^/]+']], |
|||
['/', ['param', '[^/]+'], 'opt'] |
|||
] |
|||
], |
|||
[ |
|||
'/test[/{name}[/{id:[0-9]+}]]', |
|||
[ |
|||
['/test'], |
|||
['/test/', ['name', '[^/]+']], |
|||
['/test/', ['name', '[^/]+'], '/', ['id', '[0-9]+']], |
|||
] |
|||
], |
|||
[ |
|||
'', |
|||
[ |
|||
[''], |
|||
] |
|||
], |
|||
[ |
|||
'[test]', |
|||
[ |
|||
[''], |
|||
['test'], |
|||
] |
|||
], |
|||
[ |
|||
'/{foo-bar}', |
|||
[ |
|||
['/', ['foo-bar', '[^/]+']] |
|||
] |
|||
], |
|||
[ |
|||
'/{_foo:.*}', |
|||
[ |
|||
['/', ['_foo', '.*']] |
|||
] |
|||
], |
|||
]; |
|||
} |
|||
|
|||
public function provideTestParseError() { |
|||
return [ |
|||
[ |
|||
'/test[opt', |
|||
"Number of opening '[' and closing ']' does not match" |
|||
], |
|||
[ |
|||
'/test[opt[opt2]', |
|||
"Number of opening '[' and closing ']' does not match" |
|||
], |
|||
[ |
|||
'/testopt]', |
|||
"Number of opening '[' and closing ']' does not match" |
|||
], |
|||
[ |
|||
'/test[]', |
|||
"Empty optional part" |
|||
], |
|||
[ |
|||
'/test[[opt]]', |
|||
"Empty optional part" |
|||
], |
|||
[ |
|||
'[[test]]', |
|||
"Empty optional part" |
|||
], |
|||
[ |
|||
'/test[/opt]/required', |
|||
"Optional segments can only occur at the end of a route" |
|||
], |
|||
]; |
|||
} |
|||
} |
@ -1,11 +0,0 @@ |
|||
<?php |
|||
|
|||
require_once __DIR__ . '/../src/functions.php'; |
|||
|
|||
spl_autoload_register(function($class) { |
|||
if (strpos($class, 'FastRoute\\') === 0) { |
|||
$dir = strcasecmp(substr($class, -4), 'Test') ? 'src/' : 'test/'; |
|||
$name = substr($class, strlen('FastRoute')); |
|||
require __DIR__ . '/../' . $dir . strtr($name, '\\', DIRECTORY_SEPARATOR) . '.php'; |
|||
} |
|||
}); |
@ -1,3 +0,0 @@ |
|||
phpunit.xml |
|||
composer.lock |
|||
/vendor/ |
@ -1,32 +0,0 @@ |
|||
language: php |
|||
|
|||
env: |
|||
matrix: |
|||
- PIMPLE_EXT=no |
|||
- PIMPLE_EXT=yes |
|||
global: |
|||
- REPORT_EXIT_STATUS=1 |
|||
|
|||
php: |
|||
- 5.3 |
|||
- 5.4 |
|||
- 5.5 |
|||
- 5.6 |
|||
- hhvm |
|||
|
|||
before_script: |
|||
- composer self-update |
|||
- COMPOSER_ROOT_VERSION=dev-master composer dump-autoload |
|||
- if [ "$PIMPLE_EXT" == "yes" ]; then sh -c "cd ext/pimple && phpize && ./configure && make && sudo make install"; fi |
|||
- if [ "$PIMPLE_EXT" == "yes" ]; then echo "extension=pimple.so" >> `php --ini | grep "Loaded Configuration" | sed -e "s|.*:\s*||"`; fi |
|||
|
|||
script: |
|||
- cd ext/pimple |
|||
- if [ "$PIMPLE_EXT" == "yes" ]; then yes n | make test | tee output ; grep -E 'Tests failed +. +0' output; fi |
|||
- cd ../.. |
|||
- phpunit |
|||
|
|||
matrix: |
|||
exclude: |
|||
- php: hhvm |
|||
env: PIMPLE_EXT=yes |
@ -1,35 +0,0 @@ |
|||
* 3.0.2 (2015-09-11) |
|||
|
|||
* refactored the C extension |
|||
* minor non-significant changes |
|||
|
|||
* 3.0.1 (2015-07-30) |
|||
|
|||
* simplified some code |
|||
* fixed a segfault in the C extension |
|||
|
|||
* 3.0.0 (2014-07-24) |
|||
|
|||
* removed the Pimple class alias (use Pimple\Container instead) |
|||
|
|||
* 2.1.1 (2014-07-24) |
|||
|
|||
* fixed compiler warnings for the C extension |
|||
* fixed code when dealing with circular references |
|||
|
|||
* 2.1.0 (2014-06-24) |
|||
|
|||
* moved the Pimple to Pimple\Container (with a BC layer -- Pimple is now a |
|||
deprecated alias which will be removed in Pimple 3.0) |
|||
* added Pimple\ServiceProviderInterface (and Pimple::register()) |
|||
|
|||
* 2.0.0 (2014-02-10) |
|||
|
|||
* changed extend to automatically re-assign the extended service and keep it as shared or factory |
|||
(to keep BC, extend still returns the extended service) |
|||
* changed services to be shared by default (use factory() for factory |
|||
services) |
|||
|
|||
* 1.0.0 |
|||
|
|||
* initial version |
@ -1,19 +0,0 @@ |
|||
Copyright (c) 2009-2015 Fabien Potencier |
|||
|
|||
Permission is hereby granted, free of charge, to any person obtaining a copy |
|||
of this software and associated documentation files (the "Software"), to deal |
|||
in the Software without restriction, including without limitation the rights |
|||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|||
copies of the Software, and to permit persons to whom the Software is furnished |
|||
to do so, subject to the following conditions: |
|||
|
|||
The above copyright notice and this permission notice shall be included in all |
|||
copies or substantial portions of the Software. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
|||
THE SOFTWARE. |
@ -1,201 +0,0 @@ |
|||
Pimple |
|||
====== |
|||
|
|||
.. caution:: |
|||
|
|||
This is the documentation for Pimple 3.x. If you are using Pimple 1.x, read |
|||
the `Pimple 1.x documentation`_. Reading the Pimple 1.x code is also a good |
|||
way to learn more about how to create a simple Dependency Injection |
|||
Container (recent versions of Pimple are more focused on performance). |
|||
|
|||
Pimple is a small Dependency Injection Container for PHP. |
|||
|
|||
Installation |
|||
------------ |
|||
|
|||
Before using Pimple in your project, add it to your ``composer.json`` file: |
|||
|
|||
.. code-block:: bash |
|||
|
|||
$ ./composer.phar require pimple/pimple ~3.0 |
|||
|
|||
Alternatively, Pimple is also available as a PHP C extension: |
|||
|
|||
.. code-block:: bash |
|||
|
|||
$ git clone https://github.com/silexphp/Pimple |
|||
$ cd Pimple/ext/pimple |
|||
$ phpize |
|||
$ ./configure |
|||
$ make |
|||
$ make install |
|||
|
|||
Usage |
|||
----- |
|||
|
|||
Creating a container is a matter of creating a ``Container`` instance: |
|||
|
|||
.. code-block:: php |
|||
|
|||
use Pimple\Container; |
|||
|
|||
$container = new Container(); |
|||
|
|||
As many other dependency injection containers, Pimple manages two different |
|||
kind of data: **services** and **parameters**. |
|||
|
|||
Defining Services |
|||
~~~~~~~~~~~~~~~~~ |
|||
|
|||
A service is an object that does something as part of a larger system. Examples |
|||
of services: a database connection, a templating engine, or a mailer. Almost |
|||
any **global** object can be a service. |
|||
|
|||
Services are defined by **anonymous functions** that return an instance of an |
|||
object: |
|||
|
|||
.. code-block:: php |
|||
|
|||
// define some services |
|||
$container['session_storage'] = function ($c) { |
|||
return new SessionStorage('SESSION_ID'); |
|||
}; |
|||
|
|||
$container['session'] = function ($c) { |
|||
return new Session($c['session_storage']); |
|||
}; |
|||
|
|||
Notice that the anonymous function has access to the current container |
|||
instance, allowing references to other services or parameters. |
|||
|
|||
As objects are only created when you get them, the order of the definitions |
|||
does not matter. |
|||
|
|||
Using the defined services is also very easy: |
|||
|
|||
.. code-block:: php |
|||
|
|||
// get the session object |
|||
$session = $container['session']; |
|||
|
|||
// the above call is roughly equivalent to the following code: |
|||
// $storage = new SessionStorage('SESSION_ID'); |
|||
// $session = new Session($storage); |
|||
|
|||
Defining Factory Services |
|||
~~~~~~~~~~~~~~~~~~~~~~~~~ |
|||
|
|||
By default, each time you get a service, Pimple returns the **same instance** |
|||
of it. If you want a different instance to be returned for all calls, wrap your |
|||
anonymous function with the ``factory()`` method |
|||
|
|||
.. code-block:: php |
|||
|
|||
$container['session'] = $container->factory(function ($c) { |
|||
return new Session($c['session_storage']); |
|||
}); |
|||
|
|||
Now, each call to ``$container['session']`` returns a new instance of the |
|||
session. |
|||
|
|||
Defining Parameters |
|||
~~~~~~~~~~~~~~~~~~~ |
|||
|
|||
Defining a parameter allows to ease the configuration of your container from |
|||
the outside and to store global values: |
|||
|
|||
.. code-block:: php |
|||
|
|||
// define some parameters |
|||
$container['cookie_name'] = 'SESSION_ID'; |
|||
$container['session_storage_class'] = 'SessionStorage'; |
|||
|
|||
If you change the ``session_storage`` service definition like below: |
|||
|
|||
.. code-block:: php |
|||
|
|||
$container['session_storage'] = function ($c) { |
|||
return new $c['session_storage_class']($c['cookie_name']); |
|||
}; |
|||
|
|||
You can now easily change the cookie name by overriding the |
|||
``session_storage_class`` parameter instead of redefining the service |
|||
definition. |
|||
|
|||
Protecting Parameters |
|||
~~~~~~~~~~~~~~~~~~~~~ |
|||
|
|||
Because Pimple sees anonymous functions as service definitions, you need to |
|||
wrap anonymous functions with the ``protect()`` method to store them as |
|||
parameters: |
|||
|
|||
.. code-block:: php |
|||
|
|||
$container['random_func'] = $container->protect(function () { |
|||
return rand(); |
|||
}); |
|||
|
|||
Modifying Services after Definition |
|||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|||
|
|||
In some cases you may want to modify a service definition after it has been |
|||
defined. You can use the ``extend()`` method to define additional code to be |
|||
run on your service just after it is created: |
|||
|
|||
.. code-block:: php |
|||
|
|||
$container['session_storage'] = function ($c) { |
|||
return new $c['session_storage_class']($c['cookie_name']); |
|||
}; |
|||
|
|||
$container->extend('session_storage', function ($storage, $c) { |
|||
$storage->...(); |
|||
|
|||
return $storage; |
|||
}); |
|||
|
|||
The first argument is the name of the service to extend, the second a function |
|||
that gets access to the object instance and the container. |
|||
|
|||
Extending a Container |
|||
~~~~~~~~~~~~~~~~~~~~~ |
|||
|
|||
If you use the same libraries over and over, you might want to reuse some |
|||
services from one project to the next one; package your services into a |
|||
**provider** by implementing ``Pimple\ServiceProviderInterface``: |
|||
|
|||
.. code-block:: php |
|||
|
|||
use Pimple\Container; |
|||
|
|||
class FooProvider implements Pimple\ServiceProviderInterface |
|||
{ |
|||
public function register(Container $pimple) |
|||
{ |
|||
// register some services and parameters |
|||
// on $pimple |
|||
} |
|||
} |
|||
|
|||
Then, register the provider on a Container: |
|||
|
|||
.. code-block:: php |
|||
|
|||
$pimple->register(new FooProvider()); |
|||
|
|||
Fetching the Service Creation Function |
|||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|||
|
|||
When you access an object, Pimple automatically calls the anonymous function |
|||
that you defined, which creates the service object for you. If you want to get |
|||
raw access to this function, you can use the ``raw()`` method: |
|||
|
|||
.. code-block:: php |
|||
|
|||
$container['session'] = function ($c) { |
|||
return new Session($c['session_storage']); |
|||
}; |
|||
|
|||
$sessionFunction = $container->raw('session'); |
|||
|
|||
.. _Pimple 1.x documentation: https://github.com/silexphp/Pimple/tree/1.1 |
@ -1,25 +0,0 @@ |
|||
{ |
|||
"name": "pimple/pimple", |
|||
"type": "library", |
|||
"description": "Pimple, a simple Dependency Injection Container", |
|||
"keywords": ["dependency injection", "container"], |
|||
"homepage": "http://pimple.sensiolabs.org", |
|||
"license": "MIT", |
|||
"authors": [ |
|||
{ |
|||
"name": "Fabien Potencier", |
|||
"email": "fabien@symfony.com" |
|||
} |
|||
], |
|||
"require": { |
|||
"php": ">=5.3.0" |
|||
}, |
|||
"autoload": { |
|||
"psr-0": { "Pimple": "src/" } |
|||
}, |
|||
"extra": { |
|||
"branch-alias": { |
|||
"dev-master": "3.0.x-dev" |
|||
} |
|||
} |
|||
} |
@ -1,30 +0,0 @@ |
|||
*.sw* |
|||
.deps |
|||
Makefile |
|||
Makefile.fragments |
|||
Makefile.global |
|||
Makefile.objects |
|||
acinclude.m4 |
|||
aclocal.m4 |
|||
build/ |
|||
config.cache |
|||
config.guess |
|||
config.h |
|||
config.h.in |
|||
config.log |
|||
config.nice |
|||
config.status |
|||
config.sub |
|||
configure |
|||
configure.in |
|||
install-sh |
|||
libtool |
|||
ltmain.sh |
|||
missing |
|||
mkinstalldirs |
|||
run-tests.php |
|||
*.loT |
|||
.libs/ |
|||
modules/ |
|||
*.la |
|||
*.lo |
@ -1,12 +0,0 @@ |
|||
This is Pimple 2 implemented in C |
|||
|
|||
* PHP >= 5.3 |
|||
* Not tested under Windows, might work |
|||
|
|||
Install |
|||
======= |
|||
|
|||
> phpize |
|||
> ./configure |
|||
> make |
|||
> make install |
@ -1,63 +0,0 @@ |
|||
dnl $Id$ |
|||
dnl config.m4 for extension pimple |
|||
|
|||
dnl Comments in this file start with the string 'dnl'. |
|||
dnl Remove where necessary. This file will not work |
|||
dnl without editing. |
|||
|
|||
dnl If your extension references something external, use with: |
|||
|
|||
dnl PHP_ARG_WITH(pimple, for pimple support, |
|||
dnl Make sure that the comment is aligned: |
|||
dnl [ --with-pimple Include pimple support]) |
|||
|
|||
dnl Otherwise use enable: |
|||
|
|||
PHP_ARG_ENABLE(pimple, whether to enable pimple support, |
|||
dnl Make sure that the comment is aligned: |
|||
[ --enable-pimple Enable pimple support]) |
|||
|
|||
if test "$PHP_PIMPLE" != "no"; then |
|||
dnl Write more examples of tests here... |
|||
|
|||
dnl # --with-pimple -> check with-path |
|||
dnl SEARCH_PATH="/usr/local /usr" # you might want to change this |
|||
dnl SEARCH_FOR="/include/pimple.h" # you most likely want to change this |
|||
dnl if test -r $PHP_PIMPLE/$SEARCH_FOR; then # path given as parameter |
|||
dnl PIMPLE_DIR=$PHP_PIMPLE |
|||
dnl else # search default path list |
|||
dnl AC_MSG_CHECKING([for pimple files in default path]) |
|||
dnl for i in $SEARCH_PATH ; do |
|||
dnl if test -r $i/$SEARCH_FOR; then |
|||
dnl PIMPLE_DIR=$i |
|||
dnl AC_MSG_RESULT(found in $i) |
|||
dnl fi |
|||
dnl done |
|||
dnl fi |
|||
dnl |
|||
dnl if test -z "$PIMPLE_DIR"; then |
|||
dnl AC_MSG_RESULT([not found]) |
|||
dnl AC_MSG_ERROR([Please reinstall the pimple distribution]) |
|||
dnl fi |
|||
|
|||
dnl # --with-pimple -> add include path |
|||
dnl PHP_ADD_INCLUDE($PIMPLE_DIR/include) |
|||
|
|||
dnl # --with-pimple -> check for lib and symbol presence |
|||
dnl LIBNAME=pimple # you may want to change this |
|||
dnl LIBSYMBOL=pimple # you most likely want to change this |
|||
|
|||
dnl PHP_CHECK_LIBRARY($LIBNAME,$LIBSYMBOL, |
|||
dnl [ |
|||
dnl PHP_ADD_LIBRARY_WITH_PATH($LIBNAME, $PIMPLE_DIR/lib, PIMPLE_SHARED_LIBADD) |
|||
dnl AC_DEFINE(HAVE_PIMPLELIB,1,[ ]) |
|||
dnl ],[ |
|||
dnl AC_MSG_ERROR([wrong pimple lib version or lib not found]) |
|||
dnl ],[ |
|||
dnl -L$PIMPLE_DIR/lib -lm |
|||
dnl ]) |
|||
dnl |
|||
dnl PHP_SUBST(PIMPLE_SHARED_LIBADD) |
|||
|
|||
PHP_NEW_EXTENSION(pimple, pimple.c, $ext_shared) |
|||
fi |
@ -1,13 +0,0 @@ |
|||
// $Id$ |
|||
// vim:ft=javascript |
|||
|
|||
// If your extension references something external, use ARG_WITH |
|||
// ARG_WITH("pimple", "for pimple support", "no"); |
|||
|
|||
// Otherwise, use ARG_ENABLE |
|||
// ARG_ENABLE("pimple", "enable pimple support", "no"); |
|||
|
|||
if (PHP_PIMPLE != "no") { |
|||
EXTENSION("pimple", "pimple.c"); |
|||
} |
|||
|
@ -1,121 +0,0 @@ |
|||
|
|||
/* |
|||
* This file is part of Pimple. |
|||
* |
|||
* Copyright (c) 2014 Fabien Potencier |
|||
* |
|||
* Permission is hereby granted, free of charge, to any person obtaining a copy |
|||
* of this software and associated documentation files (the "Software"), to deal |
|||
* in the Software without restriction, including without limitation the rights |
|||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|||
* copies of the Software, and to permit persons to whom the Software is furnished |
|||
* to do so, subject to the following conditions: |
|||
* |
|||
* The above copyright notice and this permission notice shall be included in all |
|||
* copies or substantial portions of the Software. |
|||
* |
|||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
|||
* THE SOFTWARE. |
|||
*/ |
|||
|
|||
#ifndef PHP_PIMPLE_H |
|||
#define PHP_PIMPLE_H |
|||
|
|||
extern zend_module_entry pimple_module_entry; |
|||
#define phpext_pimple_ptr &pimple_module_entry |
|||
|
|||
#ifdef PHP_WIN32 |
|||
# define PHP_PIMPLE_API __declspec(dllexport) |
|||
#elif defined(__GNUC__) && __GNUC__ >= 4 |
|||
# define PHP_PIMPLE_API __attribute__ ((visibility("default"))) |
|||
#else |
|||
# define PHP_PIMPLE_API |
|||
#endif |
|||
|
|||
#ifdef ZTS |
|||
#include "TSRM.h" |
|||
#endif |
|||
|
|||
#define PIMPLE_VERSION "3.0.2" |
|||
#define PIMPLE_NS "Pimple" |
|||
|
|||
#define PIMPLE_DEFAULT_ZVAL_CACHE_NUM 5 |
|||
#define PIMPLE_DEFAULT_ZVAL_VALUES_NUM 10 |
|||
|
|||
zend_module_entry *get_module(void); |
|||
|
|||
PHP_MINIT_FUNCTION(pimple); |
|||
PHP_MINFO_FUNCTION(pimple); |
|||
|
|||
PHP_METHOD(Pimple, __construct); |
|||
PHP_METHOD(Pimple, factory); |
|||
PHP_METHOD(Pimple, protect); |
|||
PHP_METHOD(Pimple, raw); |
|||
PHP_METHOD(Pimple, extend); |
|||
PHP_METHOD(Pimple, keys); |
|||
PHP_METHOD(Pimple, register); |
|||
PHP_METHOD(Pimple, offsetSet); |
|||
PHP_METHOD(Pimple, offsetUnset); |
|||
PHP_METHOD(Pimple, offsetGet); |
|||
PHP_METHOD(Pimple, offsetExists); |
|||
|
|||
PHP_METHOD(PimpleClosure, invoker); |
|||
|
|||
typedef struct _pimple_bucket_value { |
|||
zval *value; /* Must be the first element */ |
|||
zval *raw; |
|||
zend_object_handle handle_num; |
|||
enum { |
|||
PIMPLE_IS_PARAM = 0, |
|||
PIMPLE_IS_SERVICE = 2 |
|||
} type; |
|||
zend_bool initialized; |
|||
zend_fcall_info_cache fcc; |
|||
} pimple_bucket_value; |
|||
|
|||
typedef struct _pimple_object { |
|||
zend_object zobj; |
|||
HashTable values; |
|||
HashTable factories; |
|||
HashTable protected; |
|||
} pimple_object; |
|||
|
|||
typedef struct _pimple_closure_object { |
|||
zend_object zobj; |
|||
zval *callable; |
|||
zval *factory; |
|||
} pimple_closure_object; |
|||
|
|||
static const char sensiolabs_logo[] = "<img src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHYAAAAUCAMAAABvRTlyAAAAz1BMVEUAAAAAAAAAAAAsThWB5j4AAACD6T8AAACC6D+C6D6C6D+C6D4AAAAAAACC6D4AAAAAAACC6D8AAAAAAAAAAAAAAAAAAAAAAACC6D4AAAAAAAAAAACC6D4AAAAAAAAAAAAAAAAAAAAAAACC6D8AAACC6D4AAAAAAAAAAAAAAAAAAACC6D8AAACC6D6C6D+B6D+C6D+C6D+C6D8AAACC6D6C6D4AAACC6D/K/2KC6D+B6D6C6D6C6D+C6D8sTxUyWRhEeiEAAACC6D+C5z6B6D7drnEVAAAAQXRSTlMAE3oCNSUuDHFHzxaF9UFsu+irX+zlKzYimaJXktyOSFD6BolxqT7QGMMdarMIpuO28r9EolXKgR16OphfXYd4V14GtB4AAAMpSURBVEjHvVSJctowEF1jjME2RziMwUCoMfd9heZqG4n//6buLpJjkmYm03byZmxJa2nf6u2uQcG2bfhqRN4LoTKBzyGDm68M7mAwcOEdjo4zhA/Rf9Go/CVtTgiRhXfIC3EDH8F/eUX1/9KexRo+QgOdtHDsEe/sM7QT32/+K61Z1LFXcXJxN4pTbu1aTQUzuy2PIA0rDo0/0Aa5XFaJvKaVTrubywXvaa1Wq4Vu/Snr3Y7Aojh4VccwykW2N2oQ8wmjyut6+Q1t5ywIG5Npj1sh5E0B7YOzFDjfuRfaOh3O+MbbVNfTWS9COZk3Obd2su5d0a6IU9KLREbw8gEehWSr1r2sPWciXLG38r5NdW0xu9eioU87omjC9yNaMi5GNf6WppVSOqXCFkmCvMB3p9SROLoYQn5pDgQOujA1xjYvqH+plUdkwnmII8VxR/PKYkrfLLomhVlE3b/LhNbNr7hp0H2JaOc4v8dFB58HSsFTSafaqtY1sT3GO8wsy5rhokYPlRJdjPMajyYqTt1EHF/2uqSWQWmAjCUSmQ1MS3g8Btf1XOsy7YIC0CB1b5Xw1Vhba0zbxiCAQLH9TNPmHJXQUtJAN0KcDsoqLxsNvJrJExa7mKIdp2lRE2WexiS4pqWk/0jROlw6K6bV9YOBDGAuqMJ0bnuUKGB0L27bxgRhGEbzihbhxxXaQC88Vkwq8ldCi86RApWUb0Q+4VDosBCc+1s81lUdnBavH4Zp2mm3O44USwOfvSo9oBiwpFg71lMS1VKJLKljS3j9p+fOTvXXlsSNuEv6YPaZda9uRope0VJfKdo7fPiYfSmvFjXQbkhY0d9hCbBWIktRgEDieDhf1N3wbbkmNNgRy8hyl620yGQat/grV3HMpc2HDKTVmOPFz6ylPCKt/nXcAyV260jaAowwIW0YuBzrOgb/KrddZS9OmJaLgpWK4JX2DDuklcLZSDGcn8Vmx9YDNvT6UsjyBApRyFQVX7Vxm9TGxE16nmfRd8/zQoDmggQOTRh5Hv8pMt9Q/L2JmSwkMCE7dA4BuDjHJwfu0Om4QAhOjrN5XkIatglfiN/bUPdCQFjTYgAAAABJRU5ErkJggg==\">"; |
|||
|
|||
static int pimple_zval_to_pimpleval(zval *_zval, pimple_bucket_value *_pimple_bucket_value TSRMLS_DC); |
|||
static int pimple_zval_is_valid_callback(zval *_zval, pimple_bucket_value *_pimple_bucket_value TSRMLS_DC); |
|||
|
|||
static void pimple_bucket_dtor(pimple_bucket_value *bucket); |
|||
static void pimple_free_bucket(pimple_bucket_value *bucket); |
|||
|
|||
static zval *pimple_object_read_dimension(zval *object, zval *offset, int type TSRMLS_DC); |
|||
static void pimple_object_write_dimension(zval *object, zval *offset, zval *value TSRMLS_DC); |
|||
static int pimple_object_has_dimension(zval *object, zval *offset, int check_empty TSRMLS_DC); |
|||
static void pimple_object_unset_dimension(zval *object, zval *offset TSRMLS_DC); |
|||
static zend_object_value pimple_object_create(zend_class_entry *ce TSRMLS_DC); |
|||
static void pimple_free_object_storage(pimple_object *obj TSRMLS_DC); |
|||
|
|||
static void pimple_closure_free_object_storage(pimple_closure_object *obj TSRMLS_DC); |
|||
static zend_object_value pimple_closure_object_create(zend_class_entry *ce TSRMLS_DC); |
|||
static zend_function *pimple_closure_get_constructor(zval * TSRMLS_DC); |
|||
static int pimple_closure_get_closure(zval *obj, zend_class_entry **ce_ptr, union _zend_function **fptr_ptr, zval **zobj_ptr TSRMLS_DC); |
|||
|
|||
#ifdef ZTS |
|||
#define PIMPLE_G(v) TSRMG(pimple_globals_id, zend_pimple_globals *, v) |
|||
#else |
|||
#define PIMPLE_G(v) (pimple_globals.v) |
|||
#endif |
|||
|
|||
#endif /* PHP_PIMPLE_H */ |
|||
|
@ -1,922 +0,0 @@ |
|||
|
|||
/* |
|||
* This file is part of Pimple. |
|||
* |
|||
* Copyright (c) 2014 Fabien Potencier |
|||
* |
|||
* Permission is hereby granted, free of charge, to any person obtaining a copy |
|||
* of this software and associated documentation files (the "Software"), to deal |
|||
* in the Software without restriction, including without limitation the rights |
|||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|||
* copies of the Software, and to permit persons to whom the Software is furnished |
|||
* to do so, subject to the following conditions: |
|||
* |
|||
* The above copyright notice and this permission notice shall be included in all |
|||
* copies or substantial portions of the Software. |
|||
* |
|||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
|||
* THE SOFTWARE. |
|||
*/ |
|||
|
|||
#ifdef HAVE_CONFIG_H |
|||
#include "config.h" |
|||
#endif |
|||
|
|||
#include "php.h" |
|||
#include "php_ini.h" |
|||
#include "ext/standard/info.h" |
|||
#include "php_pimple.h" |
|||
#include "pimple_compat.h" |
|||
#include "zend_interfaces.h" |
|||
#include "zend.h" |
|||
#include "Zend/zend_closures.h" |
|||
#include "ext/spl/spl_exceptions.h" |
|||
#include "Zend/zend_exceptions.h" |
|||
#include "main/php_output.h" |
|||
#include "SAPI.h" |
|||
|
|||
static zend_class_entry *pimple_ce; |
|||
static zend_object_handlers pimple_object_handlers; |
|||
static zend_class_entry *pimple_closure_ce; |
|||
static zend_class_entry *pimple_serviceprovider_ce; |
|||
static zend_object_handlers pimple_closure_object_handlers; |
|||
static zend_internal_function pimple_closure_invoker_function; |
|||
|
|||
#define FETCH_DIM_HANDLERS_VARS pimple_object *pimple_obj = NULL; \ |
|||
ulong index; \ |
|||
pimple_obj = (pimple_object *)zend_object_store_get_object(object TSRMLS_CC); \ |
|||
|
|||
#define PIMPLE_OBJECT_HANDLE_INHERITANCE_OBJECT_HANDLERS do { \ |
|||
if (ce != pimple_ce) { \ |
|||
zend_hash_find(&ce->function_table, ZEND_STRS("offsetget"), (void **)&function); \ |
|||
if (function->common.scope != ce) { /* if the function is not defined in this actual class */ \ |
|||
pimple_object_handlers.read_dimension = pimple_object_read_dimension; /* then overwrite the handler to use custom one */ \ |
|||
} \ |
|||
zend_hash_find(&ce->function_table, ZEND_STRS("offsetset"), (void **)&function); \ |
|||
if (function->common.scope != ce) { \ |
|||
pimple_object_handlers.write_dimension = pimple_object_write_dimension; \ |
|||
} \ |
|||
zend_hash_find(&ce->function_table, ZEND_STRS("offsetexists"), (void **)&function); \ |
|||
if (function->common.scope != ce) { \ |
|||
pimple_object_handlers.has_dimension = pimple_object_has_dimension; \ |
|||
} \ |
|||
zend_hash_find(&ce->function_table, ZEND_STRS("offsetunset"), (void **)&function); \ |
|||
if (function->common.scope != ce) { \ |
|||
pimple_object_handlers.unset_dimension = pimple_object_unset_dimension; \ |
|||
} \ |
|||
} else { \ |
|||
pimple_object_handlers.read_dimension = pimple_object_read_dimension; \ |
|||
pimple_object_handlers.write_dimension = pimple_object_write_dimension; \ |
|||
pimple_object_handlers.has_dimension = pimple_object_has_dimension; \ |
|||
pimple_object_handlers.unset_dimension = pimple_object_unset_dimension; \ |
|||
}\ |
|||
} while(0); |
|||
|
|||
#define PIMPLE_CALL_CB do { \ |
|||
zend_fcall_info_argn(&fci TSRMLS_CC, 1, &object); \ |
|||
fci.size = sizeof(fci); \ |
|||
fci.object_ptr = retval->fcc.object_ptr; \ |
|||
fci.function_name = retval->value; \ |
|||
fci.no_separation = 1; \ |
|||
fci.retval_ptr_ptr = &retval_ptr_ptr; \ |
|||
\ |
|||
zend_call_function(&fci, &retval->fcc TSRMLS_CC); \ |
|||
efree(fci.params); \ |
|||
if (EG(exception)) { \ |
|||
return EG(uninitialized_zval_ptr); \ |
|||
} \ |
|||
} while(0); |
|||
|
|||
ZEND_BEGIN_ARG_INFO_EX(arginfo___construct, 0, 0, 0) |
|||
ZEND_ARG_ARRAY_INFO(0, value, 0) |
|||
ZEND_END_ARG_INFO() |
|||
|
|||
ZEND_BEGIN_ARG_INFO_EX(arginfo_offsetset, 0, 0, 2) |
|||
ZEND_ARG_INFO(0, offset) |
|||
ZEND_ARG_INFO(0, value) |
|||
ZEND_END_ARG_INFO() |
|||
|
|||
ZEND_BEGIN_ARG_INFO_EX(arginfo_offsetget, 0, 0, 1) |
|||
ZEND_ARG_INFO(0, offset) |
|||
ZEND_END_ARG_INFO() |
|||
|
|||
ZEND_BEGIN_ARG_INFO_EX(arginfo_offsetexists, 0, 0, 1) |
|||
ZEND_ARG_INFO(0, offset) |
|||
ZEND_END_ARG_INFO() |
|||
|
|||
ZEND_BEGIN_ARG_INFO_EX(arginfo_offsetunset, 0, 0, 1) |
|||
ZEND_ARG_INFO(0, offset) |
|||
ZEND_END_ARG_INFO() |
|||
|
|||
ZEND_BEGIN_ARG_INFO_EX(arginfo_factory, 0, 0, 1) |
|||
ZEND_ARG_INFO(0, callable) |
|||
ZEND_END_ARG_INFO() |
|||
|
|||
ZEND_BEGIN_ARG_INFO_EX(arginfo_protect, 0, 0, 1) |
|||
ZEND_ARG_INFO(0, callable) |
|||
ZEND_END_ARG_INFO() |
|||
|
|||
ZEND_BEGIN_ARG_INFO_EX(arginfo_raw, 0, 0, 1) |
|||
ZEND_ARG_INFO(0, id) |
|||
ZEND_END_ARG_INFO() |
|||
|
|||
ZEND_BEGIN_ARG_INFO_EX(arginfo_extend, 0, 0, 2) |
|||
ZEND_ARG_INFO(0, id) |
|||
ZEND_ARG_INFO(0, callable) |
|||
ZEND_END_ARG_INFO() |
|||
|
|||
ZEND_BEGIN_ARG_INFO_EX(arginfo_keys, 0, 0, 0) |
|||
ZEND_END_ARG_INFO() |
|||
|
|||
ZEND_BEGIN_ARG_INFO_EX(arginfo_register, 0, 0, 1) |
|||
ZEND_ARG_OBJ_INFO(0, provider, Pimple\\ServiceProviderInterface, 0) |
|||
ZEND_ARG_ARRAY_INFO(0, values, 1) |
|||
ZEND_END_ARG_INFO() |
|||
|
|||
ZEND_BEGIN_ARG_INFO_EX(arginfo_serviceprovider_register, 0, 0, 1) |
|||
ZEND_ARG_OBJ_INFO(0, pimple, Pimple\\Container, 0) |
|||
ZEND_END_ARG_INFO() |
|||
|
|||
static const zend_function_entry pimple_ce_functions[] = { |
|||
PHP_ME(Pimple, __construct, arginfo___construct, ZEND_ACC_PUBLIC) |
|||
PHP_ME(Pimple, factory, arginfo_factory, ZEND_ACC_PUBLIC) |
|||
PHP_ME(Pimple, protect, arginfo_protect, ZEND_ACC_PUBLIC) |
|||
PHP_ME(Pimple, raw, arginfo_raw, ZEND_ACC_PUBLIC) |
|||
PHP_ME(Pimple, extend, arginfo_extend, ZEND_ACC_PUBLIC) |
|||
PHP_ME(Pimple, keys, arginfo_keys, ZEND_ACC_PUBLIC) |
|||
PHP_ME(Pimple, register, arginfo_register, ZEND_ACC_PUBLIC) |
|||
|
|||
PHP_ME(Pimple, offsetSet, arginfo_offsetset, ZEND_ACC_PUBLIC) |
|||
PHP_ME(Pimple, offsetGet, arginfo_offsetget, ZEND_ACC_PUBLIC) |
|||
PHP_ME(Pimple, offsetExists, arginfo_offsetexists, ZEND_ACC_PUBLIC) |
|||
PHP_ME(Pimple, offsetUnset, arginfo_offsetunset, ZEND_ACC_PUBLIC) |
|||
PHP_FE_END |
|||
}; |
|||
|
|||
static const zend_function_entry pimple_serviceprovider_iface_ce_functions[] = { |
|||
PHP_ABSTRACT_ME(ServiceProviderInterface, register, arginfo_serviceprovider_register) |
|||
PHP_FE_END |
|||
}; |
|||
|
|||
static void pimple_closure_free_object_storage(pimple_closure_object *obj TSRMLS_DC) |
|||
{ |
|||
zend_object_std_dtor(&obj->zobj TSRMLS_CC); |
|||
if (obj->factory) { |
|||
zval_ptr_dtor(&obj->factory); |
|||
} |
|||
if (obj->callable) { |
|||
zval_ptr_dtor(&obj->callable); |
|||
} |
|||
efree(obj); |
|||
} |
|||
|
|||
static void pimple_free_object_storage(pimple_object *obj TSRMLS_DC) |
|||
{ |
|||
zend_hash_destroy(&obj->factories); |
|||
zend_hash_destroy(&obj->protected); |
|||
zend_hash_destroy(&obj->values); |
|||
zend_object_std_dtor(&obj->zobj TSRMLS_CC); |
|||
efree(obj); |
|||
} |
|||
|
|||
static void pimple_free_bucket(pimple_bucket_value *bucket) |
|||
{ |
|||
if (bucket->raw) { |
|||
zval_ptr_dtor(&bucket->raw); |
|||
} |
|||
} |
|||
|
|||
static zend_object_value pimple_closure_object_create(zend_class_entry *ce TSRMLS_DC) |
|||
{ |
|||
zend_object_value retval; |
|||
pimple_closure_object *pimple_closure_obj = NULL; |
|||
|
|||
pimple_closure_obj = ecalloc(1, sizeof(pimple_closure_object)); |
|||
ZEND_OBJ_INIT(&pimple_closure_obj->zobj, ce); |
|||
|
|||
pimple_closure_object_handlers.get_constructor = pimple_closure_get_constructor; |
|||
retval.handlers = &pimple_closure_object_handlers; |
|||
retval.handle = zend_objects_store_put(pimple_closure_obj, (zend_objects_store_dtor_t) zend_objects_destroy_object, (zend_objects_free_object_storage_t) pimple_closure_free_object_storage, NULL TSRMLS_CC); |
|||
|
|||
return retval; |
|||
} |
|||
|
|||
static zend_function *pimple_closure_get_constructor(zval *obj TSRMLS_DC) |
|||
{ |
|||
zend_error(E_ERROR, "Pimple\\ContainerClosure is an internal class and cannot be instantiated"); |
|||
|
|||
return NULL; |
|||
} |
|||
|
|||
static int pimple_closure_get_closure(zval *obj, zend_class_entry **ce_ptr, union _zend_function **fptr_ptr, zval **zobj_ptr TSRMLS_DC) |
|||
{ |
|||
*zobj_ptr = obj; |
|||
*ce_ptr = Z_OBJCE_P(obj); |
|||
*fptr_ptr = (zend_function *)&pimple_closure_invoker_function; |
|||
|
|||
return SUCCESS; |
|||
} |
|||
|
|||
static zend_object_value pimple_object_create(zend_class_entry *ce TSRMLS_DC) |
|||
{ |
|||
zend_object_value retval; |
|||
pimple_object *pimple_obj = NULL; |
|||
zend_function *function = NULL; |
|||
|
|||
pimple_obj = emalloc(sizeof(pimple_object)); |
|||
ZEND_OBJ_INIT(&pimple_obj->zobj, ce); |
|||
|
|||
PIMPLE_OBJECT_HANDLE_INHERITANCE_OBJECT_HANDLERS |
|||
|
|||
retval.handlers = &pimple_object_handlers; |
|||
retval.handle = zend_objects_store_put(pimple_obj, (zend_objects_store_dtor_t) zend_objects_destroy_object, (zend_objects_free_object_storage_t) pimple_free_object_storage, NULL TSRMLS_CC); |
|||
|
|||
zend_hash_init(&pimple_obj->factories, PIMPLE_DEFAULT_ZVAL_CACHE_NUM, NULL, (dtor_func_t)pimple_bucket_dtor, 0); |
|||
zend_hash_init(&pimple_obj->protected, PIMPLE_DEFAULT_ZVAL_CACHE_NUM, NULL, (dtor_func_t)pimple_bucket_dtor, 0); |
|||
zend_hash_init(&pimple_obj->values, PIMPLE_DEFAULT_ZVAL_VALUES_NUM, NULL, (dtor_func_t)pimple_bucket_dtor, 0); |
|||
|
|||
return retval; |
|||
} |
|||
|
|||
static void pimple_object_write_dimension(zval *object, zval *offset, zval *value TSRMLS_DC) |
|||
{ |
|||
FETCH_DIM_HANDLERS_VARS |
|||
|
|||
pimple_bucket_value pimple_value = {0}, *found_value = NULL; |
|||
ulong hash; |
|||
|
|||
pimple_zval_to_pimpleval(value, &pimple_value TSRMLS_CC); |
|||
|
|||
if (!offset) {/* $p[] = 'foo' when not overloaded */ |
|||
zend_hash_next_index_insert(&pimple_obj->values, (void *)&pimple_value, sizeof(pimple_bucket_value), NULL); |
|||
Z_ADDREF_P(value); |
|||
return; |
|||
} |
|||
|
|||
switch (Z_TYPE_P(offset)) { |
|||
case IS_STRING: |
|||
hash = zend_hash_func(Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1); |
|||
zend_hash_quick_find(&pimple_obj->values, Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1, hash, (void **)&found_value); |
|||
if (found_value && found_value->type == PIMPLE_IS_SERVICE && found_value->initialized == 1) { |
|||
pimple_free_bucket(&pimple_value); |
|||
zend_throw_exception_ex(spl_ce_RuntimeException, 0 TSRMLS_CC, "Cannot override frozen service \"%s\".", Z_STRVAL_P(offset)); |
|||
return; |
|||
} |
|||
if (zend_hash_quick_update(&pimple_obj->values, Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1, hash, (void *)&pimple_value, sizeof(pimple_bucket_value), NULL) == FAILURE) { |
|||
pimple_free_bucket(&pimple_value); |
|||
return; |
|||
} |
|||
Z_ADDREF_P(value); |
|||
break; |
|||
case IS_DOUBLE: |
|||
case IS_BOOL: |
|||
case IS_LONG: |
|||
if (Z_TYPE_P(offset) == IS_DOUBLE) { |
|||
index = (ulong)Z_DVAL_P(offset); |
|||
} else { |
|||
index = Z_LVAL_P(offset); |
|||
} |
|||
zend_hash_index_find(&pimple_obj->values, index, (void **)&found_value); |
|||
if (found_value && found_value->type == PIMPLE_IS_SERVICE && found_value->initialized == 1) { |
|||
pimple_free_bucket(&pimple_value); |
|||
zend_throw_exception_ex(spl_ce_RuntimeException, 0 TSRMLS_CC, "Cannot override frozen service \"%ld\".", index); |
|||
return; |
|||
} |
|||
if (zend_hash_index_update(&pimple_obj->values, index, (void *)&pimple_value, sizeof(pimple_bucket_value), NULL) == FAILURE) { |
|||
pimple_free_bucket(&pimple_value); |
|||
return; |
|||
} |
|||
Z_ADDREF_P(value); |
|||
break; |
|||
case IS_NULL: /* $p[] = 'foo' when overloaded */ |
|||
zend_hash_next_index_insert(&pimple_obj->values, (void *)&pimple_value, sizeof(pimple_bucket_value), NULL); |
|||
Z_ADDREF_P(value); |
|||
break; |
|||
default: |
|||
pimple_free_bucket(&pimple_value); |
|||
zend_error(E_WARNING, "Unsupported offset type"); |
|||
} |
|||
} |
|||
|
|||
static void pimple_object_unset_dimension(zval *object, zval *offset TSRMLS_DC) |
|||
{ |
|||
FETCH_DIM_HANDLERS_VARS |
|||
|
|||
switch (Z_TYPE_P(offset)) { |
|||
case IS_STRING: |
|||
zend_symtable_del(&pimple_obj->values, Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1); |
|||
zend_symtable_del(&pimple_obj->factories, Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1); |
|||
zend_symtable_del(&pimple_obj->protected, Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1); |
|||
break; |
|||
case IS_DOUBLE: |
|||
case IS_BOOL: |
|||
case IS_LONG: |
|||
if (Z_TYPE_P(offset) == IS_DOUBLE) { |
|||
index = (ulong)Z_DVAL_P(offset); |
|||
} else { |
|||
index = Z_LVAL_P(offset); |
|||
} |
|||
zend_hash_index_del(&pimple_obj->values, index); |
|||
zend_hash_index_del(&pimple_obj->factories, index); |
|||
zend_hash_index_del(&pimple_obj->protected, index); |
|||
break; |
|||
default: |
|||
zend_error(E_WARNING, "Unsupported offset type"); |
|||
} |
|||
} |
|||
|
|||
static int pimple_object_has_dimension(zval *object, zval *offset, int check_empty TSRMLS_DC) |
|||
{ |
|||
FETCH_DIM_HANDLERS_VARS |
|||
|
|||
pimple_bucket_value *retval = NULL; |
|||
|
|||
switch (Z_TYPE_P(offset)) { |
|||
case IS_STRING: |
|||
if (zend_symtable_find(&pimple_obj->values, Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1, (void **)&retval) == SUCCESS) { |
|||
switch (check_empty) { |
|||
case 0: /* isset */ |
|||
return 1; /* Differs from PHP behavior (Z_TYPE_P(retval->value) != IS_NULL;) */ |
|||
case 1: /* empty */ |
|||
default: |
|||
return zend_is_true(retval->value); |
|||
} |
|||
} |
|||
return 0; |
|||
break; |
|||
case IS_DOUBLE: |
|||
case IS_BOOL: |
|||
case IS_LONG: |
|||
if (Z_TYPE_P(offset) == IS_DOUBLE) { |
|||
index = (ulong)Z_DVAL_P(offset); |
|||
} else { |
|||
index = Z_LVAL_P(offset); |
|||
} |
|||
if (zend_hash_index_find(&pimple_obj->values, index, (void **)&retval) == SUCCESS) { |
|||
switch (check_empty) { |
|||
case 0: /* isset */ |
|||
return 1; /* Differs from PHP behavior (Z_TYPE_P(retval->value) != IS_NULL;)*/ |
|||
case 1: /* empty */ |
|||
default: |
|||
return zend_is_true(retval->value); |
|||
} |
|||
} |
|||
return 0; |
|||
break; |
|||
default: |
|||
zend_error(E_WARNING, "Unsupported offset type"); |
|||
return 0; |
|||
} |
|||
} |
|||
|
|||
static zval *pimple_object_read_dimension(zval *object, zval *offset, int type TSRMLS_DC) |
|||
{ |
|||
FETCH_DIM_HANDLERS_VARS |
|||
|
|||
pimple_bucket_value *retval = NULL; |
|||
zend_fcall_info fci = {0}; |
|||
zval *retval_ptr_ptr = NULL; |
|||
|
|||
switch (Z_TYPE_P(offset)) { |
|||
case IS_STRING: |
|||
if (zend_symtable_find(&pimple_obj->values, Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1, (void **)&retval) == FAILURE) { |
|||
zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0 TSRMLS_CC, "Identifier \"%s\" is not defined.", Z_STRVAL_P(offset)); |
|||
return EG(uninitialized_zval_ptr); |
|||
} |
|||
break; |
|||
case IS_DOUBLE: |
|||
case IS_BOOL: |
|||
case IS_LONG: |
|||
if (Z_TYPE_P(offset) == IS_DOUBLE) { |
|||
index = (ulong)Z_DVAL_P(offset); |
|||
} else { |
|||
index = Z_LVAL_P(offset); |
|||
} |
|||
if (zend_hash_index_find(&pimple_obj->values, index, (void **)&retval) == FAILURE) { |
|||
return EG(uninitialized_zval_ptr); |
|||
} |
|||
break; |
|||
case IS_NULL: /* $p[][3] = 'foo' first dim access */ |
|||
return EG(uninitialized_zval_ptr); |
|||
break; |
|||
default: |
|||
zend_error(E_WARNING, "Unsupported offset type"); |
|||
return EG(uninitialized_zval_ptr); |
|||
} |
|||
|
|||
if(retval->type == PIMPLE_IS_PARAM) { |
|||
return retval->value; |
|||
} |
|||
|
|||
if (zend_hash_index_exists(&pimple_obj->protected, retval->handle_num)) { |
|||
/* Service is protected, return the value every time */ |
|||
return retval->value; |
|||
} |
|||
|
|||
if (zend_hash_index_exists(&pimple_obj->factories, retval->handle_num)) { |
|||
/* Service is a factory, call it everytime and never cache its result */ |
|||
PIMPLE_CALL_CB |
|||
Z_DELREF_P(retval_ptr_ptr); /* fetch dim addr will increment refcount */ |
|||
return retval_ptr_ptr; |
|||
} |
|||
|
|||
if (retval->initialized == 1) { |
|||
/* Service has already been called, return its cached value */ |
|||
return retval->value; |
|||
} |
|||
|
|||
ALLOC_INIT_ZVAL(retval->raw); |
|||
MAKE_COPY_ZVAL(&retval->value, retval->raw); |
|||
|
|||
PIMPLE_CALL_CB |
|||
|
|||
retval->initialized = 1; |
|||
zval_ptr_dtor(&retval->value); |
|||
retval->value = retval_ptr_ptr; |
|||
|
|||
return retval->value; |
|||
} |
|||
|
|||
static int pimple_zval_is_valid_callback(zval *_zval, pimple_bucket_value *_pimple_bucket_value TSRMLS_DC) |
|||
{ |
|||
if (Z_TYPE_P(_zval) != IS_OBJECT) { |
|||
return FAILURE; |
|||
} |
|||
|
|||
if (_pimple_bucket_value->fcc.called_scope) { |
|||
return SUCCESS; |
|||
} |
|||
|
|||
if (Z_OBJ_HANDLER_P(_zval, get_closure) && Z_OBJ_HANDLER_P(_zval, get_closure)(_zval, &_pimple_bucket_value->fcc.calling_scope, &_pimple_bucket_value->fcc.function_handler, &_pimple_bucket_value->fcc.object_ptr TSRMLS_CC) == SUCCESS) { |
|||
_pimple_bucket_value->fcc.called_scope = _pimple_bucket_value->fcc.calling_scope; |
|||
return SUCCESS; |
|||
} else { |
|||
return FAILURE; |
|||
} |
|||
} |
|||
|
|||
static int pimple_zval_to_pimpleval(zval *_zval, pimple_bucket_value *_pimple_bucket_value TSRMLS_DC) |
|||
{ |
|||
_pimple_bucket_value->value = _zval; |
|||
|
|||
if (Z_TYPE_P(_zval) != IS_OBJECT) { |
|||
return PIMPLE_IS_PARAM; |
|||
} |
|||
|
|||
if (pimple_zval_is_valid_callback(_zval, _pimple_bucket_value TSRMLS_CC) == SUCCESS) { |
|||
_pimple_bucket_value->type = PIMPLE_IS_SERVICE; |
|||
_pimple_bucket_value->handle_num = Z_OBJ_HANDLE_P(_zval); |
|||
} |
|||
|
|||
return PIMPLE_IS_SERVICE; |
|||
} |
|||
|
|||
static void pimple_bucket_dtor(pimple_bucket_value *bucket) |
|||
{ |
|||
zval_ptr_dtor(&bucket->value); |
|||
pimple_free_bucket(bucket); |
|||
} |
|||
|
|||
PHP_METHOD(Pimple, protect) |
|||
{ |
|||
zval *protected = NULL; |
|||
pimple_object *pobj = NULL; |
|||
pimple_bucket_value bucket = {0}; |
|||
|
|||
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &protected) == FAILURE) { |
|||
return; |
|||
} |
|||
|
|||
if (pimple_zval_is_valid_callback(protected, &bucket TSRMLS_CC) == FAILURE) { |
|||
pimple_free_bucket(&bucket); |
|||
zend_throw_exception(spl_ce_InvalidArgumentException, "Callable is not a Closure or invokable object.", 0 TSRMLS_CC); |
|||
return; |
|||
} |
|||
|
|||
pimple_zval_to_pimpleval(protected, &bucket TSRMLS_CC); |
|||
pobj = (pimple_object *)zend_object_store_get_object(getThis() TSRMLS_CC); |
|||
|
|||
if (zend_hash_index_update(&pobj->protected, bucket.handle_num, (void *)&bucket, sizeof(pimple_bucket_value), NULL) == SUCCESS) { |
|||
Z_ADDREF_P(protected); |
|||
RETURN_ZVAL(protected, 1 , 0); |
|||
} else { |
|||
pimple_free_bucket(&bucket); |
|||
} |
|||
RETURN_FALSE; |
|||
} |
|||
|
|||
PHP_METHOD(Pimple, raw) |
|||
{ |
|||
zval *offset = NULL; |
|||
pimple_object *pobj = NULL; |
|||
pimple_bucket_value *value = NULL; |
|||
ulong index; |
|||
|
|||
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &offset) == FAILURE) { |
|||
return; |
|||
} |
|||
|
|||
pobj = zend_object_store_get_object(getThis() TSRMLS_CC); |
|||
|
|||
switch (Z_TYPE_P(offset)) { |
|||
case IS_STRING: |
|||
if (zend_symtable_find(&pobj->values, Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1, (void *)&value) == FAILURE) { |
|||
zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0 TSRMLS_CC, "Identifier \"%s\" is not defined.", Z_STRVAL_P(offset)); |
|||
RETURN_NULL(); |
|||
} |
|||
break; |
|||
case IS_DOUBLE: |
|||
case IS_BOOL: |
|||
case IS_LONG: |
|||
if (Z_TYPE_P(offset) == IS_DOUBLE) { |
|||
index = (ulong)Z_DVAL_P(offset); |
|||
} else { |
|||
index = Z_LVAL_P(offset); |
|||
} |
|||
if (zend_hash_index_find(&pobj->values, index, (void *)&value) == FAILURE) { |
|||
RETURN_NULL(); |
|||
} |
|||
break; |
|||
case IS_NULL: |
|||
default: |
|||
zend_error(E_WARNING, "Unsupported offset type"); |
|||
} |
|||
|
|||
if (value->raw) { |
|||
RETVAL_ZVAL(value->raw, 1, 0); |
|||
} else { |
|||
RETVAL_ZVAL(value->value, 1, 0); |
|||
} |
|||
} |
|||
|
|||
PHP_METHOD(Pimple, extend) |
|||
{ |
|||
zval *offset = NULL, *callable = NULL, *pimple_closure_obj = NULL; |
|||
pimple_bucket_value bucket = {0}, *value = NULL; |
|||
pimple_object *pobj = NULL; |
|||
pimple_closure_object *pcobj = NULL; |
|||
ulong index; |
|||
|
|||
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz", &offset, &callable) == FAILURE) { |
|||
return; |
|||
} |
|||
|
|||
pobj = zend_object_store_get_object(getThis() TSRMLS_CC); |
|||
|
|||
switch (Z_TYPE_P(offset)) { |
|||
case IS_STRING: |
|||
if (zend_symtable_find(&pobj->values, Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1, (void *)&value) == FAILURE) { |
|||
zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0 TSRMLS_CC, "Identifier \"%s\" is not defined.", Z_STRVAL_P(offset)); |
|||
RETURN_NULL(); |
|||
} |
|||
if (value->type != PIMPLE_IS_SERVICE) { |
|||
zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0 TSRMLS_CC, "Identifier \"%s\" does not contain an object definition.", Z_STRVAL_P(offset)); |
|||
RETURN_NULL(); |
|||
} |
|||
break; |
|||
case IS_DOUBLE: |
|||
case IS_BOOL: |
|||
case IS_LONG: |
|||
if (Z_TYPE_P(offset) == IS_DOUBLE) { |
|||
index = (ulong)Z_DVAL_P(offset); |
|||
} else { |
|||
index = Z_LVAL_P(offset); |
|||
} |
|||
if (zend_hash_index_find(&pobj->values, index, (void *)&value) == FAILURE) { |
|||
zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0 TSRMLS_CC, "Identifier \"%ld\" is not defined.", index); |
|||
RETURN_NULL(); |
|||
} |
|||
if (value->type != PIMPLE_IS_SERVICE) { |
|||
zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0 TSRMLS_CC, "Identifier \"%ld\" does not contain an object definition.", index); |
|||
RETURN_NULL(); |
|||
} |
|||
break; |
|||
case IS_NULL: |
|||
default: |
|||
zend_error(E_WARNING, "Unsupported offset type"); |
|||
} |
|||
|
|||
if (pimple_zval_is_valid_callback(callable, &bucket TSRMLS_CC) == FAILURE) { |
|||
pimple_free_bucket(&bucket); |
|||
zend_throw_exception(spl_ce_InvalidArgumentException, "Extension service definition is not a Closure or invokable object.", 0 TSRMLS_CC); |
|||
RETURN_NULL(); |
|||
} |
|||
pimple_free_bucket(&bucket); |
|||
|
|||
ALLOC_INIT_ZVAL(pimple_closure_obj); |
|||
object_init_ex(pimple_closure_obj, pimple_closure_ce); |
|||
|
|||
pcobj = zend_object_store_get_object(pimple_closure_obj TSRMLS_CC); |
|||
pcobj->callable = callable; |
|||
pcobj->factory = value->value; |
|||
Z_ADDREF_P(callable); |
|||
Z_ADDREF_P(value->value); |
|||
|
|||
if (zend_hash_index_exists(&pobj->factories, value->handle_num)) { |
|||
pimple_zval_to_pimpleval(pimple_closure_obj, &bucket TSRMLS_CC); |
|||
zend_hash_index_del(&pobj->factories, value->handle_num); |
|||
zend_hash_index_update(&pobj->factories, bucket.handle_num, (void *)&bucket, sizeof(pimple_bucket_value), NULL); |
|||
Z_ADDREF_P(pimple_closure_obj); |
|||
} |
|||
|
|||
pimple_object_write_dimension(getThis(), offset, pimple_closure_obj TSRMLS_CC); |
|||
|
|||
RETVAL_ZVAL(pimple_closure_obj, 1, 1); |
|||
} |
|||
|
|||
PHP_METHOD(Pimple, keys) |
|||
{ |
|||
HashPosition pos; |
|||
pimple_object *pobj = NULL; |
|||
zval **value = NULL; |
|||
zval *endval = NULL; |
|||
char *str_index = NULL; |
|||
int str_len; |
|||
ulong num_index; |
|||
|
|||
if (zend_parse_parameters_none() == FAILURE) { |
|||
return; |
|||
} |
|||
|
|||
pobj = zend_object_store_get_object(getThis() TSRMLS_CC); |
|||
array_init_size(return_value, zend_hash_num_elements(&pobj->values)); |
|||
|
|||
zend_hash_internal_pointer_reset_ex(&pobj->values, &pos); |
|||
|
|||
while(zend_hash_get_current_data_ex(&pobj->values, (void **)&value, &pos) == SUCCESS) { |
|||
MAKE_STD_ZVAL(endval); |
|||
switch (zend_hash_get_current_key_ex(&pobj->values, &str_index, (uint *)&str_len, &num_index, 0, &pos)) { |
|||
case HASH_KEY_IS_STRING: |
|||
ZVAL_STRINGL(endval, str_index, str_len - 1, 1); |
|||
zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &endval, sizeof(zval *), NULL); |
|||
break; |
|||
case HASH_KEY_IS_LONG: |
|||
ZVAL_LONG(endval, num_index); |
|||
zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &endval, sizeof(zval *), NULL); |
|||
break; |
|||
} |
|||
zend_hash_move_forward_ex(&pobj->values, &pos); |
|||
} |
|||
} |
|||
|
|||
PHP_METHOD(Pimple, factory) |
|||
{ |
|||
zval *factory = NULL; |
|||
pimple_object *pobj = NULL; |
|||
pimple_bucket_value bucket = {0}; |
|||
|
|||
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &factory) == FAILURE) { |
|||
return; |
|||
} |
|||
|
|||
if (pimple_zval_is_valid_callback(factory, &bucket TSRMLS_CC) == FAILURE) { |
|||
pimple_free_bucket(&bucket); |
|||
zend_throw_exception(spl_ce_InvalidArgumentException, "Service definition is not a Closure or invokable object.", 0 TSRMLS_CC); |
|||
return; |
|||
} |
|||
|
|||
pimple_zval_to_pimpleval(factory, &bucket TSRMLS_CC); |
|||
pobj = (pimple_object *)zend_object_store_get_object(getThis() TSRMLS_CC); |
|||
|
|||
if (zend_hash_index_update(&pobj->factories, bucket.handle_num, (void *)&bucket, sizeof(pimple_bucket_value), NULL) == SUCCESS) { |
|||
Z_ADDREF_P(factory); |
|||
RETURN_ZVAL(factory, 1 , 0); |
|||
} else { |
|||
pimple_free_bucket(&bucket); |
|||
} |
|||
|
|||
RETURN_FALSE; |
|||
} |
|||
|
|||
PHP_METHOD(Pimple, offsetSet) |
|||
{ |
|||
zval *offset = NULL, *value = NULL; |
|||
|
|||
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz", &offset, &value) == FAILURE) { |
|||
return; |
|||
} |
|||
|
|||
pimple_object_write_dimension(getThis(), offset, value TSRMLS_CC); |
|||
} |
|||
|
|||
PHP_METHOD(Pimple, offsetGet) |
|||
{ |
|||
zval *offset = NULL, *retval = NULL; |
|||
|
|||
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &offset) == FAILURE) { |
|||
return; |
|||
} |
|||
|
|||
retval = pimple_object_read_dimension(getThis(), offset, 0 TSRMLS_CC); |
|||
|
|||
RETVAL_ZVAL(retval, 1, 0); |
|||
} |
|||
|
|||
PHP_METHOD(Pimple, offsetUnset) |
|||
{ |
|||
zval *offset = NULL; |
|||
|
|||
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &offset) == FAILURE) { |
|||
return; |
|||
} |
|||
|
|||
pimple_object_unset_dimension(getThis(), offset TSRMLS_CC); |
|||
} |
|||
|
|||
PHP_METHOD(Pimple, offsetExists) |
|||
{ |
|||
zval *offset = NULL; |
|||
|
|||
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &offset) == FAILURE) { |
|||
return; |
|||
} |
|||
|
|||
RETVAL_BOOL(pimple_object_has_dimension(getThis(), offset, 1 TSRMLS_CC)); |
|||
} |
|||
|
|||
PHP_METHOD(Pimple, register) |
|||
{ |
|||
zval *provider; |
|||
zval **data; |
|||
zval *retval = NULL; |
|||
zval key; |
|||
|
|||
HashTable *array = NULL; |
|||
HashPosition pos; |
|||
|
|||
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O|h", &provider, pimple_serviceprovider_ce, &array) == FAILURE) { |
|||
return; |
|||
} |
|||
|
|||
RETVAL_ZVAL(getThis(), 1, 0); |
|||
|
|||
zend_call_method_with_1_params(&provider, Z_OBJCE_P(provider), NULL, "register", &retval, getThis()); |
|||
|
|||
if (retval) { |
|||
zval_ptr_dtor(&retval); |
|||
} |
|||
|
|||
if (!array) { |
|||
return; |
|||
} |
|||
|
|||
zend_hash_internal_pointer_reset_ex(array, &pos); |
|||
|
|||
while(zend_hash_get_current_data_ex(array, (void **)&data, &pos) == SUCCESS) { |
|||
zend_hash_get_current_key_zval_ex(array, &key, &pos); |
|||
pimple_object_write_dimension(getThis(), &key, *data TSRMLS_CC); |
|||
zend_hash_move_forward_ex(array, &pos); |
|||
} |
|||
} |
|||
|
|||
PHP_METHOD(Pimple, __construct) |
|||
{ |
|||
zval *values = NULL, **pData = NULL, offset; |
|||
HashPosition pos; |
|||
char *str_index = NULL; |
|||
zend_uint str_length; |
|||
ulong num_index; |
|||
|
|||
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a!", &values) == FAILURE || !values) { |
|||
return; |
|||
} |
|||
|
|||
zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(values), &pos); |
|||
while (zend_hash_has_more_elements_ex(Z_ARRVAL_P(values), &pos) == SUCCESS) { |
|||
zend_hash_get_current_data_ex(Z_ARRVAL_P(values), (void **)&pData, &pos); |
|||
zend_hash_get_current_key_ex(Z_ARRVAL_P(values), &str_index, &str_length, &num_index, 0, &pos); |
|||
INIT_ZVAL(offset); |
|||
if (zend_hash_get_current_key_type_ex(Z_ARRVAL_P(values), &pos) == HASH_KEY_IS_LONG) { |
|||
ZVAL_LONG(&offset, num_index); |
|||
} else { |
|||
ZVAL_STRINGL(&offset, str_index, (str_length - 1), 0); |
|||
} |
|||
pimple_object_write_dimension(getThis(), &offset, *pData TSRMLS_CC); |
|||
zend_hash_move_forward_ex(Z_ARRVAL_P(values), &pos); |
|||
} |
|||
} |
|||
|
|||
/* |
|||
* This is PHP code snippet handling extend()s calls : |
|||
|
|||
$extended = function ($c) use ($callable, $factory) { |
|||
return $callable($factory($c), $c); |
|||
}; |
|||
|
|||
*/ |
|||
PHP_METHOD(PimpleClosure, invoker) |
|||
{ |
|||
pimple_closure_object *pcobj = NULL; |
|||
zval *arg = NULL, *retval = NULL, *newretval = NULL; |
|||
zend_fcall_info fci = {0}; |
|||
zval **args[2]; |
|||
|
|||
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &arg) == FAILURE) { |
|||
return; |
|||
} |
|||
|
|||
pcobj = zend_object_store_get_object(getThis() TSRMLS_CC); |
|||
|
|||
fci.function_name = pcobj->factory; |
|||
args[0] = &arg; |
|||
zend_fcall_info_argp(&fci TSRMLS_CC, 1, args); |
|||
fci.retval_ptr_ptr = &retval; |
|||
fci.size = sizeof(fci); |
|||
|
|||
if (zend_call_function(&fci, NULL TSRMLS_CC) == FAILURE || EG(exception)) { |
|||
efree(fci.params); |
|||
return; /* Should here return default zval */ |
|||
} |
|||
|
|||
efree(fci.params); |
|||
memset(&fci, 0, sizeof(fci)); |
|||
fci.size = sizeof(fci); |
|||
|
|||
fci.function_name = pcobj->callable; |
|||
args[0] = &retval; |
|||
args[1] = &arg; |
|||
zend_fcall_info_argp(&fci TSRMLS_CC, 2, args); |
|||
fci.retval_ptr_ptr = &newretval; |
|||
|
|||
if (zend_call_function(&fci, NULL TSRMLS_CC) == FAILURE || EG(exception)) { |
|||
efree(fci.params); |
|||
zval_ptr_dtor(&retval); |
|||
return; |
|||
} |
|||
|
|||
efree(fci.params); |
|||
zval_ptr_dtor(&retval); |
|||
|
|||
RETVAL_ZVAL(newretval, 1 ,1); |
|||
} |
|||
|
|||
PHP_MINIT_FUNCTION(pimple) |
|||
{ |
|||
zend_class_entry tmp_pimple_ce, tmp_pimple_closure_ce, tmp_pimple_serviceprovider_iface_ce; |
|||
INIT_NS_CLASS_ENTRY(tmp_pimple_ce, PIMPLE_NS, "Container", pimple_ce_functions); |
|||
INIT_NS_CLASS_ENTRY(tmp_pimple_closure_ce, PIMPLE_NS, "ContainerClosure", NULL); |
|||
INIT_NS_CLASS_ENTRY(tmp_pimple_serviceprovider_iface_ce, PIMPLE_NS, "ServiceProviderInterface", pimple_serviceprovider_iface_ce_functions); |
|||
|
|||
tmp_pimple_ce.create_object = pimple_object_create; |
|||
tmp_pimple_closure_ce.create_object = pimple_closure_object_create; |
|||
|
|||
pimple_ce = zend_register_internal_class(&tmp_pimple_ce TSRMLS_CC); |
|||
zend_class_implements(pimple_ce TSRMLS_CC, 1, zend_ce_arrayaccess); |
|||
|
|||
pimple_closure_ce = zend_register_internal_class(&tmp_pimple_closure_ce TSRMLS_CC); |
|||
pimple_closure_ce->ce_flags |= ZEND_ACC_FINAL_CLASS; |
|||
|
|||
pimple_serviceprovider_ce = zend_register_internal_interface(&tmp_pimple_serviceprovider_iface_ce TSRMLS_CC); |
|||
|
|||
memcpy(&pimple_closure_object_handlers, zend_get_std_object_handlers(), sizeof(*zend_get_std_object_handlers())); |
|||
pimple_object_handlers = std_object_handlers; |
|||
pimple_closure_object_handlers.get_closure = pimple_closure_get_closure; |
|||
|
|||
pimple_closure_invoker_function.function_name = "Pimple closure internal invoker"; |
|||
pimple_closure_invoker_function.fn_flags |= ZEND_ACC_CLOSURE; |
|||
pimple_closure_invoker_function.handler = ZEND_MN(PimpleClosure_invoker); |
|||
pimple_closure_invoker_function.num_args = 1; |
|||
pimple_closure_invoker_function.required_num_args = 1; |
|||
pimple_closure_invoker_function.scope = pimple_closure_ce; |
|||
pimple_closure_invoker_function.type = ZEND_INTERNAL_FUNCTION; |
|||
pimple_closure_invoker_function.module = &pimple_module_entry; |
|||
|
|||
return SUCCESS; |
|||
} |
|||
|
|||
PHP_MINFO_FUNCTION(pimple) |
|||
{ |
|||
php_info_print_table_start(); |
|||
php_info_print_table_header(2, "SensioLabs Pimple C support", "enabled"); |
|||
php_info_print_table_row(2, "Pimple supported version", PIMPLE_VERSION); |
|||
php_info_print_table_end(); |
|||
|
|||
php_info_print_box_start(0); |
|||
php_write((void *)ZEND_STRL("SensioLabs Pimple C support developed by Julien Pauli") TSRMLS_CC); |
|||
if (!sapi_module.phpinfo_as_text) { |
|||
php_write((void *)ZEND_STRL(sensiolabs_logo) TSRMLS_CC); |
|||
} |
|||
php_info_print_box_end(); |
|||
} |
|||
|
|||
zend_module_entry pimple_module_entry = { |
|||
STANDARD_MODULE_HEADER, |
|||
"pimple", |
|||
NULL, |
|||
PHP_MINIT(pimple), |
|||
NULL, |
|||
NULL, |
|||
NULL, |
|||
PHP_MINFO(pimple), |
|||
PIMPLE_VERSION, |
|||
STANDARD_MODULE_PROPERTIES |
|||
}; |
|||
|
|||
#ifdef COMPILE_DL_PIMPLE |
|||
ZEND_GET_MODULE(pimple) |
|||
#endif |
@ -1,81 +0,0 @@ |
|||
|
|||
/* |
|||
* This file is part of Pimple. |
|||
* |
|||
* Copyright (c) 2014 Fabien Potencier |
|||
* |
|||
* Permission is hereby granted, free of charge, to any person obtaining a copy |
|||
* of this software and associated documentation files (the "Software"), to deal |
|||
* in the Software without restriction, including without limitation the rights |
|||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|||
* copies of the Software, and to permit persons to whom the Software is furnished |
|||
* to do so, subject to the following conditions: |
|||
* |
|||
* The above copyright notice and this permission notice shall be included in all |
|||
* copies or substantial portions of the Software. |
|||
* |
|||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
|||
* THE SOFTWARE. |
|||
*/ |
|||
|
|||
#ifndef PIMPLE_COMPAT_H_ |
|||
#define PIMPLE_COMPAT_H_ |
|||
|
|||
#include "Zend/zend_extensions.h" /* for ZEND_EXTENSION_API_NO */ |
|||
|
|||
#define PHP_5_0_X_API_NO 220040412 |
|||
#define PHP_5_1_X_API_NO 220051025 |
|||
#define PHP_5_2_X_API_NO 220060519 |
|||
#define PHP_5_3_X_API_NO 220090626 |
|||
#define PHP_5_4_X_API_NO 220100525 |
|||
#define PHP_5_5_X_API_NO 220121212 |
|||
#define PHP_5_6_X_API_NO 220131226 |
|||
|
|||
#define IS_PHP_56 ZEND_EXTENSION_API_NO == PHP_5_6_X_API_NO |
|||
#define IS_AT_LEAST_PHP_56 ZEND_EXTENSION_API_NO >= PHP_5_6_X_API_NO |
|||
|
|||
#define IS_PHP_55 ZEND_EXTENSION_API_NO == PHP_5_5_X_API_NO |
|||
#define IS_AT_LEAST_PHP_55 ZEND_EXTENSION_API_NO >= PHP_5_5_X_API_NO |
|||
|
|||
#define IS_PHP_54 ZEND_EXTENSION_API_NO == PHP_5_4_X_API_NO |
|||
#define IS_AT_LEAST_PHP_54 ZEND_EXTENSION_API_NO >= PHP_5_4_X_API_NO |
|||
|
|||
#define IS_PHP_53 ZEND_EXTENSION_API_NO == PHP_5_3_X_API_NO |
|||
#define IS_AT_LEAST_PHP_53 ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO |
|||
|
|||
#if IS_PHP_53 |
|||
#define object_properties_init(obj, ce) do { \ |
|||
zend_hash_copy(obj->properties, &ce->default_properties, zval_copy_property_ctor(ce), NULL, sizeof(zval *)); \ |
|||
} while (0); |
|||
#endif |
|||
|
|||
#define ZEND_OBJ_INIT(obj, ce) do { \ |
|||
zend_object_std_init(obj, ce TSRMLS_CC); \ |
|||
object_properties_init((obj), (ce)); \ |
|||
} while(0); |
|||
|
|||
#if IS_PHP_53 || IS_PHP_54 |
|||
static void zend_hash_get_current_key_zval_ex(const HashTable *ht, zval *key, HashPosition *pos) { |
|||
Bucket *p; |
|||
|
|||
p = pos ? (*pos) : ht->pInternalPointer; |
|||
|
|||
if (!p) { |
|||
Z_TYPE_P(key) = IS_NULL; |
|||
} else if (p->nKeyLength) { |
|||
Z_TYPE_P(key) = IS_STRING; |
|||
Z_STRVAL_P(key) = estrndup(p->arKey, p->nKeyLength - 1); |
|||
Z_STRLEN_P(key) = p->nKeyLength - 1; |
|||
} else { |
|||
Z_TYPE_P(key) = IS_LONG; |
|||
Z_LVAL_P(key) = p->h; |
|||
} |
|||
} |
|||
#endif |
|||
|
|||
#endif /* PIMPLE_COMPAT_H_ */ |
@ -1,45 +0,0 @@ |
|||
--TEST-- |
|||
Test for read_dim/write_dim handlers |
|||
--SKIPIF-- |
|||
<?php if (!extension_loaded("pimple")) print "skip"; ?> |
|||
--FILE-- |
|||
<?php |
|||
$p = new Pimple\Container(); |
|||
$p[42] = 'foo'; |
|||
$p['foo'] = 42; |
|||
|
|||
echo $p[42]; |
|||
echo "\n"; |
|||
echo $p['foo']; |
|||
echo "\n"; |
|||
try { |
|||
var_dump($p['nonexistant']); |
|||
echo "Exception excpected"; |
|||
} catch (InvalidArgumentException $e) { } |
|||
|
|||
$p[54.2] = 'foo2'; |
|||
echo $p[54]; |
|||
echo "\n"; |
|||
$p[242.99] = 'foo99'; |
|||
echo $p[242]; |
|||
|
|||
echo "\n"; |
|||
|
|||
$p[5] = 'bar'; |
|||
$p[5] = 'baz'; |
|||
echo $p[5]; |
|||
|
|||
echo "\n"; |
|||
|
|||
$p['str'] = 'str'; |
|||
$p['str'] = 'strstr'; |
|||
echo $p['str']; |
|||
?> |
|||
|
|||
--EXPECTF-- |
|||
foo |
|||
42 |
|||
foo2 |
|||
foo99 |
|||
baz |
|||
strstr |
@ -1,15 +0,0 @@ |
|||
--TEST-- |
|||
Test for constructor |
|||
--SKIPIF-- |
|||
<?php if (!extension_loaded("pimple")) print "skip"; ?> |
|||
--FILE-- |
|||
<?php |
|||
$p = new Pimple\Container(); |
|||
var_dump($p[42]); |
|||
|
|||
$p = new Pimple\Container(array(42=>'foo')); |
|||
var_dump($p[42]); |
|||
?> |
|||
--EXPECT-- |
|||
NULL |
|||
string(3) "foo" |
@ -1,16 +0,0 @@ |
|||
--TEST-- |
|||
Test empty dimensions |
|||
--SKIPIF-- |
|||
<?php if (!extension_loaded("pimple")) print "skip"; ?> |
|||
--FILE-- |
|||
<?php |
|||
$p = new Pimple\Container(); |
|||
$p[] = 42; |
|||
var_dump($p[0]); |
|||
$p[41] = 'foo'; |
|||
$p[] = 'bar'; |
|||
var_dump($p[42]); |
|||
?> |
|||
--EXPECT-- |
|||
int(42) |
|||
string(3) "bar" |
@ -1,30 +0,0 @@ |
|||
--TEST-- |
|||
Test has/unset dim handlers |
|||
--SKIPIF-- |
|||
<?php if (!extension_loaded("pimple")) print "skip"; ?> |
|||
--FILE-- |
|||
<?php |
|||
$p = new Pimple\Container(); |
|||
$p[] = 42; |
|||
var_dump($p[0]); |
|||
unset($p[0]); |
|||
var_dump($p[0]); |
|||
$p['foo'] = 'bar'; |
|||
var_dump(isset($p['foo'])); |
|||
unset($p['foo']); |
|||
try { |
|||
var_dump($p['foo']); |
|||
echo "Excpected exception"; |
|||
} catch (InvalidArgumentException $e) { } |
|||
var_dump(isset($p['bar'])); |
|||
$p['bar'] = NULL; |
|||
var_dump(isset($p['bar'])); |
|||
var_dump(empty($p['bar'])); |
|||
?> |
|||
--EXPECT-- |
|||
int(42) |
|||
NULL |
|||
bool(true) |
|||
bool(false) |
|||
bool(true) |
|||
bool(true) |
@ -1,27 +0,0 @@ |
|||
--TEST-- |
|||
Test simple class inheritance |
|||
--SKIPIF-- |
|||
<?php if (!extension_loaded("pimple")) print "skip"; ?> |
|||
--FILE-- |
|||
<?php |
|||
class MyPimple extends Pimple\Container |
|||
{ |
|||
public $someAttr = 'fooAttr'; |
|||
|
|||
public function offsetget($o) |
|||
{ |
|||
var_dump("hit"); |
|||
return parent::offsetget($o); |
|||
} |
|||
} |
|||
|
|||
$p = new MyPimple; |
|||
$p[42] = 'foo'; |
|||
echo $p[42]; |
|||
echo "\n"; |
|||
echo $p->someAttr; |
|||
?> |
|||
--EXPECT-- |
|||
string(3) "hit" |
|||
foo |
|||
fooAttr |
@ -1,51 +0,0 @@ |
|||
--TEST-- |
|||
Test complex class inheritance |
|||
--SKIPIF-- |
|||
<?php if (!extension_loaded("pimple")) print "skip"; ?> |
|||
--FILE-- |
|||
<?php |
|||
class MyPimple extends Pimple\Container |
|||
{ |
|||
public function offsetget($o) |
|||
{ |
|||
var_dump("hit offsetget in " . __CLASS__); |
|||
return parent::offsetget($o); |
|||
} |
|||
} |
|||
|
|||
class TestPimple extends MyPimple |
|||
{ |
|||
public function __construct($values) |
|||
{ |
|||
array_shift($values); |
|||
parent::__construct($values); |
|||
} |
|||
|
|||
public function offsetget($o) |
|||
{ |
|||
var_dump('hit offsetget in ' . __CLASS__); |
|||
return parent::offsetget($o); |
|||
} |
|||
|
|||
public function offsetset($o, $v) |
|||
{ |
|||
var_dump('hit offsetset'); |
|||
return parent::offsetset($o, $v); |
|||
} |
|||
} |
|||
|
|||
$defaultValues = array('foo' => 'bar', 88 => 'baz'); |
|||
|
|||
$p = new TestPimple($defaultValues); |
|||
$p[42] = 'foo'; |
|||
var_dump($p[42]); |
|||
var_dump($p[0]); |
|||
?> |
|||
--EXPECT-- |
|||
string(13) "hit offsetset" |
|||
string(27) "hit offsetget in TestPimple" |
|||
string(25) "hit offsetget in MyPimple" |
|||
string(3) "foo" |
|||
string(27) "hit offsetget in TestPimple" |
|||
string(25) "hit offsetget in MyPimple" |
|||
string(3) "baz" |
@ -1,22 +0,0 @@ |
|||
--TEST-- |
|||
Test for read_dim/write_dim handlers |
|||
--SKIPIF-- |
|||
<?php if (!extension_loaded("pimple")) print "skip"; ?> |
|||
--FILE-- |
|||
<?php |
|||
$p = new Pimple\Container(); |
|||
$p[42] = 'foo'; |
|||
$p['foo'] = 42; |
|||
|
|||
echo $p[42]; |
|||
echo "\n"; |
|||
echo $p['foo']; |
|||
echo "\n"; |
|||
try { |
|||
var_dump($p['nonexistant']); |
|||
echo "Exception excpected"; |
|||
} catch (InvalidArgumentException $e) { } |
|||
?> |
|||
--EXPECTF-- |
|||
foo |
|||
42 |
@ -1,29 +0,0 @@ |
|||
--TEST-- |
|||
Test frozen services |
|||
--SKIPIF-- |
|||
<?php if (!extension_loaded("pimple")) print "skip"; ?> |
|||
--FILE-- |
|||
<?php |
|||
$p = new Pimple\Container(); |
|||
$p[42] = 'foo'; |
|||
$p[42] = 'bar'; |
|||
|
|||
$p['foo'] = function () { }; |
|||
$p['foo'] = function () { }; |
|||
|
|||
$a = $p['foo']; |
|||
|
|||
try { |
|||
$p['foo'] = function () { }; |
|||
echo "Exception excpected"; |
|||
} catch (RuntimeException $e) { } |
|||
|
|||
$p[42] = function() { }; |
|||
$a = $p[42]; |
|||
|
|||
try { |
|||
$p[42] = function () { }; |
|||
echo "Exception excpected"; |
|||
} catch (RuntimeException $e) { } |
|||
?> |
|||
--EXPECTF-- |
@ -1,13 +0,0 @@ |
|||
--TEST-- |
|||
Test service is called as callback, and only once |
|||
--SKIPIF-- |
|||
<?php if (!extension_loaded("pimple")) print "skip"; ?> |
|||
--FILE-- |
|||
<?php |
|||
$p = new Pimple\Container(); |
|||
$p['foo'] = function($arg) use ($p) { var_dump($p === $arg); }; |
|||
$a = $p['foo']; |
|||
$b = $p['foo']; /* should return not calling the callback */ |
|||
?> |
|||
--EXPECTF-- |
|||
bool(true) |
@ -1,45 +0,0 @@ |
|||
--TEST-- |
|||
Test service is called as callback for every callback type |
|||
--SKIPIF-- |
|||
<?php if (!extension_loaded("pimple")) print "skip"; ?> |
|||
--FILE-- |
|||
<?php |
|||
function callme() |
|||
{ |
|||
return 'called'; |
|||
} |
|||
|
|||
$a = function() { return 'called'; }; |
|||
|
|||
class Foo |
|||
{ |
|||
public static function bar() |
|||
{ |
|||
return 'called'; |
|||
} |
|||
} |
|||
|
|||
$p = new Pimple\Container(); |
|||
$p['foo'] = 'callme'; |
|||
echo $p['foo'] . "\n"; |
|||
|
|||
$p['bar'] = $a; |
|||
echo $p['bar'] . "\n"; |
|||
|
|||
$p['baz'] = "Foo::bar"; |
|||
echo $p['baz'] . "\n"; |
|||
|
|||
$p['foobar'] = array('Foo', 'bar'); |
|||
var_dump($p['foobar']); |
|||
|
|||
?> |
|||
--EXPECTF-- |
|||
callme |
|||
called |
|||
Foo::bar |
|||
array(2) { |
|||
[0]=> |
|||
string(3) "Foo" |
|||
[1]=> |
|||
string(3) "bar" |
|||
} |
@ -1,19 +0,0 @@ |
|||
--TEST-- |
|||
Test service callback throwing an exception |
|||
--SKIPIF-- |
|||
<?php if (!extension_loaded("pimple")) print "skip"; ?> |
|||
--FILE-- |
|||
<?php |
|||
class CallBackException extends RuntimeException { } |
|||
|
|||
$p = new Pimple\Container(); |
|||
$p['foo'] = function () { throw new CallBackException; }; |
|||
try { |
|||
echo $p['foo'] . "\n"; |
|||
echo "should not come here"; |
|||
} catch (CallBackException $e) { |
|||
echo "all right!"; |
|||
} |
|||
?> |
|||
--EXPECTF-- |
|||
all right! |
@ -1,28 +0,0 @@ |
|||
--TEST-- |
|||
Test service factory |
|||
--SKIPIF-- |
|||
<?php if (!extension_loaded("pimple")) print "skip"; ?> |
|||
--FILE-- |
|||
<?php |
|||
|
|||
$p = new Pimple\Container(); |
|||
|
|||
$p->factory($f = function() { var_dump('called-1'); return 'ret-1';}); |
|||
|
|||
$p[] = $f; |
|||
|
|||
$p[] = function () { var_dump('called-2'); return 'ret-2'; }; |
|||
|
|||
var_dump($p[0]); |
|||
var_dump($p[0]); |
|||
var_dump($p[1]); |
|||
var_dump($p[1]); |
|||
?> |
|||
--EXPECTF-- |
|||
string(8) "called-1" |
|||
string(5) "ret-1" |
|||
string(8) "called-1" |
|||
string(5) "ret-1" |
|||
string(8) "called-2" |
|||
string(5) "ret-2" |
|||
string(5) "ret-2" |
@ -1,33 +0,0 @@ |
|||
--TEST-- |
|||
Test keys() |
|||
--SKIPIF-- |
|||
<?php if (!extension_loaded("pimple")) print "skip"; ?> |
|||
--FILE-- |
|||
<?php |
|||
|
|||
$p = new Pimple\Container(); |
|||
|
|||
var_dump($p->keys()); |
|||
|
|||
$p['foo'] = 'bar'; |
|||
$p[] = 'foo'; |
|||
|
|||
var_dump($p->keys()); |
|||
|
|||
unset($p['foo']); |
|||
|
|||
var_dump($p->keys()); |
|||
?> |
|||
--EXPECTF-- |
|||
array(0) { |
|||
} |
|||
array(2) { |
|||
[0]=> |
|||
string(3) "foo" |
|||
[1]=> |
|||
int(0) |
|||
} |
|||
array(1) { |
|||
[0]=> |
|||
int(0) |
|||
} |
@ -1,30 +0,0 @@ |
|||
--TEST-- |
|||
Test raw() |
|||
--SKIPIF-- |
|||
<?php if (!extension_loaded("pimple")) print "skip"; ?> |
|||
--FILE-- |
|||
<?php |
|||
|
|||
$p = new Pimple\Container(); |
|||
$f = function () { var_dump('called-2'); return 'ret-2'; }; |
|||
|
|||
$p['foo'] = $f; |
|||
$p[42] = $f; |
|||
|
|||
var_dump($p['foo']); |
|||
var_dump($p->raw('foo')); |
|||
var_dump($p[42]); |
|||
|
|||
unset($p['foo']); |
|||
|
|||
try { |
|||
$p->raw('foo'); |
|||
echo "expected exception"; |
|||
} catch (InvalidArgumentException $e) { } |
|||
--EXPECTF-- |
|||
string(8) "called-2" |
|||
string(5) "ret-2" |
|||
object(Closure)#%i (0) { |
|||
} |
|||
string(8) "called-2" |
|||
string(5) "ret-2" |
@ -1,17 +0,0 @@ |
|||
--TEST-- |
|||
Test protect() |
|||
--SKIPIF-- |
|||
<?php if (!extension_loaded("pimple")) print "skip"; ?> |
|||
--FILE-- |
|||
<?php |
|||
|
|||
$p = new Pimple\Container(); |
|||
$f = function () { return 'foo'; }; |
|||
$p['foo'] = $f; |
|||
|
|||
$p->protect($f); |
|||
|
|||
var_dump($p['foo']); |
|||
--EXPECTF-- |
|||
object(Closure)#%i (0) { |
|||
} |
@ -1,24 +0,0 @@ |
|||
--TEST-- |
|||
Test extend() |
|||
--SKIPIF-- |
|||
<?php if (!extension_loaded("pimple")) print "skip"; ?> |
|||
--FILE-- |
|||
<?php |
|||
/* |
|||
This is part of Pimple::extend() code : |
|||
|
|||
$extended = function ($c) use ($callable, $factory) { |
|||
return $callable($factory($c), $c); |
|||
}; |
|||
*/ |
|||
|
|||
$p = new Pimple\Container(); |
|||
$p[12] = function ($v) { var_dump($v); return 'foo';}; /* $factory in code above */ |
|||
|
|||
$c = $p->extend(12, function ($w) { var_dump($w); return 'bar'; }); /* $callable in code above */ |
|||
|
|||
var_dump($c('param')); |
|||
--EXPECTF-- |
|||
string(5) "param" |
|||
string(3) "foo" |
|||
string(3) "bar" |
@ -1,17 +0,0 @@ |
|||
--TEST-- |
|||
Test extend() with exception in service extension |
|||
--SKIPIF-- |
|||
<?php if (!extension_loaded("pimple")) print "skip"; ?> |
|||
--FILE-- |
|||
<?php |
|||
|
|||
$p = new Pimple\Container(); |
|||
$p[12] = function ($v) { return 'foo';}; |
|||
|
|||
$c = $p->extend(12, function ($w) { throw new BadMethodCallException; }); |
|||
|
|||
try { |
|||
$p[12]; |
|||
echo "Exception expected"; |
|||
} catch (BadMethodCallException $e) { } |
|||
--EXPECTF-- |
@ -1,17 +0,0 @@ |
|||
--TEST-- |
|||
Test extend() with exception in service factory |
|||
--SKIPIF-- |
|||
<?php if (!extension_loaded("pimple")) print "skip"; ?> |
|||
--FILE-- |
|||
<?php |
|||
|
|||
$p = new Pimple\Container(); |
|||
$p[12] = function ($v) { throw new BadMethodCallException; }; |
|||
|
|||
$c = $p->extend(12, function ($w) { return 'foobar'; }); |
|||
|
|||
try { |
|||
$p[12]; |
|||
echo "Exception expected"; |
|||
} catch (BadMethodCallException $e) { } |
|||
--EXPECTF-- |
@ -1,23 +0,0 @@ |
|||
--TEST-- |
|||
Test register() |
|||
--SKIPIF-- |
|||
<?php if (!extension_loaded("pimple")) print "skip"; ?> |
|||
--FILE-- |
|||
<?php |
|||
|
|||
class Foo implements Pimple\ServiceProviderInterface |
|||
{ |
|||
public function register(Pimple\Container $p) |
|||
{ |
|||
var_dump($p); |
|||
} |
|||
} |
|||
|
|||
$p = new Pimple\Container(); |
|||
$p->register(new Foo, array(42 => 'bar')); |
|||
|
|||
var_dump($p[42]); |
|||
--EXPECTF-- |
|||
object(Pimple\Container)#1 (0) { |
|||
} |
|||
string(3) "bar" |
@ -1,18 +0,0 @@ |
|||
--TEST-- |
|||
Test register() returns static and is a fluent interface |
|||
--SKIPIF-- |
|||
<?php if (!extension_loaded("pimple")) print "skip"; ?> |
|||
--FILE-- |
|||
<?php |
|||
|
|||
class Foo implements Pimple\ServiceProviderInterface |
|||
{ |
|||
public function register(Pimple\Container $p) |
|||
{ |
|||
} |
|||
} |
|||
|
|||
$p = new Pimple\Container(); |
|||
var_dump($p === $p->register(new Foo)); |
|||
--EXPECTF-- |
|||
bool(true) |
@ -1,51 +0,0 @@ |
|||
<?php |
|||
|
|||
if (!class_exists('Pimple\Container')) { |
|||
require_once __DIR__ . '/../../../src/Pimple/Container.php'; |
|||
} else { |
|||
echo "pimple-c extension detected, using...\n\n"; |
|||
} |
|||
|
|||
$time = microtime(true); |
|||
|
|||
function foo() { } |
|||
$factory = function () { }; |
|||
|
|||
for ($i=0; $i<10000; $i++) { |
|||
|
|||
$p = new Pimple\Container; |
|||
|
|||
$p['foo'] = 'bar'; |
|||
|
|||
if (!isset($p[3])) { |
|||
$p[3] = $p['foo']; |
|||
$p[] = 'bar'; |
|||
} |
|||
|
|||
$p[2] = 42; |
|||
|
|||
if (isset($p[2])) { |
|||
unset($p[2]); |
|||
} |
|||
|
|||
$p[42] = $p['foo']; |
|||
|
|||
$p['cb'] = function($arg) { }; |
|||
|
|||
$p[] = $p['cb']; |
|||
|
|||
echo $p['cb']; |
|||
echo $p['cb']; |
|||
echo $p['cb']; |
|||
|
|||
//$p->factory($factory); |
|||
|
|||
$p['factory'] = $factory; |
|||
|
|||
echo $p['factory']; |
|||
echo $p['factory']; |
|||
echo $p['factory']; |
|||
|
|||
} |
|||
|
|||
echo microtime(true) - $time; |
@ -1,25 +0,0 @@ |
|||
<?php |
|||
|
|||
if (!class_exists('Pimple\Container')) { |
|||
require_once __DIR__ . '/../../../src/Pimple/Container.php'; |
|||
} else { |
|||
echo "pimple-c extension detected, using...\n\n"; |
|||
} |
|||
|
|||
$time = microtime(true); |
|||
|
|||
|
|||
$service = function ($arg) { return "I'm a service"; }; |
|||
|
|||
for ($i=0; $i<10000; $i++) { |
|||
|
|||
$p = new Pimple\Container; |
|||
$p['my_service'] = $service; |
|||
|
|||
$a = $p['my_service']; |
|||
$b = $p['my_service']; |
|||
|
|||
} |
|||
|
|||
echo microtime(true) - $time; |
|||
?> |
@ -1,14 +0,0 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
|
|||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|||
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.1/phpunit.xsd" |
|||
backupGlobals="false" |
|||
colors="true" |
|||
bootstrap="vendor/autoload.php" |
|||
> |
|||
<testsuites> |
|||
<testsuite name="Pimple Test Suite"> |
|||
<directory>./src/Pimple/Tests</directory> |
|||
</testsuite> |
|||
</testsuites> |
|||
</phpunit> |
@ -1,282 +0,0 @@ |
|||
<?php |
|||
|
|||
/* |
|||
* This file is part of Pimple. |
|||
* |
|||
* Copyright (c) 2009 Fabien Potencier |
|||
* |
|||
* Permission is hereby granted, free of charge, to any person obtaining a copy |
|||
* of this software and associated documentation files (the "Software"), to deal |
|||
* in the Software without restriction, including without limitation the rights |
|||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|||
* copies of the Software, and to permit persons to whom the Software is furnished |
|||
* to do so, subject to the following conditions: |
|||
* |
|||
* The above copyright notice and this permission notice shall be included in all |
|||
* copies or substantial portions of the Software. |
|||
* |
|||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
|||
* THE SOFTWARE. |
|||
*/ |
|||
|
|||
namespace Pimple; |
|||
|
|||
/** |
|||
* Container main class. |
|||
* |
|||
* @author Fabien Potencier |
|||
*/ |
|||
class Container implements \ArrayAccess |
|||
{ |
|||
private $values = array(); |
|||
private $factories; |
|||
private $protected; |
|||
private $frozen = array(); |
|||
private $raw = array(); |
|||
private $keys = array(); |
|||
|
|||
/** |
|||
* Instantiate the container. |
|||
* |
|||
* Objects and parameters can be passed as argument to the constructor. |
|||
* |
|||
* @param array $values The parameters or objects. |
|||
*/ |
|||
public function __construct(array $values = array()) |
|||
{ |
|||
$this->factories = new \SplObjectStorage(); |
|||
$this->protected = new \SplObjectStorage(); |
|||
|
|||
foreach ($values as $key => $value) { |
|||
$this->offsetSet($key, $value); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Sets a parameter or an object. |
|||
* |
|||
* Objects must be defined as Closures. |
|||
* |
|||
* Allowing any PHP callable leads to difficult to debug problems |
|||
* as function names (strings) are callable (creating a function with |
|||
* the same name as an existing parameter would break your container). |
|||
* |
|||
* @param string $id The unique identifier for the parameter or object |
|||
* @param mixed $value The value of the parameter or a closure to define an object |
|||
* |
|||
* @throws \RuntimeException Prevent override of a frozen service |
|||
*/ |
|||
public function offsetSet($id, $value) |
|||
{ |
|||
if (isset($this->frozen[$id])) { |
|||
throw new \RuntimeException(sprintf('Cannot override frozen service "%s".', $id)); |
|||
} |
|||
|
|||
$this->values[$id] = $value; |
|||
$this->keys[$id] = true; |
|||
} |
|||
|
|||
/** |
|||
* Gets a parameter or an object. |
|||
* |
|||
* @param string $id The unique identifier for the parameter or object |
|||
* |
|||
* @return mixed The value of the parameter or an object |
|||
* |
|||
* @throws \InvalidArgumentException if the identifier is not defined |
|||
*/ |
|||
public function offsetGet($id) |
|||
{ |
|||
if (!isset($this->keys[$id])) { |
|||
throw new \InvalidArgumentException(sprintf('Identifier "%s" is not defined.', $id)); |
|||
} |
|||
|
|||
if ( |
|||
isset($this->raw[$id]) |
|||
|| !is_object($this->values[$id]) |
|||
|| isset($this->protected[$this->values[$id]]) |
|||
|| !method_exists($this->values[$id], '__invoke') |
|||
) { |
|||
return $this->values[$id]; |
|||
} |
|||
|
|||
if (isset($this->factories[$this->values[$id]])) { |
|||
return $this->values[$id]($this); |
|||
} |
|||
|
|||
$raw = $this->values[$id]; |
|||
$val = $this->values[$id] = $raw($this); |
|||
$this->raw[$id] = $raw; |
|||
|
|||
$this->frozen[$id] = true; |
|||
|
|||
return $val; |
|||
} |
|||
|
|||
/** |
|||
* Checks if a parameter or an object is set. |
|||
* |
|||
* @param string $id The unique identifier for the parameter or object |
|||
* |
|||
* @return bool |
|||
*/ |
|||
public function offsetExists($id) |
|||
{ |
|||
return isset($this->keys[$id]); |
|||
} |
|||
|
|||
/** |
|||
* Unsets a parameter or an object. |
|||
* |
|||
* @param string $id The unique identifier for the parameter or object |
|||
*/ |
|||
public function offsetUnset($id) |
|||
{ |
|||
if (isset($this->keys[$id])) { |
|||
if (is_object($this->values[$id])) { |
|||
unset($this->factories[$this->values[$id]], $this->protected[$this->values[$id]]); |
|||
} |
|||
|
|||
unset($this->values[$id], $this->frozen[$id], $this->raw[$id], $this->keys[$id]); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Marks a callable as being a factory service. |
|||
* |
|||
* @param callable $callable A service definition to be used as a factory |
|||
* |
|||
* @return callable The passed callable |
|||
* |
|||
* @throws \InvalidArgumentException Service definition has to be a closure of an invokable object |
|||
*/ |
|||
public function factory($callable) |
|||
{ |
|||
if (!method_exists($callable, '__invoke')) { |
|||
throw new \InvalidArgumentException('Service definition is not a Closure or invokable object.'); |
|||
} |
|||
|
|||
$this->factories->attach($callable); |
|||
|
|||
return $callable; |
|||
} |
|||
|
|||
/** |
|||
* Protects a callable from being interpreted as a service. |
|||
* |
|||
* This is useful when you want to store a callable as a parameter. |
|||
* |
|||
* @param callable $callable A callable to protect from being evaluated |
|||
* |
|||
* @return callable The passed callable |
|||
* |
|||
* @throws \InvalidArgumentException Service definition has to be a closure of an invokable object |
|||
*/ |
|||
public function protect($callable) |
|||
{ |
|||
if (!method_exists($callable, '__invoke')) { |
|||
throw new \InvalidArgumentException('Callable is not a Closure or invokable object.'); |
|||
} |
|||
|
|||
$this->protected->attach($callable); |
|||
|
|||
return $callable; |
|||
} |
|||
|
|||
/** |
|||
* Gets a parameter or the closure defining an object. |
|||
* |
|||
* @param string $id The unique identifier for the parameter or object |
|||
* |
|||
* @return mixed The value of the parameter or the closure defining an object |
|||
* |
|||
* @throws \InvalidArgumentException if the identifier is not defined |
|||
*/ |
|||
public function raw($id) |
|||
{ |
|||
if (!isset($this->keys[$id])) { |
|||
throw new \InvalidArgumentException(sprintf('Identifier "%s" is not defined.', $id)); |
|||
} |
|||
|
|||
if (isset($this->raw[$id])) { |
|||
return $this->raw[$id]; |
|||
} |
|||
|
|||
return $this->values[$id]; |
|||
} |
|||
|
|||
/** |
|||
* Extends an object definition. |
|||
* |
|||
* Useful when you want to extend an existing object definition, |
|||
* without necessarily loading that object. |
|||
* |
|||
* @param string $id The unique identifier for the object |
|||
* @param callable $callable A service definition to extend the original |
|||
* |
|||
* @return callable The wrapped callable |
|||
* |
|||
* @throws \InvalidArgumentException if the identifier is not defined or not a service definition |
|||
*/ |
|||
public function extend($id, $callable) |
|||
{ |
|||
if (!isset($this->keys[$id])) { |
|||
throw new \InvalidArgumentException(sprintf('Identifier "%s" is not defined.', $id)); |
|||
} |
|||
|
|||
if (!is_object($this->values[$id]) || !method_exists($this->values[$id], '__invoke')) { |
|||
throw new \InvalidArgumentException(sprintf('Identifier "%s" does not contain an object definition.', $id)); |
|||
} |
|||
|
|||
if (!is_object($callable) || !method_exists($callable, '__invoke')) { |
|||
throw new \InvalidArgumentException('Extension service definition is not a Closure or invokable object.'); |
|||
} |
|||
|
|||
$factory = $this->values[$id]; |
|||
|
|||
$extended = function ($c) use ($callable, $factory) { |
|||
return $callable($factory($c), $c); |
|||
}; |
|||
|
|||
if (isset($this->factories[$factory])) { |
|||
$this->factories->detach($factory); |
|||
$this->factories->attach($extended); |
|||
} |
|||
|
|||
return $this[$id] = $extended; |
|||
} |
|||
|
|||
/** |
|||
* Returns all defined value names. |
|||
* |
|||
* @return array An array of value names |
|||
*/ |
|||
public function keys() |
|||
{ |
|||
return array_keys($this->values); |
|||
} |
|||
|
|||
/** |
|||
* Registers a service provider. |
|||
* |
|||
* @param ServiceProviderInterface $provider A ServiceProviderInterface instance |
|||
* @param array $values An array of values that customizes the provider |
|||
* |
|||
* @return static |
|||
*/ |
|||
public function register(ServiceProviderInterface $provider, array $values = array()) |
|||
{ |
|||
$provider->register($this); |
|||
|
|||
foreach ($values as $key => $value) { |
|||
$this[$key] = $value; |
|||
} |
|||
|
|||
return $this; |
|||
} |
|||
} |
@ -1,46 +0,0 @@ |
|||
<?php |
|||
|
|||
/* |
|||
* This file is part of Pimple. |
|||
* |
|||
* Copyright (c) 2009 Fabien Potencier |
|||
* |
|||
* Permission is hereby granted, free of charge, to any person obtaining a copy |
|||
* of this software and associated documentation files (the "Software"), to deal |
|||
* in the Software without restriction, including without limitation the rights |
|||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|||
* copies of the Software, and to permit persons to whom the Software is furnished |
|||
* to do so, subject to the following conditions: |
|||
* |
|||
* The above copyright notice and this permission notice shall be included in all |
|||
* copies or substantial portions of the Software. |
|||
* |
|||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
|||
* THE SOFTWARE. |
|||
*/ |
|||
|
|||
namespace Pimple; |
|||
|
|||
/** |
|||
* Pimple service provider interface. |
|||
* |
|||
* @author Fabien Potencier |
|||
* @author Dominik Zogg |
|||
*/ |
|||
interface ServiceProviderInterface |
|||
{ |
|||
/** |
|||
* Registers services on the given container. |
|||
* |
|||
* This method should only be used to configure services and parameters. |
|||
* It should not get services. |
|||
* |
|||
* @param Container $pimple A container instance |
|||
*/ |
|||
public function register(Container $pimple); |
|||
} |
Some files were not shown because too many files changed in this diff
Write
Preview
Loading…
Cancel
Save
Reference in new issue