vendor updates

This commit is contained in:
Andy Miller
2014-08-19 14:58:47 -06:00
parent 3794d757f4
commit ecb412ea6d
58 changed files with 1662 additions and 2637 deletions

View File

@@ -79,11 +79,22 @@ class CleanCommand extends Command {
'vendor/ornicar/php-user-agent/composer.json',
'vendor/ornicar/php-user-agent/prove.php',
'vendor/ornicar/php-user-agent/test',
'vendor/pimple/pimple/.gitignore',
'vendor/pimple/pimple/.travis.yml',
'vendor/pimple/pimple/composer.json',
'vendor/pimple/pimple/ext',
'vendor/pimple/pimple/phpunit.xml.dist',
'vendor/pimple/pimple/src/Pimple/Tests',
'vendor/symfony/console/Symfony/Component/Console/composer.json',
'vendor/symfony/console/Symfony/Component/Console/phpunit.xml.dist',
'vendor/symfony/console/Symfony/Component/Console/.gitignore',
'vendor/symfony/console/Symfony/Component/Console/.git',
'vendor/symfony/console/Symfony/Component/Console/Tests',
'vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/.git',
'vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/.gitignore',
'vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/composer.json',
'vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/phpunit.xml.dist',
'vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests',
'vendor/symfony/yaml/Symfony/Component/Yaml/composer.json',
'vendor/symfony/yaml/Symfony/Component/Yaml/phpunit.xml.dist',
'vendor/symfony/yaml/Symfony/Component/Yaml/.gitignore',

2
vendor/autoload.php vendored
View File

@@ -4,4 +4,4 @@
require_once __DIR__ . '/composer' . '/autoload_real.php';
return ComposerAutoloaderInitaeb322692412910c492cb020797b4f37::getLoader();
return ComposerAutoloaderInit010a27b310c3146ba53b52651441075a::getLoader();

View File

@@ -143,6 +143,8 @@ class ClassLoader
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-0 base directories
* @param bool $prepend Whether to prepend the directories
*
* @throws \InvalidArgumentException
*/
public function addPsr4($prefix, $paths, $prepend = false)
{
@@ -204,6 +206,8 @@ class ClassLoader
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-4 base directories
*
* @throws \InvalidArgumentException
*/
public function setPsr4($prefix, $paths)
{

View File

@@ -8,6 +8,7 @@ $baseDir = dirname($vendorDir);
return array(
'Twig_' => array($vendorDir . '/twig/twig/lib'),
'Symfony\\Component\\Yaml\\' => array($vendorDir . '/symfony/yaml'),
'Symfony\\Component\\EventDispatcher\\' => array($vendorDir . '/symfony/event-dispatcher'),
'Symfony\\Component\\Console\\' => array($vendorDir . '/symfony/console'),
'Pimple' => array($vendorDir . '/pimple/pimple/src'),
'ParsedownExtra' => array($vendorDir . '/erusev/parsedown-extra'),

View File

@@ -2,7 +2,7 @@
// autoload_real.php @generated by Composer
class ComposerAutoloaderInitaeb322692412910c492cb020797b4f37
class ComposerAutoloaderInit010a27b310c3146ba53b52651441075a
{
private static $loader;
@@ -19,9 +19,9 @@ class ComposerAutoloaderInitaeb322692412910c492cb020797b4f37
return self::$loader;
}
spl_autoload_register(array('ComposerAutoloaderInitaeb322692412910c492cb020797b4f37', 'loadClassLoader'), true, true);
spl_autoload_register(array('ComposerAutoloaderInit010a27b310c3146ba53b52651441075a', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
spl_autoload_unregister(array('ComposerAutoloaderInitaeb322692412910c492cb020797b4f37', 'loadClassLoader'));
spl_autoload_unregister(array('ComposerAutoloaderInit010a27b310c3146ba53b52651441075a', 'loadClassLoader'));
$map = require __DIR__ . '/autoload_namespaces.php';
foreach ($map as $namespace => $path) {
@@ -42,14 +42,14 @@ class ComposerAutoloaderInitaeb322692412910c492cb020797b4f37
$includeFiles = require __DIR__ . '/autoload_files.php';
foreach ($includeFiles as $file) {
composerRequireaeb322692412910c492cb020797b4f37($file);
composerRequire010a27b310c3146ba53b52651441075a($file);
}
return $loader;
}
}
function composerRequireaeb322692412910c492cb020797b4f37($file)
function composerRequire010a27b310c3146ba53b52651441075a($file)
{
require $file;
}

View File

@@ -353,6 +353,54 @@
"description": "Minify is a PHP5 app that helps you follow several rules for client-side performance. It combines multiple CSS or Javascript files, removes unnecessary whitespace and comments, and serves them with gzip encoding and optimal client-side cache headers",
"homepage": "http://code.google.com/p/minify/"
},
{
"name": "pimple/pimple",
"version": "v3.0.0",
"version_normalized": "3.0.0.0",
"source": {
"type": "git",
"url": "https://github.com/fabpot/Pimple.git",
"reference": "876bf0899d01feacd2a2e83f04641e51350099ef"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/fabpot/Pimple/zipball/876bf0899d01feacd2a2e83f04641e51350099ef",
"reference": "876bf0899d01feacd2a2e83f04641e51350099ef",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"time": "2014-07-24 09:48:15",
"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 is a simple Dependency Injection Container for PHP 5.3",
"homepage": "http://pimple.sensiolabs.org",
"keywords": [
"container",
"dependency injection"
]
},
{
"name": "twig/twig",
"version": "dev-master",
@@ -518,6 +566,65 @@
"description": "Symfony Console Component",
"homepage": "http://symfony.com"
},
{
"name": "symfony/event-dispatcher",
"version": "2.5.x-dev",
"version_normalized": "2.5.9999999.9999999-dev",
"target-dir": "Symfony/Component/EventDispatcher",
"source": {
"type": "git",
"url": "https://github.com/symfony/EventDispatcher.git",
"reference": "8faf5cc7e80fde74a650a36e60d32ce3c3e0457b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/EventDispatcher/zipball/8faf5cc7e80fde74a650a36e60d32ce3c3e0457b",
"reference": "8faf5cc7e80fde74a650a36e60d32ce3c3e0457b",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"psr/log": "~1.0",
"symfony/config": "~2.0",
"symfony/dependency-injection": "~2.0",
"symfony/stopwatch": "~2.2"
},
"suggest": {
"symfony/dependency-injection": "",
"symfony/http-kernel": ""
},
"time": "2014-07-28 13:20:46",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.5-dev"
}
},
"installation-source": "source",
"autoload": {
"psr-0": {
"Symfony\\Component\\EventDispatcher\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
},
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
}
],
"description": "Symfony EventDispatcher Component",
"homepage": "http://symfony.com"
},
{
"name": "ircmaxell/password-compat",
"version": "1.0.3",
@@ -605,53 +712,5 @@
"keywords": [
"user-agent"
]
},
{
"name": "pimple/pimple",
"version": "v3.0.0",
"version_normalized": "3.0.0.0",
"source": {
"type": "git",
"url": "https://github.com/fabpot/Pimple.git",
"reference": "876bf0899d01feacd2a2e83f04641e51350099ef"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/fabpot/Pimple/zipball/876bf0899d01feacd2a2e83f04641e51350099ef",
"reference": "876bf0899d01feacd2a2e83f04641e51350099ef",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"time": "2014-07-24 09:48:15",
"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 is a simple Dependency Injection Container for PHP 5.3",
"homepage": "http://pimple.sensiolabs.org",
"keywords": [
"container",
"dependency injection"
]
}
]

View File

@@ -1,3 +0,0 @@
phpunit.xml
composer.lock
/vendor/

View File

@@ -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

View File

@@ -1,25 +0,0 @@
{
"name": "pimple/pimple",
"type": "library",
"description": "Pimple is a simple Dependency Injection Container for PHP 5.3",
"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"
}
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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");
}

View File

@@ -1,122 +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.0"
#define PIMPLE_NS "Pimple"
#define PIMPLE_DEFAULT_ZVAL_CACHE_NUM 5
#define PIMPLE_DEFAULT_ZVAL_VALUES_NUM 10
PHP_MINIT_FUNCTION(pimple);
PHP_MSHUTDOWN_FUNCTION(pimple);
PHP_RINIT_FUNCTION(pimple);
PHP_RSHUTDOWN_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, __invoke);
typedef struct _pimple_bucket_value {
zval *value; /* Must be the first element */
zval *raw;
zend_fcall_info_cache *fcc;
zend_object_handle handle_num;
enum {
PIMPLE_IS_PARAM = 0,
PIMPLE_IS_SERVICE = 2
} type;
zend_bool initialized;
} 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_init_bucket(pimple_bucket_value *bucket);
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);
#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 */

View File

@@ -1,940 +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 "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;
#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_pimpleclosure___invoke, 0, 0, 1)
ZEND_ARG_INFO(0, callarg)
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_closure_ce_functions[] = {
PHP_ME(PimpleClosure, __invoke, arginfo_pimpleclosure___invoke, ZEND_ACC_PRIVATE)
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->fcc) {
efree(bucket->fcc);
bucket->fcc = NULL;
}
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 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_init_bucket(&pimple_value);
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 void pimple_init_bucket(pimple_bucket_value *bucket)
{
memset(bucket, 0, sizeof(pimple_bucket_value));
bucket->fcc = ecalloc(1, sizeof(zend_fcall_info_cache));
}
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;
}
pimple_init_bucket(&bucket);
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) {
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");
}
pimple_init_bucket(&bucket);
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_init_bucket(&bucket);
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;
}
pimple_init_bucket(&bucket);
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;
}
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);
}
RETVAL_ZVAL(getThis(), 1, 0);
}
PHP_METHOD(Pimple, __construct)
{
zval *values = NULL, **pData = NULL, offset = {0};
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);
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, __invoke)
{
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", pimple_closure_ce_functions);
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);
pimple_closure_object_handlers = std_object_handlers;
pimple_object_handlers = std_object_handlers;
return SUCCESS;
}
PHP_MSHUTDOWN_FUNCTION(pimple)
{
return SUCCESS;
}
PHP_RINIT_FUNCTION(pimple)
{
return SUCCESS;
}
PHP_RSHUTDOWN_FUNCTION(pimple)
{
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),
PHP_MSHUTDOWN(pimple),
PHP_RINIT(pimple),
PHP_RSHUTDOWN(pimple),
PHP_MINFO(pimple),
PIMPLE_VERSION,
STANDARD_MODULE_PROPERTIES
};
#ifdef COMPILE_DL_PIMPLE
ZEND_GET_MODULE(pimple)
#endif

View File

@@ -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_ */

View File

@@ -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

View File

@@ -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"

View File

@@ -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"

View File

@@ -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)

View File

@@ -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

View File

@@ -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"

View File

@@ -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

View File

@@ -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--

View File

@@ -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)

View File

@@ -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"
}

View File

@@ -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!

View File

@@ -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"

View File

@@ -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)
}

View File

@@ -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"

View File

@@ -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) {
}

View File

@@ -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"

View File

@@ -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--

View File

@@ -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--

View File

@@ -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"

View File

@@ -1,51 +0,0 @@
<?php
if (!class_exists('Pimple\Container')) {
require_once __DIR__ . '/../../../lib/Pimple.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;

View File

@@ -1,25 +0,0 @@
<?php
if (!class_exists('Pimple\Container')) {
require_once __DIR__ . '/../../../lib/Pimple.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;
?>

View File

@@ -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>

View File

@@ -1,38 +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\Tests\Fixtures;
class Invokable
{
public function __invoke($value = null)
{
$service = new Service();
$service->value = $value;
return $service;
}
}

View File

@@ -1,34 +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\Tests\Fixtures;
class NonInvokable
{
public function __call($a, $b)
{
}
}

View File

@@ -1,54 +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\Tests\Fixtures;
use Pimple\Container;
use Pimple\ServiceProviderInterface;
class PimpleServiceProvider implements 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 An Container instance
*/
public function register(Container $pimple)
{
$pimple['param'] = 'value';
$pimple['service'] = function () {
return new Service();
};
$pimple['factory'] = $pimple->factory(function () {
return new Service();
});
}
}

View File

@@ -1,35 +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\Tests\Fixtures;
/**
* @author Igor Wiedler <igor@wiedler.ch>
*/
class Service
{
public $value;
}

View File

@@ -1,76 +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\Tests;
use Pimple\Container;
/**
* @author Dominik Zogg <dominik.zogg@gmail.com>
*/
class PimpleServiceProviderInterfaceTest extends \PHPUnit_Framework_TestCase
{
public function testProvider()
{
$pimple = new Container();
$pimpleServiceProvider = new Fixtures\PimpleServiceProvider();
$pimpleServiceProvider->register($pimple);
$this->assertEquals('value', $pimple['param']);
$this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $pimple['service']);
$serviceOne = $pimple['factory'];
$this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceOne);
$serviceTwo = $pimple['factory'];
$this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceTwo);
$this->assertNotSame($serviceOne, $serviceTwo);
}
public function testProviderWithRegisterMethod()
{
$pimple = new Container();
$pimple->register(new Fixtures\PimpleServiceProvider(), array(
'anotherParameter' => 'anotherValue'
));
$this->assertEquals('value', $pimple['param']);
$this->assertEquals('anotherValue', $pimple['anotherParameter']);
$this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $pimple['service']);
$serviceOne = $pimple['factory'];
$this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceOne);
$serviceTwo = $pimple['factory'];
$this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceTwo);
$this->assertNotSame($serviceOne, $serviceTwo);
}
}

View File

@@ -1,434 +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\Tests;
use Pimple\Container;
/**
* @author Igor Wiedler <igor@wiedler.ch>
*/
class PimpleTest extends \PHPUnit_Framework_TestCase
{
public function testWithString()
{
$pimple = new Container();
$pimple['param'] = 'value';
$this->assertEquals('value', $pimple['param']);
}
public function testWithClosure()
{
$pimple = new Container();
$pimple['service'] = function () {
return new Fixtures\Service();
};
$this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $pimple['service']);
}
public function testServicesShouldBeDifferent()
{
$pimple = new Container();
$pimple['service'] = $pimple->factory(function () {
return new Fixtures\Service();
});
$serviceOne = $pimple['service'];
$this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceOne);
$serviceTwo = $pimple['service'];
$this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceTwo);
$this->assertNotSame($serviceOne, $serviceTwo);
}
public function testShouldPassContainerAsParameter()
{
$pimple = new Container();
$pimple['service'] = function () {
return new Fixtures\Service();
};
$pimple['container'] = function ($container) {
return $container;
};
$this->assertNotSame($pimple, $pimple['service']);
$this->assertSame($pimple, $pimple['container']);
}
public function testIsset()
{
$pimple = new Container();
$pimple['param'] = 'value';
$pimple['service'] = function () {
return new Fixtures\Service();
};
$pimple['null'] = null;
$this->assertTrue(isset($pimple['param']));
$this->assertTrue(isset($pimple['service']));
$this->assertTrue(isset($pimple['null']));
$this->assertFalse(isset($pimple['non_existent']));
}
public function testConstructorInjection()
{
$params = array("param" => "value");
$pimple = new Container($params);
$this->assertSame($params['param'], $pimple['param']);
}
/**
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage Identifier "foo" is not defined.
*/
public function testOffsetGetValidatesKeyIsPresent()
{
$pimple = new Container();
echo $pimple['foo'];
}
public function testOffsetGetHonorsNullValues()
{
$pimple = new Container();
$pimple['foo'] = null;
$this->assertNull($pimple['foo']);
}
public function testUnset()
{
$pimple = new Container();
$pimple['param'] = 'value';
$pimple['service'] = function () {
return new Fixtures\Service();
};
unset($pimple['param'], $pimple['service']);
$this->assertFalse(isset($pimple['param']));
$this->assertFalse(isset($pimple['service']));
}
/**
* @dataProvider serviceDefinitionProvider
*/
public function testShare($service)
{
$pimple = new Container();
$pimple['shared_service'] = $service;
$serviceOne = $pimple['shared_service'];
$this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceOne);
$serviceTwo = $pimple['shared_service'];
$this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceTwo);
$this->assertSame($serviceOne, $serviceTwo);
}
/**
* @dataProvider serviceDefinitionProvider
*/
public function testProtect($service)
{
$pimple = new Container();
$pimple['protected'] = $pimple->protect($service);
$this->assertSame($service, $pimple['protected']);
}
public function testGlobalFunctionNameAsParameterValue()
{
$pimple = new Container();
$pimple['global_function'] = 'strlen';
$this->assertSame('strlen', $pimple['global_function']);
}
public function testRaw()
{
$pimple = new Container();
$pimple['service'] = $definition = $pimple->factory(function () { return 'foo'; });
$this->assertSame($definition, $pimple->raw('service'));
}
public function testRawHonorsNullValues()
{
$pimple = new Container();
$pimple['foo'] = null;
$this->assertNull($pimple->raw('foo'));
}
/**
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage Identifier "foo" is not defined.
*/
public function testRawValidatesKeyIsPresent()
{
$pimple = new Container();
$pimple->raw('foo');
}
/**
* @dataProvider serviceDefinitionProvider
*/
public function testExtend($service)
{
$pimple = new Container();
$pimple['shared_service'] = function () {
return new Fixtures\Service();
};
$pimple['factory_service'] = $pimple->factory(function () {
return new Fixtures\Service();
});
$pimple->extend('shared_service', $service);
$serviceOne = $pimple['shared_service'];
$this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceOne);
$serviceTwo = $pimple['shared_service'];
$this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceTwo);
$this->assertSame($serviceOne, $serviceTwo);
$this->assertSame($serviceOne->value, $serviceTwo->value);
$pimple->extend('factory_service', $service);
$serviceOne = $pimple['factory_service'];
$this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceOne);
$serviceTwo = $pimple['factory_service'];
$this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceTwo);
$this->assertNotSame($serviceOne, $serviceTwo);
$this->assertNotSame($serviceOne->value, $serviceTwo->value);
}
public function testExtendDoesNotLeakWithFactories()
{
if (extension_loaded('pimple')) {
$this->markTestSkipped('Pimple extension does not support this test');
}
$pimple = new Container();
$pimple['foo'] = $pimple->factory(function () { return; });
$pimple['foo'] = $pimple->extend('foo', function ($foo, $pimple) { return; });
unset($pimple['foo']);
$p = new \ReflectionProperty($pimple, 'values');
$p->setAccessible(true);
$this->assertEmpty($p->getValue($pimple));
$p = new \ReflectionProperty($pimple, 'factories');
$p->setAccessible(true);
$this->assertCount(0, $p->getValue($pimple));
}
/**
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage Identifier "foo" is not defined.
*/
public function testExtendValidatesKeyIsPresent()
{
$pimple = new Container();
$pimple->extend('foo', function () {});
}
public function testKeys()
{
$pimple = new Container();
$pimple['foo'] = 123;
$pimple['bar'] = 123;
$this->assertEquals(array('foo', 'bar'), $pimple->keys());
}
/** @test */
public function settingAnInvokableObjectShouldTreatItAsFactory()
{
$pimple = new Container();
$pimple['invokable'] = new Fixtures\Invokable();
$this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $pimple['invokable']);
}
/** @test */
public function settingNonInvokableObjectShouldTreatItAsParameter()
{
$pimple = new Container();
$pimple['non_invokable'] = new Fixtures\NonInvokable();
$this->assertInstanceOf('Pimple\Tests\Fixtures\NonInvokable', $pimple['non_invokable']);
}
/**
* @dataProvider badServiceDefinitionProvider
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage Service definition is not a Closure or invokable object.
*/
public function testFactoryFailsForInvalidServiceDefinitions($service)
{
$pimple = new Container();
$pimple->factory($service);
}
/**
* @dataProvider badServiceDefinitionProvider
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage Callable is not a Closure or invokable object.
*/
public function testProtectFailsForInvalidServiceDefinitions($service)
{
$pimple = new Container();
$pimple->protect($service);
}
/**
* @dataProvider badServiceDefinitionProvider
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage Identifier "foo" does not contain an object definition.
*/
public function testExtendFailsForKeysNotContainingServiceDefinitions($service)
{
$pimple = new Container();
$pimple['foo'] = $service;
$pimple->extend('foo', function () {});
}
/**
* @dataProvider badServiceDefinitionProvider
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage Extension service definition is not a Closure or invokable object.
*/
public function testExtendFailsForInvalidServiceDefinitions($service)
{
$pimple = new Container();
$pimple['foo'] = function () {};
$pimple->extend('foo', $service);
}
/**
* Provider for invalid service definitions
*/
public function badServiceDefinitionProvider()
{
return array(
array(123),
array(new Fixtures\NonInvokable())
);
}
/**
* Provider for service definitions
*/
public function serviceDefinitionProvider()
{
return array(
array(function ($value) {
$service = new Fixtures\Service();
$service->value = $value;
return $service;
}),
array(new Fixtures\Invokable())
);
}
public function testDefiningNewServiceAfterFreeze()
{
$pimple = new Container();
$pimple['foo'] = function () {
return 'foo';
};
$foo = $pimple['foo'];
$pimple['bar'] = function () {
return 'bar';
};
$this->assertSame('bar', $pimple['bar']);
}
/**
* @expectedException RuntimeException
* @expectedExceptionMessage Cannot override frozen service "foo".
*/
public function testOverridingServiceAfterFreeze()
{
$pimple = new Container();
$pimple['foo'] = function () {
return 'foo';
};
$foo = $pimple['foo'];
$pimple['foo'] = function () {
return 'bar';
};
}
public function testRemovingServiceAfterFreeze()
{
$pimple = new Container();
$pimple['foo'] = function () {
return 'foo';
};
$foo = $pimple['foo'];
unset($pimple['foo']);
$pimple['foo'] = function () {
return 'bar';
};
$this->assertSame('bar', $pimple['foo']);
}
public function testExtendingService()
{
$pimple = new Container();
$pimple['foo'] = function () {
return 'foo';
};
$pimple['foo'] = $pimple->extend('foo', function ($foo, $app) {
return "$foo.bar";
});
$pimple['foo'] = $pimple->extend('foo', function ($foo, $app) {
return "$foo.baz";
});
$this->assertSame('foo.bar.baz', $pimple['foo']);
}
public function testExtendingServiceAfterOtherServiceFreeze()
{
$pimple = new Container();
$pimple['foo'] = function () {
return 'foo';
};
$pimple['bar'] = function () {
return 'bar';
};
$foo = $pimple['foo'];
$pimple['bar'] = $pimple->extend('bar', function ($bar, $app) {
return "$bar.baz";
});
$this->assertSame('bar.baz', $pimple['bar']);
}
}

View File

@@ -0,0 +1,23 @@
CHANGELOG
=========
2.5.0
-----
* added Debug\TraceableEventDispatcher (originally in HttpKernel)
* changed Debug\TraceableEventDispatcherInterface to extend EventDispatcherInterface
* added RegisterListenersPass (originally in HttpKernel)
2.1.0
-----
* added TraceableEventDispatcherInterface
* added ContainerAwareEventDispatcher
* added a reference to the EventDispatcher on the Event
* added a reference to the Event name on the event
* added fluid interface to the dispatch() method which now returns the Event
object
* added GenericEvent event class
* added the possibility for subscribers to subscribe several times for the
same event
* added ImmutableEventDispatcher

View File

@@ -0,0 +1,202 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\EventDispatcher;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Lazily loads listeners and subscribers from the dependency injection
* container
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Bernhard Schussek <bschussek@gmail.com>
* @author Jordan Alliot <jordan.alliot@gmail.com>
*/
class ContainerAwareEventDispatcher extends EventDispatcher
{
/**
* The container from where services are loaded
* @var ContainerInterface
*/
private $container;
/**
* The service IDs of the event listeners and subscribers
* @var array
*/
private $listenerIds = array();
/**
* The services registered as listeners
* @var array
*/
private $listeners = array();
/**
* Constructor.
*
* @param ContainerInterface $container A ContainerInterface instance
*/
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
/**
* Adds a service as event listener
*
* @param string $eventName Event for which the listener is added
* @param array $callback The service ID of the listener service & the method
* name that has to be called
* @param int $priority The higher this value, the earlier an event listener
* will be triggered in the chain.
* Defaults to 0.
*
* @throws \InvalidArgumentException
*/
public function addListenerService($eventName, $callback, $priority = 0)
{
if (!is_array($callback) || 2 !== count($callback)) {
throw new \InvalidArgumentException('Expected an array("service", "method") argument');
}
$this->listenerIds[$eventName][] = array($callback[0], $callback[1], $priority);
}
public function removeListener($eventName, $listener)
{
$this->lazyLoad($eventName);
if (isset($this->listeners[$eventName])) {
foreach ($this->listeners[$eventName] as $key => $l) {
foreach ($this->listenerIds[$eventName] as $i => $args) {
list($serviceId, $method, $priority) = $args;
if ($key === $serviceId.'.'.$method) {
if ($listener === array($l, $method)) {
unset($this->listeners[$eventName][$key]);
if (empty($this->listeners[$eventName])) {
unset($this->listeners[$eventName]);
}
unset($this->listenerIds[$eventName][$i]);
if (empty($this->listenerIds[$eventName])) {
unset($this->listenerIds[$eventName]);
}
}
}
}
}
}
parent::removeListener($eventName, $listener);
}
/**
* @see EventDispatcherInterface::hasListeners
*/
public function hasListeners($eventName = null)
{
if (null === $eventName) {
return (bool) count($this->listenerIds) || (bool) count($this->listeners);
}
if (isset($this->listenerIds[$eventName])) {
return true;
}
return parent::hasListeners($eventName);
}
/**
* @see EventDispatcherInterface::getListeners
*/
public function getListeners($eventName = null)
{
if (null === $eventName) {
foreach (array_keys($this->listenerIds) as $serviceEventName) {
$this->lazyLoad($serviceEventName);
}
} else {
$this->lazyLoad($eventName);
}
return parent::getListeners($eventName);
}
/**
* Adds a service as event subscriber
*
* @param string $serviceId The service ID of the subscriber service
* @param string $class The service's class name (which must implement EventSubscriberInterface)
*/
public function addSubscriberService($serviceId, $class)
{
foreach ($class::getSubscribedEvents() as $eventName => $params) {
if (is_string($params)) {
$this->listenerIds[$eventName][] = array($serviceId, $params, 0);
} elseif (is_string($params[0])) {
$this->listenerIds[$eventName][] = array($serviceId, $params[0], isset($params[1]) ? $params[1] : 0);
} else {
foreach ($params as $listener) {
$this->listenerIds[$eventName][] = array($serviceId, $listener[0], isset($listener[1]) ? $listener[1] : 0);
}
}
}
}
/**
* {@inheritdoc}
*
* Lazily loads listeners for this event from the dependency injection
* container.
*
* @throws \InvalidArgumentException if the service is not defined
*/
public function dispatch($eventName, Event $event = null)
{
$this->lazyLoad($eventName);
return parent::dispatch($eventName, $event);
}
public function getContainer()
{
return $this->container;
}
/**
* Lazily loads listeners for this event from the dependency injection
* container.
*
* @param string $eventName The name of the event to dispatch. The name of
* the event is the name of the method that is
* invoked on listeners.
*/
protected function lazyLoad($eventName)
{
if (isset($this->listenerIds[$eventName])) {
foreach ($this->listenerIds[$eventName] as $args) {
list($serviceId, $method, $priority) = $args;
$listener = $this->container->get($serviceId);
$key = $serviceId.'.'.$method;
if (!isset($this->listeners[$eventName][$key])) {
$this->addListener($eventName, array($listener, $method), $priority);
} elseif ($listener !== $this->listeners[$eventName][$key]) {
parent::removeListener($eventName, array($this->listeners[$eventName][$key], $method));
$this->addListener($eventName, array($listener, $method), $priority);
}
$this->listeners[$eventName][$key] = $listener;
}
}
}
}

View File

@@ -0,0 +1,317 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\EventDispatcher\Debug;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\EventDispatcher\Event;
use Symfony\Component\Stopwatch\Stopwatch;
use Psr\Log\LoggerInterface;
/**
* Collects some data about event listeners.
*
* This event dispatcher delegates the dispatching to another one.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class TraceableEventDispatcher implements TraceableEventDispatcherInterface
{
protected $logger;
protected $stopwatch;
private $called;
private $dispatcher;
/**
* Constructor.
*
* @param EventDispatcherInterface $dispatcher An EventDispatcherInterface instance
* @param Stopwatch $stopwatch A Stopwatch instance
* @param LoggerInterface $logger A LoggerInterface instance
*/
public function __construct(EventDispatcherInterface $dispatcher, Stopwatch $stopwatch, LoggerInterface $logger = null)
{
$this->dispatcher = $dispatcher;
$this->stopwatch = $stopwatch;
$this->logger = $logger;
$this->called = array();
}
/**
* {@inheritdoc}
*/
public function addListener($eventName, $listener, $priority = 0)
{
$this->dispatcher->addListener($eventName, $listener, $priority);
}
/**
* {@inheritdoc}
*/
public function addSubscriber(EventSubscriberInterface $subscriber)
{
$this->dispatcher->addSubscriber($subscriber);
}
/**
* {@inheritdoc}
*/
public function removeListener($eventName, $listener)
{
return $this->dispatcher->removeListener($eventName, $listener);
}
/**
* {@inheritdoc}
*/
public function removeSubscriber(EventSubscriberInterface $subscriber)
{
return $this->dispatcher->removeSubscriber($subscriber);
}
/**
* {@inheritdoc}
*/
public function getListeners($eventName = null)
{
return $this->dispatcher->getListeners($eventName);
}
/**
* {@inheritdoc}
*/
public function hasListeners($eventName = null)
{
return $this->dispatcher->hasListeners($eventName);
}
/**
* {@inheritdoc}
*/
public function dispatch($eventName, Event $event = null)
{
if (null === $event) {
$event = new Event();
}
$this->preProcess($eventName);
$this->preDispatch($eventName, $event);
$e = $this->stopwatch->start($eventName, 'section');
$this->dispatcher->dispatch($eventName, $event);
if ($e->isStarted()) {
$e->stop();
}
$this->postDispatch($eventName, $event);
$this->postProcess($eventName);
return $event;
}
/**
* {@inheritdoc}
*/
public function getCalledListeners()
{
$called = array();
foreach ($this->called as $eventName => $listeners) {
foreach ($listeners as $listener) {
$info = $this->getListenerInfo($listener->getWrappedListener(), $eventName);
$called[$eventName.'.'.$info['pretty']] = $info;
}
}
return $called;
}
/**
* {@inheritdoc}
*/
public function getNotCalledListeners()
{
try {
$allListeners = $this->getListeners();
} catch (\Exception $e) {
if (null !== $this->logger) {
$this->logger->info(sprintf('An exception was thrown while getting the uncalled listeners (%s)', $e->getMessage()), array('exception' => $e));
}
// unable to retrieve the uncalled listeners
return array();
}
$notCalled = array();
foreach ($allListeners as $eventName => $listeners) {
foreach ($listeners as $listener) {
$called = false;
if (isset($this->called[$eventName])) {
foreach ($this->called[$eventName] as $l) {
if ($l->getWrappedListener() === $listener) {
$called = true;
break;
}
}
}
if (!$called) {
$info = $this->getListenerInfo($listener, $eventName);
$notCalled[$eventName.'.'.$info['pretty']] = $info;
}
}
}
return $notCalled;
}
/**
* Proxies all method calls to the original event dispatcher.
*
* @param string $method The method name
* @param array $arguments The method arguments
*
* @return mixed
*/
public function __call($method, $arguments)
{
return call_user_func_array(array($this->dispatcher, $method), $arguments);
}
/**
* Called before dispatching the event.
*
* @param string $eventName The event name
* @param Event $event The event
*/
protected function preDispatch($eventName, Event $event)
{
}
/**
* Called after dispatching the event.
*
* @param string $eventName The event name
* @param Event $event The event
*/
protected function postDispatch($eventName, Event $event)
{
}
private function preProcess($eventName)
{
foreach ($this->dispatcher->getListeners($eventName) as $listener) {
$this->dispatcher->removeListener($eventName, $listener);
$info = $this->getListenerInfo($listener, $eventName);
$name = isset($info['class']) ? $info['class'] : $info['type'];
$this->dispatcher->addListener($eventName, new WrappedListener($listener, $name, $this->stopwatch));
}
}
private function postProcess($eventName)
{
$skipped = false;
foreach ($this->dispatcher->getListeners($eventName) as $listener) {
// Unwrap listener
$this->dispatcher->removeListener($eventName, $listener);
$this->dispatcher->addListener($eventName, $listener->getWrappedListener());
$info = $this->getListenerInfo($listener->getWrappedListener(), $eventName);
if ($listener->wasCalled()) {
if (null !== $this->logger) {
$this->logger->debug(sprintf('Notified event "%s" to listener "%s".', $eventName, $info['pretty']));
}
if (!isset($this->called[$eventName])) {
$this->called[$eventName] = new \SplObjectStorage();
}
$this->called[$eventName]->attach($listener);
}
if (null !== $this->logger && $skipped) {
$this->logger->debug(sprintf('Listener "%s" was not called for event "%s".', $info['pretty'], $eventName));
}
if ($listener->stoppedPropagation()) {
if (null !== $this->logger) {
$this->logger->debug(sprintf('Listener "%s" stopped propagation of the event "%s".', $info['pretty'], $eventName));
}
$skipped = true;
}
}
}
/**
* Returns information about the listener
*
* @param object $listener The listener
* @param string $eventName The event name
*
* @return array Information about the listener
*/
private function getListenerInfo($listener, $eventName)
{
$info = array(
'event' => $eventName,
);
if ($listener instanceof \Closure) {
$info += array(
'type' => 'Closure',
'pretty' => 'closure'
);
} elseif (is_string($listener)) {
try {
$r = new \ReflectionFunction($listener);
$file = $r->getFileName();
$line = $r->getStartLine();
} catch (\ReflectionException $e) {
$file = null;
$line = null;
}
$info += array(
'type' => 'Function',
'function' => $listener,
'file' => $file,
'line' => $line,
'pretty' => $listener,
);
} elseif (is_array($listener) || (is_object($listener) && is_callable($listener))) {
if (!is_array($listener)) {
$listener = array($listener, '__invoke');
}
$class = is_object($listener[0]) ? get_class($listener[0]) : $listener[0];
try {
$r = new \ReflectionMethod($class, $listener[1]);
$file = $r->getFileName();
$line = $r->getStartLine();
} catch (\ReflectionException $e) {
$file = null;
$line = null;
}
$info += array(
'type' => 'Method',
'class' => $class,
'method' => $listener[1],
'file' => $file,
'line' => $line,
'pretty' => $class.'::'.$listener[1],
);
}
return $info;
}
}

View File

@@ -0,0 +1,34 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\EventDispatcher\Debug;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
/**
* @author Fabien Potencier <fabien@symfony.com>
*/
interface TraceableEventDispatcherInterface extends EventDispatcherInterface
{
/**
* Gets the called listeners.
*
* @return array An array of called listeners
*/
public function getCalledListeners();
/**
* Gets the not called listeners.
*
* @return array An array of not called listeners
*/
public function getNotCalledListeners();
}

View File

@@ -0,0 +1,69 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\EventDispatcher\Debug;
use Symfony\Component\Stopwatch\Stopwatch;
use Symfony\Component\EventDispatcher\Event;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
/**
* @author Fabien Potencier <fabien@symfony.com>
*/
class WrappedListener
{
private $listener;
private $name;
private $called;
private $stoppedPropagation;
private $stopwatch;
public function __construct($listener, $name, Stopwatch $stopwatch)
{
$this->listener = $listener;
$this->name = $name;
$this->stopwatch = $stopwatch;
$this->called = false;
$this->stoppedPropagation = false;
}
public function getWrappedListener()
{
return $this->listener;
}
public function wasCalled()
{
return $this->called;
}
public function stoppedPropagation()
{
return $this->stoppedPropagation;
}
public function __invoke(Event $event, $eventName, EventDispatcherInterface $dispatcher)
{
$this->called = true;
$e = $this->stopwatch->start($this->name, 'event_listener');
call_user_func($this->listener, $event, $eventName, $dispatcher);
if ($e->isStarted()) {
$e->stop();
}
if ($event->isPropagationStopped()) {
$this->stoppedPropagation = true;
}
}
}

View File

@@ -0,0 +1,106 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\EventDispatcher\DependencyInjection;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
/**
* Compiler pass to register tagged services for an event dispatcher.
*/
class RegisterListenersPass implements CompilerPassInterface
{
/**
* @var string
*/
protected $dispatcherService;
/**
* @var string
*/
protected $listenerTag;
/**
* @var string
*/
protected $subscriberTag;
/**
* Constructor.
*
* @param string $dispatcherService Service name of the event dispatcher in processed container
* @param string $listenerTag Tag name used for listener
* @param string $subscriberTag Tag name used for subscribers
*/
public function __construct($dispatcherService = 'event_dispatcher', $listenerTag = 'kernel.event_listener', $subscriberTag = 'kernel.event_subscriber')
{
$this->dispatcherService = $dispatcherService;
$this->listenerTag = $listenerTag;
$this->subscriberTag = $subscriberTag;
}
public function process(ContainerBuilder $container)
{
if (!$container->hasDefinition($this->dispatcherService) && !$container->hasAlias($this->dispatcherService)) {
return;
}
$definition = $container->findDefinition($this->dispatcherService);
foreach ($container->findTaggedServiceIds($this->listenerTag) as $id => $events) {
$def = $container->getDefinition($id);
if (!$def->isPublic()) {
throw new \InvalidArgumentException(sprintf('The service "%s" must be public as event listeners are lazy-loaded.', $id));
}
if ($def->isAbstract()) {
throw new \InvalidArgumentException(sprintf('The service "%s" must not be abstract as event listeners are lazy-loaded.', $id));
}
foreach ($events as $event) {
$priority = isset($event['priority']) ? $event['priority'] : 0;
if (!isset($event['event'])) {
throw new \InvalidArgumentException(sprintf('Service "%s" must define the "event" attribute on "%s" tags.', $id, $this->listenerTag));
}
if (!isset($event['method'])) {
$event['method'] = 'on'.preg_replace_callback(array(
'/(?<=\b)[a-z]/i',
'/[^a-z0-9]/i',
), function ($matches) { return strtoupper($matches[0]); }, $event['event']);
$event['method'] = preg_replace('/[^a-z0-9]/i', '', $event['method']);
}
$definition->addMethodCall('addListenerService', array($event['event'], array($id, $event['method']), $priority));
}
}
foreach ($container->findTaggedServiceIds($this->subscriberTag) as $id => $attributes) {
$def = $container->getDefinition($id);
if (!$def->isPublic()) {
throw new \InvalidArgumentException(sprintf('The service "%s" must be public as event subscribers are lazy-loaded.', $id));
}
// We must assume that the class value has been correctly filled, even if the service is created by a factory
$class = $def->getClass();
$refClass = new \ReflectionClass($class);
$interface = 'Symfony\Component\EventDispatcher\EventSubscriberInterface';
if (!$refClass->implementsInterface($interface)) {
throw new \InvalidArgumentException(sprintf('Service "%s" must implement interface "%s".', $id, $interface));
}
$definition->addMethodCall('addSubscriberService', array($id, $class));
}
}
}

View File

@@ -0,0 +1,129 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\EventDispatcher;
/**
* Event is the base class for classes containing event data.
*
* This class contains no event data. It is used by events that do not pass
* state information to an event handler when an event is raised.
*
* You can call the method stopPropagation() to abort the execution of
* further listeners in your event listener.
*
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @api
*/
class Event
{
/**
* @var bool Whether no further event listeners should be triggered
*/
private $propagationStopped = false;
/**
* @var EventDispatcher Dispatcher that dispatched this event
*/
private $dispatcher;
/**
* @var string This event's name
*/
private $name;
/**
* Returns whether further event listeners should be triggered.
*
* @see Event::stopPropagation
* @return bool Whether propagation was already stopped for this event.
*
* @api
*/
public function isPropagationStopped()
{
return $this->propagationStopped;
}
/**
* Stops the propagation of the event to further event listeners.
*
* If multiple event listeners are connected to the same event, no
* further event listener will be triggered once any trigger calls
* stopPropagation().
*
* @api
*/
public function stopPropagation()
{
$this->propagationStopped = true;
}
/**
* Stores the EventDispatcher that dispatches this Event
*
* @param EventDispatcherInterface $dispatcher
*
* @deprecated Deprecated in 2.4, to be removed in 3.0. The event dispatcher is passed to the listener call.
*
* @api
*/
public function setDispatcher(EventDispatcherInterface $dispatcher)
{
$this->dispatcher = $dispatcher;
}
/**
* Returns the EventDispatcher that dispatches this Event
*
* @return EventDispatcherInterface
*
* @deprecated Deprecated in 2.4, to be removed in 3.0. The event dispatcher is passed to the listener call.
*
* @api
*/
public function getDispatcher()
{
return $this->dispatcher;
}
/**
* Gets the event's name.
*
* @return string
*
* @deprecated Deprecated in 2.4, to be removed in 3.0. The event name is passed to the listener call.
*
* @api
*/
public function getName()
{
return $this->name;
}
/**
* Sets the event's name property.
*
* @param string $name The event name.
*
* @deprecated Deprecated in 2.4, to be removed in 3.0. The event name is passed to the listener call.
*
* @api
*/
public function setName($name)
{
$this->name = $name;
}
}

View File

@@ -0,0 +1,185 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\EventDispatcher;
/**
* The EventDispatcherInterface is the central point of Symfony's event listener system.
*
* Listeners are registered on the manager and events are dispatched through the
* manager.
*
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
* @author Bernhard Schussek <bschussek@gmail.com>
* @author Fabien Potencier <fabien@symfony.com>
* @author Jordi Boggiano <j.boggiano@seld.be>
* @author Jordan Alliot <jordan.alliot@gmail.com>
*
* @api
*/
class EventDispatcher implements EventDispatcherInterface
{
private $listeners = array();
private $sorted = array();
/**
* @see EventDispatcherInterface::dispatch
*
* @api
*/
public function dispatch($eventName, Event $event = null)
{
if (null === $event) {
$event = new Event();
}
$event->setDispatcher($this);
$event->setName($eventName);
if (!isset($this->listeners[$eventName])) {
return $event;
}
$this->doDispatch($this->getListeners($eventName), $eventName, $event);
return $event;
}
/**
* @see EventDispatcherInterface::getListeners
*/
public function getListeners($eventName = null)
{
if (null !== $eventName) {
if (!isset($this->sorted[$eventName])) {
$this->sortListeners($eventName);
}
return $this->sorted[$eventName];
}
foreach (array_keys($this->listeners) as $eventName) {
if (!isset($this->sorted[$eventName])) {
$this->sortListeners($eventName);
}
}
return array_filter($this->sorted);
}
/**
* @see EventDispatcherInterface::hasListeners
*/
public function hasListeners($eventName = null)
{
return (bool) count($this->getListeners($eventName));
}
/**
* @see EventDispatcherInterface::addListener
*
* @api
*/
public function addListener($eventName, $listener, $priority = 0)
{
$this->listeners[$eventName][$priority][] = $listener;
unset($this->sorted[$eventName]);
}
/**
* @see EventDispatcherInterface::removeListener
*/
public function removeListener($eventName, $listener)
{
if (!isset($this->listeners[$eventName])) {
return;
}
foreach ($this->listeners[$eventName] as $priority => $listeners) {
if (false !== ($key = array_search($listener, $listeners, true))) {
unset($this->listeners[$eventName][$priority][$key], $this->sorted[$eventName]);
}
}
}
/**
* @see EventDispatcherInterface::addSubscriber
*
* @api
*/
public function addSubscriber(EventSubscriberInterface $subscriber)
{
foreach ($subscriber->getSubscribedEvents() as $eventName => $params) {
if (is_string($params)) {
$this->addListener($eventName, array($subscriber, $params));
} elseif (is_string($params[0])) {
$this->addListener($eventName, array($subscriber, $params[0]), isset($params[1]) ? $params[1] : 0);
} else {
foreach ($params as $listener) {
$this->addListener($eventName, array($subscriber, $listener[0]), isset($listener[1]) ? $listener[1] : 0);
}
}
}
}
/**
* @see EventDispatcherInterface::removeSubscriber
*/
public function removeSubscriber(EventSubscriberInterface $subscriber)
{
foreach ($subscriber->getSubscribedEvents() as $eventName => $params) {
if (is_array($params) && is_array($params[0])) {
foreach ($params as $listener) {
$this->removeListener($eventName, array($subscriber, $listener[0]));
}
} else {
$this->removeListener($eventName, array($subscriber, is_string($params) ? $params : $params[0]));
}
}
}
/**
* Triggers the listeners of an event.
*
* This method can be overridden to add functionality that is executed
* for each listener.
*
* @param callable[] $listeners The event listeners.
* @param string $eventName The name of the event to dispatch.
* @param Event $event The event object to pass to the event handlers/listeners.
*/
protected function doDispatch($listeners, $eventName, Event $event)
{
foreach ($listeners as $listener) {
call_user_func($listener, $event, $eventName, $this);
if ($event->isPropagationStopped()) {
break;
}
}
}
/**
* Sorts the internal list of listeners for the given event by priority.
*
* @param string $eventName The name of the event.
*/
private function sortListeners($eventName)
{
$this->sorted[$eventName] = array();
if (isset($this->listeners[$eventName])) {
krsort($this->listeners[$eventName]);
$this->sorted[$eventName] = call_user_func_array('array_merge', $this->listeners[$eventName]);
}
}
}

View File

@@ -0,0 +1,96 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\EventDispatcher;
/**
* The EventDispatcherInterface is the central point of Symfony's event listener system.
* Listeners are registered on the manager and events are dispatched through the
* manager.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @api
*/
interface EventDispatcherInterface
{
/**
* Dispatches an event to all registered listeners.
*
* @param string $eventName The name of the event to dispatch. The name of
* the event is the name of the method that is
* invoked on listeners.
* @param Event $event The event to pass to the event handlers/listeners.
* If not supplied, an empty Event instance is created.
*
* @return Event
*
* @api
*/
public function dispatch($eventName, Event $event = null);
/**
* Adds an event listener that listens on the specified events.
*
* @param string $eventName The event to listen on
* @param callable $listener The listener
* @param int $priority The higher this value, the earlier an event
* listener will be triggered in the chain (defaults to 0)
*
* @api
*/
public function addListener($eventName, $listener, $priority = 0);
/**
* Adds an event subscriber.
*
* The subscriber is asked for all the events he is
* interested in and added as a listener for these events.
*
* @param EventSubscriberInterface $subscriber The subscriber.
*
* @api
*/
public function addSubscriber(EventSubscriberInterface $subscriber);
/**
* Removes an event listener from the specified events.
*
* @param string|array $eventName The event(s) to remove a listener from
* @param callable $listener The listener to remove
*/
public function removeListener($eventName, $listener);
/**
* Removes an event subscriber.
*
* @param EventSubscriberInterface $subscriber The subscriber
*/
public function removeSubscriber(EventSubscriberInterface $subscriber);
/**
* Gets the listeners of a specific event or all listeners.
*
* @param string $eventName The name of the event
*
* @return array The event listeners for the specified event, or all event listeners by event name
*/
public function getListeners($eventName = null);
/**
* Checks whether an event has any registered listeners.
*
* @param string $eventName The name of the event
*
* @return bool true if the specified event has any listeners, false otherwise
*/
public function hasListeners($eventName = null);
}

View File

@@ -0,0 +1,50 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\EventDispatcher;
/**
* An EventSubscriber knows himself what events he is interested in.
* If an EventSubscriber is added to an EventDispatcherInterface, the manager invokes
* {@link getSubscribedEvents} and registers the subscriber as a listener for all
* returned events.
*
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @api
*/
interface EventSubscriberInterface
{
/**
* Returns an array of event names this subscriber wants to listen to.
*
* The array keys are event names and the value can be:
*
* * The method name to call (priority defaults to 0)
* * An array composed of the method name to call and the priority
* * An array of arrays composed of the method names to call and respective
* priorities, or 0 if unset
*
* For instance:
*
* * array('eventName' => 'methodName')
* * array('eventName' => array('methodName', $priority))
* * array('eventName' => array(array('methodName1', $priority), array('methodName2'))
*
* @return array The event names to listen to
*
* @api
*/
public static function getSubscribedEvents();
}

View File

@@ -0,0 +1,186 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\EventDispatcher;
/**
* Event encapsulation class.
*
* Encapsulates events thus decoupling the observer from the subject they encapsulate.
*
* @author Drak <drak@zikula.org>
*/
class GenericEvent extends Event implements \ArrayAccess, \IteratorAggregate
{
/**
* Event subject.
*
* @var mixed usually object or callable
*/
protected $subject;
/**
* Array of arguments.
*
* @var array
*/
protected $arguments;
/**
* Encapsulate an event with $subject and $args.
*
* @param mixed $subject The subject of the event, usually an object.
* @param array $arguments Arguments to store in the event.
*/
public function __construct($subject = null, array $arguments = array())
{
$this->subject = $subject;
$this->arguments = $arguments;
}
/**
* Getter for subject property.
*
* @return mixed $subject The observer subject.
*/
public function getSubject()
{
return $this->subject;
}
/**
* Get argument by key.
*
* @param string $key Key.
*
* @throws \InvalidArgumentException If key is not found.
*
* @return mixed Contents of array key.
*/
public function getArgument($key)
{
if ($this->hasArgument($key)) {
return $this->arguments[$key];
}
throw new \InvalidArgumentException(sprintf('%s not found in %s', $key, $this->getName()));
}
/**
* Add argument to event.
*
* @param string $key Argument name.
* @param mixed $value Value.
*
* @return GenericEvent
*/
public function setArgument($key, $value)
{
$this->arguments[$key] = $value;
return $this;
}
/**
* Getter for all arguments.
*
* @return array
*/
public function getArguments()
{
return $this->arguments;
}
/**
* Set args property.
*
* @param array $args Arguments.
*
* @return GenericEvent
*/
public function setArguments(array $args = array())
{
$this->arguments = $args;
return $this;
}
/**
* Has argument.
*
* @param string $key Key of arguments array.
*
* @return bool
*/
public function hasArgument($key)
{
return array_key_exists($key, $this->arguments);
}
/**
* ArrayAccess for argument getter.
*
* @param string $key Array key.
*
* @throws \InvalidArgumentException If key does not exist in $this->args.
*
* @return mixed
*/
public function offsetGet($key)
{
return $this->getArgument($key);
}
/**
* ArrayAccess for argument setter.
*
* @param string $key Array key to set.
* @param mixed $value Value.
*/
public function offsetSet($key, $value)
{
$this->setArgument($key, $value);
}
/**
* ArrayAccess for unset argument.
*
* @param string $key Array key.
*/
public function offsetUnset($key)
{
if ($this->hasArgument($key)) {
unset($this->arguments[$key]);
}
}
/**
* ArrayAccess has argument.
*
* @param string $key Array key.
*
* @return bool
*/
public function offsetExists($key)
{
return $this->hasArgument($key);
}
/**
* IteratorAggregate for iterating over the object like an array
*
* @return \ArrayIterator
*/
public function getIterator()
{
return new \ArrayIterator($this->arguments);
}
}

View File

@@ -0,0 +1,92 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\EventDispatcher;
/**
* A read-only proxy for an event dispatcher.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class ImmutableEventDispatcher implements EventDispatcherInterface
{
/**
* The proxied dispatcher.
* @var EventDispatcherInterface
*/
private $dispatcher;
/**
* Creates an unmodifiable proxy for an event dispatcher.
*
* @param EventDispatcherInterface $dispatcher The proxied event dispatcher.
*/
public function __construct(EventDispatcherInterface $dispatcher)
{
$this->dispatcher = $dispatcher;
}
/**
* {@inheritdoc}
*/
public function dispatch($eventName, Event $event = null)
{
return $this->dispatcher->dispatch($eventName, $event);
}
/**
* {@inheritdoc}
*/
public function addListener($eventName, $listener, $priority = 0)
{
throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.');
}
/**
* {@inheritdoc}
*/
public function addSubscriber(EventSubscriberInterface $subscriber)
{
throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.');
}
/**
* {@inheritdoc}
*/
public function removeListener($eventName, $listener)
{
throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.');
}
/**
* {@inheritdoc}
*/
public function removeSubscriber(EventSubscriberInterface $subscriber)
{
throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.');
}
/**
* {@inheritdoc}
*/
public function getListeners($eventName = null)
{
return $this->dispatcher->getListeners($eventName);
}
/**
* {@inheritdoc}
*/
public function hasListeners($eventName = null)
{
return $this->dispatcher->hasListeners($eventName);
}
}

View File

@@ -0,0 +1,19 @@
Copyright (c) 2004-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.

View File

@@ -0,0 +1,25 @@
EventDispatcher Component
=========================
The Symfony2 EventDispatcher component implements the Mediator pattern in a
simple and effective way to make your projects truly extensible.
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\EventDispatcher\Event;
$dispatcher = new EventDispatcher();
$dispatcher->addListener('event_name', function (Event $event) {
// ...
});
$dispatcher->dispatch('event_name');
Resources
---------
You can run the unit tests with the following command:
$ cd path/to/Symfony/Component/EventDispatcher/
$ composer.phar install
$ phpunit