anxietypb
8 years ago
169 changed files with 2 additions and 16633 deletions
-
1.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