Merge pull request #586 from MrPetovan/task/add-advancedcontentfilter-addon

Add advancedcontentfilter addon
This commit is contained in:
Michael Vogel 2018-05-01 11:05:55 +02:00 committed by GitHub
commit b0dba96b0d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
387 changed files with 37629 additions and 1 deletions

View File

@ -0,0 +1,24 @@
Copyright (c) 2011-2018 Hypolite Petovan
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of Friendica nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL FRIENDICA BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -0,0 +1,11 @@
Advanced Content Filter
=======================
Main author Hypolite Petovan.
## License
The _Advanced Content Filter_ addon is licensed under the [3-clause BSD license][2] see the LICENSE file in the addons directory.
[1]: http://opensource.org/licenses/BSD-3-Clause

View File

@ -0,0 +1,122 @@
$.ajaxSetup({headers: {'X-CSRF-Token': document.querySelector('#csrf').getAttribute('value')}});
$.extend({
ajaxJSON: function(method, url, data) {
return $.ajax({
type: method.toUpperCase(),
url: url,
data: JSON.stringify(data),
contentType: 'application/json; charset=utf-8',
dataType: 'json'
});
}
});
new Vue({
el: '#rules',
data: {
showModal: false,
errorMessage: '',
editedIndex: null,
rule: {id: '', name: '', expression: '', created: ''},
rules: existingRules || [],
itemUrl: '',
itemJson: ''
},
watch: {
showModal: function () {
if (this.showModal) {
$(this.$refs.vuemodal).modal('show');
} else {
$(this.$refs.vuemodal).modal('hide');
}
}
},
methods: {
resetForm: function() {
this.rule = {id: '', name: '', expression: '', created: ''};
this.showModal = false;
this.editedIndex = null;
},
addRule: function () {
if (this.rule.name.trim()) {
this.errorMessage = '';
var self = this;
$.ajaxJSON('post', '/advancedcontentfilter/api/rules', this.rule)
.then(function (responseJSON) {
self.rules.push(responseJSON.rule);
self.resetForm();
}, function (response) {
self.errorMessage = response.responseJSON.message;
});
}
},
editRule: function (rule) {
this.editedIndex = this.rules.indexOf(rule);
this.rule = Object.assign({}, rule);
this.showModal = true;
},
saveRule: function (rule) {
this.errorMessage = '';
var self = this;
$.ajaxJSON('put', '/advancedcontentfilter/api/rules/' + rule.id, rule)
.then(function () {
self.rules[self.editedIndex] = rule;
self.resetForm();
}, function (response) {
self.errorMessage = response.responseJSON.message;
});
},
toggleActive: function (rule) {
var previousValue = this.rules[this.rules.indexOf(rule)].active;
var newValue = Math.abs(parseInt(rule.active) - 1);
this.rules[this.rules.indexOf(rule)].active = newValue;
var self = this;
$.ajaxJSON('put', '/advancedcontentfilter/api/rules/' + rule.id, {'active': newValue})
.fail(function (response) {
self.rules[self.rules.indexOf(rule)].active = previousValue;
console.log(response.responseJSON.message);
});
},
deleteRule: function (rule) {
if (confirm('Are you sure you want to delete this rule?')) {
var self = this;
$.ajaxJSON('delete', '/advancedcontentfilter/api/rules/' + rule.id)
.then(function () {
self.rules.splice(self.rules.indexOf(rule), 1);
}, function (response) {
console.log(response.responseJSON.message);
});
}
},
showVariables: function () {
var urlParts = this.itemUrl.split('/');
var guid = urlParts[urlParts.length - 1];
this.itemJson = '';
var self = this;
$.ajaxJSON('get', '/advancedcontentfilter/api/variables/' + guid)
.then(function (responseJSON) {
self.itemJson = responseJSON.variables;
}, function (response) {
self.itemJson = response.responseJSON.message;
});
return false;
}
}
});

View File

@ -0,0 +1,420 @@
<?php
/**
* Name: Advanced content Filter
* Description: Expression-based content filter
* Version: 1.0
* Author: Hypolite Petovan <https://friendica.mrpetovan.com/profile/hypolite>
* Maintainer: Hypolite Petovan <https://friendica.mrpetovan.com/profile/hypolite>
*
* Copyright (c) 2018 Hypolite Petovan
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* * copyright notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the distribution.
* * Neither the name of Friendica nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL FRIENDICA BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
use Friendica\App;
use Friendica\Core\Addon;
use Friendica\Core\L10n;
use Friendica\Core\System;
use Friendica\Database\DBStructure;
use Friendica\Network\HTTPException;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Symfony\Component\ExpressionLanguage;
require_once 'boot.php';
require_once 'include/conversation.php';
require_once 'include/dba.php';
require_once 'include/security.php';
require_once __DIR__ . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'autoload.php';
function advancedcontentfilter_install()
{
Addon::registerHook('dbstructure_definition' , __FILE__, 'advancedcontentfilter_dbstructure_definition');
Addon::registerHook('prepare_body_content_filter', __FILE__, 'advancedcontentfilter_prepare_body_content_filter');
Addon::registerHook('addon_settings' , __FILE__, 'advancedcontentfilter_addon_settings');
DBStructure::update(false, true);
logger("installed advancedcontentfilter");
}
function advancedcontentfilter_uninstall()
{
Addon::unregisterHook('dbstructure_definition' , __FILE__, 'advancedcontentfilter_dbstructure_definition');
Addon::unregisterHook('prepare_body_content_filter', __FILE__, 'advancedcontentfilter_prepare_body_content_filter');
Addon::unregisterHook('addon_settings' , __FILE__, 'advancedcontentfilter_addon_settings');
}
/*
* Hooks
*/
function advancedcontentfilter_dbstructure_definition(App $a, &$database)
{
$database["advancedcontentfilter_rules"] = [
"comment" => "Advancedcontentfilter addon rules",
"fields" => [
"id" => ["type" => "int unsigned", "not null" => "1", "extra" => "auto_increment", "primary" => "1", "comment" => "Auto incremented rule id"],
"uid" => ["type" => "int unsigned", "not null" => "1", "comment" => "Owner user id"],
"name" => ["type" => "varchar(255)", "not null" => "1", "comment" => "Rule name"],
"expression" => ["type" => "mediumtext" , "not null" => "1", "comment" => "Expression text"],
"serialized" => ["type" => "mediumtext" , "not null" => "1", "comment" => "Serialized parsed expression"],
"active" => ["type" => "boolean" , "not null" => "1", "default" => "1", "comment" => "Whether the rule is active or not"],
"created" => ["type" => "datetime" , "not null" => "1", "default" => NULL_DATE, "comment" => "Creation date"],
],
"indexes" => [
"PRIMARY" => ["id"],
"uid_active" => ["uid", "active"],
]
];
}
function advancedcontentfilter_prepare_body_content_filter(App $a, &$hook_data)
{
static $expressionLanguage;
if (is_null($expressionLanguage)) {
$expressionLanguage = new ExpressionLanguage\ExpressionLanguage();
}
if (!local_user()) {
return;
}
$vars = [];
foreach ($hook_data['item'] as $key => $value) {
$vars[str_replace('-', '_', $key)] = $value;
}
$rules = Friendica\Core\Cache::get('rules_' . local_user());
if (!isset($rules)) {
$rules = dba::inArray(dba::select(
'advancedcontentfilter_rules',
['name', 'expression', 'serialized'],
['uid' => local_user(), 'active' => true]
));
}
foreach($rules as $rule) {
try {
$serializedParsedExpression = new ExpressionLanguage\SerializedParsedExpression(
$rule['expression'],
$rule['serialized']
);
$found = (bool) $expressionLanguage->evaluate($serializedParsedExpression, $vars);
} catch (Exception $e) {
$found = false;
}
if ($found) {
$hook_data['filter_reasons'][] = L10n::t('Filtered by rule: %s', $rule['name']);
break;
}
}
}
function advancedcontentfilter_addon_settings(App $a, &$s)
{
if (!local_user()) {
return;
}
$advancedcontentfilter = L10n::t('Advanced Content Filter');
$s .= <<<HTML
<span class="settings-block fakelink" style="display: block;"><h3><a href="advancedcontentfilter">$advancedcontentfilter <i class="glyphicon glyphicon-share"></i></a></h3></span>
HTML;
return;
}
/*
* Module
*/
function advancedcontentfilter_module() {}
function advancedcontentfilter_init(App $a)
{
if ($a->argv[1] == 'api') {
$slim = new \Slim\App();
require __DIR__ . '/src/middlewares.php';
require __DIR__ . '/src/routes.php';
$slim->run();
exit;
}
}
function advancedcontentfilter_content(App $a)
{
if (!local_user()) {
return \Friendica\Module\Login::form('/' . implode('/', $a->argv));
}
if ($a->argc > 0 && $a->argv[1] == 'help') {
$lang = $a->user['language'];
$default_dir = 'addon/advancedcontentfilter/doc/';
$help_file = 'advancedcontentfilter.md';
$help_path = $default_dir . $help_file;
if (file_exists($default_dir . $lang . '/' . $help_file)) {
$help_path = $default_dir . $lang . '/' . $help_file;
}
$content = file_get_contents($help_path);
$html = \Friendica\Content\Text\Markdown::convert($content, false);
$html = str_replace('code>', 'key>', $html);
return $html;
} else {
$t = get_markup_template('settings.tpl', 'addon/advancedcontentfilter/');
return replace_macros($t, [
'$backtosettings' => L10n::t('Back to Addon Settings'),
'$title' => L10n::t('Advanced Content Filter'),
'$add_a_rule' => L10n::t('Add a Rule'),
'$help' => L10n::t('Help'),
'$advanced_content_filter_intro' => L10n::t('Add and manage your personal content filter rules in this screen. Rules have a name and an arbitrary expression that will be matched against post data. For a complete reference of the available operations and variables, check the <a href="advancedcontentfilter/help">help page</a>.'),
'$your_rules' => L10n::t('Your rules'),
'$no_rules' => L10n::t('You have no rules yet! Start adding one by clicking on the button above next to the title.'),
'$disabled' => L10n::t('Disabled'),
'$enabled' => L10n::t('Enabled'),
'$disable_this_rule' => L10n::t('Disable this rule'),
'$enable_this_rule' => L10n::t('Enable this rule'),
'$edit_this_rule' => L10n::t('Edit this rule'),
'$edit_the_rule' => L10n::t('Edit the rule'),
'$save_this_rule' => L10n::t('Save this rule'),
'$delete_this_rule' => L10n::t('Delete this rule'),
'$rule' => L10n::t('Rule'),
'$close' => L10n::t('Close'),
'$addtitle' => L10n::t('Add new rule'),
'$rule_name' => L10n::t('Rule Name'),
'$rule_expression' => L10n::t('Rule Expression'),
'$examples' => L10n::t('<p>Examples:</p><ul><li><pre>author_link == \'https://friendica.mrpetovan.com/profile/hypolite\'</pre></li><li>tags</li></ul>'),
'$cancel' => L10n::t('Cancel'),
'$rules' => advancedcontentfilter_get_rules(),
'$baseurl' => System::baseUrl(true),
'$form_security_token' => get_form_security_token()
]);
}
}
/*
* Common functions
*/
function advancedcontentfilter_build_fields($data)
{
$fields = [];
if (!empty($data['name'])) {
$fields['name'] = $data['name'];
}
if (!empty($data['expression'])) {
$allowed_keys = [
'author_id', 'author_link', 'author_name', 'author_avatar',
'owner_id', 'owner_link', 'owner_name', 'owner_avatar',
'contact_id', 'uid', 'id', 'parent', 'uri',
'thr_parent', 'parent_uri',
'content_warning',
'commented', 'created', 'edited', 'received',
'verb', 'object_type', 'postopts', 'plink', 'guid', 'wall', 'private', 'starred',
'title', 'body',
'file', 'event_id', 'location', 'coord', 'app', 'attach',
'rendered_hash', 'rendered_html', 'object',
'allow_cid', 'allow_gid', 'deny_cid', 'deny_gid',
'item_id', 'item_network', 'author_thumb', 'owner_thumb',
'network', 'url', 'name', 'writable', 'self',
'cid', 'alias',
'event_created', 'event_edited', 'event_start', 'event_finish', 'event_summary',
'event_desc', 'event_location', 'event_type', 'event_nofinish', 'event_adjust', 'event_ignore',
'children', 'pagedrop', 'tags', 'hashtags', 'mentions',
];
$expressionLanguage = new ExpressionLanguage\ExpressionLanguage();
$parsedExpression = $expressionLanguage->parse($data['expression'], $allowed_keys);
$serialized = serialize($parsedExpression->getNodes());
$fields['expression'] = $data['expression'];
$fields['serialized'] = $serialized;
}
if (isset($data['active'])) {
$fields['active'] = intval($data['active']);
} else {
$fields['active'] = 1;
}
return $fields;
}
/*
* API
*/
function advancedcontentfilter_get_rules()
{
if (!local_user()) {
throw new HTTPException\UnauthorizedException(L10n::t('You must be logged in to use this method'));
}
$rules = dba::inArray(dba::select('advancedcontentfilter_rules', [], ['uid' => local_user()]));
return json_encode($rules);
}
function advancedcontentfilter_get_rules_id(ServerRequestInterface $request, ResponseInterface $response, $args)
{
if (!local_user()) {
throw new HTTPException\UnauthorizedException(L10n::t('You must be logged in to use this method'));
}
$rule = dba::selectFirst('advancedcontentfilter_rules', [], ['id' => $args['id'], 'uid' => local_user()]);
return json_encode($rule);
}
function advancedcontentfilter_post_rules(ServerRequestInterface $request)
{
if (!local_user()) {
throw new HTTPException\UnauthorizedException(L10n::t('You must be logged in to use this method'));
}
if (!check_form_security_token()) {
throw new HTTPException\BadRequestException(L10n::t('Invalid form security token, please refresh the page.'));
}
$data = json_decode($request->getBody(), true);
try {
$fields = advancedcontentfilter_build_fields($data);
} catch (Exception $e) {
throw new HTTPException\BadRequestException($e->getMessage(), 0, $e);
}
if (empty($fields['name']) || empty($fields['expression'])) {
throw new HTTPException\BadRequestException(L10n::t('The rule name and expression are required.'));
}
$fields['uid'] = local_user();
$fields['created'] = \Friendica\Util\DateTimeFormat::utcNow();
if (!dba::insert('advancedcontentfilter_rules', $fields)) {
throw new HTTPException\ServiceUnavaiableException(dba::errorMessage());
}
$rule = dba::selectFirst('advancedcontentfilter_rules', [], ['id' => dba::lastInsertId()]);
return json_encode(['message' => L10n::t('Rule successfully added'), 'rule' => $rule]);
}
function advancedcontentfilter_put_rules_id(ServerRequestInterface $request, ResponseInterface $response, $args)
{
if (!local_user()) {
throw new HTTPException\UnauthorizedException(L10n::t('You must be logged in to use this method'));
}
if (!check_form_security_token()) {
throw new HTTPException\BadRequestException(L10n::t('Invalid form security token, please refresh the page.'));
}
if (!dba::exists('advancedcontentfilter_rules', ['id' => $args['id'], 'uid' => local_user()])) {
throw new HTTPException\NotFoundException(L10n::t('Rule doesn\'t exist or doesn\'t belong to you.'));
}
$data = json_decode($request->getBody(), true);
try {
$fields = advancedcontentfilter_build_fields($data);
} catch (Exception $e) {
throw new HTTPException\BadRequestException($e->getMessage(), 0, $e);
}
if (!dba::update('advancedcontentfilter_rules', $fields, ['id' => $args['id']])) {
throw new HTTPException\ServiceUnavaiableException(dba::errorMessage());
}
return json_encode(['message' => L10n::t('Rule successfully updated')]);
}
function advancedcontentfilter_delete_rules_id(ServerRequestInterface $request, ResponseInterface $response, $args)
{
if (!local_user()) {
throw new HTTPException\UnauthorizedException(L10n::t('You must be logged in to use this method'));
}
if (!check_form_security_token()) {
throw new HTTPException\BadRequestException(L10n::t('Invalid form security token, please refresh the page.'));
}
if (!dba::exists('advancedcontentfilter_rules', ['id' => $args['id'], 'uid' => local_user()])) {
throw new HTTPException\NotFoundException(L10n::t('Rule doesn\'t exist or doesn\'t belong to you.'));
}
if (!dba::delete('advancedcontentfilter_rules', ['id' => $args['id']])) {
throw new HTTPException\ServiceUnavaiableException(dba::errorMessage());
}
return json_encode(['message' => L10n::t('Rule successfully deleted')]);
}
function advancedcontentfilter_get_variables_guid(ServerRequestInterface $request, ResponseInterface $response, $args)
{
if (!local_user()) {
throw new HTTPException\UnauthorizedException(L10n::t('You must be logged in to use this method'));
}
if (!isset($args['guid'])) {
throw new HTTPException\BadRequestException(L10n::t('Missing argument: guid.'));
}
$item = dba::fetch_first(item_query() . " AND `item`.`guid` = ? AND (`item`.`uid` = ? OR `item`.`uid` = 0) ORDER BY `item`.`uid` DESC", $args['guid'], local_user());
if (!\Friendica\Database\DBM::is_result($item)) {
throw new HTTPException\NotFoundException(L10n::t('Unknown post with guid: %s', $args['guid']));
}
$tags = \Friendica\Model\Term::populateTagsFromItem($item);
$item['tags'] = $tags['tags'];
$item['hashtags'] = $tags['hashtags'];
$item['mentions'] = $tags['mentions'];
$return = [];
foreach ($item as $key => $value) {
$return[str_replace('-', '_', $key)] = $value;
}
return json_encode(['variables' => str_replace('\\\'', '\'', var_export($return, true))]);
}

View File

@ -0,0 +1,25 @@
{
"name": "friendica-addons/advancedcontentfilter",
"description": "Advanced Content Filter addon for Friendica",
"type": "friendica-addon",
"authors": [
{
"name": "Hypolite Petovan",
"email": "mrpetovan@gmail.com",
"homepage": "https://friendica.mrpetovan.com/profile/hypolite",
"role": "Developer"
}
],
"require": {
"php": ">=5.6.0",
"slim/slim": "^3.1",
"symfony/expression-language": "^3.4"
},
"license": "3-clause BSD license",
"minimum-stability": "stable",
"config": {
"optimize-autoloader": true,
"autoloader-suffix": "AdvancedContentFilterAddon",
"preferred-install": "dist"
}
}

634
advancedcontentfilter/composer.lock generated Normal file
View File

@ -0,0 +1,634 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"content-hash": "d0e3662dd9d910ffe4f71d325bc39319",
"packages": [
{
"name": "container-interop/container-interop",
"version": "1.2.0",
"source": {
"type": "git",
"url": "https://github.com/container-interop/container-interop.git",
"reference": "79cbf1341c22ec75643d841642dd5d6acd83bdb8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/container-interop/container-interop/zipball/79cbf1341c22ec75643d841642dd5d6acd83bdb8",
"reference": "79cbf1341c22ec75643d841642dd5d6acd83bdb8",
"shasum": ""
},
"require": {
"psr/container": "^1.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Interop\\Container\\": "src/Interop/Container/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "Promoting the interoperability of container objects (DIC, SL, etc.)",
"homepage": "https://github.com/container-interop/container-interop",
"time": "2017-02-14T19:40:03+00:00"
},
{
"name": "nikic/fast-route",
"version": "v1.3.0",
"source": {
"type": "git",
"url": "https://github.com/nikic/FastRoute.git",
"reference": "181d480e08d9476e61381e04a71b34dc0432e812"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nikic/FastRoute/zipball/181d480e08d9476e61381e04a71b34dc0432e812",
"reference": "181d480e08d9476e61381e04a71b34dc0432e812",
"shasum": ""
},
"require": {
"php": ">=5.4.0"
},
"require-dev": {
"phpunit/phpunit": "^4.8.35|~5.7"
},
"type": "library",
"autoload": {
"psr-4": {
"FastRoute\\": "src/"
},
"files": [
"src/functions.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Nikita Popov",
"email": "nikic@php.net"
}
],
"description": "Fast request router for PHP",
"keywords": [
"router",
"routing"
],
"time": "2018-02-13T20:26:39+00:00"
},
{
"name": "pimple/pimple",
"version": "v3.2.3",
"source": {
"type": "git",
"url": "https://github.com/silexphp/Pimple.git",
"reference": "9e403941ef9d65d20cba7d54e29fe906db42cf32"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/silexphp/Pimple/zipball/9e403941ef9d65d20cba7d54e29fe906db42cf32",
"reference": "9e403941ef9d65d20cba7d54e29fe906db42cf32",
"shasum": ""
},
"require": {
"php": ">=5.3.0",
"psr/container": "^1.0"
},
"require-dev": {
"symfony/phpunit-bridge": "^3.2"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.2.x-dev"
}
},
"autoload": {
"psr-0": {
"Pimple": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
}
],
"description": "Pimple, a simple Dependency Injection Container",
"homepage": "http://pimple.sensiolabs.org",
"keywords": [
"container",
"dependency injection"
],
"time": "2018-01-21T07:42:36+00:00"
},
{
"name": "psr/cache",
"version": "1.0.1",
"source": {
"type": "git",
"url": "https://github.com/php-fig/cache.git",
"reference": "d11b50ad223250cf17b86e38383413f5a6764bf8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/cache/zipball/d11b50ad223250cf17b86e38383413f5a6764bf8",
"reference": "d11b50ad223250cf17b86e38383413f5a6764bf8",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\Cache\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
}
],
"description": "Common interface for caching libraries",
"keywords": [
"cache",
"psr",
"psr-6"
],
"time": "2016-08-06T20:24:11+00:00"
},
{
"name": "psr/container",
"version": "1.0.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/container.git",
"reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
"reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\Container\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
}
],
"description": "Common Container Interface (PHP FIG PSR-11)",
"homepage": "https://github.com/php-fig/container",
"keywords": [
"PSR-11",
"container",
"container-interface",
"container-interop",
"psr"
],
"time": "2017-02-14T16:28:37+00:00"
},
{
"name": "psr/http-message",
"version": "1.0.1",
"source": {
"type": "git",
"url": "https://github.com/php-fig/http-message.git",
"reference": "f6561bf28d520154e4b0ec72be95418abe6d9363"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363",
"reference": "f6561bf28d520154e4b0ec72be95418abe6d9363",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\Http\\Message\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
}
],
"description": "Common interface for HTTP messages",
"homepage": "https://github.com/php-fig/http-message",
"keywords": [
"http",
"http-message",
"psr",
"psr-7",
"request",
"response"
],
"time": "2016-08-06T14:39:51+00:00"
},
{
"name": "psr/log",
"version": "1.0.2",
"source": {
"type": "git",
"url": "https://github.com/php-fig/log.git",
"reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/log/zipball/4ebe3a8bf773a19edfe0a84b6585ba3d401b724d",
"reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\Log\\": "Psr/Log/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
}
],
"description": "Common interface for logging libraries",
"homepage": "https://github.com/php-fig/log",
"keywords": [
"log",
"psr",
"psr-3"
],
"time": "2016-10-10T12:19:37+00:00"
},
{
"name": "psr/simple-cache",
"version": "1.0.1",
"source": {
"type": "git",
"url": "https://github.com/php-fig/simple-cache.git",
"reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/simple-cache/zipball/408d5eafb83c57f6365a3ca330ff23aa4a5fa39b",
"reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\SimpleCache\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
}
],
"description": "Common interfaces for simple caching",
"keywords": [
"cache",
"caching",
"psr",
"psr-16",
"simple-cache"
],
"time": "2017-10-23T01:57:42+00:00"
},
{
"name": "slim/slim",
"version": "3.9.2",
"source": {
"type": "git",
"url": "https://github.com/slimphp/Slim.git",
"reference": "4086d0106cf5a7135c69fce4161fe355a8feb118"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/slimphp/Slim/zipball/4086d0106cf5a7135c69fce4161fe355a8feb118",
"reference": "4086d0106cf5a7135c69fce4161fe355a8feb118",
"shasum": ""
},
"require": {
"container-interop/container-interop": "^1.2",
"nikic/fast-route": "^1.0",
"php": ">=5.5.0",
"pimple/pimple": "^3.0",
"psr/container": "^1.0",
"psr/http-message": "^1.0"
},
"provide": {
"psr/http-message-implementation": "1.0"
},
"require-dev": {
"phpunit/phpunit": "^4.0",
"squizlabs/php_codesniffer": "^2.5"
},
"type": "library",
"autoload": {
"psr-4": {
"Slim\\": "Slim"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Rob Allen",
"email": "rob@akrabat.com",
"homepage": "http://akrabat.com"
},
{
"name": "Josh Lockhart",
"email": "hello@joshlockhart.com",
"homepage": "https://joshlockhart.com"
},
{
"name": "Gabriel Manricks",
"email": "gmanricks@me.com",
"homepage": "http://gabrielmanricks.com"
},
{
"name": "Andrew Smith",
"email": "a.smith@silentworks.co.uk",
"homepage": "http://silentworks.co.uk"
}
],
"description": "Slim is a PHP micro framework that helps you quickly write simple yet powerful web applications and APIs",
"homepage": "https://slimframework.com",
"keywords": [
"api",
"framework",
"micro",
"router"
],
"time": "2017-11-26T19:13:09+00:00"
},
{
"name": "symfony/cache",
"version": "v3.4.8",
"source": {
"type": "git",
"url": "https://github.com/symfony/cache.git",
"reference": "13255ddd056e49f3154747943f8ee175d555d394"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/cache/zipball/13255ddd056e49f3154747943f8ee175d555d394",
"reference": "13255ddd056e49f3154747943f8ee175d555d394",
"shasum": ""
},
"require": {
"php": "^5.5.9|>=7.0.8",
"psr/cache": "~1.0",
"psr/log": "~1.0",
"psr/simple-cache": "^1.0",
"symfony/polyfill-apcu": "~1.1"
},
"conflict": {
"symfony/var-dumper": "<3.3"
},
"provide": {
"psr/cache-implementation": "1.0",
"psr/simple-cache-implementation": "1.0"
},
"require-dev": {
"cache/integration-tests": "dev-master",
"doctrine/cache": "~1.6",
"doctrine/dbal": "~2.4",
"predis/predis": "~1.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.4-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Component\\Cache\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony Cache component with PSR-6, PSR-16, and tags",
"homepage": "https://symfony.com",
"keywords": [
"caching",
"psr6"
],
"time": "2018-04-02T14:35:16+00:00"
},
{
"name": "symfony/expression-language",
"version": "v3.4.8",
"source": {
"type": "git",
"url": "https://github.com/symfony/expression-language.git",
"reference": "867e4d1f5d4e52435a8ffff6b24fd6a801582241"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/expression-language/zipball/867e4d1f5d4e52435a8ffff6b24fd6a801582241",
"reference": "867e4d1f5d4e52435a8ffff6b24fd6a801582241",
"shasum": ""
},
"require": {
"php": "^5.5.9|>=7.0.8",
"symfony/cache": "~3.1|~4.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.4-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Component\\ExpressionLanguage\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony ExpressionLanguage Component",
"homepage": "https://symfony.com",
"time": "2018-01-03T07:37:34+00:00"
},
{
"name": "symfony/polyfill-apcu",
"version": "v1.7.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-apcu.git",
"reference": "e8ae2136ddb53dea314df56fcd88e318ab936c00"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-apcu/zipball/e8ae2136ddb53dea314df56fcd88e318ab936c00",
"reference": "e8ae2136ddb53dea314df56fcd88e318ab936c00",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.7-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Polyfill\\Apcu\\": ""
},
"files": [
"bootstrap.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill backporting apcu_* functions to lower PHP versions",
"homepage": "https://symfony.com",
"keywords": [
"apcu",
"compatibility",
"polyfill",
"portable",
"shim"
],
"time": "2018-01-30T19:27:44+00:00"
}
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
"php": ">=5.6.0"
},
"platform-dev": []
}

View File

@ -0,0 +1,509 @@
<style>
.advancedcontentfilter-content-wrapper {
min-height: calc(100vh - 150px);
padding: 15px;
padding-bottom: 20px;
margin-bottom: 20px;
border: none;
/*background-color: #fff;*/
background-color: rgba(255,255,255,0.95);
border-radius: 4px;
position: relative;
/*overflow: hidden;*/
color: #555;
box-shadow: 0 0 3px #dadada;
-webkit-box-shadow: 0 0 3px #dadada;
-moz-box-shadow: 0 0 3px #dadada;
}
</style>
<a href="advancedcontentfilter">🔙 Back to Advanced Content Filter Settings</a>
# Advanced Content Filter Help
The advanced Content Filter uses Symfony's Expression Language.
This help page includes a summary of [the Symfony's Expression Language documentation page.](https://symfony.com/doc/current/components/expression_language/syntax.html)
## Basics
The advanced content filter matches each post that is about to be displayed against each enabled rule you set.
A rule is a boolean expression that should return either `true` or `false` depending on post variables.
If the expression using a post variables returns `true`, the post will be collapsed and the matching rule name will be displayed above the collapsed content.
A post will be collapsed if at least one rule matches, but all matching rule names will be displayed above the collapsed content.
## Expression Syntax
### Supported Literals
- **strings** - single and double quotes (e.g. `'hello'`).
- **numbers** - e.g. `103`.
- **arrays** - using JSON-like notation (e.g. `[1, 2]`).
- **hashes** - using JSON-like notation (e.g. `{ foo: 'bar' }`).
- **booleans** - `true` and `false`.
- **null** - `null`.
A backslash (``\``) must be escaped by 2 backslashes (``\\``) in a string and 4 backslashes (``\\\\``) in a regex::
`"a\\b" matches "/^a\\\\b$/"`
Control characters (e.g. ``\n``) in expressions are replaced with whitespace. To avoid this, escape the sequence with a single backslash (e.g. ``\\n``).
### Supported Operators
The component comes with a lot of operators:
#### Arithmetic Operators
* ``+`` (addition)
* ``-`` (subtraction)
* ``*`` (multiplication)
* ``/`` (division)
* ``%`` (modulus)
* ``**`` (pow)
#### Bitwise Operators
* ``&`` (and)
* ``|`` (or)
* ``^`` (xor)
#### Comparison Operators
* ``==`` (equal)
* ``===`` (identical)
* ``!=`` (not equal)
* ``!==`` (not identical)
* ``<`` (less than)
* ``>`` (greater than)
* ``<=`` (less than or equal to)
* ``>=`` (greater than or equal to)
* ``matches`` (regex match)
To test if a string does *not* match a regex, use the logical ``not`` operator in combination with the ``matches`` operator:
`not ("foo" matches "/bar/")`
You must use parenthesis because the unary operator ``not`` has precedence over the binary operator ``matches``.
#### Logical Operators
* ``not`` or ``!``
* ``and`` or ``&&``
* ``or`` or ``||``
#### String Operators
* ``~`` (concatenation)
For example: ``firstName ~ " " ~ lastName``
#### Array Operators
* ``in`` (contain)
* ``not in`` (does not contain)
For example: ``user.group in ["human_resources", "marketing"]``
#### Numeric Operators
* ``..`` (range)
For example: ``user.age in 18..45``
#### Ternary Operators
* ``foo ? 'yes' : 'no'``
* ``foo ?: 'no'`` (equal to ``foo ? foo : 'no'``)
* ``foo ? 'yes'`` (equal to ``foo ? 'yes' : ''``)
### Supported variables
Here are a sample of the available variables you can use in your expressions.
You can also retrieve the variables of a specific post by pasting its URL below the rule list.
<table class="table-bordered table-condensed table-striped">
<thead>
<tr>
<th>Variable</th>
<th>Type</th>
<th>Sample Value</th>
</tr>
</thead>
<tbody>
<tr>
<th>author_id</th>
<td>number</td>
<td>6</td>
</tr>
<tr>
<th>author_link</th>
<td>string</td>
<td>https://friendica.mrpetovan.com/profile/hypolite</td>
</tr>
<tr>
<th>author_name</th>
<td>string</td>
<td>Hypolite Petovan</td>
</tr>
<tr>
<th>author_avatar</th>
<td>string</td>
<td>https://friendica.mrpetovan.com/photo/41084997915a94a8c83cc39708500207-5.png</td>
</tr>
<tr>
<th>owner_id</th>
<td>number</td>
<td>6</td>
</tr>
<tr>
<th>owner_link</th>
<td>string</td>
<td>https://friendica.mrpetovan.com/profile/hypolite</td>
</tr>
<tr>
<th>owner_name</th>
<td>string</td>
<td>Hypolite Petovan</td>
</tr>
<tr>
<th>owner_avatar</th>
<td>string</td>
<td>https://friendica.mrpetovan.com/photo/41084997915a94a8c83cc39708500207-5.png</td>
</tr>
<tr>
<th>contact_id</th>
<td>number</td>
<td>1</td>
</tr>
<tr>
<th>uid</th>
<td>number</td>
<td>1</td>
</tr>
<tr>
<th>id</th>
<td>number</td>
<td>791875</td>
</tr>
<tr>
<th>parent</th>
<td>number</td>
<td>791875</td>
</tr>
<tr>
<th>uri</th>
<td>string</td>
<td>urn:X-dfrn:friendica.mrpetovan.com:1:twit:978740198937907200</td>
</tr>
<tr>
<th>thr_parent</th>
<td>string</td>
<td>urn:X-dfrn:friendica.mrpetovan.com:1:twit:978740198937907200</td>
</tr>
<tr>
<th>parent_uri</th>
<td>string</td>
<td>urn:X-dfrn:friendica.mrpetovan.com:1:twit:978740198937907200</td>
</tr>
<tr>
<th>content_warning</th>
<td>string</td>
<td></td>
</tr>
<tr>
<th>commented</th>
<td>date</td>
<td>2018-03-27 21:10:18</td>
</tr>
<tr>
<th>created</th>
<td>date</td>
<td>2018-03-27 21:10:18</td>
</tr>
<tr>
<th>edited</th>
<td>date</td>
<td>2018-03-27 21:10:18</td>
</tr>
<tr>
<th>received</th>
<td>date</td>
<td>2018-03-27 21:10:18</td>
</tr>
<tr>
<th>verb</th>
<td>string</td>
<td>http://activitystrea.ms/schema/1.0/post</td>
</tr>
<tr>
<th>object_type</th>
<td>string</td>
<td>http://activitystrea.ms/schema/1.0/bookmark</td>
</tr>
<tr>
<th>postopts</th>
<td>string</td>
<td>twitter&lang=pidgin;0.24032407407407:english;0.225:french;0.18055555555556</td>
</tr>
<tr>
<th>plink</th>
<td>string</td>
<td>https://friendica.mrpetovan.com/display/735a2029995abab33a5c006052376776</td>
</tr>
<tr>
<th>guid</th>
<td>string</td>
<td>735a2029995abab33a5c006052376776</td>
</tr>
<tr>
<th>wall</th>
<td>boolean</td>
<td>1</td>
</tr>
<tr>
<th>private</th>
<td>boolean</td>
<td>0</td>
</tr>
<tr>
<th>starred</th>
<td>boolean</td>
<td>0</td>
</tr>
<tr>
<th>title</th>
<td>string</td>
<td></td>
</tr>
<tr>
<th>body</th>
<td>string</td>
<td>Over-compensation #[url=https://friendica.mrpetovan.com/search?tag=Street]Street[/url] #[url=https://friendica.mrpetovan.com/search?tag=Night]Night[/url] #[url=https://friendica.mrpetovan.com/search?tag=CarLights]CarLights[/url] #[url=https://friendica.mrpetovan.com/search?tag=Jeep]Jeep[/url] #[url=https://friendica.mrpetovan.com/search?tag=NoPeople]NoPeople[/url] #[url=https://friendica.mrpetovan.com/search?tag=Close]Close[/url]-up
[attachment type='link' url='https://www.eyeem.com/p/120800309' title='Over-compensation Street Night Car Lights Jeep No | EyeEm' image='https://cdn.eyeem.com/thumb/b2f019738cbeef06e2f8c9517c6286a8adcd3a00-1522184820641/640/480']Photo by @[url=https://twitter.com/MrPetovan]MrPetovan[/url][/attachment]</td>
</tr>
<tr>
<th>file</th>
<td>string</td>
<td></td>
</tr>
<tr>
<th>event_id</th>
<td>number</td>
<td>null
<tr>
<th>location</th>
<td>string</td>
<td></td>
</tr>
<tr>
<th>coord</th>
<td>string</td>
<td></td>
</tr>
<tr>
<th>app</th>
<td>string</td>
<td>EyeEm</td>
</tr>
<tr>
<th>attach</th>
<td>string</td>
<td></td>
</tr>
<tr>
<th>rendered_hash</th>
<td>string</td>
<td>b70abdea8b362dc5dcf63e1b2836ad89</td>
</tr>
<tr>
<th>rendered_html</th>
<td>string</td>
<td>
Over-compensation #&lt;a href="https://friendica.mrpetovan.com/search?tag=Street" class="tag" title="Street"&gt;Street&lt;/a&gt; #&lt;a href="https://friendica.mrpetovan.com/search?tag=Night" class="tag" title="Night"&gt;Night&lt;/a&gt; #&lt;a href="https://friendica.mrpetovan.com/search?tag=CarLights" class="tag" title="CarLights"&gt;CarLights&lt;/a&gt; #&lt;a href="https://friendica.mrpetovan.com/search?tag=Jeep" class="tag" title="Jeep"&gt;Jeep&lt;/a&gt; #&lt;a href="https://friendica.mrpetovan.com/search?tag=NoPeople" class="tag" title="NoPeople"&gt;NoPeople&lt;/a&gt; #&lt;a href="https://friendica.mrpetovan.com/search?tag=Close" class="tag" title="Close"&gt;Close&lt;/a&gt;-up &lt;div class="type-link"&gt;&lt;a href="https://www.eyeem.com/p/120800309" target="_blank"&gt;&lt;img src="https://friendica.mrpetovan.com/proxy/bb/aHR0cHM6Ly9jZG4uZXllZW0uY29tL3RodW1iL2IyZjAxOTczOGNiZWVmMDZlMmY4Yzk1MTdjNjI4NmE4YWRjZDNhMDAtMTUyMjE4NDgyMDY0MS82NDAvNDgw" alt="" title="Over-compensation Street Night Car Lights Jeep No | EyeEm" class="attachment-image"&gt;&lt;/a&gt;&lt;br&gt;&lt;h4&gt;&lt;a href="https://www.eyeem.com/p/120800309"&gt;Over-compensation Street Night Car Lights Jeep No | EyeEm&lt;/a&gt;&lt;/h4&gt;&lt;blockquote&gt;Photo by @&lt;a href="https://twitter.com/MrPetovan" class="userinfo mention" title="MrPetovan"&gt;MrPetovan&lt;/a&gt;&lt;/blockquote&gt;&lt;sup&gt;&lt;a href="https://www.eyeem.com/p/120800309"&gt;www.eyeem.com&lt;/a&gt;&lt;/sup&gt;&lt;/div&gt;
</td>
</tr>
<tr>
<th>object</th>
<td>string</td>
<td>{"created_at":"Tue Mar 27 21:07:02 +0000 2018","id":978740198937907200,"id_str":"978740198937907200","full_text":"Over-compensation #Street #Night #CarLights #Jeep #NoPeople #Close-up https:\/\/t.co\/7w4ua13QA7","truncated":false,"display_text_range":[0,93],"entities":{"hashtags":[{"text":"Street","indices":[18,25]},{"text":"Night","indices":[26,32]},{"text":"CarLights","indices":[33,43]},{"text":"Jeep","indices":[44,49]},{"text":"NoPeople","indices":[50,59]},{"text":"Close","indices":[60,66]}],"symbols":[],"user_mentions":[],"urls":[{"url":"https:\/\/t.co\/7w4ua13QA7","expanded_url":"http:\/\/EyeEm.com\/p\/120800309","display_url":"EyeEm.com\/p\/120800309","indices":[70,93]}]},"source":"&lt;a href=\"http:\/\/www.eyeem.com\" rel=\"nofollow\"&gt;EyeEm&lt;\/a&gt;","in_reply_to_status_id":null,"in_reply_to_status_id_str":null,"in_reply_to_user_id":null,"in_reply_to_user_id_str":null,"in_reply_to_screen_name":null,"user":{"id":403748896,"id_str":"403748896","name":"\ud83d\udc30yp\ud83e\udd5ali\u271d\ufe0fe Pet\ud83e\udd5avan","screen_name":"MrPetovan","location":"NYC","description":"White male form of milquetoast. Avatar by @DearMsDear inspired by @TSG_LAB.\n\nFriendica\/Diaspora\/Mastodon: hypolite@friendica.mrpetovan.com","url":"https:\/\/t.co\/PcARi5OhQO","entities":{"url":{"urls":[{"url":"https:\/\/t.co\/PcARi5OhQO","expanded_url":"https:\/\/mrpetovan.com","display_url":"mrpetovan.com","indices":[0,23]}]},"description":{"urls":[]}},"protected":false,"followers_count":182,"friends_count":146,"listed_count":15,"created_at":"Wed Nov 02 23:13:14 +0000 2011","favourites_count":45826,"utc_offset":-14400,"time_zone":"Eastern Time (US &amp; Canada)","geo_enabled":false,"verified":false,"statuses_count":15554,"lang":"en","contributors_enabled":false,"is_translator":false,"is_translation_enabled":false,"profile_background_color":"000000","profile_background_image_url":"http:\/\/pbs.twimg.com\/profile_background_images\/370213187\/fond_twitter_mrpetovan.png","profile_background_image_url_https":"https:\/\/pbs.twimg.com\/profile_background_images\/370213187\/fond_twitter_mrpetovan.png","profile_background_tile":false,"profile_image_url":"http:\/\/pbs.twimg.com\/profile_images\/968008546322395136\/6qLCiu0o_normal.jpg","profile_image_url_https":"https:\/\/pbs.twimg.com\/profile_images\/968008546322395136\/6qLCiu0o_normal.jpg","profile_banner_url":"https:\/\/pbs.twimg.com\/profile_banners\/403748896\/1464321684","profile_link_color":"0084B4","profile_sidebar_border_color":"C0DEED","profile_sidebar_fill_color":"DDEEF6","profile_text_color":"000000","profile_use_background_image":true,"has_extended_profile":true,"default_profile":false,"default_profile_image":false,"following":false,"follow_request_sent":false,"notifications":false,"translator_type":"none"},"geo":null,"coordinates":null,"place":null,"contributors":null,"is_quote_status":false,"retweet_count":0,"favorite_count":0,"favorited":false,"retweeted":false,"possibly_sensitive":false,"lang":"en"}</td>
</tr>
<tr>
<th>allow_cid</th>
<td>string</td>
<td></td>
</tr>
<tr>
<th>allow_gid</th>
<td>string</td>
<td></td>
</tr>
<tr>
<th>deny_cid</th>
<td>string</td>
<td></td>
</tr>
<tr>
<th>deny_gid</th>
<td>string</td>
<td></td>
</tr>
<tr>
<th>item_id</th>
<td>number</td>
<td>791875</td>
</tr>
<tr>
<th>item_network</th>
<td>string</td>
<td>dfrn</td>
</tr>
<tr>
<th>author_thumb</th>
<td>string</td>
<td>https://friendica.mrpetovan.com/photo/0cb3d7231eb751139d7d309c7c686c49-5.png?ts=1522941604</td>
</tr>
<tr>
<th>owner_thumb</th>
<td>string</td>
<td>https://friendica.mrpetovan.com/photo/0cb3d7231eb751139d7d309c7c686c49-5.png?ts=1522941604</td>
</tr>
<tr>
<th>network</th>
<td>string</td>
<td></td>
</tr>
<tr>
<th>url</th>
<td>string</td>
<td>https://friendica.mrpetovan.com/profile/hypolite</td>
</tr>
<tr>
<th>name</th>
<td>string</td>
<td>Hypolite Petovan</td>
</tr>
<tr>
<th>writable</th>
<td>boolean</td>
<td>0</td>
</tr>
<tr>
<th>self</th>
<td>boolean</td>
<td>1</td>
</tr>
<tr>
<th>cid</th>
<td>number</td>
<td>1</td>
</tr>
<tr>
<th>alias</th>
<td>string</td>
<td></td>
</tr>
<tr>
<th>event_created</th>
<td>date</td>
<td>null</td>
</tr>
<tr>
<th>event_edited</th>
<td>date</td>
<td>null</td>
</tr>
<tr>
<th>event_start</th>
<td>date</td>
<td>null</td>
</tr>
<tr>
<th>event_finish</th>
<td>date</td>
<td>null</td>
</tr>
<tr>
<th>event_summary</th>
<td>string</td>
<td>null</td>
</tr>
<tr>
<th>event_desc</th>
<td>string</td>
<td>null</td>
</tr>
<tr>
<th>event_location</th>
<td>string</td>
<td>null</td>
</tr>
<tr>
<th>event_type</th>
<td>string</td>
<td>null</td>
</tr>
<tr>
<th>event_nofinish</th>
<td>string</td>
<td>null</td>
</tr>
<tr>
<th>event_adjust</th>
<td>boolean</td>
<td>null</td>
</tr>
<tr>
<th>event_ignore</th>
<td>boolean</td>
<td>null</td>
</tr>
<tr>
<th>pagedrop</th>
<td>string</td>
<td>true</td>
</tr>
<tr>
<th>tags</th>
<td>list</td>
<td>
<ol start="0">
<li>#&lt;a href="https://friendica.mrpetovan.com/search?tag=Street" target="_blank"&gt;street&lt;/a&gt;</li>
<li>#&lt;a href="https://friendica.mrpetovan.com/search?tag=Night" target="_blank"&gt;night&lt;/a&gt;</li>
<li>#&lt;a href="https://friendica.mrpetovan.com/search?tag=CarLights" target="_blank"&gt;carlights&lt;/a&gt;</li>
<li>#&lt;a href="https://friendica.mrpetovan.com/search?tag=Jeep" target="_blank"&gt;jeep&lt;/a&gt;</li>
<li>#&lt;a href="https://friendica.mrpetovan.com/search?tag=NoPeople" target="_blank"&gt;nopeople&lt;/a&gt;</li>
<li>#&lt;a href="https://friendica.mrpetovan.com/search?tag=Close" target="_blank"&gt;close&lt;/a&gt;</li>
<li>@&lt;a href="https://twitter.com/MrPetovan" target="_blank"&gt;mrpetovan&lt;/a&gt;</li>
<li>#&lt;a href="https://friendica.mrpetovan.com/search?tag=Close-up" target="_blank"&gt;close-up&lt;/a&gt;</li>
</ol>
</td>
</tr>
<tr>
<th>hashtags</th>
<td>list</td>
<td>
<ol start="0">
<li>#&lt;a href="https://friendica.mrpetovan.com/search?tag=Street" target="_blank"&gt;street&lt;/a&gt;</li>
<li>#&lt;a href="https://friendica.mrpetovan.com/search?tag=Night" target="_blank"&gt;night&lt;/a&gt;</li>
<li>#&lt;a href="https://friendica.mrpetovan.com/search?tag=CarLights" target="_blank"&gt;carlights&lt;/a&gt;</li>
<li>#&lt;a href="https://friendica.mrpetovan.com/search?tag=Jeep" target="_blank"&gt;jeep&lt;/a&gt;</li>
<li>#&lt;a href="https://friendica.mrpetovan.com/search?tag=NoPeople" target="_blank"&gt;nopeople&lt;/a&gt;</li>
<li>#&lt;a href="https://friendica.mrpetovan.com/search?tag=Close" target="_blank"&gt;close&lt;/a&gt;</li>
<li>#&lt;a href="https://friendica.mrpetovan.com/search?tag=Close-up" target="_blank"&gt;close-up&lt;/a&gt;</li>
</ol>
</td>
</tr>
<tr>
<th>mentions</th>
<td>string</td>
<td>
<ol start="0">
<li>@&lt;a href="https://twitter.com/MrPetovan" target="_blank"&gt;mrpetovan&lt;/a&gt;</li>
</ol>
</td>
</tr>
</tbody>
</table>

View File

@ -0,0 +1,163 @@
# ADDON advancedcontentfilter
# Copyright (C)
# This file is distributed under the same license as the Friendica advancedcontentfilter addon package.
#
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-04-17 04:04+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: advancedcontentfilter.php:134
#, php-format
msgid "Filtered by rule: %s"
msgstr ""
#: advancedcontentfilter.php:147 advancedcontentfilter.php:204
msgid "Advanced Content Filter"
msgstr ""
#: advancedcontentfilter.php:203
msgid "Back to Addon Settings"
msgstr ""
#: advancedcontentfilter.php:205
msgid "Add a Rule"
msgstr ""
#: advancedcontentfilter.php:206
msgid "Help"
msgstr ""
#: advancedcontentfilter.php:207
msgid ""
"Add and manage your personal content filter rules in this screen. Rules have "
"a name and an arbitrary expression that will be matched against post data. "
"For a complete reference of the available operations and variables, check "
"the <a href=\"advancedcontentfilter/help\">help page</a>."
msgstr ""
#: advancedcontentfilter.php:208
msgid "Your rules"
msgstr ""
#: advancedcontentfilter.php:209
msgid ""
"You have no rules yet! Start adding one by clicking on the button above next "
"to the title."
msgstr ""
#: advancedcontentfilter.php:210
msgid "Disabled"
msgstr ""
#: advancedcontentfilter.php:211
msgid "Enabled"
msgstr ""
#: advancedcontentfilter.php:212
msgid "Disable this rule"
msgstr ""
#: advancedcontentfilter.php:213
msgid "Enable this rule"
msgstr ""
#: advancedcontentfilter.php:214
msgid "Edit this rule"
msgstr ""
#: advancedcontentfilter.php:215
msgid "Edit the rule"
msgstr ""
#: advancedcontentfilter.php:216
msgid "Save this rule"
msgstr ""
#: advancedcontentfilter.php:217
msgid "Delete this rule"
msgstr ""
#: advancedcontentfilter.php:218
msgid "Rule"
msgstr ""
#: advancedcontentfilter.php:219
msgid "Close"
msgstr ""
#: advancedcontentfilter.php:220
msgid "Add new rule"
msgstr ""
#: advancedcontentfilter.php:221
msgid "Rule Name"
msgstr ""
#: advancedcontentfilter.php:222
msgid "Rule Expression"
msgstr ""
#: advancedcontentfilter.php:223
msgid ""
"<p>Examples:</p><ul><li><pre>author_link == 'https://friendica.mrpetovan.com/"
"profile/hypolite'</pre></li><li>tags</li></ul>"
msgstr ""
#: advancedcontentfilter.php:224
msgid "Cancel"
msgstr ""
#: advancedcontentfilter.php:290 advancedcontentfilter.php:301
#: advancedcontentfilter.php:312 advancedcontentfilter.php:346
#: advancedcontentfilter.php:375 advancedcontentfilter.php:396
msgid "You must be logged in to use this method"
msgstr ""
#: advancedcontentfilter.php:316 advancedcontentfilter.php:350
#: advancedcontentfilter.php:379
msgid "Invalid form security token, please refresh the page."
msgstr ""
#: advancedcontentfilter.php:328
msgid "The rule name and expression are required."
msgstr ""
#: advancedcontentfilter.php:340
msgid "Rule successfully added"
msgstr ""
#: advancedcontentfilter.php:354 advancedcontentfilter.php:383
msgid "Rule doesn't exist or doesn't belong to you."
msgstr ""
#: advancedcontentfilter.php:369
msgid "Rule successfully updated"
msgstr ""
#: advancedcontentfilter.php:390
msgid "Rule successfully deleted"
msgstr ""
#: advancedcontentfilter.php:400
msgid "Missing argument: guid."
msgstr ""
#: advancedcontentfilter.php:406
#, php-format
msgid "Unknown post with guid: %s"
msgstr ""
#: src/middlewares.php:28
msgid "Method not found"
msgstr ""

View File

@ -0,0 +1,30 @@
<?php
$container = $slim->getContainer();
// Error handler based off https://stackoverflow.com/a/48135009/757392
$container['errorHandler'] = function () {
return function(Psr\Http\Message\RequestInterface $request, Psr\Http\Message\ResponseInterface $response, Exception $exception)
{
$responseCode = 500;
if (is_a($exception, 'Friendica\Network\HTTPException')) {
$responseCode = $exception->httpcode;
}
$errors['message'] = $exception->getMessage();
$errors['responseCode'] = $responseCode;
return $response
->withStatus($responseCode)
->withJson($errors);
};
};
$container['notFoundHandler'] = function () {
return function ()
{
throw new \Friendica\Network\HTTPException\NotFoundException(L10n::t('Method not found'));
};
};

View File

@ -0,0 +1,22 @@
<?php
// Routes
/* @var $slim Slim\App */
$slim->group('/advancedcontentfilter/api', function () {
/* @var $this Slim\App */
$this->group('/rules', function () {
/* @var $this Slim\App */
$this->get('', 'advancedcontentfilter_get_rules');
$this->post('', 'advancedcontentfilter_post_rules');
$this->get('/{id}', 'advancedcontentfilter_get_rules_id');
$this->put('/{id}', 'advancedcontentfilter_put_rules_id');
$this->delete('/{id}', 'advancedcontentfilter_delete_rules_id');
});
$this->group('/variables', function () {
/* @var $this Slim\App */
$this->get('/{guid}', 'advancedcontentfilter_get_variables_guid');
});
});

View File

@ -0,0 +1,98 @@
<div id="adminpage">
<style>[v-cloak] { display: none; }</style>
<div id="rules">
<p><a href="settings/addon">🔙 {{$backtosettings}}</a></p>
<h1>
{{$title}}
<a href="{{$baseurl}}/advancedcontentfilter/help" class="btn btn-default btn-sm" title="{{$help}}">
<i class="fa fa-question fa-2x" aria-hidden="true"></i>
</a>
</h1>
<div>{{$advanced_content_filter_intro}}</div>
<h2>
{{$your_rules}}
<button class="btn btn-primary btn-sm" title="{{$add_a_rule}}" @click="showModal = true">
<i class="fa fa-plus fa-2x" aria-hidden="true"></i>
</button>
</h2>
<div v-if="rules.length === 0" v-cloak>
{{$no_rules}}
</div>
<ul class="list-group" v-cloak>
<li class="list-group-item" v-for="rule in rules">
<p class="pull-right">
<button type="button" class="btn btn-xs btn-primary" v-on:click="toggleActive(rule)" aria-label="{{$disable_this_rule}}" title="{{$disable_this_rule}}" v-if="parseInt(rule.active)">
<i class="fa fa-toggle-on" aria-hidden="true"></i> {{$enabled}}
</button>
<button type="button" class="btn btn-xs btn-default" v-on:click="toggleActive(rule)" aria-label="{{$enable_this_rule}}" title="{{$enable_this_rule}}" v-else>
<i class="fa fa-toggle-off" aria-hidden="true"></i> {{$disabled}}
</button>
<button type="button" class="btn btn-xs btn-primary" v-on:click="editRule(rule)" aria-label="{{$edit_this_rule}}" title="{{$edit_this_rule}}">
<i class="fa fa-pencil" aria-hidden="true"></i>
</button>
<button type="button" class="btn btn-xs btn-default" v-on:click="deleteRule(rule)" aria-label="{{$delete_this_rule}}" title="{{$delete_this_rule}}">
<i class="fa fa-trash-o" aria-hidden="true"></i>
</button>
</p>
<h3 class="list-group-item-heading">
{{$rule}} #{{ rule.id }}: {{ rule.name }}
</h3>
<pre class="list-group-item-text" v-if="rule.expression">{{ rule.expression }}</pre>
</li>
</ul>
<div class="modal fade" ref="vuemodal" tabindex="-1" role="dialog" v-cloak>
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
{{if current_theme() == 'frio'}}
<button type="button" class="close" data-dismiss="modal" aria-label="{{$close}}" @click="showModal = false"><span aria-hidden="true">&times;</span></button>
{{/if}}
<h3 v-if="rule.id">{{$edit_the_rule}} "{{ rule.name }}"</h3>
<h3 v-if="!rule.id">{{$add_a_rule}}</h3>
</div>
<div class="modal-body">
<form>
<input type="hidden" name="form_security_token" id="csrf" value="{{$form_security_token}}" />
<div class="alert alert-danger" role="alert" v-if="errorMessage">{{ errorMessage }}</div>
<div class="form-group">
<input class="form-control" placeholder="{{$rule_name}}" v-model="rule.name">
</div>
<div class="form-group">
<input class="form-control" placeholder="{{$rule_expression}}" v-model="rule.expression">
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal" aria-label="Close" @click="resetForm()">{{$cancel}}</button>
<button slot="button" class="btn btn-primary" type="button" v-if="rule.id" v-on:click="saveRule(rule)">{{$save_this_rule}}</button>
<button slot="button" class="btn btn-primary" type="button" v-if="!rule.id" v-on:click="addRule()">{{$add_a_rule}}</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
<form class="form-inline" v-on:submit.prevent="showVariables()">
<fieldset>
<legend>Show post variables</legend>
<div class="form-group" style="width: 50%">
<label for="itemUrl" class="sr-only">Post URL or item guid</label>
<input class="form-control" id="itemUrl" placeholder="Post URL or item guid" v-model="itemUrl" style="width: 100%">
</div>
<button type="submit" class="btn btn-primary">Show Variables</button>
</fieldset>
</form>
<pre v-cloak>
{{ itemJson }}
</pre>
</div>
<script> var existingRules = {{$rules}};</script>
<!-- JS -->
<script src="{{$baseurl}}/view/asset/vue/dist/vue.min.js"></script>
<script src="{{$baseurl}}/addon/advancedcontentfilter/advancedcontentfilter.js"></script>
</div>

View File

@ -0,0 +1,7 @@
<?php
// autoload.php @generated by Composer
require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInitAdvancedContentFilterAddon::getLoader();

View File

@ -0,0 +1,445 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Autoload;
/**
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
*
* $loader = new \Composer\Autoload\ClassLoader();
*
* // register classes with namespaces
* $loader->add('Symfony\Component', __DIR__.'/component');
* $loader->add('Symfony', __DIR__.'/framework');
*
* // activate the autoloader
* $loader->register();
*
* // to enable searching the include path (eg. for PEAR packages)
* $loader->setUseIncludePath(true);
*
* In this example, if you try to use a class in the Symfony\Component
* namespace or one of its children (Symfony\Component\Console for instance),
* the autoloader will first look for the class under the component/
* directory, and it will then fallback to the framework/ directory if not
* found before giving up.
*
* This class is loosely based on the Symfony UniversalClassLoader.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jordi Boggiano <j.boggiano@seld.be>
* @see http://www.php-fig.org/psr/psr-0/
* @see http://www.php-fig.org/psr/psr-4/
*/
class ClassLoader
{
// PSR-4
private $prefixLengthsPsr4 = array();
private $prefixDirsPsr4 = array();
private $fallbackDirsPsr4 = array();
// PSR-0
private $prefixesPsr0 = array();
private $fallbackDirsPsr0 = array();
private $useIncludePath = false;
private $classMap = array();
private $classMapAuthoritative = false;
private $missingClasses = array();
private $apcuPrefix;
public function getPrefixes()
{
if (!empty($this->prefixesPsr0)) {
return call_user_func_array('array_merge', $this->prefixesPsr0);
}
return array();
}
public function getPrefixesPsr4()
{
return $this->prefixDirsPsr4;
}
public function getFallbackDirs()
{
return $this->fallbackDirsPsr0;
}
public function getFallbackDirsPsr4()
{
return $this->fallbackDirsPsr4;
}
public function getClassMap()
{
return $this->classMap;
}
/**
* @param array $classMap Class to filename map
*/
public function addClassMap(array $classMap)
{
if ($this->classMap) {
$this->classMap = array_merge($this->classMap, $classMap);
} else {
$this->classMap = $classMap;
}
}
/**
* Registers a set of PSR-0 directories for a given prefix, either
* appending or prepending to the ones previously set for this prefix.
*
* @param string $prefix The prefix
* @param array|string $paths The PSR-0 root directories
* @param bool $prepend Whether to prepend the directories
*/
public function add($prefix, $paths, $prepend = false)
{
if (!$prefix) {
if ($prepend) {
$this->fallbackDirsPsr0 = array_merge(
(array) $paths,
$this->fallbackDirsPsr0
);
} else {
$this->fallbackDirsPsr0 = array_merge(
$this->fallbackDirsPsr0,
(array) $paths
);
}
return;
}
$first = $prefix[0];
if (!isset($this->prefixesPsr0[$first][$prefix])) {
$this->prefixesPsr0[$first][$prefix] = (array) $paths;
return;
}
if ($prepend) {
$this->prefixesPsr0[$first][$prefix] = array_merge(
(array) $paths,
$this->prefixesPsr0[$first][$prefix]
);
} else {
$this->prefixesPsr0[$first][$prefix] = array_merge(
$this->prefixesPsr0[$first][$prefix],
(array) $paths
);
}
}
/**
* Registers a set of PSR-4 directories for a given namespace, either
* appending or prepending to the ones previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-4 base directories
* @param bool $prepend Whether to prepend the directories
*
* @throws \InvalidArgumentException
*/
public function addPsr4($prefix, $paths, $prepend = false)
{
if (!$prefix) {
// Register directories for the root namespace.
if ($prepend) {
$this->fallbackDirsPsr4 = array_merge(
(array) $paths,
$this->fallbackDirsPsr4
);
} else {
$this->fallbackDirsPsr4 = array_merge(
$this->fallbackDirsPsr4,
(array) $paths
);
}
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
// Register directories for a new namespace.
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
} elseif ($prepend) {
// Prepend directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
(array) $paths,
$this->prefixDirsPsr4[$prefix]
);
} else {
// Append directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
$this->prefixDirsPsr4[$prefix],
(array) $paths
);
}
}
/**
* Registers a set of PSR-0 directories for a given prefix,
* replacing any others previously set for this prefix.
*
* @param string $prefix The prefix
* @param array|string $paths The PSR-0 base directories
*/
public function set($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr0 = (array) $paths;
} else {
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
}
}
/**
* Registers a set of PSR-4 directories for a given namespace,
* replacing any others previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-4 base directories
*
* @throws \InvalidArgumentException
*/
public function setPsr4($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr4 = (array) $paths;
} else {
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
}
}
/**
* Turns on searching the include path for class files.
*
* @param bool $useIncludePath
*/
public function setUseIncludePath($useIncludePath)
{
$this->useIncludePath = $useIncludePath;
}
/**
* Can be used to check if the autoloader uses the include path to check
* for classes.
*
* @return bool
*/
public function getUseIncludePath()
{
return $this->useIncludePath;
}
/**
* Turns off searching the prefix and fallback directories for classes
* that have not been registered with the class map.
*
* @param bool $classMapAuthoritative
*/
public function setClassMapAuthoritative($classMapAuthoritative)
{
$this->classMapAuthoritative = $classMapAuthoritative;
}
/**
* Should class lookup fail if not found in the current class map?
*
* @return bool
*/
public function isClassMapAuthoritative()
{
return $this->classMapAuthoritative;
}
/**
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
*
* @param string|null $apcuPrefix
*/
public function setApcuPrefix($apcuPrefix)
{
$this->apcuPrefix = function_exists('apcu_fetch') && ini_get('apc.enabled') ? $apcuPrefix : null;
}
/**
* The APCu prefix in use, or null if APCu caching is not enabled.
*
* @return string|null
*/
public function getApcuPrefix()
{
return $this->apcuPrefix;
}
/**
* Registers this instance as an autoloader.
*
* @param bool $prepend Whether to prepend the autoloader or not
*/
public function register($prepend = false)
{
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
}
/**
* Unregisters this instance as an autoloader.
*/
public function unregister()
{
spl_autoload_unregister(array($this, 'loadClass'));
}
/**
* Loads the given class or interface.
*
* @param string $class The name of the class
* @return bool|null True if loaded, null otherwise
*/
public function loadClass($class)
{
if ($file = $this->findFile($class)) {
includeFile($file);
return true;
}
}
/**
* Finds the path to the file where the class is defined.
*
* @param string $class The name of the class
*
* @return string|false The path if found, false otherwise
*/
public function findFile($class)
{
// class map lookup
if (isset($this->classMap[$class])) {
return $this->classMap[$class];
}
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
return false;
}
if (null !== $this->apcuPrefix) {
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
if ($hit) {
return $file;
}
}
$file = $this->findFileWithExtension($class, '.php');
// Search for Hack files if we are running on HHVM
if (false === $file && defined('HHVM_VERSION')) {
$file = $this->findFileWithExtension($class, '.hh');
}
if (null !== $this->apcuPrefix) {
apcu_add($this->apcuPrefix.$class, $file);
}
if (false === $file) {
// Remember that this class does not exist.
$this->missingClasses[$class] = true;
}
return $file;
}
private function findFileWithExtension($class, $ext)
{
// PSR-4 lookup
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
$first = $class[0];
if (isset($this->prefixLengthsPsr4[$first])) {
$subPath = $class;
while (false !== $lastPos = strrpos($subPath, '\\')) {
$subPath = substr($subPath, 0, $lastPos);
$search = $subPath.'\\';
if (isset($this->prefixDirsPsr4[$search])) {
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
foreach ($this->prefixDirsPsr4[$search] as $dir) {
if (file_exists($file = $dir . $pathEnd)) {
return $file;
}
}
}
}
}
// PSR-4 fallback dirs
foreach ($this->fallbackDirsPsr4 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
return $file;
}
}
// PSR-0 lookup
if (false !== $pos = strrpos($class, '\\')) {
// namespaced class name
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
} else {
// PEAR-like class name
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
}
if (isset($this->prefixesPsr0[$first])) {
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
if (0 === strpos($class, $prefix)) {
foreach ($dirs as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
}
}
}
// PSR-0 fallback dirs
foreach ($this->fallbackDirsPsr0 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
// PSR-0 include paths.
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
return $file;
}
return false;
}
}
/**
* Scope isolated include.
*
* Prevents access to $this/self from included files.
*/
function includeFile($file)
{
include $file;
}

View File

@ -0,0 +1,21 @@
Copyright (c) Nils Adermann, Jordi Boggiano
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@ -0,0 +1,201 @@
<?php
// autoload_classmap.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'FastRoute\\BadRouteException' => $vendorDir . '/nikic/fast-route/src/BadRouteException.php',
'FastRoute\\DataGenerator' => $vendorDir . '/nikic/fast-route/src/DataGenerator.php',
'FastRoute\\DataGenerator\\CharCountBased' => $vendorDir . '/nikic/fast-route/src/DataGenerator/CharCountBased.php',
'FastRoute\\DataGenerator\\GroupCountBased' => $vendorDir . '/nikic/fast-route/src/DataGenerator/GroupCountBased.php',
'FastRoute\\DataGenerator\\GroupPosBased' => $vendorDir . '/nikic/fast-route/src/DataGenerator/GroupPosBased.php',
'FastRoute\\DataGenerator\\MarkBased' => $vendorDir . '/nikic/fast-route/src/DataGenerator/MarkBased.php',
'FastRoute\\DataGenerator\\RegexBasedAbstract' => $vendorDir . '/nikic/fast-route/src/DataGenerator/RegexBasedAbstract.php',
'FastRoute\\Dispatcher' => $vendorDir . '/nikic/fast-route/src/Dispatcher.php',
'FastRoute\\Dispatcher\\CharCountBased' => $vendorDir . '/nikic/fast-route/src/Dispatcher/CharCountBased.php',
'FastRoute\\Dispatcher\\GroupCountBased' => $vendorDir . '/nikic/fast-route/src/Dispatcher/GroupCountBased.php',
'FastRoute\\Dispatcher\\GroupPosBased' => $vendorDir . '/nikic/fast-route/src/Dispatcher/GroupPosBased.php',
'FastRoute\\Dispatcher\\MarkBased' => $vendorDir . '/nikic/fast-route/src/Dispatcher/MarkBased.php',
'FastRoute\\Dispatcher\\RegexBasedAbstract' => $vendorDir . '/nikic/fast-route/src/Dispatcher/RegexBasedAbstract.php',
'FastRoute\\Route' => $vendorDir . '/nikic/fast-route/src/Route.php',
'FastRoute\\RouteCollector' => $vendorDir . '/nikic/fast-route/src/RouteCollector.php',
'FastRoute\\RouteParser' => $vendorDir . '/nikic/fast-route/src/RouteParser.php',
'FastRoute\\RouteParser\\Std' => $vendorDir . '/nikic/fast-route/src/RouteParser/Std.php',
'Interop\\Container\\ContainerInterface' => $vendorDir . '/container-interop/container-interop/src/Interop/Container/ContainerInterface.php',
'Interop\\Container\\Exception\\ContainerException' => $vendorDir . '/container-interop/container-interop/src/Interop/Container/Exception/ContainerException.php',
'Interop\\Container\\Exception\\NotFoundException' => $vendorDir . '/container-interop/container-interop/src/Interop/Container/Exception/NotFoundException.php',
'Pimple\\Container' => $vendorDir . '/pimple/pimple/src/Pimple/Container.php',
'Pimple\\Exception\\ExpectedInvokableException' => $vendorDir . '/pimple/pimple/src/Pimple/Exception/ExpectedInvokableException.php',
'Pimple\\Exception\\FrozenServiceException' => $vendorDir . '/pimple/pimple/src/Pimple/Exception/FrozenServiceException.php',
'Pimple\\Exception\\InvalidServiceIdentifierException' => $vendorDir . '/pimple/pimple/src/Pimple/Exception/InvalidServiceIdentifierException.php',
'Pimple\\Exception\\UnknownIdentifierException' => $vendorDir . '/pimple/pimple/src/Pimple/Exception/UnknownIdentifierException.php',
'Pimple\\Psr11\\Container' => $vendorDir . '/pimple/pimple/src/Pimple/Psr11/Container.php',
'Pimple\\Psr11\\ServiceLocator' => $vendorDir . '/pimple/pimple/src/Pimple/Psr11/ServiceLocator.php',
'Pimple\\ServiceIterator' => $vendorDir . '/pimple/pimple/src/Pimple/ServiceIterator.php',
'Pimple\\ServiceProviderInterface' => $vendorDir . '/pimple/pimple/src/Pimple/ServiceProviderInterface.php',
'Pimple\\Tests\\Fixtures\\Invokable' => $vendorDir . '/pimple/pimple/src/Pimple/Tests/Fixtures/Invokable.php',
'Pimple\\Tests\\Fixtures\\NonInvokable' => $vendorDir . '/pimple/pimple/src/Pimple/Tests/Fixtures/NonInvokable.php',
'Pimple\\Tests\\Fixtures\\PimpleServiceProvider' => $vendorDir . '/pimple/pimple/src/Pimple/Tests/Fixtures/PimpleServiceProvider.php',
'Pimple\\Tests\\Fixtures\\Service' => $vendorDir . '/pimple/pimple/src/Pimple/Tests/Fixtures/Service.php',
'Pimple\\Tests\\PimpleServiceProviderInterfaceTest' => $vendorDir . '/pimple/pimple/src/Pimple/Tests/PimpleServiceProviderInterfaceTest.php',
'Pimple\\Tests\\PimpleTest' => $vendorDir . '/pimple/pimple/src/Pimple/Tests/PimpleTest.php',
'Pimple\\Tests\\Psr11\\ContainerTest' => $vendorDir . '/pimple/pimple/src/Pimple/Tests/Psr11/ContainerTest.php',
'Pimple\\Tests\\Psr11\\ServiceLocatorTest' => $vendorDir . '/pimple/pimple/src/Pimple/Tests/Psr11/ServiceLocatorTest.php',
'Pimple\\Tests\\ServiceIteratorTest' => $vendorDir . '/pimple/pimple/src/Pimple/Tests/ServiceIteratorTest.php',
'Psr\\Cache\\CacheException' => $vendorDir . '/psr/cache/src/CacheException.php',
'Psr\\Cache\\CacheItemInterface' => $vendorDir . '/psr/cache/src/CacheItemInterface.php',
'Psr\\Cache\\CacheItemPoolInterface' => $vendorDir . '/psr/cache/src/CacheItemPoolInterface.php',
'Psr\\Cache\\InvalidArgumentException' => $vendorDir . '/psr/cache/src/InvalidArgumentException.php',
'Psr\\Container\\ContainerExceptionInterface' => $vendorDir . '/psr/container/src/ContainerExceptionInterface.php',
'Psr\\Container\\ContainerInterface' => $vendorDir . '/psr/container/src/ContainerInterface.php',
'Psr\\Container\\NotFoundExceptionInterface' => $vendorDir . '/psr/container/src/NotFoundExceptionInterface.php',
'Psr\\Http\\Message\\MessageInterface' => $vendorDir . '/psr/http-message/src/MessageInterface.php',
'Psr\\Http\\Message\\RequestInterface' => $vendorDir . '/psr/http-message/src/RequestInterface.php',
'Psr\\Http\\Message\\ResponseInterface' => $vendorDir . '/psr/http-message/src/ResponseInterface.php',
'Psr\\Http\\Message\\ServerRequestInterface' => $vendorDir . '/psr/http-message/src/ServerRequestInterface.php',
'Psr\\Http\\Message\\StreamInterface' => $vendorDir . '/psr/http-message/src/StreamInterface.php',
'Psr\\Http\\Message\\UploadedFileInterface' => $vendorDir . '/psr/http-message/src/UploadedFileInterface.php',
'Psr\\Http\\Message\\UriInterface' => $vendorDir . '/psr/http-message/src/UriInterface.php',
'Psr\\Log\\AbstractLogger' => $vendorDir . '/psr/log/Psr/Log/AbstractLogger.php',
'Psr\\Log\\InvalidArgumentException' => $vendorDir . '/psr/log/Psr/Log/InvalidArgumentException.php',
'Psr\\Log\\LogLevel' => $vendorDir . '/psr/log/Psr/Log/LogLevel.php',
'Psr\\Log\\LoggerAwareInterface' => $vendorDir . '/psr/log/Psr/Log/LoggerAwareInterface.php',
'Psr\\Log\\LoggerAwareTrait' => $vendorDir . '/psr/log/Psr/Log/LoggerAwareTrait.php',
'Psr\\Log\\LoggerInterface' => $vendorDir . '/psr/log/Psr/Log/LoggerInterface.php',
'Psr\\Log\\LoggerTrait' => $vendorDir . '/psr/log/Psr/Log/LoggerTrait.php',
'Psr\\Log\\NullLogger' => $vendorDir . '/psr/log/Psr/Log/NullLogger.php',
'Psr\\Log\\Test\\DummyTest' => $vendorDir . '/psr/log/Psr/Log/Test/LoggerInterfaceTest.php',
'Psr\\Log\\Test\\LoggerInterfaceTest' => $vendorDir . '/psr/log/Psr/Log/Test/LoggerInterfaceTest.php',
'Psr\\SimpleCache\\CacheException' => $vendorDir . '/psr/simple-cache/src/CacheException.php',
'Psr\\SimpleCache\\CacheInterface' => $vendorDir . '/psr/simple-cache/src/CacheInterface.php',
'Psr\\SimpleCache\\InvalidArgumentException' => $vendorDir . '/psr/simple-cache/src/InvalidArgumentException.php',
'Slim\\App' => $vendorDir . '/slim/slim/Slim/App.php',
'Slim\\CallableResolver' => $vendorDir . '/slim/slim/Slim/CallableResolver.php',
'Slim\\CallableResolverAwareTrait' => $vendorDir . '/slim/slim/Slim/CallableResolverAwareTrait.php',
'Slim\\Collection' => $vendorDir . '/slim/slim/Slim/Collection.php',
'Slim\\Container' => $vendorDir . '/slim/slim/Slim/Container.php',
'Slim\\DefaultServicesProvider' => $vendorDir . '/slim/slim/Slim/DefaultServicesProvider.php',
'Slim\\DeferredCallable' => $vendorDir . '/slim/slim/Slim/DeferredCallable.php',
'Slim\\Exception\\ContainerException' => $vendorDir . '/slim/slim/Slim/Exception/ContainerException.php',
'Slim\\Exception\\ContainerValueNotFoundException' => $vendorDir . '/slim/slim/Slim/Exception/ContainerValueNotFoundException.php',
'Slim\\Exception\\InvalidMethodException' => $vendorDir . '/slim/slim/Slim/Exception/InvalidMethodException.php',
'Slim\\Exception\\MethodNotAllowedException' => $vendorDir . '/slim/slim/Slim/Exception/MethodNotAllowedException.php',
'Slim\\Exception\\NotFoundException' => $vendorDir . '/slim/slim/Slim/Exception/NotFoundException.php',
'Slim\\Exception\\SlimException' => $vendorDir . '/slim/slim/Slim/Exception/SlimException.php',
'Slim\\Handlers\\AbstractError' => $vendorDir . '/slim/slim/Slim/Handlers/AbstractError.php',
'Slim\\Handlers\\AbstractHandler' => $vendorDir . '/slim/slim/Slim/Handlers/AbstractHandler.php',
'Slim\\Handlers\\Error' => $vendorDir . '/slim/slim/Slim/Handlers/Error.php',
'Slim\\Handlers\\NotAllowed' => $vendorDir . '/slim/slim/Slim/Handlers/NotAllowed.php',
'Slim\\Handlers\\NotFound' => $vendorDir . '/slim/slim/Slim/Handlers/NotFound.php',
'Slim\\Handlers\\PhpError' => $vendorDir . '/slim/slim/Slim/Handlers/PhpError.php',
'Slim\\Handlers\\Strategies\\RequestResponse' => $vendorDir . '/slim/slim/Slim/Handlers/Strategies/RequestResponse.php',
'Slim\\Handlers\\Strategies\\RequestResponseArgs' => $vendorDir . '/slim/slim/Slim/Handlers/Strategies/RequestResponseArgs.php',
'Slim\\Http\\Body' => $vendorDir . '/slim/slim/Slim/Http/Body.php',
'Slim\\Http\\Cookies' => $vendorDir . '/slim/slim/Slim/Http/Cookies.php',
'Slim\\Http\\Environment' => $vendorDir . '/slim/slim/Slim/Http/Environment.php',
'Slim\\Http\\Headers' => $vendorDir . '/slim/slim/Slim/Http/Headers.php',
'Slim\\Http\\Message' => $vendorDir . '/slim/slim/Slim/Http/Message.php',
'Slim\\Http\\Request' => $vendorDir . '/slim/slim/Slim/Http/Request.php',
'Slim\\Http\\RequestBody' => $vendorDir . '/slim/slim/Slim/Http/RequestBody.php',
'Slim\\Http\\Response' => $vendorDir . '/slim/slim/Slim/Http/Response.php',
'Slim\\Http\\Stream' => $vendorDir . '/slim/slim/Slim/Http/Stream.php',
'Slim\\Http\\UploadedFile' => $vendorDir . '/slim/slim/Slim/Http/UploadedFile.php',
'Slim\\Http\\Uri' => $vendorDir . '/slim/slim/Slim/Http/Uri.php',
'Slim\\Interfaces\\CallableResolverInterface' => $vendorDir . '/slim/slim/Slim/Interfaces/CallableResolverInterface.php',
'Slim\\Interfaces\\CollectionInterface' => $vendorDir . '/slim/slim/Slim/Interfaces/CollectionInterface.php',
'Slim\\Interfaces\\Http\\CookiesInterface' => $vendorDir . '/slim/slim/Slim/Interfaces/Http/CookiesInterface.php',
'Slim\\Interfaces\\Http\\EnvironmentInterface' => $vendorDir . '/slim/slim/Slim/Interfaces/Http/EnvironmentInterface.php',
'Slim\\Interfaces\\Http\\HeadersInterface' => $vendorDir . '/slim/slim/Slim/Interfaces/Http/HeadersInterface.php',
'Slim\\Interfaces\\InvocationStrategyInterface' => $vendorDir . '/slim/slim/Slim/Interfaces/InvocationStrategyInterface.php',
'Slim\\Interfaces\\RouteGroupInterface' => $vendorDir . '/slim/slim/Slim/Interfaces/RouteGroupInterface.php',
'Slim\\Interfaces\\RouteInterface' => $vendorDir . '/slim/slim/Slim/Interfaces/RouteInterface.php',
'Slim\\Interfaces\\RouterInterface' => $vendorDir . '/slim/slim/Slim/Interfaces/RouterInterface.php',
'Slim\\MiddlewareAwareTrait' => $vendorDir . '/slim/slim/Slim/MiddlewareAwareTrait.php',
'Slim\\Routable' => $vendorDir . '/slim/slim/Slim/Routable.php',
'Slim\\Route' => $vendorDir . '/slim/slim/Slim/Route.php',
'Slim\\RouteGroup' => $vendorDir . '/slim/slim/Slim/RouteGroup.php',
'Slim\\Router' => $vendorDir . '/slim/slim/Slim/Router.php',
'Symfony\\Component\\Cache\\Adapter\\AbstractAdapter' => $vendorDir . '/symfony/cache/Adapter/AbstractAdapter.php',
'Symfony\\Component\\Cache\\Adapter\\AdapterInterface' => $vendorDir . '/symfony/cache/Adapter/AdapterInterface.php',
'Symfony\\Component\\Cache\\Adapter\\ApcuAdapter' => $vendorDir . '/symfony/cache/Adapter/ApcuAdapter.php',
'Symfony\\Component\\Cache\\Adapter\\ArrayAdapter' => $vendorDir . '/symfony/cache/Adapter/ArrayAdapter.php',
'Symfony\\Component\\Cache\\Adapter\\ChainAdapter' => $vendorDir . '/symfony/cache/Adapter/ChainAdapter.php',
'Symfony\\Component\\Cache\\Adapter\\DoctrineAdapter' => $vendorDir . '/symfony/cache/Adapter/DoctrineAdapter.php',
'Symfony\\Component\\Cache\\Adapter\\FilesystemAdapter' => $vendorDir . '/symfony/cache/Adapter/FilesystemAdapter.php',
'Symfony\\Component\\Cache\\Adapter\\MemcachedAdapter' => $vendorDir . '/symfony/cache/Adapter/MemcachedAdapter.php',
'Symfony\\Component\\Cache\\Adapter\\NullAdapter' => $vendorDir . '/symfony/cache/Adapter/NullAdapter.php',
'Symfony\\Component\\Cache\\Adapter\\PdoAdapter' => $vendorDir . '/symfony/cache/Adapter/PdoAdapter.php',
'Symfony\\Component\\Cache\\Adapter\\PhpArrayAdapter' => $vendorDir . '/symfony/cache/Adapter/PhpArrayAdapter.php',
'Symfony\\Component\\Cache\\Adapter\\PhpFilesAdapter' => $vendorDir . '/symfony/cache/Adapter/PhpFilesAdapter.php',
'Symfony\\Component\\Cache\\Adapter\\ProxyAdapter' => $vendorDir . '/symfony/cache/Adapter/ProxyAdapter.php',
'Symfony\\Component\\Cache\\Adapter\\RedisAdapter' => $vendorDir . '/symfony/cache/Adapter/RedisAdapter.php',
'Symfony\\Component\\Cache\\Adapter\\SimpleCacheAdapter' => $vendorDir . '/symfony/cache/Adapter/SimpleCacheAdapter.php',
'Symfony\\Component\\Cache\\Adapter\\TagAwareAdapter' => $vendorDir . '/symfony/cache/Adapter/TagAwareAdapter.php',
'Symfony\\Component\\Cache\\Adapter\\TagAwareAdapterInterface' => $vendorDir . '/symfony/cache/Adapter/TagAwareAdapterInterface.php',
'Symfony\\Component\\Cache\\Adapter\\TraceableAdapter' => $vendorDir . '/symfony/cache/Adapter/TraceableAdapter.php',
'Symfony\\Component\\Cache\\Adapter\\TraceableAdapterEvent' => $vendorDir . '/symfony/cache/Adapter/TraceableAdapter.php',
'Symfony\\Component\\Cache\\Adapter\\TraceableTagAwareAdapter' => $vendorDir . '/symfony/cache/Adapter/TraceableTagAwareAdapter.php',
'Symfony\\Component\\Cache\\CacheItem' => $vendorDir . '/symfony/cache/CacheItem.php',
'Symfony\\Component\\Cache\\DataCollector\\CacheDataCollector' => $vendorDir . '/symfony/cache/DataCollector/CacheDataCollector.php',
'Symfony\\Component\\Cache\\DoctrineProvider' => $vendorDir . '/symfony/cache/DoctrineProvider.php',
'Symfony\\Component\\Cache\\Exception\\CacheException' => $vendorDir . '/symfony/cache/Exception/CacheException.php',
'Symfony\\Component\\Cache\\Exception\\InvalidArgumentException' => $vendorDir . '/symfony/cache/Exception/InvalidArgumentException.php',
'Symfony\\Component\\Cache\\PruneableInterface' => $vendorDir . '/symfony/cache/PruneableInterface.php',
'Symfony\\Component\\Cache\\ResettableInterface' => $vendorDir . '/symfony/cache/ResettableInterface.php',
'Symfony\\Component\\Cache\\Simple\\AbstractCache' => $vendorDir . '/symfony/cache/Simple/AbstractCache.php',
'Symfony\\Component\\Cache\\Simple\\ApcuCache' => $vendorDir . '/symfony/cache/Simple/ApcuCache.php',
'Symfony\\Component\\Cache\\Simple\\ArrayCache' => $vendorDir . '/symfony/cache/Simple/ArrayCache.php',
'Symfony\\Component\\Cache\\Simple\\ChainCache' => $vendorDir . '/symfony/cache/Simple/ChainCache.php',
'Symfony\\Component\\Cache\\Simple\\DoctrineCache' => $vendorDir . '/symfony/cache/Simple/DoctrineCache.php',
'Symfony\\Component\\Cache\\Simple\\FilesystemCache' => $vendorDir . '/symfony/cache/Simple/FilesystemCache.php',
'Symfony\\Component\\Cache\\Simple\\MemcachedCache' => $vendorDir . '/symfony/cache/Simple/MemcachedCache.php',
'Symfony\\Component\\Cache\\Simple\\NullCache' => $vendorDir . '/symfony/cache/Simple/NullCache.php',
'Symfony\\Component\\Cache\\Simple\\PdoCache' => $vendorDir . '/symfony/cache/Simple/PdoCache.php',
'Symfony\\Component\\Cache\\Simple\\PhpArrayCache' => $vendorDir . '/symfony/cache/Simple/PhpArrayCache.php',
'Symfony\\Component\\Cache\\Simple\\PhpFilesCache' => $vendorDir . '/symfony/cache/Simple/PhpFilesCache.php',
'Symfony\\Component\\Cache\\Simple\\Psr6Cache' => $vendorDir . '/symfony/cache/Simple/Psr6Cache.php',
'Symfony\\Component\\Cache\\Simple\\RedisCache' => $vendorDir . '/symfony/cache/Simple/RedisCache.php',
'Symfony\\Component\\Cache\\Simple\\TraceableCache' => $vendorDir . '/symfony/cache/Simple/TraceableCache.php',
'Symfony\\Component\\Cache\\Simple\\TraceableCacheEvent' => $vendorDir . '/symfony/cache/Simple/TraceableCache.php',
'Symfony\\Component\\Cache\\Traits\\AbstractTrait' => $vendorDir . '/symfony/cache/Traits/AbstractTrait.php',
'Symfony\\Component\\Cache\\Traits\\ApcuTrait' => $vendorDir . '/symfony/cache/Traits/ApcuTrait.php',
'Symfony\\Component\\Cache\\Traits\\ArrayTrait' => $vendorDir . '/symfony/cache/Traits/ArrayTrait.php',
'Symfony\\Component\\Cache\\Traits\\DoctrineTrait' => $vendorDir . '/symfony/cache/Traits/DoctrineTrait.php',
'Symfony\\Component\\Cache\\Traits\\FilesystemCommonTrait' => $vendorDir . '/symfony/cache/Traits/FilesystemCommonTrait.php',
'Symfony\\Component\\Cache\\Traits\\FilesystemTrait' => $vendorDir . '/symfony/cache/Traits/FilesystemTrait.php',
'Symfony\\Component\\Cache\\Traits\\MemcachedTrait' => $vendorDir . '/symfony/cache/Traits/MemcachedTrait.php',
'Symfony\\Component\\Cache\\Traits\\PdoTrait' => $vendorDir . '/symfony/cache/Traits/PdoTrait.php',
'Symfony\\Component\\Cache\\Traits\\PhpArrayTrait' => $vendorDir . '/symfony/cache/Traits/PhpArrayTrait.php',
'Symfony\\Component\\Cache\\Traits\\PhpFilesTrait' => $vendorDir . '/symfony/cache/Traits/PhpFilesTrait.php',
'Symfony\\Component\\Cache\\Traits\\ProxyTrait' => $vendorDir . '/symfony/cache/Traits/ProxyTrait.php',
'Symfony\\Component\\Cache\\Traits\\RedisProxy' => $vendorDir . '/symfony/cache/Traits/RedisProxy.php',
'Symfony\\Component\\Cache\\Traits\\RedisTrait' => $vendorDir . '/symfony/cache/Traits/RedisTrait.php',
'Symfony\\Component\\ExpressionLanguage\\Compiler' => $vendorDir . '/symfony/expression-language/Compiler.php',
'Symfony\\Component\\ExpressionLanguage\\Expression' => $vendorDir . '/symfony/expression-language/Expression.php',
'Symfony\\Component\\ExpressionLanguage\\ExpressionFunction' => $vendorDir . '/symfony/expression-language/ExpressionFunction.php',
'Symfony\\Component\\ExpressionLanguage\\ExpressionFunctionProviderInterface' => $vendorDir . '/symfony/expression-language/ExpressionFunctionProviderInterface.php',
'Symfony\\Component\\ExpressionLanguage\\ExpressionLanguage' => $vendorDir . '/symfony/expression-language/ExpressionLanguage.php',
'Symfony\\Component\\ExpressionLanguage\\Lexer' => $vendorDir . '/symfony/expression-language/Lexer.php',
'Symfony\\Component\\ExpressionLanguage\\Node\\ArgumentsNode' => $vendorDir . '/symfony/expression-language/Node/ArgumentsNode.php',
'Symfony\\Component\\ExpressionLanguage\\Node\\ArrayNode' => $vendorDir . '/symfony/expression-language/Node/ArrayNode.php',
'Symfony\\Component\\ExpressionLanguage\\Node\\BinaryNode' => $vendorDir . '/symfony/expression-language/Node/BinaryNode.php',
'Symfony\\Component\\ExpressionLanguage\\Node\\ConditionalNode' => $vendorDir . '/symfony/expression-language/Node/ConditionalNode.php',
'Symfony\\Component\\ExpressionLanguage\\Node\\ConstantNode' => $vendorDir . '/symfony/expression-language/Node/ConstantNode.php',
'Symfony\\Component\\ExpressionLanguage\\Node\\FunctionNode' => $vendorDir . '/symfony/expression-language/Node/FunctionNode.php',
'Symfony\\Component\\ExpressionLanguage\\Node\\GetAttrNode' => $vendorDir . '/symfony/expression-language/Node/GetAttrNode.php',
'Symfony\\Component\\ExpressionLanguage\\Node\\NameNode' => $vendorDir . '/symfony/expression-language/Node/NameNode.php',
'Symfony\\Component\\ExpressionLanguage\\Node\\Node' => $vendorDir . '/symfony/expression-language/Node/Node.php',
'Symfony\\Component\\ExpressionLanguage\\Node\\UnaryNode' => $vendorDir . '/symfony/expression-language/Node/UnaryNode.php',
'Symfony\\Component\\ExpressionLanguage\\ParsedExpression' => $vendorDir . '/symfony/expression-language/ParsedExpression.php',
'Symfony\\Component\\ExpressionLanguage\\Parser' => $vendorDir . '/symfony/expression-language/Parser.php',
'Symfony\\Component\\ExpressionLanguage\\ParserCache\\ArrayParserCache' => $vendorDir . '/symfony/expression-language/ParserCache/ArrayParserCache.php',
'Symfony\\Component\\ExpressionLanguage\\ParserCache\\ParserCacheAdapter' => $vendorDir . '/symfony/expression-language/ParserCache/ParserCacheAdapter.php',
'Symfony\\Component\\ExpressionLanguage\\ParserCache\\ParserCacheInterface' => $vendorDir . '/symfony/expression-language/ParserCache/ParserCacheInterface.php',
'Symfony\\Component\\ExpressionLanguage\\SerializedParsedExpression' => $vendorDir . '/symfony/expression-language/SerializedParsedExpression.php',
'Symfony\\Component\\ExpressionLanguage\\SyntaxError' => $vendorDir . '/symfony/expression-language/SyntaxError.php',
'Symfony\\Component\\ExpressionLanguage\\Token' => $vendorDir . '/symfony/expression-language/Token.php',
'Symfony\\Component\\ExpressionLanguage\\TokenStream' => $vendorDir . '/symfony/expression-language/TokenStream.php',
'Symfony\\Polyfill\\Apcu\\Apcu' => $vendorDir . '/symfony/polyfill-apcu/Apcu.php',
);

View File

@ -0,0 +1,11 @@
<?php
// autoload_files.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'32dcc8afd4335739640db7d200c1971d' => $vendorDir . '/symfony/polyfill-apcu/bootstrap.php',
'253c157292f75eb38082b5acb06f3f01' => $vendorDir . '/nikic/fast-route/src/functions.php',
);

View File

@ -0,0 +1,10 @@
<?php
// autoload_namespaces.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'Pimple' => array($vendorDir . '/pimple/pimple/src'),
);

View File

@ -0,0 +1,20 @@
<?php
// autoload_psr4.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'Symfony\\Polyfill\\Apcu\\' => array($vendorDir . '/symfony/polyfill-apcu'),
'Symfony\\Component\\ExpressionLanguage\\' => array($vendorDir . '/symfony/expression-language'),
'Symfony\\Component\\Cache\\' => array($vendorDir . '/symfony/cache'),
'Slim\\' => array($vendorDir . '/slim/slim/Slim'),
'Psr\\SimpleCache\\' => array($vendorDir . '/psr/simple-cache/src'),
'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'),
'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-message/src'),
'Psr\\Container\\' => array($vendorDir . '/psr/container/src'),
'Psr\\Cache\\' => array($vendorDir . '/psr/cache/src'),
'Interop\\Container\\' => array($vendorDir . '/container-interop/container-interop/src/Interop/Container'),
'FastRoute\\' => array($vendorDir . '/nikic/fast-route/src'),
);

View File

@ -0,0 +1,70 @@
<?php
// autoload_real.php @generated by Composer
class ComposerAutoloaderInitAdvancedContentFilterAddon
{
private static $loader;
public static function loadClassLoader($class)
{
if ('Composer\Autoload\ClassLoader' === $class) {
require __DIR__ . '/ClassLoader.php';
}
}
public static function getLoader()
{
if (null !== self::$loader) {
return self::$loader;
}
spl_autoload_register(array('ComposerAutoloaderInitAdvancedContentFilterAddon', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
spl_autoload_unregister(array('ComposerAutoloaderInitAdvancedContentFilterAddon', 'loadClassLoader'));
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
if ($useStaticLoader) {
require_once __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInitAdvancedContentFilterAddon::getInitializer($loader));
} else {
$map = require __DIR__ . '/autoload_namespaces.php';
foreach ($map as $namespace => $path) {
$loader->set($namespace, $path);
}
$map = require __DIR__ . '/autoload_psr4.php';
foreach ($map as $namespace => $path) {
$loader->setPsr4($namespace, $path);
}
$classMap = require __DIR__ . '/autoload_classmap.php';
if ($classMap) {
$loader->addClassMap($classMap);
}
}
$loader->register(true);
if ($useStaticLoader) {
$includeFiles = Composer\Autoload\ComposerStaticInitAdvancedContentFilterAddon::$files;
} else {
$includeFiles = require __DIR__ . '/autoload_files.php';
}
foreach ($includeFiles as $fileIdentifier => $file) {
composerRequireAdvancedContentFilterAddon($fileIdentifier, $file);
}
return $loader;
}
}
function composerRequireAdvancedContentFilterAddon($fileIdentifier, $file)
{
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
require $file;
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
}
}

View File

@ -0,0 +1,302 @@
<?php
// autoload_static.php @generated by Composer
namespace Composer\Autoload;
class ComposerStaticInitAdvancedContentFilterAddon
{
public static $files = array (
'32dcc8afd4335739640db7d200c1971d' => __DIR__ . '/..' . '/symfony/polyfill-apcu/bootstrap.php',
'253c157292f75eb38082b5acb06f3f01' => __DIR__ . '/..' . '/nikic/fast-route/src/functions.php',
);
public static $prefixLengthsPsr4 = array (
'S' =>
array (
'Symfony\\Polyfill\\Apcu\\' => 22,
'Symfony\\Component\\ExpressionLanguage\\' => 37,
'Symfony\\Component\\Cache\\' => 24,
'Slim\\' => 5,
),
'P' =>
array (
'Psr\\SimpleCache\\' => 16,
'Psr\\Log\\' => 8,
'Psr\\Http\\Message\\' => 17,
'Psr\\Container\\' => 14,
'Psr\\Cache\\' => 10,
),
'I' =>
array (
'Interop\\Container\\' => 18,
),
'F' =>
array (
'FastRoute\\' => 10,
),
);
public static $prefixDirsPsr4 = array (
'Symfony\\Polyfill\\Apcu\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/polyfill-apcu',
),
'Symfony\\Component\\ExpressionLanguage\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/expression-language',
),
'Symfony\\Component\\Cache\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/cache',
),
'Slim\\' =>
array (
0 => __DIR__ . '/..' . '/slim/slim/Slim',
),
'Psr\\SimpleCache\\' =>
array (
0 => __DIR__ . '/..' . '/psr/simple-cache/src',
),
'Psr\\Log\\' =>
array (
0 => __DIR__ . '/..' . '/psr/log/Psr/Log',
),
'Psr\\Http\\Message\\' =>
array (
0 => __DIR__ . '/..' . '/psr/http-message/src',
),
'Psr\\Container\\' =>
array (
0 => __DIR__ . '/..' . '/psr/container/src',
),
'Psr\\Cache\\' =>
array (
0 => __DIR__ . '/..' . '/psr/cache/src',
),
'Interop\\Container\\' =>
array (
0 => __DIR__ . '/..' . '/container-interop/container-interop/src/Interop/Container',
),
'FastRoute\\' =>
array (
0 => __DIR__ . '/..' . '/nikic/fast-route/src',
),
);
public static $prefixesPsr0 = array (
'P' =>
array (
'Pimple' =>
array (
0 => __DIR__ . '/..' . '/pimple/pimple/src',
),
),
);
public static $classMap = array (
'FastRoute\\BadRouteException' => __DIR__ . '/..' . '/nikic/fast-route/src/BadRouteException.php',
'FastRoute\\DataGenerator' => __DIR__ . '/..' . '/nikic/fast-route/src/DataGenerator.php',
'FastRoute\\DataGenerator\\CharCountBased' => __DIR__ . '/..' . '/nikic/fast-route/src/DataGenerator/CharCountBased.php',
'FastRoute\\DataGenerator\\GroupCountBased' => __DIR__ . '/..' . '/nikic/fast-route/src/DataGenerator/GroupCountBased.php',
'FastRoute\\DataGenerator\\GroupPosBased' => __DIR__ . '/..' . '/nikic/fast-route/src/DataGenerator/GroupPosBased.php',
'FastRoute\\DataGenerator\\MarkBased' => __DIR__ . '/..' . '/nikic/fast-route/src/DataGenerator/MarkBased.php',
'FastRoute\\DataGenerator\\RegexBasedAbstract' => __DIR__ . '/..' . '/nikic/fast-route/src/DataGenerator/RegexBasedAbstract.php',
'FastRoute\\Dispatcher' => __DIR__ . '/..' . '/nikic/fast-route/src/Dispatcher.php',
'FastRoute\\Dispatcher\\CharCountBased' => __DIR__ . '/..' . '/nikic/fast-route/src/Dispatcher/CharCountBased.php',
'FastRoute\\Dispatcher\\GroupCountBased' => __DIR__ . '/..' . '/nikic/fast-route/src/Dispatcher/GroupCountBased.php',
'FastRoute\\Dispatcher\\GroupPosBased' => __DIR__ . '/..' . '/nikic/fast-route/src/Dispatcher/GroupPosBased.php',
'FastRoute\\Dispatcher\\MarkBased' => __DIR__ . '/..' . '/nikic/fast-route/src/Dispatcher/MarkBased.php',
'FastRoute\\Dispatcher\\RegexBasedAbstract' => __DIR__ . '/..' . '/nikic/fast-route/src/Dispatcher/RegexBasedAbstract.php',
'FastRoute\\Route' => __DIR__ . '/..' . '/nikic/fast-route/src/Route.php',
'FastRoute\\RouteCollector' => __DIR__ . '/..' . '/nikic/fast-route/src/RouteCollector.php',
'FastRoute\\RouteParser' => __DIR__ . '/..' . '/nikic/fast-route/src/RouteParser.php',
'FastRoute\\RouteParser\\Std' => __DIR__ . '/..' . '/nikic/fast-route/src/RouteParser/Std.php',
'Interop\\Container\\ContainerInterface' => __DIR__ . '/..' . '/container-interop/container-interop/src/Interop/Container/ContainerInterface.php',
'Interop\\Container\\Exception\\ContainerException' => __DIR__ . '/..' . '/container-interop/container-interop/src/Interop/Container/Exception/ContainerException.php',
'Interop\\Container\\Exception\\NotFoundException' => __DIR__ . '/..' . '/container-interop/container-interop/src/Interop/Container/Exception/NotFoundException.php',
'Pimple\\Container' => __DIR__ . '/..' . '/pimple/pimple/src/Pimple/Container.php',
'Pimple\\Exception\\ExpectedInvokableException' => __DIR__ . '/..' . '/pimple/pimple/src/Pimple/Exception/ExpectedInvokableException.php',
'Pimple\\Exception\\FrozenServiceException' => __DIR__ . '/..' . '/pimple/pimple/src/Pimple/Exception/FrozenServiceException.php',
'Pimple\\Exception\\InvalidServiceIdentifierException' => __DIR__ . '/..' . '/pimple/pimple/src/Pimple/Exception/InvalidServiceIdentifierException.php',
'Pimple\\Exception\\UnknownIdentifierException' => __DIR__ . '/..' . '/pimple/pimple/src/Pimple/Exception/UnknownIdentifierException.php',
'Pimple\\Psr11\\Container' => __DIR__ . '/..' . '/pimple/pimple/src/Pimple/Psr11/Container.php',
'Pimple\\Psr11\\ServiceLocator' => __DIR__ . '/..' . '/pimple/pimple/src/Pimple/Psr11/ServiceLocator.php',
'Pimple\\ServiceIterator' => __DIR__ . '/..' . '/pimple/pimple/src/Pimple/ServiceIterator.php',
'Pimple\\ServiceProviderInterface' => __DIR__ . '/..' . '/pimple/pimple/src/Pimple/ServiceProviderInterface.php',
'Pimple\\Tests\\Fixtures\\Invokable' => __DIR__ . '/..' . '/pimple/pimple/src/Pimple/Tests/Fixtures/Invokable.php',
'Pimple\\Tests\\Fixtures\\NonInvokable' => __DIR__ . '/..' . '/pimple/pimple/src/Pimple/Tests/Fixtures/NonInvokable.php',
'Pimple\\Tests\\Fixtures\\PimpleServiceProvider' => __DIR__ . '/..' . '/pimple/pimple/src/Pimple/Tests/Fixtures/PimpleServiceProvider.php',
'Pimple\\Tests\\Fixtures\\Service' => __DIR__ . '/..' . '/pimple/pimple/src/Pimple/Tests/Fixtures/Service.php',
'Pimple\\Tests\\PimpleServiceProviderInterfaceTest' => __DIR__ . '/..' . '/pimple/pimple/src/Pimple/Tests/PimpleServiceProviderInterfaceTest.php',
'Pimple\\Tests\\PimpleTest' => __DIR__ . '/..' . '/pimple/pimple/src/Pimple/Tests/PimpleTest.php',
'Pimple\\Tests\\Psr11\\ContainerTest' => __DIR__ . '/..' . '/pimple/pimple/src/Pimple/Tests/Psr11/ContainerTest.php',
'Pimple\\Tests\\Psr11\\ServiceLocatorTest' => __DIR__ . '/..' . '/pimple/pimple/src/Pimple/Tests/Psr11/ServiceLocatorTest.php',
'Pimple\\Tests\\ServiceIteratorTest' => __DIR__ . '/..' . '/pimple/pimple/src/Pimple/Tests/ServiceIteratorTest.php',
'Psr\\Cache\\CacheException' => __DIR__ . '/..' . '/psr/cache/src/CacheException.php',
'Psr\\Cache\\CacheItemInterface' => __DIR__ . '/..' . '/psr/cache/src/CacheItemInterface.php',
'Psr\\Cache\\CacheItemPoolInterface' => __DIR__ . '/..' . '/psr/cache/src/CacheItemPoolInterface.php',
'Psr\\Cache\\InvalidArgumentException' => __DIR__ . '/..' . '/psr/cache/src/InvalidArgumentException.php',
'Psr\\Container\\ContainerExceptionInterface' => __DIR__ . '/..' . '/psr/container/src/ContainerExceptionInterface.php',
'Psr\\Container\\ContainerInterface' => __DIR__ . '/..' . '/psr/container/src/ContainerInterface.php',
'Psr\\Container\\NotFoundExceptionInterface' => __DIR__ . '/..' . '/psr/container/src/NotFoundExceptionInterface.php',
'Psr\\Http\\Message\\MessageInterface' => __DIR__ . '/..' . '/psr/http-message/src/MessageInterface.php',
'Psr\\Http\\Message\\RequestInterface' => __DIR__ . '/..' . '/psr/http-message/src/RequestInterface.php',
'Psr\\Http\\Message\\ResponseInterface' => __DIR__ . '/..' . '/psr/http-message/src/ResponseInterface.php',
'Psr\\Http\\Message\\ServerRequestInterface' => __DIR__ . '/..' . '/psr/http-message/src/ServerRequestInterface.php',
'Psr\\Http\\Message\\StreamInterface' => __DIR__ . '/..' . '/psr/http-message/src/StreamInterface.php',
'Psr\\Http\\Message\\UploadedFileInterface' => __DIR__ . '/..' . '/psr/http-message/src/UploadedFileInterface.php',
'Psr\\Http\\Message\\UriInterface' => __DIR__ . '/..' . '/psr/http-message/src/UriInterface.php',
'Psr\\Log\\AbstractLogger' => __DIR__ . '/..' . '/psr/log/Psr/Log/AbstractLogger.php',
'Psr\\Log\\InvalidArgumentException' => __DIR__ . '/..' . '/psr/log/Psr/Log/InvalidArgumentException.php',
'Psr\\Log\\LogLevel' => __DIR__ . '/..' . '/psr/log/Psr/Log/LogLevel.php',
'Psr\\Log\\LoggerAwareInterface' => __DIR__ . '/..' . '/psr/log/Psr/Log/LoggerAwareInterface.php',
'Psr\\Log\\LoggerAwareTrait' => __DIR__ . '/..' . '/psr/log/Psr/Log/LoggerAwareTrait.php',
'Psr\\Log\\LoggerInterface' => __DIR__ . '/..' . '/psr/log/Psr/Log/LoggerInterface.php',
'Psr\\Log\\LoggerTrait' => __DIR__ . '/..' . '/psr/log/Psr/Log/LoggerTrait.php',
'Psr\\Log\\NullLogger' => __DIR__ . '/..' . '/psr/log/Psr/Log/NullLogger.php',
'Psr\\Log\\Test\\DummyTest' => __DIR__ . '/..' . '/psr/log/Psr/Log/Test/LoggerInterfaceTest.php',
'Psr\\Log\\Test\\LoggerInterfaceTest' => __DIR__ . '/..' . '/psr/log/Psr/Log/Test/LoggerInterfaceTest.php',
'Psr\\SimpleCache\\CacheException' => __DIR__ . '/..' . '/psr/simple-cache/src/CacheException.php',
'Psr\\SimpleCache\\CacheInterface' => __DIR__ . '/..' . '/psr/simple-cache/src/CacheInterface.php',
'Psr\\SimpleCache\\InvalidArgumentException' => __DIR__ . '/..' . '/psr/simple-cache/src/InvalidArgumentException.php',
'Slim\\App' => __DIR__ . '/..' . '/slim/slim/Slim/App.php',
'Slim\\CallableResolver' => __DIR__ . '/..' . '/slim/slim/Slim/CallableResolver.php',
'Slim\\CallableResolverAwareTrait' => __DIR__ . '/..' . '/slim/slim/Slim/CallableResolverAwareTrait.php',
'Slim\\Collection' => __DIR__ . '/..' . '/slim/slim/Slim/Collection.php',
'Slim\\Container' => __DIR__ . '/..' . '/slim/slim/Slim/Container.php',
'Slim\\DefaultServicesProvider' => __DIR__ . '/..' . '/slim/slim/Slim/DefaultServicesProvider.php',
'Slim\\DeferredCallable' => __DIR__ . '/..' . '/slim/slim/Slim/DeferredCallable.php',
'Slim\\Exception\\ContainerException' => __DIR__ . '/..' . '/slim/slim/Slim/Exception/ContainerException.php',
'Slim\\Exception\\ContainerValueNotFoundException' => __DIR__ . '/..' . '/slim/slim/Slim/Exception/ContainerValueNotFoundException.php',
'Slim\\Exception\\InvalidMethodException' => __DIR__ . '/..' . '/slim/slim/Slim/Exception/InvalidMethodException.php',
'Slim\\Exception\\MethodNotAllowedException' => __DIR__ . '/..' . '/slim/slim/Slim/Exception/MethodNotAllowedException.php',
'Slim\\Exception\\NotFoundException' => __DIR__ . '/..' . '/slim/slim/Slim/Exception/NotFoundException.php',
'Slim\\Exception\\SlimException' => __DIR__ . '/..' . '/slim/slim/Slim/Exception/SlimException.php',
'Slim\\Handlers\\AbstractError' => __DIR__ . '/..' . '/slim/slim/Slim/Handlers/AbstractError.php',
'Slim\\Handlers\\AbstractHandler' => __DIR__ . '/..' . '/slim/slim/Slim/Handlers/AbstractHandler.php',
'Slim\\Handlers\\Error' => __DIR__ . '/..' . '/slim/slim/Slim/Handlers/Error.php',
'Slim\\Handlers\\NotAllowed' => __DIR__ . '/..' . '/slim/slim/Slim/Handlers/NotAllowed.php',
'Slim\\Handlers\\NotFound' => __DIR__ . '/..' . '/slim/slim/Slim/Handlers/NotFound.php',
'Slim\\Handlers\\PhpError' => __DIR__ . '/..' . '/slim/slim/Slim/Handlers/PhpError.php',
'Slim\\Handlers\\Strategies\\RequestResponse' => __DIR__ . '/..' . '/slim/slim/Slim/Handlers/Strategies/RequestResponse.php',
'Slim\\Handlers\\Strategies\\RequestResponseArgs' => __DIR__ . '/..' . '/slim/slim/Slim/Handlers/Strategies/RequestResponseArgs.php',
'Slim\\Http\\Body' => __DIR__ . '/..' . '/slim/slim/Slim/Http/Body.php',
'Slim\\Http\\Cookies' => __DIR__ . '/..' . '/slim/slim/Slim/Http/Cookies.php',
'Slim\\Http\\Environment' => __DIR__ . '/..' . '/slim/slim/Slim/Http/Environment.php',
'Slim\\Http\\Headers' => __DIR__ . '/..' . '/slim/slim/Slim/Http/Headers.php',
'Slim\\Http\\Message' => __DIR__ . '/..' . '/slim/slim/Slim/Http/Message.php',
'Slim\\Http\\Request' => __DIR__ . '/..' . '/slim/slim/Slim/Http/Request.php',
'Slim\\Http\\RequestBody' => __DIR__ . '/..' . '/slim/slim/Slim/Http/RequestBody.php',
'Slim\\Http\\Response' => __DIR__ . '/..' . '/slim/slim/Slim/Http/Response.php',
'Slim\\Http\\Stream' => __DIR__ . '/..' . '/slim/slim/Slim/Http/Stream.php',
'Slim\\Http\\UploadedFile' => __DIR__ . '/..' . '/slim/slim/Slim/Http/UploadedFile.php',
'Slim\\Http\\Uri' => __DIR__ . '/..' . '/slim/slim/Slim/Http/Uri.php',
'Slim\\Interfaces\\CallableResolverInterface' => __DIR__ . '/..' . '/slim/slim/Slim/Interfaces/CallableResolverInterface.php',
'Slim\\Interfaces\\CollectionInterface' => __DIR__ . '/..' . '/slim/slim/Slim/Interfaces/CollectionInterface.php',
'Slim\\Interfaces\\Http\\CookiesInterface' => __DIR__ . '/..' . '/slim/slim/Slim/Interfaces/Http/CookiesInterface.php',
'Slim\\Interfaces\\Http\\EnvironmentInterface' => __DIR__ . '/..' . '/slim/slim/Slim/Interfaces/Http/EnvironmentInterface.php',
'Slim\\Interfaces\\Http\\HeadersInterface' => __DIR__ . '/..' . '/slim/slim/Slim/Interfaces/Http/HeadersInterface.php',
'Slim\\Interfaces\\InvocationStrategyInterface' => __DIR__ . '/..' . '/slim/slim/Slim/Interfaces/InvocationStrategyInterface.php',
'Slim\\Interfaces\\RouteGroupInterface' => __DIR__ . '/..' . '/slim/slim/Slim/Interfaces/RouteGroupInterface.php',
'Slim\\Interfaces\\RouteInterface' => __DIR__ . '/..' . '/slim/slim/Slim/Interfaces/RouteInterface.php',
'Slim\\Interfaces\\RouterInterface' => __DIR__ . '/..' . '/slim/slim/Slim/Interfaces/RouterInterface.php',
'Slim\\MiddlewareAwareTrait' => __DIR__ . '/..' . '/slim/slim/Slim/MiddlewareAwareTrait.php',
'Slim\\Routable' => __DIR__ . '/..' . '/slim/slim/Slim/Routable.php',
'Slim\\Route' => __DIR__ . '/..' . '/slim/slim/Slim/Route.php',
'Slim\\RouteGroup' => __DIR__ . '/..' . '/slim/slim/Slim/RouteGroup.php',
'Slim\\Router' => __DIR__ . '/..' . '/slim/slim/Slim/Router.php',
'Symfony\\Component\\Cache\\Adapter\\AbstractAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/AbstractAdapter.php',
'Symfony\\Component\\Cache\\Adapter\\AdapterInterface' => __DIR__ . '/..' . '/symfony/cache/Adapter/AdapterInterface.php',
'Symfony\\Component\\Cache\\Adapter\\ApcuAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/ApcuAdapter.php',
'Symfony\\Component\\Cache\\Adapter\\ArrayAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/ArrayAdapter.php',
'Symfony\\Component\\Cache\\Adapter\\ChainAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/ChainAdapter.php',
'Symfony\\Component\\Cache\\Adapter\\DoctrineAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/DoctrineAdapter.php',
'Symfony\\Component\\Cache\\Adapter\\FilesystemAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/FilesystemAdapter.php',
'Symfony\\Component\\Cache\\Adapter\\MemcachedAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/MemcachedAdapter.php',
'Symfony\\Component\\Cache\\Adapter\\NullAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/NullAdapter.php',
'Symfony\\Component\\Cache\\Adapter\\PdoAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/PdoAdapter.php',
'Symfony\\Component\\Cache\\Adapter\\PhpArrayAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/PhpArrayAdapter.php',
'Symfony\\Component\\Cache\\Adapter\\PhpFilesAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/PhpFilesAdapter.php',
'Symfony\\Component\\Cache\\Adapter\\ProxyAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/ProxyAdapter.php',
'Symfony\\Component\\Cache\\Adapter\\RedisAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/RedisAdapter.php',
'Symfony\\Component\\Cache\\Adapter\\SimpleCacheAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/SimpleCacheAdapter.php',
'Symfony\\Component\\Cache\\Adapter\\TagAwareAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/TagAwareAdapter.php',
'Symfony\\Component\\Cache\\Adapter\\TagAwareAdapterInterface' => __DIR__ . '/..' . '/symfony/cache/Adapter/TagAwareAdapterInterface.php',
'Symfony\\Component\\Cache\\Adapter\\TraceableAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/TraceableAdapter.php',
'Symfony\\Component\\Cache\\Adapter\\TraceableAdapterEvent' => __DIR__ . '/..' . '/symfony/cache/Adapter/TraceableAdapter.php',
'Symfony\\Component\\Cache\\Adapter\\TraceableTagAwareAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/TraceableTagAwareAdapter.php',
'Symfony\\Component\\Cache\\CacheItem' => __DIR__ . '/..' . '/symfony/cache/CacheItem.php',
'Symfony\\Component\\Cache\\DataCollector\\CacheDataCollector' => __DIR__ . '/..' . '/symfony/cache/DataCollector/CacheDataCollector.php',
'Symfony\\Component\\Cache\\DoctrineProvider' => __DIR__ . '/..' . '/symfony/cache/DoctrineProvider.php',
'Symfony\\Component\\Cache\\Exception\\CacheException' => __DIR__ . '/..' . '/symfony/cache/Exception/CacheException.php',
'Symfony\\Component\\Cache\\Exception\\InvalidArgumentException' => __DIR__ . '/..' . '/symfony/cache/Exception/InvalidArgumentException.php',
'Symfony\\Component\\Cache\\PruneableInterface' => __DIR__ . '/..' . '/symfony/cache/PruneableInterface.php',
'Symfony\\Component\\Cache\\ResettableInterface' => __DIR__ . '/..' . '/symfony/cache/ResettableInterface.php',
'Symfony\\Component\\Cache\\Simple\\AbstractCache' => __DIR__ . '/..' . '/symfony/cache/Simple/AbstractCache.php',
'Symfony\\Component\\Cache\\Simple\\ApcuCache' => __DIR__ . '/..' . '/symfony/cache/Simple/ApcuCache.php',
'Symfony\\Component\\Cache\\Simple\\ArrayCache' => __DIR__ . '/..' . '/symfony/cache/Simple/ArrayCache.php',
'Symfony\\Component\\Cache\\Simple\\ChainCache' => __DIR__ . '/..' . '/symfony/cache/Simple/ChainCache.php',
'Symfony\\Component\\Cache\\Simple\\DoctrineCache' => __DIR__ . '/..' . '/symfony/cache/Simple/DoctrineCache.php',
'Symfony\\Component\\Cache\\Simple\\FilesystemCache' => __DIR__ . '/..' . '/symfony/cache/Simple/FilesystemCache.php',
'Symfony\\Component\\Cache\\Simple\\MemcachedCache' => __DIR__ . '/..' . '/symfony/cache/Simple/MemcachedCache.php',
'Symfony\\Component\\Cache\\Simple\\NullCache' => __DIR__ . '/..' . '/symfony/cache/Simple/NullCache.php',
'Symfony\\Component\\Cache\\Simple\\PdoCache' => __DIR__ . '/..' . '/symfony/cache/Simple/PdoCache.php',
'Symfony\\Component\\Cache\\Simple\\PhpArrayCache' => __DIR__ . '/..' . '/symfony/cache/Simple/PhpArrayCache.php',
'Symfony\\Component\\Cache\\Simple\\PhpFilesCache' => __DIR__ . '/..' . '/symfony/cache/Simple/PhpFilesCache.php',
'Symfony\\Component\\Cache\\Simple\\Psr6Cache' => __DIR__ . '/..' . '/symfony/cache/Simple/Psr6Cache.php',
'Symfony\\Component\\Cache\\Simple\\RedisCache' => __DIR__ . '/..' . '/symfony/cache/Simple/RedisCache.php',
'Symfony\\Component\\Cache\\Simple\\TraceableCache' => __DIR__ . '/..' . '/symfony/cache/Simple/TraceableCache.php',
'Symfony\\Component\\Cache\\Simple\\TraceableCacheEvent' => __DIR__ . '/..' . '/symfony/cache/Simple/TraceableCache.php',
'Symfony\\Component\\Cache\\Traits\\AbstractTrait' => __DIR__ . '/..' . '/symfony/cache/Traits/AbstractTrait.php',
'Symfony\\Component\\Cache\\Traits\\ApcuTrait' => __DIR__ . '/..' . '/symfony/cache/Traits/ApcuTrait.php',
'Symfony\\Component\\Cache\\Traits\\ArrayTrait' => __DIR__ . '/..' . '/symfony/cache/Traits/ArrayTrait.php',
'Symfony\\Component\\Cache\\Traits\\DoctrineTrait' => __DIR__ . '/..' . '/symfony/cache/Traits/DoctrineTrait.php',
'Symfony\\Component\\Cache\\Traits\\FilesystemCommonTrait' => __DIR__ . '/..' . '/symfony/cache/Traits/FilesystemCommonTrait.php',
'Symfony\\Component\\Cache\\Traits\\FilesystemTrait' => __DIR__ . '/..' . '/symfony/cache/Traits/FilesystemTrait.php',
'Symfony\\Component\\Cache\\Traits\\MemcachedTrait' => __DIR__ . '/..' . '/symfony/cache/Traits/MemcachedTrait.php',
'Symfony\\Component\\Cache\\Traits\\PdoTrait' => __DIR__ . '/..' . '/symfony/cache/Traits/PdoTrait.php',
'Symfony\\Component\\Cache\\Traits\\PhpArrayTrait' => __DIR__ . '/..' . '/symfony/cache/Traits/PhpArrayTrait.php',
'Symfony\\Component\\Cache\\Traits\\PhpFilesTrait' => __DIR__ . '/..' . '/symfony/cache/Traits/PhpFilesTrait.php',
'Symfony\\Component\\Cache\\Traits\\ProxyTrait' => __DIR__ . '/..' . '/symfony/cache/Traits/ProxyTrait.php',
'Symfony\\Component\\Cache\\Traits\\RedisProxy' => __DIR__ . '/..' . '/symfony/cache/Traits/RedisProxy.php',
'Symfony\\Component\\Cache\\Traits\\RedisTrait' => __DIR__ . '/..' . '/symfony/cache/Traits/RedisTrait.php',
'Symfony\\Component\\ExpressionLanguage\\Compiler' => __DIR__ . '/..' . '/symfony/expression-language/Compiler.php',
'Symfony\\Component\\ExpressionLanguage\\Expression' => __DIR__ . '/..' . '/symfony/expression-language/Expression.php',
'Symfony\\Component\\ExpressionLanguage\\ExpressionFunction' => __DIR__ . '/..' . '/symfony/expression-language/ExpressionFunction.php',
'Symfony\\Component\\ExpressionLanguage\\ExpressionFunctionProviderInterface' => __DIR__ . '/..' . '/symfony/expression-language/ExpressionFunctionProviderInterface.php',
'Symfony\\Component\\ExpressionLanguage\\ExpressionLanguage' => __DIR__ . '/..' . '/symfony/expression-language/ExpressionLanguage.php',
'Symfony\\Component\\ExpressionLanguage\\Lexer' => __DIR__ . '/..' . '/symfony/expression-language/Lexer.php',
'Symfony\\Component\\ExpressionLanguage\\Node\\ArgumentsNode' => __DIR__ . '/..' . '/symfony/expression-language/Node/ArgumentsNode.php',
'Symfony\\Component\\ExpressionLanguage\\Node\\ArrayNode' => __DIR__ . '/..' . '/symfony/expression-language/Node/ArrayNode.php',
'Symfony\\Component\\ExpressionLanguage\\Node\\BinaryNode' => __DIR__ . '/..' . '/symfony/expression-language/Node/BinaryNode.php',
'Symfony\\Component\\ExpressionLanguage\\Node\\ConditionalNode' => __DIR__ . '/..' . '/symfony/expression-language/Node/ConditionalNode.php',
'Symfony\\Component\\ExpressionLanguage\\Node\\ConstantNode' => __DIR__ . '/..' . '/symfony/expression-language/Node/ConstantNode.php',
'Symfony\\Component\\ExpressionLanguage\\Node\\FunctionNode' => __DIR__ . '/..' . '/symfony/expression-language/Node/FunctionNode.php',
'Symfony\\Component\\ExpressionLanguage\\Node\\GetAttrNode' => __DIR__ . '/..' . '/symfony/expression-language/Node/GetAttrNode.php',
'Symfony\\Component\\ExpressionLanguage\\Node\\NameNode' => __DIR__ . '/..' . '/symfony/expression-language/Node/NameNode.php',
'Symfony\\Component\\ExpressionLanguage\\Node\\Node' => __DIR__ . '/..' . '/symfony/expression-language/Node/Node.php',
'Symfony\\Component\\ExpressionLanguage\\Node\\UnaryNode' => __DIR__ . '/..' . '/symfony/expression-language/Node/UnaryNode.php',
'Symfony\\Component\\ExpressionLanguage\\ParsedExpression' => __DIR__ . '/..' . '/symfony/expression-language/ParsedExpression.php',
'Symfony\\Component\\ExpressionLanguage\\Parser' => __DIR__ . '/..' . '/symfony/expression-language/Parser.php',
'Symfony\\Component\\ExpressionLanguage\\ParserCache\\ArrayParserCache' => __DIR__ . '/..' . '/symfony/expression-language/ParserCache/ArrayParserCache.php',
'Symfony\\Component\\ExpressionLanguage\\ParserCache\\ParserCacheAdapter' => __DIR__ . '/..' . '/symfony/expression-language/ParserCache/ParserCacheAdapter.php',
'Symfony\\Component\\ExpressionLanguage\\ParserCache\\ParserCacheInterface' => __DIR__ . '/..' . '/symfony/expression-language/ParserCache/ParserCacheInterface.php',
'Symfony\\Component\\ExpressionLanguage\\SerializedParsedExpression' => __DIR__ . '/..' . '/symfony/expression-language/SerializedParsedExpression.php',
'Symfony\\Component\\ExpressionLanguage\\SyntaxError' => __DIR__ . '/..' . '/symfony/expression-language/SyntaxError.php',
'Symfony\\Component\\ExpressionLanguage\\Token' => __DIR__ . '/..' . '/symfony/expression-language/Token.php',
'Symfony\\Component\\ExpressionLanguage\\TokenStream' => __DIR__ . '/..' . '/symfony/expression-language/TokenStream.php',
'Symfony\\Polyfill\\Apcu\\Apcu' => __DIR__ . '/..' . '/symfony/polyfill-apcu/Apcu.php',
);
public static function getInitializer(ClassLoader $loader)
{
return \Closure::bind(function () use ($loader) {
$loader->prefixLengthsPsr4 = ComposerStaticInitAdvancedContentFilterAddon::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInitAdvancedContentFilterAddon::$prefixDirsPsr4;
$loader->prefixesPsr0 = ComposerStaticInitAdvancedContentFilterAddon::$prefixesPsr0;
$loader->classMap = ComposerStaticInitAdvancedContentFilterAddon::$classMap;
}, null, ClassLoader::class);
}
}

View File

@ -0,0 +1,640 @@
[
{
"name": "container-interop/container-interop",
"version": "1.2.0",
"version_normalized": "1.2.0.0",
"source": {
"type": "git",
"url": "https://github.com/container-interop/container-interop.git",
"reference": "79cbf1341c22ec75643d841642dd5d6acd83bdb8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/container-interop/container-interop/zipball/79cbf1341c22ec75643d841642dd5d6acd83bdb8",
"reference": "79cbf1341c22ec75643d841642dd5d6acd83bdb8",
"shasum": ""
},
"require": {
"psr/container": "^1.0"
},
"time": "2017-02-14T19:40:03+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-4": {
"Interop\\Container\\": "src/Interop/Container/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "Promoting the interoperability of container objects (DIC, SL, etc.)",
"homepage": "https://github.com/container-interop/container-interop"
},
{
"name": "nikic/fast-route",
"version": "v1.3.0",
"version_normalized": "1.3.0.0",
"source": {
"type": "git",
"url": "https://github.com/nikic/FastRoute.git",
"reference": "181d480e08d9476e61381e04a71b34dc0432e812"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nikic/FastRoute/zipball/181d480e08d9476e61381e04a71b34dc0432e812",
"reference": "181d480e08d9476e61381e04a71b34dc0432e812",
"shasum": ""
},
"require": {
"php": ">=5.4.0"
},
"require-dev": {
"phpunit/phpunit": "^4.8.35|~5.7"
},
"time": "2018-02-13T20:26:39+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-4": {
"FastRoute\\": "src/"
},
"files": [
"src/functions.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Nikita Popov",
"email": "nikic@php.net"
}
],
"description": "Fast request router for PHP",
"keywords": [
"router",
"routing"
]
},
{
"name": "pimple/pimple",
"version": "v3.2.3",
"version_normalized": "3.2.3.0",
"source": {
"type": "git",
"url": "https://github.com/silexphp/Pimple.git",
"reference": "9e403941ef9d65d20cba7d54e29fe906db42cf32"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/silexphp/Pimple/zipball/9e403941ef9d65d20cba7d54e29fe906db42cf32",
"reference": "9e403941ef9d65d20cba7d54e29fe906db42cf32",
"shasum": ""
},
"require": {
"php": ">=5.3.0",
"psr/container": "^1.0"
},
"require-dev": {
"symfony/phpunit-bridge": "^3.2"
},
"time": "2018-01-21T07:42:36+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.2.x-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-0": {
"Pimple": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
}
],
"description": "Pimple, a simple Dependency Injection Container",
"homepage": "http://pimple.sensiolabs.org",
"keywords": [
"container",
"dependency injection"
]
},
{
"name": "psr/cache",
"version": "1.0.1",
"version_normalized": "1.0.1.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/cache.git",
"reference": "d11b50ad223250cf17b86e38383413f5a6764bf8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/cache/zipball/d11b50ad223250cf17b86e38383413f5a6764bf8",
"reference": "d11b50ad223250cf17b86e38383413f5a6764bf8",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"time": "2016-08-06T20:24:11+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"Psr\\Cache\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
}
],
"description": "Common interface for caching libraries",
"keywords": [
"cache",
"psr",
"psr-6"
]
},
{
"name": "psr/container",
"version": "1.0.0",
"version_normalized": "1.0.0.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/container.git",
"reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
"reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"time": "2017-02-14T16:28:37+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"Psr\\Container\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
}
],
"description": "Common Container Interface (PHP FIG PSR-11)",
"homepage": "https://github.com/php-fig/container",
"keywords": [
"PSR-11",
"container",
"container-interface",
"container-interop",
"psr"
]
},
{
"name": "psr/http-message",
"version": "1.0.1",
"version_normalized": "1.0.1.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/http-message.git",
"reference": "f6561bf28d520154e4b0ec72be95418abe6d9363"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363",
"reference": "f6561bf28d520154e4b0ec72be95418abe6d9363",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"time": "2016-08-06T14:39:51+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"Psr\\Http\\Message\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
}
],
"description": "Common interface for HTTP messages",
"homepage": "https://github.com/php-fig/http-message",
"keywords": [
"http",
"http-message",
"psr",
"psr-7",
"request",
"response"
]
},
{
"name": "psr/log",
"version": "1.0.2",
"version_normalized": "1.0.2.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/log.git",
"reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/log/zipball/4ebe3a8bf773a19edfe0a84b6585ba3d401b724d",
"reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"time": "2016-10-10T12:19:37+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"Psr\\Log\\": "Psr/Log/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
}
],
"description": "Common interface for logging libraries",
"homepage": "https://github.com/php-fig/log",
"keywords": [
"log",
"psr",
"psr-3"
]
},
{
"name": "psr/simple-cache",
"version": "1.0.1",
"version_normalized": "1.0.1.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/simple-cache.git",
"reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/simple-cache/zipball/408d5eafb83c57f6365a3ca330ff23aa4a5fa39b",
"reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"time": "2017-10-23T01:57:42+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"Psr\\SimpleCache\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
}
],
"description": "Common interfaces for simple caching",
"keywords": [
"cache",
"caching",
"psr",
"psr-16",
"simple-cache"
]
},
{
"name": "slim/slim",
"version": "3.9.2",
"version_normalized": "3.9.2.0",
"source": {
"type": "git",
"url": "https://github.com/slimphp/Slim.git",
"reference": "4086d0106cf5a7135c69fce4161fe355a8feb118"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/slimphp/Slim/zipball/4086d0106cf5a7135c69fce4161fe355a8feb118",
"reference": "4086d0106cf5a7135c69fce4161fe355a8feb118",
"shasum": ""
},
"require": {
"container-interop/container-interop": "^1.2",
"nikic/fast-route": "^1.0",
"php": ">=5.5.0",
"pimple/pimple": "^3.0",
"psr/container": "^1.0",
"psr/http-message": "^1.0"
},
"provide": {
"psr/http-message-implementation": "1.0"
},
"require-dev": {
"phpunit/phpunit": "^4.0",
"squizlabs/php_codesniffer": "^2.5"
},
"time": "2017-11-26T19:13:09+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-4": {
"Slim\\": "Slim"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Rob Allen",
"email": "rob@akrabat.com",
"homepage": "http://akrabat.com"
},
{
"name": "Josh Lockhart",
"email": "hello@joshlockhart.com",
"homepage": "https://joshlockhart.com"
},
{
"name": "Gabriel Manricks",
"email": "gmanricks@me.com",
"homepage": "http://gabrielmanricks.com"
},
{
"name": "Andrew Smith",
"email": "a.smith@silentworks.co.uk",
"homepage": "http://silentworks.co.uk"
}
],
"description": "Slim is a PHP micro framework that helps you quickly write simple yet powerful web applications and APIs",
"homepage": "https://slimframework.com",
"keywords": [
"api",
"framework",
"micro",
"router"
]
},
{
"name": "symfony/cache",
"version": "v3.4.8",
"version_normalized": "3.4.8.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/cache.git",
"reference": "13255ddd056e49f3154747943f8ee175d555d394"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/cache/zipball/13255ddd056e49f3154747943f8ee175d555d394",
"reference": "13255ddd056e49f3154747943f8ee175d555d394",
"shasum": ""
},
"require": {
"php": "^5.5.9|>=7.0.8",
"psr/cache": "~1.0",
"psr/log": "~1.0",
"psr/simple-cache": "^1.0",
"symfony/polyfill-apcu": "~1.1"
},
"conflict": {
"symfony/var-dumper": "<3.3"
},
"provide": {
"psr/cache-implementation": "1.0",
"psr/simple-cache-implementation": "1.0"
},
"require-dev": {
"cache/integration-tests": "dev-master",
"doctrine/cache": "~1.6",
"doctrine/dbal": "~2.4",
"predis/predis": "~1.0"
},
"time": "2018-04-02T14:35:16+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.4-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"Symfony\\Component\\Cache\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony Cache component with PSR-6, PSR-16, and tags",
"homepage": "https://symfony.com",
"keywords": [
"caching",
"psr6"
]
},
{
"name": "symfony/expression-language",
"version": "v3.4.8",
"version_normalized": "3.4.8.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/expression-language.git",
"reference": "867e4d1f5d4e52435a8ffff6b24fd6a801582241"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/expression-language/zipball/867e4d1f5d4e52435a8ffff6b24fd6a801582241",
"reference": "867e4d1f5d4e52435a8ffff6b24fd6a801582241",
"shasum": ""
},
"require": {
"php": "^5.5.9|>=7.0.8",
"symfony/cache": "~3.1|~4.0"
},
"time": "2018-01-03T07:37:34+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.4-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"Symfony\\Component\\ExpressionLanguage\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony ExpressionLanguage Component",
"homepage": "https://symfony.com"
},
{
"name": "symfony/polyfill-apcu",
"version": "v1.7.0",
"version_normalized": "1.7.0.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-apcu.git",
"reference": "e8ae2136ddb53dea314df56fcd88e318ab936c00"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-apcu/zipball/e8ae2136ddb53dea314df56fcd88e318ab936c00",
"reference": "e8ae2136ddb53dea314df56fcd88e318ab936c00",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"time": "2018-01-30T19:27:44+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.7-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"Symfony\\Polyfill\\Apcu\\": ""
},
"files": [
"bootstrap.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill backporting apcu_* functions to lower PHP versions",
"homepage": "https://symfony.com",
"keywords": [
"apcu",
"compatibility",
"polyfill",
"portable",
"shim"
]
}
]

View File

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

View File

@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2013 container-interop
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,148 @@
# Container Interoperability
[![Latest Stable Version](https://poser.pugx.org/container-interop/container-interop/v/stable.png)](https://packagist.org/packages/container-interop/container-interop)
[![Total Downloads](https://poser.pugx.org/container-interop/container-interop/downloads.svg)](https://packagist.org/packages/container-interop/container-interop)
## Deprecation warning!
Starting Feb. 13th 2017, container-interop is officially deprecated in favor of [PSR-11](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-11-container.md).
Container-interop has been the test-bed of PSR-11. From v1.2, container-interop directly extends PSR-11 interfaces.
Therefore, all containers implementing container-interop are now *de-facto* compatible with PSR-11.
- Projects implementing container-interop interfaces are encouraged to directly implement PSR-11 interfaces instead.
- Projects consuming container-interop interfaces are very strongly encouraged to directly type-hint on PSR-11 interfaces, in order to be compatible with PSR-11 containers that are not compatible with container-interop.
Regarding the delegate lookup feature, that is present in container-interop and not in PSR-11, the feature is actually a design pattern. It is therefore not deprecated. Documentation regarding this design pattern will be migrated from this repository into a separate website in the future.
## About
*container-interop* tries to identify and standardize features in *container* objects (service locators,
dependency injection containers, etc.) to achieve interoperability.
Through discussions and trials, we try to create a standard, made of common interfaces but also recommendations.
If PHP projects that provide container implementations begin to adopt these common standards, then PHP
applications and projects that use containers can depend on the common interfaces instead of specific
implementations. This facilitates a high-level of interoperability and flexibility that allows users to consume
*any* container implementation that can be adapted to these interfaces.
The work done in this project is not officially endorsed by the [PHP-FIG](http://www.php-fig.org/), but it is being
worked on by members of PHP-FIG and other good developers. We adhere to the spirit and ideals of PHP-FIG, and hope
this project will pave the way for one or more future PSRs.
## Installation
You can install this package through Composer:
```json
composer require container-interop/container-interop
```
The packages adheres to the [SemVer](http://semver.org/) specification, and there will be full backward compatibility
between minor versions.
## Standards
### Available
- [`ContainerInterface`](src/Interop/Container/ContainerInterface.php).
[Description](docs/ContainerInterface.md) [Meta Document](docs/ContainerInterface-meta.md).
Describes the interface of a container that exposes methods to read its entries.
- [*Delegate lookup feature*](docs/Delegate-lookup.md).
[Meta Document](docs/Delegate-lookup-meta.md).
Describes the ability for a container to delegate the lookup of its dependencies to a third-party container. This
feature lets several containers work together in a single application.
### Proposed
View open [request for comments](https://github.com/container-interop/container-interop/labels/RFC)
## Compatible projects
### Projects implementing `ContainerInterface`
- [Acclimate](https://github.com/jeremeamia/acclimate-container): Adapters for
Aura.Di, Laravel, Nette DI, Pimple, Symfony DI, ZF2 Service manager, ZF2
Dependency injection and any container using `ArrayAccess`
- [Aura.Di](https://github.com/auraphp/Aura.Di)
- [auryn-container-interop](https://github.com/elazar/auryn-container-interop)
- [Burlap](https://github.com/codeeverything/burlap)
- [Chernozem](https://github.com/pyrsmk/Chernozem)
- [Data Manager](https://github.com/chrismichaels84/data-manager)
- [Disco](https://github.com/bitexpert/disco)
- [InDI](https://github.com/idealogica/indi)
- [League/Container](http://container.thephpleague.com/)
- [Mouf](http://mouf-php.com)
- [Njasm Container](https://github.com/njasm/container)
- [PHP-DI](http://php-di.org)
- [Picotainer](https://github.com/thecodingmachine/picotainer)
- [PimpleInterop](https://github.com/moufmouf/pimple-interop)
- [Pimple3-ContainerInterop](https://github.com/Sam-Burns/pimple3-containerinterop) (using Pimple v3)
- [SitePoint Container](https://github.com/sitepoint/Container)
- [Thruster Container](https://github.com/ThrusterIO/container) (PHP7 only)
- [Ultra-Lite Container](https://github.com/ultra-lite/container)
- [Unbox](https://github.com/mindplay-dk/unbox)
- [XStatic](https://github.com/jeremeamia/xstatic)
- [Zend\ServiceManager](https://github.com/zendframework/zend-servicemanager)
- [Zit](https://github.com/inxilpro/Zit)
### Projects implementing the *delegate lookup* feature
- [Aura.Di](https://github.com/auraphp/Aura.Di)
- [Burlap](https://github.com/codeeverything/burlap)
- [Chernozem](https://github.com/pyrsmk/Chernozem)
- [InDI](https://github.com/idealogica/indi)
- [League/Container](http://container.thephpleague.com/)
- [Mouf](http://mouf-php.com)
- [Picotainer](https://github.com/thecodingmachine/picotainer)
- [PHP-DI](http://php-di.org)
- [PimpleInterop](https://github.com/moufmouf/pimple-interop)
- [Ultra-Lite Container](https://github.com/ultra-lite/container)
### Middlewares implementing `ContainerInterface`
- [Alias-Container](https://github.com/thecodingmachine/alias-container): add
aliases support to any container
- [Prefixer-Container](https://github.com/thecodingmachine/prefixer-container):
dynamically prefix identifiers
- [Lazy-Container](https://github.com/snapshotpl/lazy-container): lazy services
### Projects using `ContainerInterface`
The list below contains only a sample of all the projects consuming `ContainerInterface`. For a more complete list have a look [here](http://packanalyst.com/class?q=Interop%5CContainer%5CContainerInterface).
| | Downloads |
| --- | --- |
| [Adroit](https://github.com/bitexpert/adroit) | ![](https://img.shields.io/packagist/dt/bitexpert/adroit.svg) |
| [Behat](https://github.com/Behat/Behat/pull/974) | ![](https://img.shields.io/packagist/dt/behat/behat.svg) |
| [blast-facades](https://github.com/phpthinktank/blast-facades): Minimize complexity and represent dependencies as facades. | ![](https://img.shields.io/packagist/dt/blast/facades.svg) |
| [interop.silex.di](https://github.com/thecodingmachine/interop.silex.di): an extension to [Silex](http://silex.sensiolabs.org/) that adds support for any *container-interop* compatible container | ![](https://img.shields.io/packagist/dt/mouf/interop.silex.di.svg) |
| [mindplay/walkway](https://github.com/mindplay-dk/walkway): a modular request router | ![](https://img.shields.io/packagist/dt/mindplay/walkway.svg) |
| [mindplay/middleman](https://github.com/mindplay-dk/middleman): minimalist PSR-7 middleware dispatcher | ![](https://img.shields.io/packagist/dt/mindplay/middleman.svg) |
| [PHP-DI/Invoker](https://github.com/PHP-DI/Invoker): extensible and configurable invoker/dispatcher | ![](https://img.shields.io/packagist/dt/php-di/invoker.svg) |
| [Prophiler](https://github.com/fabfuel/prophiler) | ![](https://img.shields.io/packagist/dt/fabfuel/prophiler.svg) |
| [Silly](https://github.com/mnapoli/silly): CLI micro-framework | ![](https://img.shields.io/packagist/dt/mnapoli/silly.svg) |
| [Slim v3](https://github.com/slimphp/Slim) | ![](https://img.shields.io/packagist/dt/slim/slim.svg) |
| [Splash](http://mouf-php.com/packages/mouf/mvc.splash-common/version/8.0-dev/README.md) | ![](https://img.shields.io/packagist/dt/mouf/mvc.splash-common.svg) |
| [Woohoo Labs. Harmony](https://github.com/woohoolabs/harmony): a flexible micro-framework | ![](https://img.shields.io/packagist/dt/woohoolabs/harmony.svg) |
| [zend-expressive](https://github.com/zendframework/zend-expressive) | ![](https://img.shields.io/packagist/dt/zendframework/zend-expressive.svg) |
## Workflow
Everyone is welcome to join and contribute.
The general workflow looks like this:
1. Someone opens a discussion (GitHub issue) to suggest an interface
1. Feedback is gathered
1. The interface is added to a development branch
1. We release alpha versions so that the interface can be experimented with
1. Discussions and edits ensue until the interface is deemed stable by a general consensus
1. A new minor version of the package is released
We try to not break BC by creating new interfaces instead of editing existing ones.
While we currently work on interfaces, we are open to anything that might help towards interoperability, may that
be code, best practices, etc.

View File

@ -0,0 +1,15 @@
{
"name": "container-interop/container-interop",
"type": "library",
"description": "Promoting the interoperability of container objects (DIC, SL, etc.)",
"homepage": "https://github.com/container-interop/container-interop",
"license": "MIT",
"autoload": {
"psr-4": {
"Interop\\Container\\": "src/Interop/Container/"
}
},
"require": {
"psr/container": "^1.0"
}
}

View File

@ -0,0 +1,114 @@
# ContainerInterface Meta Document
## Introduction
This document describes the process and discussions that lead to the `ContainerInterface`.
Its goal is to explain the reasons behind each decision.
## Goal
The goal set by `ContainerInterface` is to standardize how frameworks and libraries make use of a
container to obtain objects and parameters.
By standardizing such a behavior, frameworks and libraries using the `ContainerInterface`
could work with any compatible container.
That would allow end users to choose their own container based on their own preferences.
It is important to distinguish the two usages of a container:
- configuring entries
- fetching entries
Most of the time, those two sides are not used by the same party.
While it is often end users who tend to configure entries, it is generally the framework that fetch
entries to build the application.
This is why this interface focuses only on how entries can be fetched from a container.
## Interface name
The interface name has been thoroughly discussed and was decided by a vote.
The list of options considered with their respective votes are:
- `ContainerInterface`: +8
- `ProviderInterface`: +2
- `LocatorInterface`: 0
- `ReadableContainerInterface`: -5
- `ServiceLocatorInterface`: -6
- `ObjectFactory`: -6
- `ObjectStore`: -8
- `ConsumerInterface`: -9
[Full results of the vote](https://github.com/container-interop/container-interop/wiki/%231-interface-name:-Vote)
The complete discussion can be read in [the issue #1](https://github.com/container-interop/container-interop/issues/1).
## Interface methods
The choice of which methods the interface would contain was made after a statistical analysis of existing containers.
The results of this analysis are available [in this document](https://gist.github.com/mnapoli/6159681).
The summary of the analysis showed that:
- all containers offer a method to get an entry by its id
- a large majority name such method `get()`
- for all containers, the `get()` method has 1 mandatory parameter of type string
- some containers have an optional additional argument for `get()`, but it doesn't have the same purpose between containers
- a large majority of the containers offer a method to test if it can return an entry by its id
- a majority name such method `has()`
- for all containers offering `has()`, the method has exactly 1 parameter of type string
- a large majority of the containers throw an exception rather than returning null when an entry is not found in `get()`
- a large majority of the containers don't implement `ArrayAccess`
The question of whether to include methods to define entries has been discussed in
[issue #1](https://github.com/container-interop/container-interop/issues/1).
It has been judged that such methods do not belong in the interface described here because it is out of its scope
(see the "Goal" section).
As a result, the `ContainerInterface` contains two methods:
- `get()`, returning anything, with one mandatory string parameter. Should throw an exception if the entry is not found.
- `has()`, returning a boolean, with one mandatory string parameter.
### Number of parameters in `get()` method
While `ContainerInterface` only defines one mandatory parameter in `get()`, it is not incompatible with
existing containers that have additional optional parameters. PHP allows an implementation to offer more parameters
as long as they are optional, because the implementation *does* satisfy the interface.
This issue has been discussed in [issue #6](https://github.com/container-interop/container-interop/issues/6).
### Type of the `$id` parameter
The type of the `$id` parameter in `get()` and `has()` has been discussed in
[issue #6](https://github.com/container-interop/container-interop/issues/6).
While `string` is used in all the containers that were analyzed, it was suggested that allowing
anything (such as objects) could allow containers to offer a more advanced query API.
An example given was to use the container as an object builder. The `$id` parameter would then be an
object that would describe how to create an instance.
The conclusion of the discussion was that this was beyond the scope of getting entries from a container without
knowing how the container provided them, and it was more fit for a factory.
## Contributors
Are listed here all people that contributed in the discussions or votes, by alphabetical order:
- [Amy Stephen](https://github.com/AmyStephen)
- [David Négrier](https://github.com/moufmouf)
- [Don Gilbert](https://github.com/dongilbert)
- [Jason Judge](https://github.com/judgej)
- [Jeremy Lindblom](https://github.com/jeremeamia)
- [Marco Pivetta](https://github.com/Ocramius)
- [Matthieu Napoli](https://github.com/mnapoli)
- [Paul M. Jones](https://github.com/pmjones)
- [Stephan Hochdörfer](https://github.com/shochdoerfer)
- [Taylor Otwell](https://github.com/taylorotwell)
## Relevant links
- [`ContainerInterface.php`](https://github.com/container-interop/container-interop/blob/master/src/Interop/Container/ContainerInterface.php)
- [List of all issues](https://github.com/container-interop/container-interop/issues?labels=ContainerInterface&milestone=&page=1&state=closed)
- [Vote for the interface name](https://github.com/container-interop/container-interop/wiki/%231-interface-name:-Vote)

View File

@ -0,0 +1,158 @@
Container interface
===================
This document describes a common interface for dependency injection containers.
The goal set by `ContainerInterface` is to standardize how frameworks and libraries make use of a
container to obtain objects and parameters (called *entries* in the rest of this document).
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD",
"SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be
interpreted as described in [RFC 2119][].
The word `implementor` in this document is to be interpreted as someone
implementing the `ContainerInterface` in a dependency injection-related library or framework.
Users of dependency injections containers (DIC) are referred to as `user`.
[RFC 2119]: http://tools.ietf.org/html/rfc2119
1. Specification
-----------------
### 1.1 Basics
- The `Interop\Container\ContainerInterface` exposes two methods : `get` and `has`.
- `get` takes one mandatory parameter: an entry identifier. It MUST be a string.
A call to `get` can return anything (a *mixed* value), or throws an exception if the identifier
is not known to the container. Two successive calls to `get` with the same
identifier SHOULD return the same value. However, depending on the `implementor`
design and/or `user` configuration, different values might be returned, so
`user` SHOULD NOT rely on getting the same value on 2 successive calls.
While `ContainerInterface` only defines one mandatory parameter in `get()`, implementations
MAY accept additional optional parameters.
- `has` takes one unique parameter: an entry identifier. It MUST return `true`
if an entry identifier is known to the container and `false` if it is not.
`has($id)` returning true does not mean that `get($id)` will not throw an exception.
It does however mean that `get($id)` will not throw a `NotFoundException`.
### 1.2 Exceptions
Exceptions directly thrown by the container MUST implement the
[`Interop\Container\Exception\ContainerException`](../src/Interop/Container/Exception/ContainerException.php).
A call to the `get` method with a non-existing id SHOULD throw a
[`Interop\Container\Exception\NotFoundException`](../src/Interop/Container/Exception/NotFoundException.php).
### 1.3 Additional features
This section describes additional features that MAY be added to a container. Containers are not
required to implement these features to respect the ContainerInterface.
#### 1.3.1 Delegate lookup feature
The goal of the *delegate lookup* feature is to allow several containers to share entries.
Containers implementing this feature can perform dependency lookups in other containers.
Containers implementing this feature will offer a greater lever of interoperability
with other containers. Implementation of this feature is therefore RECOMMENDED.
A container implementing this feature:
- MUST implement the `ContainerInterface`
- MUST provide a way to register a delegate container (using a constructor parameter, or a setter,
or any possible way). The delegate container MUST implement the `ContainerInterface`.
When a container is configured to use a delegate container for dependencies:
- Calls to the `get` method should only return an entry if the entry is part of the container.
If the entry is not part of the container, an exception should be thrown
(as requested by the `ContainerInterface`).
- Calls to the `has` method should only return `true` if the entry is part of the container.
If the entry is not part of the container, `false` should be returned.
- If the fetched entry has dependencies, **instead** of performing
the dependency lookup in the container, the lookup is performed on the *delegate container*.
Important! By default, the lookup SHOULD be performed on the delegate container **only**, not on the container itself.
It is however allowed for containers to provide exception cases for special entries, and a way to lookup
into the same container (or another container) instead of the delegate container.
2. Package
----------
The interfaces and classes described as well as relevant exception are provided as part of the
[container-interop/container-interop](https://packagist.org/packages/container-interop/container-interop) package.
3. `Interop\Container\ContainerInterface`
-----------------------------------------
```php
<?php
namespace Interop\Container;
use Interop\Container\Exception\ContainerException;
use Interop\Container\Exception\NotFoundException;
/**
* Describes the interface of a container that exposes methods to read its entries.
*/
interface ContainerInterface
{
/**
* Finds an entry of the container by its identifier and returns it.
*
* @param string $id Identifier of the entry to look for.
*
* @throws NotFoundException No entry was found for this identifier.
* @throws ContainerException Error while retrieving the entry.
*
* @return mixed Entry.
*/
public function get($id);
/**
* Returns true if the container can return an entry for the given identifier.
* Returns false otherwise.
*
* `has($id)` returning true does not mean that `get($id)` will not throw an exception.
* It does however mean that `get($id)` will not throw a `NotFoundException`.
*
* @param string $id Identifier of the entry to look for.
*
* @return boolean
*/
public function has($id);
}
```
4. `Interop\Container\Exception\ContainerException`
---------------------------------------------------
```php
<?php
namespace Interop\Container\Exception;
/**
* Base interface representing a generic exception in a container.
*/
interface ContainerException
{
}
```
5. `Interop\Container\Exception\NotFoundException`
---------------------------------------------------
```php
<?php
namespace Interop\Container\Exception;
/**
* No entry was found in the container.
*/
interface NotFoundException extends ContainerException
{
}
```

View File

@ -0,0 +1,259 @@
Delegate lookup feature Meta Document
=====================================
1. Summary
----------
This document describes the *delegate lookup feature*.
Containers are not required to implement this feature to respect the `ContainerInterface`.
However, containers implementing this feature will offer a greater lever of interoperability
with other containers, allowing multiple containers to share entries in the same application.
Implementation of this feature is therefore recommanded.
2. Why Bother?
--------------
The [`ContainerInterface`](../src/Interop/Container/ContainerInterface.php) ([meta doc](ContainerInterface.md))
standardizes how frameworks and libraries make use of a container to obtain objects and parameters.
By standardizing such a behavior, frameworks and libraries relying on the `ContainerInterface`
could work with any compatible container.
That would allow end users to choose their own container based on their own preferences.
The `ContainerInterface` is also enough if we want to have several containers side-by-side in the same
application. For instance, this is what the [CompositeContainer](https://github.com/jeremeamia/acclimate-container/blob/master/src/CompositeContainer.php)
class of [Acclimate](https://github.com/jeremeamia/acclimate-container) is designed for:
![Side by side containers](images/side_by_side_containers.png)
However, an instance in container 1 cannot reference an instance in container 2.
It would be better if an instance of container 1 could reference an instance in container 2,
and the opposite should be true.
![Interoperating containers](images/interoperating_containers.png)
In the sample above, entry 1 in container 1 is referencing entry 3 in container 2.
3. Scope
--------
### 3.1 Goals
The goal of the *delegate lookup* feature is to allow several containers to share entries.
4. Approaches
-------------
### 4.1 Chosen Approach
Containers implementing this feature can perform dependency lookups in other containers.
A container implementing this feature:
- must implement the `ContainerInterface`
- must provide a way to register a *delegate container* (using a constructor parameter, or a setter, or any
possible way). The *delegate container* must implement the `ContainerInterface`.
When a *delegate container* is configured on a container:
- Calls to the `get` method should only return an entry if the entry is part of the container.
If the entry is not part of the container, an exception should be thrown (as required in the `ContainerInterface`).
- Calls to the `has` method should only return *true* if the entry is part of the container.
If the entry is not part of the container, *false* should be returned.
- Finally, the important part: if the entry we are fetching has dependencies,
**instead** of perfoming the dependency lookup in the container, the lookup is performed on the *delegate container*.
Important! By default, the lookup should be performed on the delegate container **only**, not on the container itself.
It is however allowed for containers to provide exception cases for special entries, and a way to lookup into
the same container (or another container) instead of the delegate container.
### 4.2 Typical usage
The *delegate container* will usually be a composite container. A composite container is a container that
contains several other containers. When performing a lookup on a composite container, the inner containers are
queried until one container returns an entry.
An inner container implementing the *delegate lookup feature* will return entries it contains, but if these
entries have dependencies, the dependencies lookup calls will be performed on the composite container, giving
a chance to all containers to answer.
Interestingly enough, the order in which containers are added in the composite container matters. Indeed,
the first containers to be added in the composite container can "override" the entries of containers with
lower priority.
![Containers priority](images/priority.png)
In the example above, "container 2" contains a controller "myController" and the controller is referencing an
"entityManager" entry. "Container 1" contains also an entry named "entityManager".
Without the *delegate lookup* feature, when requesting the "myController" instance to container 2, it would take
in charge the instanciation of both entries.
However, using the *delegate lookup* feature, here is what happens when we ask the composite container for the
"myController" instance:
- The composite container asks container 1 if if contains the "myController" instance. The answer is no.
- The composite container asks container 2 if if contains the "myController" instance. The answer is yes.
- The composite container performs a `get` call on container 2 for the "myController" instance.
- Container 2 sees that "myController" has a dependency on "entityManager".
- Container 2 delegates the lookup of "entityManager" to the composite container.
- The composite container asks container 1 if if contains the "entityManager" instance. The answer is yes.
- The composite container performs a `get` call on container 1 for the "entityManager" instance.
In the end, we get a controller instanciated by container 2 that references an entityManager instanciated
by container 1.
### 4.3 Alternative: the fallback strategy
The first proposed approach we tried was to perform all the lookups in the "local" container,
and if a lookup fails in the container, to use the delegate container. In this scenario, the
delegate container is used in "fallback" mode.
This strategy has been described in @moufmouf blog post: http://mouf-php.com/container-interop-whats-next (solution 1).
It was also discussed [here](https://github.com/container-interop/container-interop/pull/8#issuecomment-33570697) and
[here](https://github.com/container-interop/container-interop/pull/20#issuecomment-56599631).
Problems with this strategy:
- Heavy problem regarding infinite loops
- Unable to overload a container entry with the delegate container entry
### 4.4 Alternative: force implementing an interface
The first proposed approach was to develop a `ParentAwareContainerInterface` interface.
It was proposed here: https://github.com/container-interop/container-interop/pull/8
The interface would have had the behaviour of the delegate lookup feature but would have forced the addition of
a `setParentContainter` method:
```php
interface ParentAwareContainerInterface extends ReadableContainerInterface {
/**
* Sets the parent container associated to that container. This container will call
* the parent container to fetch dependencies.
*
* @param ContainerInterface $container
*/
public function setParentContainer(ContainerInterface $container);
}
```
The interface idea was first questioned by @Ocramius [here](https://github.com/container-interop/container-interop/pull/8#issuecomment-51721777).
@Ocramius expressed the idea that an interface should not contain setters, otherwise, it is forcing implementation
details on the class implementing the interface.
Then @mnapoli made a proposal for a "convention" [here](https://github.com/container-interop/container-interop/pull/8#issuecomment-51841079),
this idea was further discussed until all participants in the discussion agreed to remove the interface idea
and replace it with a "standard" feature.
**Pros:**
If we had had an interface, we could have delegated the registration of the delegate/composite container to the
the delegate/composite container itself.
For instance:
```php
$containerA = new ContainerA();
$containerB = new ContainerB();
$compositeContainer = new CompositeContainer([$containerA, $containerB]);
// The call to 'setParentContainer' is delegated to the CompositeContainer
// It is not the responsibility of the user anymore.
class CompositeContainer {
...
public function __construct($containers) {
foreach ($containers as $container) {
if ($container instanceof ParentAwareContainerInterface) {
$container->setParentContainer($this);
}
}
...
}
}
```
**Cons:**
Cons have been extensively discussed [here](https://github.com/container-interop/container-interop/pull/8#issuecomment-51721777).
Basically, forcing a setter into an interface is a bad idea. Setters are similar to constructor arguments,
and it's a bad idea to standardize a constructor: how the delegate container is configured into a container is an implementation detail. This outweights the benefits of the interface.
### 4.4 Alternative: no exception case for delegate lookups
Originally, the proposed wording for delegate lookup calls was:
> Important! The lookup MUST be performed on the delegate container **only**, not on the container itself.
This was later replaced by:
> Important! By default, the lookup SHOULD be performed on the delegate container **only**, not on the container itself.
>
> It is however allowed for containers to provide exception cases for special entries, and a way to lookup
> into the same container (or another container) instead of the delegate container.
Exception cases have been allowed to avoid breaking dependencies with some services that must be provided
by the container (on @njasm proposal). This was proposed here: https://github.com/container-interop/container-interop/pull/20#issuecomment-56597235
### 4.5 Alternative: having one of the containers act as the composite container
In real-life scenarios, we usually have a big framework (Symfony 2, Zend Framework 2, etc...) and we want to
add another DI container to this container. Most of the time, the "big" framework will be responsible for
creating the controller's instances, using it's own DI container. Until *container-interop* is fully adopted,
the "big" framework will not be aware of the existence of a composite container that it should use instead
of its own container.
For this real-life use cases, @mnapoli and @moufmouf proposed to extend the "big" framework's DI container
to make it act as a composite container.
This has been discussed [here](https://github.com/container-interop/container-interop/pull/8#issuecomment-40367194)
and [here](http://mouf-php.com/container-interop-whats-next#solution4).
This was implemented in Symfony 2 using:
- [interop.symfony.di](https://github.com/thecodingmachine/interop.symfony.di/tree/v0.1.0)
- [framework interop](https://github.com/mnapoli/framework-interop/)
This was implemented in Silex using:
- [interop.silex.di](https://github.com/thecodingmachine/interop.silex.di)
Having a container act as the composite container is not part of the delegate lookup standard because it is
simply a temporary design pattern used to make existing frameworks that do not support yet ContainerInterop
play nice with other DI containers.
5. Implementations
------------------
The following projects already implement the delegate lookup feature:
- [Mouf](http://mouf-php.com), through the [`setDelegateLookupContainer` method](https://github.com/thecodingmachine/mouf/blob/2.0/src/Mouf/MoufManager.php#L2120)
- [PHP-DI](http://php-di.org/), through the [`$wrapperContainer` parameter of the constructor](https://github.com/mnapoli/PHP-DI/blob/master/src/DI/Container.php#L72)
- [pimple-interop](https://github.com/moufmouf/pimple-interop), through the [`$container` parameter of the constructor](https://github.com/moufmouf/pimple-interop/blob/master/src/Interop/Container/Pimple/PimpleInterop.php#L62)
6. People
---------
Are listed here all people that contributed in the discussions, by alphabetical order:
- [Alexandru Pătrănescu](https://github.com/drealecs)
- [Ben Peachey](https://github.com/potherca)
- [David Négrier](https://github.com/moufmouf)
- [Jeremy Lindblom](https://github.com/jeremeamia)
- [Marco Pivetta](https://github.com/Ocramius)
- [Matthieu Napoli](https://github.com/mnapoli)
- [Nelson J Morais](https://github.com/njasm)
- [Phil Sturgeon](https://github.com/philsturgeon)
- [Stephan Hochdörfer](https://github.com/shochdoerfer)
7. Relevant Links
-----------------
_**Note:** Order descending chronologically._
- [Pull request on the delegate lookup feature](https://github.com/container-interop/container-interop/pull/20)
- [Pull request on the interface idea](https://github.com/container-interop/container-interop/pull/8)
- [Original article exposing the delegate lookup idea along many others](http://mouf-php.com/container-interop-whats-next)

View File

@ -0,0 +1,60 @@
Delegate lookup feature
=======================
This document describes a standard for dependency injection containers.
The goal set by the *delegate lookup* feature is to allow several containers to share entries.
Containers implementing this feature can perform dependency lookups in other containers.
Containers implementing this feature will offer a greater lever of interoperability
with other containers. Implementation of this feature is therefore RECOMMENDED.
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD",
"SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be
interpreted as described in [RFC 2119][].
The word `implementor` in this document is to be interpreted as someone
implementing the delegate lookup feature in a dependency injection-related library or framework.
Users of dependency injections containers (DIC) are referred to as `user`.
[RFC 2119]: http://tools.ietf.org/html/rfc2119
1. Vocabulary
-------------
In a dependency injection container, the container is used to fetch entries.
Entries can have dependencies on other entries. Usually, these other entries are fetched by the container.
The *delegate lookup* feature is the ability for a container to fetch dependencies in
another container. In the rest of the document, the word "container" will reference the container
implemented by the implementor. The word "delegate container" will reference the container we are
fetching the dependencies from.
2. Specification
----------------
A container implementing the *delegate lookup* feature:
- MUST implement the [`ContainerInterface`](ContainerInterface.md)
- MUST provide a way to register a delegate container (using a constructor parameter, or a setter,
or any possible way). The delegate container MUST implement the [`ContainerInterface`](ContainerInterface.md).
When a container is configured to use a delegate container for dependencies:
- Calls to the `get` method should only return an entry if the entry is part of the container.
If the entry is not part of the container, an exception should be thrown
(as requested by the [`ContainerInterface`](ContainerInterface.md)).
- Calls to the `has` method should only return `true` if the entry is part of the container.
If the entry is not part of the container, `false` should be returned.
- If the fetched entry has dependencies, **instead** of performing
the dependency lookup in the container, the lookup is performed on the *delegate container*.
Important: By default, the dependency lookups SHOULD be performed on the delegate container **only**, not on the container itself.
It is however allowed for containers to provide exception cases for special entries, and a way to lookup
into the same container (or another container) instead of the delegate container.
3. Package / Interface
----------------------
This feature is not tied to any code, interface or package.

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -0,0 +1,15 @@
<?php
/**
* @license http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
*/
namespace Interop\Container;
use Psr\Container\ContainerInterface as PsrContainerInterface;
/**
* Describes the interface of a container that exposes methods to read its entries.
*/
interface ContainerInterface extends PsrContainerInterface
{
}

View File

@ -0,0 +1,15 @@
<?php
/**
* @license http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
*/
namespace Interop\Container\Exception;
use Psr\Container\ContainerExceptionInterface as PsrContainerException;
/**
* Base interface representing a generic exception in a container.
*/
interface ContainerException extends PsrContainerException
{
}

View File

@ -0,0 +1,15 @@
<?php
/**
* @license http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
*/
namespace Interop\Container\Exception;
use Psr\Container\NotFoundExceptionInterface as PsrNotFoundException;
/**
* No entry was found in the container.
*/
interface NotFoundException extends ContainerException, PsrNotFoundException
{
}

View File

@ -0,0 +1,5 @@
/vendor/
.idea/
# ignore lock file since we have no extra dependencies
composer.lock

View File

@ -0,0 +1 @@
assume_php=false

View File

@ -0,0 +1,20 @@
sudo: false
language: php
php:
- 5.4
- 5.5
- 5.6
- 7.0
- 7.1
- 7.2
- hhvm
script:
- ./vendor/bin/phpunit
before_install:
- travis_retry composer self-update
install:
- composer install

View File

@ -0,0 +1,126 @@
<?hh // decl
namespace FastRoute {
class BadRouteException extends \LogicException {
}
interface RouteParser {
public function parse(string $route): array<array>;
}
class RouteCollector {
public function __construct(RouteParser $routeParser, DataGenerator $dataGenerator);
public function addRoute(mixed $httpMethod, string $route, mixed $handler): void;
public function getData(): array;
}
class Route {
public function __construct(string $httpMethod, mixed $handler, string $regex, array $variables);
public function matches(string $str): bool;
}
interface DataGenerator {
public function addRoute(string $httpMethod, array $routeData, mixed $handler);
public function getData(): array;
}
interface Dispatcher {
const int NOT_FOUND = 0;
const int FOUND = 1;
const int METHOD_NOT_ALLOWED = 2;
public function dispatch(string $httpMethod, string $uri): array;
}
function simpleDispatcher(
(function(RouteCollector): void) $routeDefinitionCallback,
shape(
?'routeParser' => classname<RouteParser>,
?'dataGenerator' => classname<DataGenerator>,
?'dispatcher' => classname<Dispatcher>,
?'routeCollector' => classname<RouteCollector>,
) $options = shape()): Dispatcher;
function cachedDispatcher(
(function(RouteCollector): void) $routeDefinitionCallback,
shape(
?'routeParser' => classname<RouteParser>,
?'dataGenerator' => classname<DataGenerator>,
?'dispatcher' => classname<Dispatcher>,
?'routeCollector' => classname<RouteCollector>,
?'cacheDisabled' => bool,
?'cacheFile' => string,
) $options = shape()): Dispatcher;
}
namespace FastRoute\DataGenerator {
abstract class RegexBasedAbstract implements \FastRoute\DataGenerator {
protected abstract function getApproxChunkSize();
protected abstract function processChunk($regexToRoutesMap);
public function addRoute(string $httpMethod, array $routeData, mixed $handler): void;
public function getData(): array;
}
class CharCountBased extends RegexBasedAbstract {
protected function getApproxChunkSize(): int;
protected function processChunk(array<string, string> $regexToRoutesMap): array<string, mixed>;
}
class GroupCountBased extends RegexBasedAbstract {
protected function getApproxChunkSize(): int;
protected function processChunk(array<string, string> $regexToRoutesMap): array<string, mixed>;
}
class GroupPosBased extends RegexBasedAbstract {
protected function getApproxChunkSize(): int;
protected function processChunk(array<string, string> $regexToRoutesMap): array<string, mixed>;
}
class MarkBased extends RegexBasedAbstract {
protected function getApproxChunkSize(): int;
protected function processChunk(array<string, string> $regexToRoutesMap): array<string, mixed>;
}
}
namespace FastRoute\Dispatcher {
abstract class RegexBasedAbstract implements \FastRoute\Dispatcher {
protected abstract function dispatchVariableRoute(array<array> $routeData, string $uri): array;
public function dispatch(string $httpMethod, string $uri): array;
}
class GroupPosBased extends RegexBasedAbstract {
public function __construct(array $data);
protected function dispatchVariableRoute(array<array> $routeData, string $uri): array;
}
class GroupCountBased extends RegexBasedAbstract {
public function __construct(array $data);
protected function dispatchVariableRoute(array<array> $routeData, string $uri): array;
}
class CharCountBased extends RegexBasedAbstract {
public function __construct(array $data);
protected function dispatchVariableRoute(array<array> $routeData, string $uri): array;
}
class MarkBased extends RegexBasedAbstract {
public function __construct(array $data);
protected function dispatchVariableRoute(array<array> $routeData, string $uri): array;
}
}
namespace FastRoute\RouteParser {
class Std implements \FastRoute\RouteParser {
const string VARIABLE_REGEX = <<<'REGEX'
\{
\s* ([a-zA-Z][a-zA-Z0-9_]*) \s*
(?:
: \s* ([^{}]*(?:\{(?-1)\}[^{}]*)*)
)?
\}
REGEX;
const string DEFAULT_DISPATCH_REGEX = '[^/]+';
public function parse(string $route): array<array>;
}
}

View File

@ -0,0 +1,31 @@
Copyright (c) 2013 by Nikita Popov.
Some rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* The names of the contributors may not be used to endorse or
promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -0,0 +1,313 @@
FastRoute - Fast request router for PHP
=======================================
This library provides a fast implementation of a regular expression based router. [Blog post explaining how the
implementation works and why it is fast.][blog_post]
Install
-------
To install with composer:
```sh
composer require nikic/fast-route
```
Requires PHP 5.4 or newer.
Usage
-----
Here's a basic usage example:
```php
<?php
require '/path/to/vendor/autoload.php';
$dispatcher = FastRoute\simpleDispatcher(function(FastRoute\RouteCollector $r) {
$r->addRoute('GET', '/users', 'get_all_users_handler');
// {id} must be a number (\d+)
$r->addRoute('GET', '/user/{id:\d+}', 'get_user_handler');
// The /{title} suffix is optional
$r->addRoute('GET', '/articles/{id:\d+}[/{title}]', 'get_article_handler');
});
// Fetch method and URI from somewhere
$httpMethod = $_SERVER['REQUEST_METHOD'];
$uri = $_SERVER['REQUEST_URI'];
// Strip query string (?foo=bar) and decode URI
if (false !== $pos = strpos($uri, '?')) {
$uri = substr($uri, 0, $pos);
}
$uri = rawurldecode($uri);
$routeInfo = $dispatcher->dispatch($httpMethod, $uri);
switch ($routeInfo[0]) {
case FastRoute\Dispatcher::NOT_FOUND:
// ... 404 Not Found
break;
case FastRoute\Dispatcher::METHOD_NOT_ALLOWED:
$allowedMethods = $routeInfo[1];
// ... 405 Method Not Allowed
break;
case FastRoute\Dispatcher::FOUND:
$handler = $routeInfo[1];
$vars = $routeInfo[2];
// ... call $handler with $vars
break;
}
```
### Defining routes
The routes are defined by calling the `FastRoute\simpleDispatcher()` function, which accepts
a callable taking a `FastRoute\RouteCollector` instance. The routes are added by calling
`addRoute()` on the collector instance:
```php
$r->addRoute($method, $routePattern, $handler);
```
The `$method` is an uppercase HTTP method string for which a certain route should match. It
is possible to specify multiple valid methods using an array:
```php
// These two calls
$r->addRoute('GET', '/test', 'handler');
$r->addRoute('POST', '/test', 'handler');
// Are equivalent to this one call
$r->addRoute(['GET', 'POST'], '/test', 'handler');
```
By default the `$routePattern` uses a syntax where `{foo}` specifies a placeholder with name `foo`
and matching the regex `[^/]+`. To adjust the pattern the placeholder matches, you can specify
a custom pattern by writing `{bar:[0-9]+}`. Some examples:
```php
// Matches /user/42, but not /user/xyz
$r->addRoute('GET', '/user/{id:\d+}', 'handler');
// Matches /user/foobar, but not /user/foo/bar
$r->addRoute('GET', '/user/{name}', 'handler');
// Matches /user/foo/bar as well
$r->addRoute('GET', '/user/{name:.+}', 'handler');
```
Custom patterns for route placeholders cannot use capturing groups. For example `{lang:(en|de)}`
is not a valid placeholder, because `()` is a capturing group. Instead you can use either
`{lang:en|de}` or `{lang:(?:en|de)}`.
Furthermore parts of the route enclosed in `[...]` are considered optional, so that `/foo[bar]`
will match both `/foo` and `/foobar`. Optional parts are only supported in a trailing position,
not in the middle of a route.
```php
// This route
$r->addRoute('GET', '/user/{id:\d+}[/{name}]', 'handler');
// Is equivalent to these two routes
$r->addRoute('GET', '/user/{id:\d+}', 'handler');
$r->addRoute('GET', '/user/{id:\d+}/{name}', 'handler');
// Multiple nested optional parts are possible as well
$r->addRoute('GET', '/user[/{id:\d+}[/{name}]]', 'handler');
// This route is NOT valid, because optional parts can only occur at the end
$r->addRoute('GET', '/user[/{id:\d+}]/{name}', 'handler');
```
The `$handler` parameter does not necessarily have to be a callback, it could also be a controller
class name or any other kind of data you wish to associate with the route. FastRoute only tells you
which handler corresponds to your URI, how you interpret it is up to you.
#### Shorcut methods for common request methods
For the `GET`, `POST`, `PUT`, `PATCH`, `DELETE` and `HEAD` request methods shortcut methods are available. For example:
```php
$r->get('/get-route', 'get_handler');
$r->post('/post-route', 'post_handler');
```
Is equivalent to:
```php
$r->addRoute('GET', '/get-route', 'get_handler');
$r->addRoute('POST', '/post-route', 'post_handler');
```
#### Route Groups
Additionally, you can specify routes inside of a group. All routes defined inside a group will have a common prefix.
For example, defining your routes as:
```php
$r->addGroup('/admin', function (RouteCollector $r) {
$r->addRoute('GET', '/do-something', 'handler');
$r->addRoute('GET', '/do-another-thing', 'handler');
$r->addRoute('GET', '/do-something-else', 'handler');
});
```
Will have the same result as:
```php
$r->addRoute('GET', '/admin/do-something', 'handler');
$r->addRoute('GET', '/admin/do-another-thing', 'handler');
$r->addRoute('GET', '/admin/do-something-else', 'handler');
```
Nested groups are also supported, in which case the prefixes of all the nested groups are combined.
### Caching
The reason `simpleDispatcher` accepts a callback for defining the routes is to allow seamless
caching. By using `cachedDispatcher` instead of `simpleDispatcher` you can cache the generated
routing data and construct the dispatcher from the cached information:
```php
<?php
$dispatcher = FastRoute\cachedDispatcher(function(FastRoute\RouteCollector $r) {
$r->addRoute('GET', '/user/{name}/{id:[0-9]+}', 'handler0');
$r->addRoute('GET', '/user/{id:[0-9]+}', 'handler1');
$r->addRoute('GET', '/user/{name}', 'handler2');
}, [
'cacheFile' => __DIR__ . '/route.cache', /* required */
'cacheDisabled' => IS_DEBUG_ENABLED, /* optional, enabled by default */
]);
```
The second parameter to the function is an options array, which can be used to specify the cache
file location, among other things.
### Dispatching a URI
A URI is dispatched by calling the `dispatch()` method of the created dispatcher. This method
accepts the HTTP method and a URI. Getting those two bits of information (and normalizing them
appropriately) is your job - this library is not bound to the PHP web SAPIs.
The `dispatch()` method returns an array whose first element contains a status code. It is one
of `Dispatcher::NOT_FOUND`, `Dispatcher::METHOD_NOT_ALLOWED` and `Dispatcher::FOUND`. For the
method not allowed status the second array element contains a list of HTTP methods allowed for
the supplied URI. For example:
[FastRoute\Dispatcher::METHOD_NOT_ALLOWED, ['GET', 'POST']]
> **NOTE:** The HTTP specification requires that a `405 Method Not Allowed` response include the
`Allow:` header to detail available methods for the requested resource. Applications using FastRoute
should use the second array element to add this header when relaying a 405 response.
For the found status the second array element is the handler that was associated with the route
and the third array element is a dictionary of placeholder names to their values. For example:
/* Routing against GET /user/nikic/42 */
[FastRoute\Dispatcher::FOUND, 'handler0', ['name' => 'nikic', 'id' => '42']]
### Overriding the route parser and dispatcher
The routing process makes use of three components: A route parser, a data generator and a
dispatcher. The three components adhere to the following interfaces:
```php
<?php
namespace FastRoute;
interface RouteParser {
public function parse($route);
}
interface DataGenerator {
public function addRoute($httpMethod, $routeData, $handler);
public function getData();
}
interface Dispatcher {
const NOT_FOUND = 0, FOUND = 1, METHOD_NOT_ALLOWED = 2;
public function dispatch($httpMethod, $uri);
}
```
The route parser takes a route pattern string and converts it into an array of route infos, where
each route info is again an array of it's parts. The structure is best understood using an example:
/* The route /user/{id:\d+}[/{name}] converts to the following array: */
[
[
'/user/',
['id', '\d+'],
],
[
'/user/',
['id', '\d+'],
'/',
['name', '[^/]+'],
],
]
This array can then be passed to the `addRoute()` method of a data generator. After all routes have
been added the `getData()` of the generator is invoked, which returns all the routing data required
by the dispatcher. The format of this data is not further specified - it is tightly coupled to
the corresponding dispatcher.
The dispatcher accepts the routing data via a constructor and provides a `dispatch()` method, which
you're already familiar with.
The route parser can be overwritten individually (to make use of some different pattern syntax),
however the data generator and dispatcher should always be changed as a pair, as the output from
the former is tightly coupled to the input of the latter. The reason the generator and the
dispatcher are separate is that only the latter is needed when using caching (as the output of
the former is what is being cached.)
When using the `simpleDispatcher` / `cachedDispatcher` functions from above the override happens
through the options array:
```php
<?php
$dispatcher = FastRoute\simpleDispatcher(function(FastRoute\RouteCollector $r) {
/* ... */
}, [
'routeParser' => 'FastRoute\\RouteParser\\Std',
'dataGenerator' => 'FastRoute\\DataGenerator\\GroupCountBased',
'dispatcher' => 'FastRoute\\Dispatcher\\GroupCountBased',
]);
```
The above options array corresponds to the defaults. By replacing `GroupCountBased` by
`GroupPosBased` you could switch to a different dispatching strategy.
### A Note on HEAD Requests
The HTTP spec requires servers to [support both GET and HEAD methods][2616-511]:
> The methods GET and HEAD MUST be supported by all general-purpose servers
To avoid forcing users to manually register HEAD routes for each resource we fallback to matching an
available GET route for a given resource. The PHP web SAPI transparently removes the entity body
from HEAD responses so this behavior has no effect on the vast majority of users.
However, implementers using FastRoute outside the web SAPI environment (e.g. a custom server) MUST
NOT send entity bodies generated in response to HEAD requests. If you are a non-SAPI user this is
*your responsibility*; FastRoute has no purview to prevent you from breaking HTTP in such cases.
Finally, note that applications MAY always specify their own HEAD method route for a given
resource to bypass this behavior entirely.
### Credits
This library is based on a router that [Levi Morrison][levi] implemented for the Aerys server.
A large number of tests, as well as HTTP compliance considerations, were provided by [Daniel Lowrey][rdlowrey].
[2616-511]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.1 "RFC 2616 Section 5.1.1"
[blog_post]: http://nikic.github.io/2014/02/18/Fast-request-routing-using-regular-expressions.html
[levi]: https://github.com/morrisonlevi
[rdlowrey]: https://github.com/rdlowrey

View File

@ -0,0 +1,24 @@
{
"name": "nikic/fast-route",
"description": "Fast request router for PHP",
"keywords": ["routing", "router"],
"license": "BSD-3-Clause",
"authors": [
{
"name": "Nikita Popov",
"email": "nikic@php.net"
}
],
"autoload": {
"psr-4": {
"FastRoute\\": "src/"
},
"files": ["src/functions.php"]
},
"require": {
"php": ">=5.4.0"
},
"require-dev": {
"phpunit/phpunit": "^4.8.35|~5.7"
}
}

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
syntaxCheck="false"
bootstrap="test/bootstrap.php"
>
<testsuites>
<testsuite name="FastRoute Tests">
<directory>./test/</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory>./src/</directory>
</whitelist>
</filter>
</phpunit>

View File

@ -0,0 +1,28 @@
<?xml version="1.0"?>
<psalm
name="Example Psalm config with recommended defaults"
stopOnFirstError="false"
useDocblockTypes="true"
totallyTyped="false"
requireVoidReturnType="false"
>
<projectFiles>
<directory name="src" />
</projectFiles>
<issueHandlers>
<LessSpecificReturnType errorLevel="info" />
<!-- level 3 issues - slightly lazy code writing, but provably low false-negatives -->
<DeprecatedMethod errorLevel="info" />
<MissingClosureReturnType errorLevel="info" />
<MissingReturnType errorLevel="info" />
<MissingPropertyType errorLevel="info" />
<InvalidDocblock errorLevel="info" />
<MisplacedRequiredParam errorLevel="info" />
<PropertyNotSetInConstructor errorLevel="info" />
<MissingConstructor errorLevel="info" />
</issueHandlers>
</psalm>

View File

@ -0,0 +1,7 @@
<?php
namespace FastRoute;
class BadRouteException extends \LogicException
{
}

View File

@ -0,0 +1,26 @@
<?php
namespace FastRoute;
interface DataGenerator
{
/**
* Adds a route to the data generator. The route data uses the
* same format that is returned by RouterParser::parser().
*
* The handler doesn't necessarily need to be a callable, it
* can be arbitrary data that will be returned when the route
* matches.
*
* @param string $httpMethod
* @param array $routeData
* @param mixed $handler
*/
public function addRoute($httpMethod, $routeData, $handler);
/**
* Returns dispatcher data in some unspecified format, which
* depends on the used method of dispatch.
*/
public function getData();
}

View File

@ -0,0 +1,31 @@
<?php
namespace FastRoute\DataGenerator;
class CharCountBased extends RegexBasedAbstract
{
protected function getApproxChunkSize()
{
return 30;
}
protected function processChunk($regexToRoutesMap)
{
$routeMap = [];
$regexes = [];
$suffixLen = 0;
$suffix = '';
$count = count($regexToRoutesMap);
foreach ($regexToRoutesMap as $regex => $route) {
$suffixLen++;
$suffix .= "\t";
$regexes[] = '(?:' . $regex . '/(\t{' . $suffixLen . '})\t{' . ($count - $suffixLen) . '})';
$routeMap[$suffix] = [$route->handler, $route->variables];
}
$regex = '~^(?|' . implode('|', $regexes) . ')$~';
return ['regex' => $regex, 'suffix' => '/' . $suffix, 'routeMap' => $routeMap];
}
}

View File

@ -0,0 +1,30 @@
<?php
namespace FastRoute\DataGenerator;
class GroupCountBased extends RegexBasedAbstract
{
protected function getApproxChunkSize()
{
return 10;
}
protected function processChunk($regexToRoutesMap)
{
$routeMap = [];
$regexes = [];
$numGroups = 0;
foreach ($regexToRoutesMap as $regex => $route) {
$numVariables = count($route->variables);
$numGroups = max($numGroups, $numVariables);
$regexes[] = $regex . str_repeat('()', $numGroups - $numVariables);
$routeMap[$numGroups + 1] = [$route->handler, $route->variables];
++$numGroups;
}
$regex = '~^(?|' . implode('|', $regexes) . ')$~';
return ['regex' => $regex, 'routeMap' => $routeMap];
}
}

View File

@ -0,0 +1,27 @@
<?php
namespace FastRoute\DataGenerator;
class GroupPosBased extends RegexBasedAbstract
{
protected function getApproxChunkSize()
{
return 10;
}
protected function processChunk($regexToRoutesMap)
{
$routeMap = [];
$regexes = [];
$offset = 1;
foreach ($regexToRoutesMap as $regex => $route) {
$regexes[] = $regex;
$routeMap[$offset] = [$route->handler, $route->variables];
$offset += count($route->variables);
}
$regex = '~^(?:' . implode('|', $regexes) . ')$~';
return ['regex' => $regex, 'routeMap' => $routeMap];
}
}

View File

@ -0,0 +1,27 @@
<?php
namespace FastRoute\DataGenerator;
class MarkBased extends RegexBasedAbstract
{
protected function getApproxChunkSize()
{
return 30;
}
protected function processChunk($regexToRoutesMap)
{
$routeMap = [];
$regexes = [];
$markName = 'a';
foreach ($regexToRoutesMap as $regex => $route) {
$regexes[] = $regex . '(*MARK:' . $markName . ')';
$routeMap[$markName] = [$route->handler, $route->variables];
++$markName;
}
$regex = '~^(?|' . implode('|', $regexes) . ')$~';
return ['regex' => $regex, 'routeMap' => $routeMap];
}
}

View File

@ -0,0 +1,186 @@
<?php
namespace FastRoute\DataGenerator;
use FastRoute\BadRouteException;
use FastRoute\DataGenerator;
use FastRoute\Route;
abstract class RegexBasedAbstract implements DataGenerator
{
/** @var mixed[][] */
protected $staticRoutes = [];
/** @var Route[][] */
protected $methodToRegexToRoutesMap = [];
/**
* @return int
*/
abstract protected function getApproxChunkSize();
/**
* @return mixed[]
*/
abstract protected function processChunk($regexToRoutesMap);
public function addRoute($httpMethod, $routeData, $handler)
{
if ($this->isStaticRoute($routeData)) {
$this->addStaticRoute($httpMethod, $routeData, $handler);
} else {
$this->addVariableRoute($httpMethod, $routeData, $handler);
}
}
/**
* @return mixed[]
*/
public function getData()
{
if (empty($this->methodToRegexToRoutesMap)) {
return [$this->staticRoutes, []];
}
return [$this->staticRoutes, $this->generateVariableRouteData()];
}
/**
* @return mixed[]
*/
private function generateVariableRouteData()
{
$data = [];
foreach ($this->methodToRegexToRoutesMap as $method => $regexToRoutesMap) {
$chunkSize = $this->computeChunkSize(count($regexToRoutesMap));
$chunks = array_chunk($regexToRoutesMap, $chunkSize, true);
$data[$method] = array_map([$this, 'processChunk'], $chunks);
}
return $data;
}
/**
* @param int
* @return int
*/
private function computeChunkSize($count)
{
$numParts = max(1, round($count / $this->getApproxChunkSize()));
return (int) ceil($count / $numParts);
}
/**
* @param mixed[]
* @return bool
*/
private function isStaticRoute($routeData)
{
return count($routeData) === 1 && is_string($routeData[0]);
}
private function addStaticRoute($httpMethod, $routeData, $handler)
{
$routeStr = $routeData[0];
if (isset($this->staticRoutes[$httpMethod][$routeStr])) {
throw new BadRouteException(sprintf(
'Cannot register two routes matching "%s" for method "%s"',
$routeStr, $httpMethod
));
}
if (isset($this->methodToRegexToRoutesMap[$httpMethod])) {
foreach ($this->methodToRegexToRoutesMap[$httpMethod] as $route) {
if ($route->matches($routeStr)) {
throw new BadRouteException(sprintf(
'Static route "%s" is shadowed by previously defined variable route "%s" for method "%s"',
$routeStr, $route->regex, $httpMethod
));
}
}
}
$this->staticRoutes[$httpMethod][$routeStr] = $handler;
}
private function addVariableRoute($httpMethod, $routeData, $handler)
{
list($regex, $variables) = $this->buildRegexForRoute($routeData);
if (isset($this->methodToRegexToRoutesMap[$httpMethod][$regex])) {
throw new BadRouteException(sprintf(
'Cannot register two routes matching "%s" for method "%s"',
$regex, $httpMethod
));
}
$this->methodToRegexToRoutesMap[$httpMethod][$regex] = new Route(
$httpMethod, $handler, $regex, $variables
);
}
/**
* @param mixed[]
* @return mixed[]
*/
private function buildRegexForRoute($routeData)
{
$regex = '';
$variables = [];
foreach ($routeData as $part) {
if (is_string($part)) {
$regex .= preg_quote($part, '~');
continue;
}
list($varName, $regexPart) = $part;
if (isset($variables[$varName])) {
throw new BadRouteException(sprintf(
'Cannot use the same placeholder "%s" twice', $varName
));
}
if ($this->regexHasCapturingGroups($regexPart)) {
throw new BadRouteException(sprintf(
'Regex "%s" for parameter "%s" contains a capturing group',
$regexPart, $varName
));
}
$variables[$varName] = $varName;
$regex .= '(' . $regexPart . ')';
}
return [$regex, $variables];
}
/**
* @param string
* @return bool
*/
private function regexHasCapturingGroups($regex)
{
if (false === strpos($regex, '(')) {
// Needs to have at least a ( to contain a capturing group
return false;
}
// Semi-accurate detection for capturing groups
return (bool) preg_match(
'~
(?:
\(\?\(
| \[ [^\]\\\\]* (?: \\\\ . [^\]\\\\]* )* \]
| \\\\ .
) (*SKIP)(*FAIL) |
\(
(?!
\? (?! <(?![!=]) | P< | \' )
| \*
)
~x',
$regex
);
}
}

View File

@ -0,0 +1,26 @@
<?php
namespace FastRoute;
interface Dispatcher
{
const NOT_FOUND = 0;
const FOUND = 1;
const METHOD_NOT_ALLOWED = 2;
/**
* Dispatches against the provided HTTP method verb and URI.
*
* Returns array with one of the following formats:
*
* [self::NOT_FOUND]
* [self::METHOD_NOT_ALLOWED, ['GET', 'OTHER_ALLOWED_METHODS']]
* [self::FOUND, $handler, ['varName' => 'value', ...]]
*
* @param string $httpMethod
* @param string $uri
*
* @return array
*/
public function dispatch($httpMethod, $uri);
}

View File

@ -0,0 +1,31 @@
<?php
namespace FastRoute\Dispatcher;
class CharCountBased extends RegexBasedAbstract
{
public function __construct($data)
{
list($this->staticRouteMap, $this->variableRouteData) = $data;
}
protected function dispatchVariableRoute($routeData, $uri)
{
foreach ($routeData as $data) {
if (!preg_match($data['regex'], $uri . $data['suffix'], $matches)) {
continue;
}
list($handler, $varNames) = $data['routeMap'][end($matches)];
$vars = [];
$i = 0;
foreach ($varNames as $varName) {
$vars[$varName] = $matches[++$i];
}
return [self::FOUND, $handler, $vars];
}
return [self::NOT_FOUND];
}
}

View File

@ -0,0 +1,31 @@
<?php
namespace FastRoute\Dispatcher;
class GroupCountBased extends RegexBasedAbstract
{
public function __construct($data)
{
list($this->staticRouteMap, $this->variableRouteData) = $data;
}
protected function dispatchVariableRoute($routeData, $uri)
{
foreach ($routeData as $data) {
if (!preg_match($data['regex'], $uri, $matches)) {
continue;
}
list($handler, $varNames) = $data['routeMap'][count($matches)];
$vars = [];
$i = 0;
foreach ($varNames as $varName) {
$vars[$varName] = $matches[++$i];
}
return [self::FOUND, $handler, $vars];
}
return [self::NOT_FOUND];
}
}

View File

@ -0,0 +1,33 @@
<?php
namespace FastRoute\Dispatcher;
class GroupPosBased extends RegexBasedAbstract
{
public function __construct($data)
{
list($this->staticRouteMap, $this->variableRouteData) = $data;
}
protected function dispatchVariableRoute($routeData, $uri)
{
foreach ($routeData as $data) {
if (!preg_match($data['regex'], $uri, $matches)) {
continue;
}
// find first non-empty match
for ($i = 1; '' === $matches[$i]; ++$i);
list($handler, $varNames) = $data['routeMap'][$i];
$vars = [];
foreach ($varNames as $varName) {
$vars[$varName] = $matches[$i++];
}
return [self::FOUND, $handler, $vars];
}
return [self::NOT_FOUND];
}
}

View File

@ -0,0 +1,31 @@
<?php
namespace FastRoute\Dispatcher;
class MarkBased extends RegexBasedAbstract
{
public function __construct($data)
{
list($this->staticRouteMap, $this->variableRouteData) = $data;
}
protected function dispatchVariableRoute($routeData, $uri)
{
foreach ($routeData as $data) {
if (!preg_match($data['regex'], $uri, $matches)) {
continue;
}
list($handler, $varNames) = $data['routeMap'][$matches['MARK']];
$vars = [];
$i = 0;
foreach ($varNames as $varName) {
$vars[$varName] = $matches[++$i];
}
return [self::FOUND, $handler, $vars];
}
return [self::NOT_FOUND];
}
}

View File

@ -0,0 +1,88 @@
<?php
namespace FastRoute\Dispatcher;
use FastRoute\Dispatcher;
abstract class RegexBasedAbstract implements Dispatcher
{
/** @var mixed[][] */
protected $staticRouteMap = [];
/** @var mixed[] */
protected $variableRouteData = [];
/**
* @return mixed[]
*/
abstract protected function dispatchVariableRoute($routeData, $uri);
public function dispatch($httpMethod, $uri)
{
if (isset($this->staticRouteMap[$httpMethod][$uri])) {
$handler = $this->staticRouteMap[$httpMethod][$uri];
return [self::FOUND, $handler, []];
}
$varRouteData = $this->variableRouteData;
if (isset($varRouteData[$httpMethod])) {
$result = $this->dispatchVariableRoute($varRouteData[$httpMethod], $uri);
if ($result[0] === self::FOUND) {
return $result;
}
}
// For HEAD requests, attempt fallback to GET
if ($httpMethod === 'HEAD') {
if (isset($this->staticRouteMap['GET'][$uri])) {
$handler = $this->staticRouteMap['GET'][$uri];
return [self::FOUND, $handler, []];
}
if (isset($varRouteData['GET'])) {
$result = $this->dispatchVariableRoute($varRouteData['GET'], $uri);
if ($result[0] === self::FOUND) {
return $result;
}
}
}
// If nothing else matches, try fallback routes
if (isset($this->staticRouteMap['*'][$uri])) {
$handler = $this->staticRouteMap['*'][$uri];
return [self::FOUND, $handler, []];
}
if (isset($varRouteData['*'])) {
$result = $this->dispatchVariableRoute($varRouteData['*'], $uri);
if ($result[0] === self::FOUND) {
return $result;
}
}
// Find allowed methods for this URI by matching against all other HTTP methods as well
$allowedMethods = [];
foreach ($this->staticRouteMap as $method => $uriMap) {
if ($method !== $httpMethod && isset($uriMap[$uri])) {
$allowedMethods[] = $method;
}
}
foreach ($varRouteData as $method => $routeData) {
if ($method === $httpMethod) {
continue;
}
$result = $this->dispatchVariableRoute($routeData, $uri);
if ($result[0] === self::FOUND) {
$allowedMethods[] = $method;
}
}
// If there are no allowed methods the route simply does not exist
if ($allowedMethods) {
return [self::METHOD_NOT_ALLOWED, $allowedMethods];
}
return [self::NOT_FOUND];
}
}

View File

@ -0,0 +1,47 @@
<?php
namespace FastRoute;
class Route
{
/** @var string */
public $httpMethod;
/** @var string */
public $regex;
/** @var array */
public $variables;
/** @var mixed */
public $handler;
/**
* Constructs a route (value object).
*
* @param string $httpMethod
* @param mixed $handler
* @param string $regex
* @param array $variables
*/
public function __construct($httpMethod, $handler, $regex, $variables)
{
$this->httpMethod = $httpMethod;
$this->handler = $handler;
$this->regex = $regex;
$this->variables = $variables;
}
/**
* Tests whether this route matches the given string.
*
* @param string $str
*
* @return bool
*/
public function matches($str)
{
$regex = '~^' . $this->regex . '$~';
return (bool) preg_match($regex, $str);
}
}

View File

@ -0,0 +1,152 @@
<?php
namespace FastRoute;
class RouteCollector
{
/** @var RouteParser */
protected $routeParser;
/** @var DataGenerator */
protected $dataGenerator;
/** @var string */
protected $currentGroupPrefix;
/**
* Constructs a route collector.
*
* @param RouteParser $routeParser
* @param DataGenerator $dataGenerator
*/
public function __construct(RouteParser $routeParser, DataGenerator $dataGenerator)
{
$this->routeParser = $routeParser;
$this->dataGenerator = $dataGenerator;
$this->currentGroupPrefix = '';
}
/**
* Adds a route to the collection.
*
* The syntax used in the $route string depends on the used route parser.
*
* @param string|string[] $httpMethod
* @param string $route
* @param mixed $handler
*/
public function addRoute($httpMethod, $route, $handler)
{
$route = $this->currentGroupPrefix . $route;
$routeDatas = $this->routeParser->parse($route);
foreach ((array) $httpMethod as $method) {
foreach ($routeDatas as $routeData) {
$this->dataGenerator->addRoute($method, $routeData, $handler);
}
}
}
/**
* Create a route group with a common prefix.
*
* All routes created in the passed callback will have the given group prefix prepended.
*
* @param string $prefix
* @param callable $callback
*/
public function addGroup($prefix, callable $callback)
{
$previousGroupPrefix = $this->currentGroupPrefix;
$this->currentGroupPrefix = $previousGroupPrefix . $prefix;
$callback($this);
$this->currentGroupPrefix = $previousGroupPrefix;
}
/**
* Adds a GET route to the collection
*
* This is simply an alias of $this->addRoute('GET', $route, $handler)
*
* @param string $route
* @param mixed $handler
*/
public function get($route, $handler)
{
$this->addRoute('GET', $route, $handler);
}
/**
* Adds a POST route to the collection
*
* This is simply an alias of $this->addRoute('POST', $route, $handler)
*
* @param string $route
* @param mixed $handler
*/
public function post($route, $handler)
{
$this->addRoute('POST', $route, $handler);
}
/**
* Adds a PUT route to the collection
*
* This is simply an alias of $this->addRoute('PUT', $route, $handler)
*
* @param string $route
* @param mixed $handler
*/
public function put($route, $handler)
{
$this->addRoute('PUT', $route, $handler);
}
/**
* Adds a DELETE route to the collection
*
* This is simply an alias of $this->addRoute('DELETE', $route, $handler)
*
* @param string $route
* @param mixed $handler
*/
public function delete($route, $handler)
{
$this->addRoute('DELETE', $route, $handler);
}
/**
* Adds a PATCH route to the collection
*
* This is simply an alias of $this->addRoute('PATCH', $route, $handler)
*
* @param string $route
* @param mixed $handler
*/
public function patch($route, $handler)
{
$this->addRoute('PATCH', $route, $handler);
}
/**
* Adds a HEAD route to the collection
*
* This is simply an alias of $this->addRoute('HEAD', $route, $handler)
*
* @param string $route
* @param mixed $handler
*/
public function head($route, $handler)
{
$this->addRoute('HEAD', $route, $handler);
}
/**
* Returns the collected route data, as provided by the data generator.
*
* @return array
*/
public function getData()
{
return $this->dataGenerator->getData();
}
}

View File

@ -0,0 +1,37 @@
<?php
namespace FastRoute;
interface RouteParser
{
/**
* Parses a route string into multiple route data arrays.
*
* The expected output is defined using an example:
*
* For the route string "/fixedRoutePart/{varName}[/moreFixed/{varName2:\d+}]", if {varName} is interpreted as
* a placeholder and [...] is interpreted as an optional route part, the expected result is:
*
* [
* // first route: without optional part
* [
* "/fixedRoutePart/",
* ["varName", "[^/]+"],
* ],
* // second route: with optional part
* [
* "/fixedRoutePart/",
* ["varName", "[^/]+"],
* "/moreFixed/",
* ["varName2", [0-9]+"],
* ],
* ]
*
* Here one route string was converted into two route data arrays.
*
* @param string $route Route string to parse
*
* @return mixed[][] Array of route data arrays
*/
public function parse($route);
}

View File

@ -0,0 +1,87 @@
<?php
namespace FastRoute\RouteParser;
use FastRoute\BadRouteException;
use FastRoute\RouteParser;
/**
* Parses route strings of the following form:
*
* "/user/{name}[/{id:[0-9]+}]"
*/
class Std implements RouteParser
{
const VARIABLE_REGEX = <<<'REGEX'
\{
\s* ([a-zA-Z_][a-zA-Z0-9_-]*) \s*
(?:
: \s* ([^{}]*(?:\{(?-1)\}[^{}]*)*)
)?
\}
REGEX;
const DEFAULT_DISPATCH_REGEX = '[^/]+';
public function parse($route)
{
$routeWithoutClosingOptionals = rtrim($route, ']');
$numOptionals = strlen($route) - strlen($routeWithoutClosingOptionals);
// Split on [ while skipping placeholders
$segments = preg_split('~' . self::VARIABLE_REGEX . '(*SKIP)(*F) | \[~x', $routeWithoutClosingOptionals);
if ($numOptionals !== count($segments) - 1) {
// If there are any ] in the middle of the route, throw a more specific error message
if (preg_match('~' . self::VARIABLE_REGEX . '(*SKIP)(*F) | \]~x', $routeWithoutClosingOptionals)) {
throw new BadRouteException('Optional segments can only occur at the end of a route');
}
throw new BadRouteException("Number of opening '[' and closing ']' does not match");
}
$currentRoute = '';
$routeDatas = [];
foreach ($segments as $n => $segment) {
if ($segment === '' && $n !== 0) {
throw new BadRouteException('Empty optional part');
}
$currentRoute .= $segment;
$routeDatas[] = $this->parsePlaceholders($currentRoute);
}
return $routeDatas;
}
/**
* Parses a route string that does not contain optional segments.
*
* @param string
* @return mixed[]
*/
private function parsePlaceholders($route)
{
if (!preg_match_all(
'~' . self::VARIABLE_REGEX . '~x', $route, $matches,
PREG_OFFSET_CAPTURE | PREG_SET_ORDER
)) {
return [$route];
}
$offset = 0;
$routeData = [];
foreach ($matches as $set) {
if ($set[0][1] > $offset) {
$routeData[] = substr($route, $offset, $set[0][1] - $offset);
}
$routeData[] = [
$set[1][0],
isset($set[2]) ? trim($set[2][0]) : self::DEFAULT_DISPATCH_REGEX
];
$offset = $set[0][1] + strlen($set[0][0]);
}
if ($offset !== strlen($route)) {
$routeData[] = substr($route, $offset);
}
return $routeData;
}
}

View File

@ -0,0 +1,12 @@
<?php
namespace FastRoute;
require __DIR__ . '/functions.php';
spl_autoload_register(function ($class) {
if (strpos($class, 'FastRoute\\') === 0) {
$name = substr($class, strlen('FastRoute'));
require __DIR__ . strtr($name, '\\', DIRECTORY_SEPARATOR) . '.php';
}
});

View File

@ -0,0 +1,74 @@
<?php
namespace FastRoute;
if (!function_exists('FastRoute\simpleDispatcher')) {
/**
* @param callable $routeDefinitionCallback
* @param array $options
*
* @return Dispatcher
*/
function simpleDispatcher(callable $routeDefinitionCallback, array $options = [])
{
$options += [
'routeParser' => 'FastRoute\\RouteParser\\Std',
'dataGenerator' => 'FastRoute\\DataGenerator\\GroupCountBased',
'dispatcher' => 'FastRoute\\Dispatcher\\GroupCountBased',
'routeCollector' => 'FastRoute\\RouteCollector',
];
/** @var RouteCollector $routeCollector */
$routeCollector = new $options['routeCollector'](
new $options['routeParser'], new $options['dataGenerator']
);
$routeDefinitionCallback($routeCollector);
return new $options['dispatcher']($routeCollector->getData());
}
/**
* @param callable $routeDefinitionCallback
* @param array $options
*
* @return Dispatcher
*/
function cachedDispatcher(callable $routeDefinitionCallback, array $options = [])
{
$options += [
'routeParser' => 'FastRoute\\RouteParser\\Std',
'dataGenerator' => 'FastRoute\\DataGenerator\\GroupCountBased',
'dispatcher' => 'FastRoute\\Dispatcher\\GroupCountBased',
'routeCollector' => 'FastRoute\\RouteCollector',
'cacheDisabled' => false,
];
if (!isset($options['cacheFile'])) {
throw new \LogicException('Must specify "cacheFile" option');
}
if (!$options['cacheDisabled'] && file_exists($options['cacheFile'])) {
$dispatchData = require $options['cacheFile'];
if (!is_array($dispatchData)) {
throw new \RuntimeException('Invalid cache file "' . $options['cacheFile'] . '"');
}
return new $options['dispatcher']($dispatchData);
}
$routeCollector = new $options['routeCollector'](
new $options['routeParser'], new $options['dataGenerator']
);
$routeDefinitionCallback($routeCollector);
/** @var RouteCollector $routeCollector */
$dispatchData = $routeCollector->getData();
if (!$options['cacheDisabled']) {
file_put_contents(
$options['cacheFile'],
'<?php return ' . var_export($dispatchData, true) . ';'
);
}
return new $options['dispatcher']($dispatchData);
}
}

View File

@ -0,0 +1,16 @@
<?php
namespace FastRoute\Dispatcher;
class CharCountBasedTest extends DispatcherTest
{
protected function getDispatcherClass()
{
return 'FastRoute\\Dispatcher\\CharCountBased';
}
protected function getDataGeneratorClass()
{
return 'FastRoute\\DataGenerator\\CharCountBased';
}
}

View File

@ -0,0 +1,581 @@
<?php
namespace FastRoute\Dispatcher;
use FastRoute\RouteCollector;
use PHPUnit\Framework\TestCase;
abstract class DispatcherTest extends TestCase
{
/**
* Delegate dispatcher selection to child test classes
*/
abstract protected function getDispatcherClass();
/**
* Delegate dataGenerator selection to child test classes
*/
abstract protected function getDataGeneratorClass();
/**
* Set appropriate options for the specific Dispatcher class we're testing
*/
private function generateDispatcherOptions()
{
return [
'dataGenerator' => $this->getDataGeneratorClass(),
'dispatcher' => $this->getDispatcherClass()
];
}
/**
* @dataProvider provideFoundDispatchCases
*/
public function testFoundDispatches($method, $uri, $callback, $handler, $argDict)
{
$dispatcher = \FastRoute\simpleDispatcher($callback, $this->generateDispatcherOptions());
$info = $dispatcher->dispatch($method, $uri);
$this->assertSame($dispatcher::FOUND, $info[0]);
$this->assertSame($handler, $info[1]);
$this->assertSame($argDict, $info[2]);
}
/**
* @dataProvider provideNotFoundDispatchCases
*/
public function testNotFoundDispatches($method, $uri, $callback)
{
$dispatcher = \FastRoute\simpleDispatcher($callback, $this->generateDispatcherOptions());
$routeInfo = $dispatcher->dispatch($method, $uri);
$this->assertArrayNotHasKey(1, $routeInfo,
'NOT_FOUND result must only contain a single element in the returned info array'
);
$this->assertSame($dispatcher::NOT_FOUND, $routeInfo[0]);
}
/**
* @dataProvider provideMethodNotAllowedDispatchCases
*/
public function testMethodNotAllowedDispatches($method, $uri, $callback, $availableMethods)
{
$dispatcher = \FastRoute\simpleDispatcher($callback, $this->generateDispatcherOptions());
$routeInfo = $dispatcher->dispatch($method, $uri);
$this->assertArrayHasKey(1, $routeInfo,
'METHOD_NOT_ALLOWED result must return an array of allowed methods at index 1'
);
list($routedStatus, $methodArray) = $dispatcher->dispatch($method, $uri);
$this->assertSame($dispatcher::METHOD_NOT_ALLOWED, $routedStatus);
$this->assertSame($availableMethods, $methodArray);
}
/**
* @expectedException \FastRoute\BadRouteException
* @expectedExceptionMessage Cannot use the same placeholder "test" twice
*/
public function testDuplicateVariableNameError()
{
\FastRoute\simpleDispatcher(function (RouteCollector $r) {
$r->addRoute('GET', '/foo/{test}/{test:\d+}', 'handler0');
}, $this->generateDispatcherOptions());
}
/**
* @expectedException \FastRoute\BadRouteException
* @expectedExceptionMessage Cannot register two routes matching "/user/([^/]+)" for method "GET"
*/
public function testDuplicateVariableRoute()
{
\FastRoute\simpleDispatcher(function (RouteCollector $r) {
$r->addRoute('GET', '/user/{id}', 'handler0'); // oops, forgot \d+ restriction ;)
$r->addRoute('GET', '/user/{name}', 'handler1');
}, $this->generateDispatcherOptions());
}
/**
* @expectedException \FastRoute\BadRouteException
* @expectedExceptionMessage Cannot register two routes matching "/user" for method "GET"
*/
public function testDuplicateStaticRoute()
{
\FastRoute\simpleDispatcher(function (RouteCollector $r) {
$r->addRoute('GET', '/user', 'handler0');
$r->addRoute('GET', '/user', 'handler1');
}, $this->generateDispatcherOptions());
}
/**
* @expectedException \FastRoute\BadRouteException
* @expectedExceptionMessage Static route "/user/nikic" is shadowed by previously defined variable route "/user/([^/]+)" for method "GET"
*/
public function testShadowedStaticRoute()
{
\FastRoute\simpleDispatcher(function (RouteCollector $r) {
$r->addRoute('GET', '/user/{name}', 'handler0');
$r->addRoute('GET', '/user/nikic', 'handler1');
}, $this->generateDispatcherOptions());
}
/**
* @expectedException \FastRoute\BadRouteException
* @expectedExceptionMessage Regex "(en|de)" for parameter "lang" contains a capturing group
*/
public function testCapturing()
{
\FastRoute\simpleDispatcher(function (RouteCollector $r) {
$r->addRoute('GET', '/{lang:(en|de)}', 'handler0');
}, $this->generateDispatcherOptions());
}
public function provideFoundDispatchCases()
{
$cases = [];
// 0 -------------------------------------------------------------------------------------->
$callback = function (RouteCollector $r) {
$r->addRoute('GET', '/resource/123/456', 'handler0');
};
$method = 'GET';
$uri = '/resource/123/456';
$handler = 'handler0';
$argDict = [];
$cases[] = [$method, $uri, $callback, $handler, $argDict];
// 1 -------------------------------------------------------------------------------------->
$callback = function (RouteCollector $r) {
$r->addRoute('GET', '/handler0', 'handler0');
$r->addRoute('GET', '/handler1', 'handler1');
$r->addRoute('GET', '/handler2', 'handler2');
};
$method = 'GET';
$uri = '/handler2';
$handler = 'handler2';
$argDict = [];
$cases[] = [$method, $uri, $callback, $handler, $argDict];
// 2 -------------------------------------------------------------------------------------->
$callback = function (RouteCollector $r) {
$r->addRoute('GET', '/user/{name}/{id:[0-9]+}', 'handler0');
$r->addRoute('GET', '/user/{id:[0-9]+}', 'handler1');
$r->addRoute('GET', '/user/{name}', 'handler2');
};
$method = 'GET';
$uri = '/user/rdlowrey';
$handler = 'handler2';
$argDict = ['name' => 'rdlowrey'];
$cases[] = [$method, $uri, $callback, $handler, $argDict];
// 3 -------------------------------------------------------------------------------------->
// reuse $callback from #2
$method = 'GET';
$uri = '/user/12345';
$handler = 'handler1';
$argDict = ['id' => '12345'];
$cases[] = [$method, $uri, $callback, $handler, $argDict];
// 4 -------------------------------------------------------------------------------------->
// reuse $callback from #3
$method = 'GET';
$uri = '/user/NaN';
$handler = 'handler2';
$argDict = ['name' => 'NaN'];
$cases[] = [$method, $uri, $callback, $handler, $argDict];
// 5 -------------------------------------------------------------------------------------->
// reuse $callback from #4
$method = 'GET';
$uri = '/user/rdlowrey/12345';
$handler = 'handler0';
$argDict = ['name' => 'rdlowrey', 'id' => '12345'];
$cases[] = [$method, $uri, $callback, $handler, $argDict];
// 6 -------------------------------------------------------------------------------------->
$callback = function (RouteCollector $r) {
$r->addRoute('GET', '/user/{id:[0-9]+}', 'handler0');
$r->addRoute('GET', '/user/12345/extension', 'handler1');
$r->addRoute('GET', '/user/{id:[0-9]+}.{extension}', 'handler2');
};
$method = 'GET';
$uri = '/user/12345.svg';
$handler = 'handler2';
$argDict = ['id' => '12345', 'extension' => 'svg'];
$cases[] = [$method, $uri, $callback, $handler, $argDict];
// 7 ----- Test GET method fallback on HEAD route miss ------------------------------------>
$callback = function (RouteCollector $r) {
$r->addRoute('GET', '/user/{name}', 'handler0');
$r->addRoute('GET', '/user/{name}/{id:[0-9]+}', 'handler1');
$r->addRoute('GET', '/static0', 'handler2');
$r->addRoute('GET', '/static1', 'handler3');
$r->addRoute('HEAD', '/static1', 'handler4');
};
$method = 'HEAD';
$uri = '/user/rdlowrey';
$handler = 'handler0';
$argDict = ['name' => 'rdlowrey'];
$cases[] = [$method, $uri, $callback, $handler, $argDict];
// 8 ----- Test GET method fallback on HEAD route miss ------------------------------------>
// reuse $callback from #7
$method = 'HEAD';
$uri = '/user/rdlowrey/1234';
$handler = 'handler1';
$argDict = ['name' => 'rdlowrey', 'id' => '1234'];
$cases[] = [$method, $uri, $callback, $handler, $argDict];
// 9 ----- Test GET method fallback on HEAD route miss ------------------------------------>
// reuse $callback from #8
$method = 'HEAD';
$uri = '/static0';
$handler = 'handler2';
$argDict = [];
$cases[] = [$method, $uri, $callback, $handler, $argDict];
// 10 ---- Test existing HEAD route used if available (no fallback) ----------------------->
// reuse $callback from #9
$method = 'HEAD';
$uri = '/static1';
$handler = 'handler4';
$argDict = [];
$cases[] = [$method, $uri, $callback, $handler, $argDict];
// 11 ---- More specified routes are not shadowed by less specific of another method ------>
$callback = function (RouteCollector $r) {
$r->addRoute('GET', '/user/{name}', 'handler0');
$r->addRoute('POST', '/user/{name:[a-z]+}', 'handler1');
};
$method = 'POST';
$uri = '/user/rdlowrey';
$handler = 'handler1';
$argDict = ['name' => 'rdlowrey'];
$cases[] = [$method, $uri, $callback, $handler, $argDict];
// 12 ---- Handler of more specific routes is used, if it occurs first -------------------->
$callback = function (RouteCollector $r) {
$r->addRoute('GET', '/user/{name}', 'handler0');
$r->addRoute('POST', '/user/{name:[a-z]+}', 'handler1');
$r->addRoute('POST', '/user/{name}', 'handler2');
};
$method = 'POST';
$uri = '/user/rdlowrey';
$handler = 'handler1';
$argDict = ['name' => 'rdlowrey'];
$cases[] = [$method, $uri, $callback, $handler, $argDict];
// 13 ---- Route with constant suffix ----------------------------------------------------->
$callback = function (RouteCollector $r) {
$r->addRoute('GET', '/user/{name}', 'handler0');
$r->addRoute('GET', '/user/{name}/edit', 'handler1');
};
$method = 'GET';
$uri = '/user/rdlowrey/edit';
$handler = 'handler1';
$argDict = ['name' => 'rdlowrey'];
$cases[] = [$method, $uri, $callback, $handler, $argDict];
// 14 ---- Handle multiple methods with the same handler ---------------------------------->
$callback = function (RouteCollector $r) {
$r->addRoute(['GET', 'POST'], '/user', 'handlerGetPost');
$r->addRoute(['DELETE'], '/user', 'handlerDelete');
$r->addRoute([], '/user', 'handlerNone');
};
$argDict = [];
$cases[] = ['GET', '/user', $callback, 'handlerGetPost', $argDict];
$cases[] = ['POST', '/user', $callback, 'handlerGetPost', $argDict];
$cases[] = ['DELETE', '/user', $callback, 'handlerDelete', $argDict];
// 17 ----
$callback = function (RouteCollector $r) {
$r->addRoute('POST', '/user.json', 'handler0');
$r->addRoute('GET', '/{entity}.json', 'handler1');
};
$cases[] = ['GET', '/user.json', $callback, 'handler1', ['entity' => 'user']];
// 18 ----
$callback = function (RouteCollector $r) {
$r->addRoute('GET', '', 'handler0');
};
$cases[] = ['GET', '', $callback, 'handler0', []];
// 19 ----
$callback = function (RouteCollector $r) {
$r->addRoute('HEAD', '/a/{foo}', 'handler0');
$r->addRoute('GET', '/b/{foo}', 'handler1');
};
$cases[] = ['HEAD', '/b/bar', $callback, 'handler1', ['foo' => 'bar']];
// 20 ----
$callback = function (RouteCollector $r) {
$r->addRoute('HEAD', '/a', 'handler0');
$r->addRoute('GET', '/b', 'handler1');
};
$cases[] = ['HEAD', '/b', $callback, 'handler1', []];
// 21 ----
$callback = function (RouteCollector $r) {
$r->addRoute('GET', '/foo', 'handler0');
$r->addRoute('HEAD', '/{bar}', 'handler1');
};
$cases[] = ['HEAD', '/foo', $callback, 'handler1', ['bar' => 'foo']];
// 22 ----
$callback = function (RouteCollector $r) {
$r->addRoute('*', '/user', 'handler0');
$r->addRoute('*', '/{user}', 'handler1');
$r->addRoute('GET', '/user', 'handler2');
};
$cases[] = ['GET', '/user', $callback, 'handler2', []];
// 23 ----
$callback = function (RouteCollector $r) {
$r->addRoute('*', '/user', 'handler0');
$r->addRoute('GET', '/user', 'handler1');
};
$cases[] = ['POST', '/user', $callback, 'handler0', []];
// 24 ----
$cases[] = ['HEAD', '/user', $callback, 'handler1', []];
// 25 ----
$callback = function (RouteCollector $r) {
$r->addRoute('GET', '/{bar}', 'handler0');
$r->addRoute('*', '/foo', 'handler1');
};
$cases[] = ['GET', '/foo', $callback, 'handler0', ['bar' => 'foo']];
// 26 ----
$callback = function(RouteCollector $r) {
$r->addRoute('GET', '/user', 'handler0');
$r->addRoute('*', '/{foo:.*}', 'handler1');
};
$cases[] = ['POST', '/bar', $callback, 'handler1', ['foo' => 'bar']];
// x -------------------------------------------------------------------------------------->
return $cases;
}
public function provideNotFoundDispatchCases()
{
$cases = [];
// 0 -------------------------------------------------------------------------------------->
$callback = function (RouteCollector $r) {
$r->addRoute('GET', '/resource/123/456', 'handler0');
};
$method = 'GET';
$uri = '/not-found';
$cases[] = [$method, $uri, $callback];
// 1 -------------------------------------------------------------------------------------->
// reuse callback from #0
$method = 'POST';
$uri = '/not-found';
$cases[] = [$method, $uri, $callback];
// 2 -------------------------------------------------------------------------------------->
// reuse callback from #1
$method = 'PUT';
$uri = '/not-found';
$cases[] = [$method, $uri, $callback];
// 3 -------------------------------------------------------------------------------------->
$callback = function (RouteCollector $r) {
$r->addRoute('GET', '/handler0', 'handler0');
$r->addRoute('GET', '/handler1', 'handler1');
$r->addRoute('GET', '/handler2', 'handler2');
};
$method = 'GET';
$uri = '/not-found';
$cases[] = [$method, $uri, $callback];
// 4 -------------------------------------------------------------------------------------->
$callback = function (RouteCollector $r) {
$r->addRoute('GET', '/user/{name}/{id:[0-9]+}', 'handler0');
$r->addRoute('GET', '/user/{id:[0-9]+}', 'handler1');
$r->addRoute('GET', '/user/{name}', 'handler2');
};
$method = 'GET';
$uri = '/not-found';
$cases[] = [$method, $uri, $callback];
// 5 -------------------------------------------------------------------------------------->
// reuse callback from #4
$method = 'GET';
$uri = '/user/rdlowrey/12345/not-found';
$cases[] = [$method, $uri, $callback];
// 6 -------------------------------------------------------------------------------------->
// reuse callback from #5
$method = 'HEAD';
$cases[] = [$method, $uri, $callback];
// x -------------------------------------------------------------------------------------->
return $cases;
}
public function provideMethodNotAllowedDispatchCases()
{
$cases = [];
// 0 -------------------------------------------------------------------------------------->
$callback = function (RouteCollector $r) {
$r->addRoute('GET', '/resource/123/456', 'handler0');
};
$method = 'POST';
$uri = '/resource/123/456';
$allowedMethods = ['GET'];
$cases[] = [$method, $uri, $callback, $allowedMethods];
// 1 -------------------------------------------------------------------------------------->
$callback = function (RouteCollector $r) {
$r->addRoute('GET', '/resource/123/456', 'handler0');
$r->addRoute('POST', '/resource/123/456', 'handler1');
$r->addRoute('PUT', '/resource/123/456', 'handler2');
$r->addRoute('*', '/', 'handler3');
};
$method = 'DELETE';
$uri = '/resource/123/456';
$allowedMethods = ['GET', 'POST', 'PUT'];
$cases[] = [$method, $uri, $callback, $allowedMethods];
// 2 -------------------------------------------------------------------------------------->
$callback = function (RouteCollector $r) {
$r->addRoute('GET', '/user/{name}/{id:[0-9]+}', 'handler0');
$r->addRoute('POST', '/user/{name}/{id:[0-9]+}', 'handler1');
$r->addRoute('PUT', '/user/{name}/{id:[0-9]+}', 'handler2');
$r->addRoute('PATCH', '/user/{name}/{id:[0-9]+}', 'handler3');
};
$method = 'DELETE';
$uri = '/user/rdlowrey/42';
$allowedMethods = ['GET', 'POST', 'PUT', 'PATCH'];
$cases[] = [$method, $uri, $callback, $allowedMethods];
// 3 -------------------------------------------------------------------------------------->
$callback = function (RouteCollector $r) {
$r->addRoute('POST', '/user/{name}', 'handler1');
$r->addRoute('PUT', '/user/{name:[a-z]+}', 'handler2');
$r->addRoute('PATCH', '/user/{name:[a-z]+}', 'handler3');
};
$method = 'GET';
$uri = '/user/rdlowrey';
$allowedMethods = ['POST', 'PUT', 'PATCH'];
$cases[] = [$method, $uri, $callback, $allowedMethods];
// 4 -------------------------------------------------------------------------------------->
$callback = function (RouteCollector $r) {
$r->addRoute(['GET', 'POST'], '/user', 'handlerGetPost');
$r->addRoute(['DELETE'], '/user', 'handlerDelete');
$r->addRoute([], '/user', 'handlerNone');
};
$cases[] = ['PUT', '/user', $callback, ['GET', 'POST', 'DELETE']];
// 5
$callback = function (RouteCollector $r) {
$r->addRoute('POST', '/user.json', 'handler0');
$r->addRoute('GET', '/{entity}.json', 'handler1');
};
$cases[] = ['PUT', '/user.json', $callback, ['POST', 'GET']];
// x -------------------------------------------------------------------------------------->
return $cases;
}
}

View File

@ -0,0 +1,16 @@
<?php
namespace FastRoute\Dispatcher;
class GroupCountBasedTest extends DispatcherTest
{
protected function getDispatcherClass()
{
return 'FastRoute\\Dispatcher\\GroupCountBased';
}
protected function getDataGeneratorClass()
{
return 'FastRoute\\DataGenerator\\GroupCountBased';
}
}

View File

@ -0,0 +1,16 @@
<?php
namespace FastRoute\Dispatcher;
class GroupPosBasedTest extends DispatcherTest
{
protected function getDispatcherClass()
{
return 'FastRoute\\Dispatcher\\GroupPosBased';
}
protected function getDataGeneratorClass()
{
return 'FastRoute\\DataGenerator\\GroupPosBased';
}
}

View File

@ -0,0 +1,24 @@
<?php
namespace FastRoute\Dispatcher;
class MarkBasedTest extends DispatcherTest
{
public function setUp()
{
preg_match('/(*MARK:A)a/', 'a', $matches);
if (!isset($matches['MARK'])) {
$this->markTestSkipped('PHP 5.6 required for MARK support');
}
}
protected function getDispatcherClass()
{
return 'FastRoute\\Dispatcher\\MarkBased';
}
protected function getDataGeneratorClass()
{
return 'FastRoute\\DataGenerator\\MarkBased';
}
}

View File

@ -0,0 +1,44 @@
<?php
namespace FastRoute;
use PHPUnit\Framework\TestCase;
class HackTypecheckerTest extends TestCase
{
const SERVER_ALREADY_RUNNING_CODE = 77;
public function testTypechecks($recurse = true)
{
if (!defined('HHVM_VERSION')) {
$this->markTestSkipped('HHVM only');
}
if (!version_compare(HHVM_VERSION, '3.9.0', '>=')) {
$this->markTestSkipped('classname<T> requires HHVM 3.9+');
}
// The typechecker recurses the whole tree, so it makes sure
// that everything in fixtures/ is valid when this runs.
$output = [];
$exit_code = null;
exec(
'hh_server --check ' . escapeshellarg(__DIR__ . '/../../') . ' 2>&1',
$output,
$exit_code
);
if ($exit_code === self::SERVER_ALREADY_RUNNING_CODE) {
$this->assertTrue(
$recurse,
'Typechecker still running after running hh_client stop'
);
// Server already running - 3.10 => 3.11 regression:
// https://github.com/facebook/hhvm/issues/6646
exec('hh_client stop 2>/dev/null');
$this->testTypechecks(/* recurse = */ false);
return;
}
$this->assertSame(0, $exit_code, implode("\n", $output));
}
}

View File

@ -0,0 +1,29 @@
<?hh
namespace FastRoute\TestFixtures;
function all_options_simple(): \FastRoute\Dispatcher {
return \FastRoute\simpleDispatcher(
$collector ==> {},
shape(
'routeParser' => \FastRoute\RouteParser\Std::class,
'dataGenerator' => \FastRoute\DataGenerator\GroupCountBased::class,
'dispatcher' => \FastRoute\Dispatcher\GroupCountBased::class,
'routeCollector' => \FastRoute\RouteCollector::class,
),
);
}
function all_options_cached(): \FastRoute\Dispatcher {
return \FastRoute\cachedDispatcher(
$collector ==> {},
shape(
'routeParser' => \FastRoute\RouteParser\Std::class,
'dataGenerator' => \FastRoute\DataGenerator\GroupCountBased::class,
'dispatcher' => \FastRoute\Dispatcher\GroupCountBased::class,
'routeCollector' => \FastRoute\RouteCollector::class,
'cacheFile' => '/dev/null',
'cacheDisabled' => false,
),
);
}

View File

@ -0,0 +1,11 @@
<?hh
namespace FastRoute\TestFixtures;
function empty_options_simple(): \FastRoute\Dispatcher {
return \FastRoute\simpleDispatcher($collector ==> {}, shape());
}
function empty_options_cached(): \FastRoute\Dispatcher {
return \FastRoute\cachedDispatcher($collector ==> {}, shape());
}

View File

@ -0,0 +1,11 @@
<?hh
namespace FastRoute\TestFixtures;
function no_options_simple(): \FastRoute\Dispatcher {
return \FastRoute\simpleDispatcher($collector ==> {});
}
function no_options_cached(): \FastRoute\Dispatcher {
return \FastRoute\cachedDispatcher($collector ==> {});
}

View File

@ -0,0 +1,108 @@
<?php
namespace FastRoute;
use PHPUnit\Framework\TestCase;
class RouteCollectorTest extends TestCase
{
public function testShortcuts()
{
$r = new DummyRouteCollector();
$r->delete('/delete', 'delete');
$r->get('/get', 'get');
$r->head('/head', 'head');
$r->patch('/patch', 'patch');
$r->post('/post', 'post');
$r->put('/put', 'put');
$expected = [
['DELETE', '/delete', 'delete'],
['GET', '/get', 'get'],
['HEAD', '/head', 'head'],
['PATCH', '/patch', 'patch'],
['POST', '/post', 'post'],
['PUT', '/put', 'put'],
];
$this->assertSame($expected, $r->routes);
}
public function testGroups()
{
$r = new DummyRouteCollector();
$r->delete('/delete', 'delete');
$r->get('/get', 'get');
$r->head('/head', 'head');
$r->patch('/patch', 'patch');
$r->post('/post', 'post');
$r->put('/put', 'put');
$r->addGroup('/group-one', function (DummyRouteCollector $r) {
$r->delete('/delete', 'delete');
$r->get('/get', 'get');
$r->head('/head', 'head');
$r->patch('/patch', 'patch');
$r->post('/post', 'post');
$r->put('/put', 'put');
$r->addGroup('/group-two', function (DummyRouteCollector $r) {
$r->delete('/delete', 'delete');
$r->get('/get', 'get');
$r->head('/head', 'head');
$r->patch('/patch', 'patch');
$r->post('/post', 'post');
$r->put('/put', 'put');
});
});
$r->addGroup('/admin', function (DummyRouteCollector $r) {
$r->get('-some-info', 'admin-some-info');
});
$r->addGroup('/admin-', function (DummyRouteCollector $r) {
$r->get('more-info', 'admin-more-info');
});
$expected = [
['DELETE', '/delete', 'delete'],
['GET', '/get', 'get'],
['HEAD', '/head', 'head'],
['PATCH', '/patch', 'patch'],
['POST', '/post', 'post'],
['PUT', '/put', 'put'],
['DELETE', '/group-one/delete', 'delete'],
['GET', '/group-one/get', 'get'],
['HEAD', '/group-one/head', 'head'],
['PATCH', '/group-one/patch', 'patch'],
['POST', '/group-one/post', 'post'],
['PUT', '/group-one/put', 'put'],
['DELETE', '/group-one/group-two/delete', 'delete'],
['GET', '/group-one/group-two/get', 'get'],
['HEAD', '/group-one/group-two/head', 'head'],
['PATCH', '/group-one/group-two/patch', 'patch'],
['POST', '/group-one/group-two/post', 'post'],
['PUT', '/group-one/group-two/put', 'put'],
['GET', '/admin-some-info', 'admin-some-info'],
['GET', '/admin-more-info', 'admin-more-info'],
];
$this->assertSame($expected, $r->routes);
}
}
class DummyRouteCollector extends RouteCollector
{
public $routes = [];
public function __construct()
{
}
public function addRoute($method, $route, $handler)
{
$route = $this->currentGroupPrefix . $route;
$this->routes[] = [$method, $route, $handler];
}
}

View File

@ -0,0 +1,154 @@
<?php
namespace FastRoute\RouteParser;
use PHPUnit\Framework\TestCase;
class StdTest extends TestCase
{
/** @dataProvider provideTestParse */
public function testParse($routeString, $expectedRouteDatas)
{
$parser = new Std();
$routeDatas = $parser->parse($routeString);
$this->assertSame($expectedRouteDatas, $routeDatas);
}
/** @dataProvider provideTestParseError */
public function testParseError($routeString, $expectedExceptionMessage)
{
$parser = new Std();
$this->setExpectedException('FastRoute\\BadRouteException', $expectedExceptionMessage);
$parser->parse($routeString);
}
public function provideTestParse()
{
return [
[
'/test',
[
['/test'],
]
],
[
'/test/{param}',
[
['/test/', ['param', '[^/]+']],
]
],
[
'/te{ param }st',
[
['/te', ['param', '[^/]+'], 'st']
]
],
[
'/test/{param1}/test2/{param2}',
[
['/test/', ['param1', '[^/]+'], '/test2/', ['param2', '[^/]+']]
]
],
[
'/test/{param:\d+}',
[
['/test/', ['param', '\d+']]
]
],
[
'/test/{ param : \d{1,9} }',
[
['/test/', ['param', '\d{1,9}']]
]
],
[
'/test[opt]',
[
['/test'],
['/testopt'],
]
],
[
'/test[/{param}]',
[
['/test'],
['/test/', ['param', '[^/]+']],
]
],
[
'/{param}[opt]',
[
['/', ['param', '[^/]+']],
['/', ['param', '[^/]+'], 'opt']
]
],
[
'/test[/{name}[/{id:[0-9]+}]]',
[
['/test'],
['/test/', ['name', '[^/]+']],
['/test/', ['name', '[^/]+'], '/', ['id', '[0-9]+']],
]
],
[
'',
[
[''],
]
],
[
'[test]',
[
[''],
['test'],
]
],
[
'/{foo-bar}',
[
['/', ['foo-bar', '[^/]+']]
]
],
[
'/{_foo:.*}',
[
['/', ['_foo', '.*']]
]
],
];
}
public function provideTestParseError()
{
return [
[
'/test[opt',
"Number of opening '[' and closing ']' does not match"
],
[
'/test[opt[opt2]',
"Number of opening '[' and closing ']' does not match"
],
[
'/testopt]',
"Number of opening '[' and closing ']' does not match"
],
[
'/test[]',
'Empty optional part'
],
[
'/test[[opt]]',
'Empty optional part'
],
[
'[[test]]',
'Empty optional part'
],
[
'/test[/opt]/required',
'Optional segments can only occur at the end of a route'
],
];
}
}

View File

@ -0,0 +1,11 @@
<?php
require_once __DIR__ . '/../src/functions.php';
spl_autoload_register(function ($class) {
if (strpos($class, 'FastRoute\\') === 0) {
$dir = strcasecmp(substr($class, -4), 'Test') ? 'src/' : 'test/';
$name = substr($class, strlen('FastRoute'));
require __DIR__ . '/../' . $dir . strtr($name, '\\', DIRECTORY_SEPARATOR) . '.php';
}
});

View File

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

View File

@ -0,0 +1,40 @@
language: php
env:
matrix:
- PIMPLE_EXT=no
- PIMPLE_EXT=yes
global:
- REPORT_EXIT_STATUS=1
php:
- 5.3
- 5.4
- 5.5
- 5.6
- 7.0
- 7.1
before_script:
- composer self-update
- COMPOSER_ROOT_VERSION=dev-master composer install
- 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
- if [ "$PIMPLE_EXT" == "yes" ]; then export SYMFONY_DEPRECATIONS_HELPER=weak; fi
- cd ../..
- ./vendor/bin/simple-phpunit
matrix:
include:
- php: hhvm
dist: trusty
env: PIMPLE_EXT=no
exclude:
- php: 7.0
env: PIMPLE_EXT=yes
- php: 7.1
env: PIMPLE_EXT=yes

View File

@ -0,0 +1,59 @@
* 3.2.3 (2017-XX-XX)
* n/a
* 3.2.2 (2017-07-23)
* reverted extending a protected closure throws an exception (deprecated it instead)
* 3.2.1 (2017-07-17)
* fixed PHP error
* 3.2.0 (2017-07-17)
* added a PSR-11 service locator
* added a PSR-11 wrapper
* added ServiceIterator
* fixed extending a protected closure (now throws InvalidServiceIdentifierException)
* 3.1.0 (2017-07-03)
* deprecated the C extension
* added support for PSR-11 exceptions
* 3.0.2 (2015-09-11)
* refactored the C extension
* minor non-significant changes
* 3.0.1 (2015-07-30)
* simplified some code
* fixed a segfault in the C extension
* 3.0.0 (2014-07-24)
* removed the Pimple class alias (use Pimple\Container instead)
* 2.1.1 (2014-07-24)
* fixed compiler warnings for the C extension
* fixed code when dealing with circular references
* 2.1.0 (2014-06-24)
* moved the Pimple to Pimple\Container (with a BC layer -- Pimple is now a
deprecated alias which will be removed in Pimple 3.0)
* added Pimple\ServiceProviderInterface (and Pimple::register())
* 2.0.0 (2014-02-10)
* changed extend to automatically re-assign the extended service and keep it as shared or factory
(to keep BC, extend still returns the extended service)
* changed services to be shared by default (use factory() for factory
services)
* 1.0.0
* initial version

View File

@ -0,0 +1,19 @@
Copyright (c) 2009-2017 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,326 @@
Pimple
======
.. caution::
This is the documentation for Pimple 3.x. If you are using Pimple 1.x, read
the `Pimple 1.x documentation`_. Reading the Pimple 1.x code is also a good
way to learn more about how to create a simple Dependency Injection
Container (recent versions of Pimple are more focused on performance).
Pimple is a small Dependency Injection Container for PHP.
Installation
------------
Before using Pimple in your project, add it to your ``composer.json`` file:
.. code-block:: bash
$ ./composer.phar require pimple/pimple "^3.0"
Usage
-----
Creating a container is a matter of creating a ``Container`` instance:
.. code-block:: php
use Pimple\Container;
$container = new Container();
As many other dependency injection containers, Pimple manages two different
kind of data: **services** and **parameters**.
Defining Services
~~~~~~~~~~~~~~~~~
A service is an object that does something as part of a larger system. Examples
of services: a database connection, a templating engine, or a mailer. Almost
any **global** object can be a service.
Services are defined by **anonymous functions** that return an instance of an
object:
.. code-block:: php
// define some services
$container['session_storage'] = function ($c) {
return new SessionStorage('SESSION_ID');
};
$container['session'] = function ($c) {
return new Session($c['session_storage']);
};
Notice that the anonymous function has access to the current container
instance, allowing references to other services or parameters.
As objects are only created when you get them, the order of the definitions
does not matter.
Using the defined services is also very easy:
.. code-block:: php
// get the session object
$session = $container['session'];
// the above call is roughly equivalent to the following code:
// $storage = new SessionStorage('SESSION_ID');
// $session = new Session($storage);
Defining Factory Services
~~~~~~~~~~~~~~~~~~~~~~~~~
By default, each time you get a service, Pimple returns the **same instance**
of it. If you want a different instance to be returned for all calls, wrap your
anonymous function with the ``factory()`` method
.. code-block:: php
$container['session'] = $container->factory(function ($c) {
return new Session($c['session_storage']);
});
Now, each call to ``$container['session']`` returns a new instance of the
session.
Defining Parameters
~~~~~~~~~~~~~~~~~~~
Defining a parameter allows to ease the configuration of your container from
the outside and to store global values:
.. code-block:: php
// define some parameters
$container['cookie_name'] = 'SESSION_ID';
$container['session_storage_class'] = 'SessionStorage';
If you change the ``session_storage`` service definition like below:
.. code-block:: php
$container['session_storage'] = function ($c) {
return new $c['session_storage_class']($c['cookie_name']);
};
You can now easily change the cookie name by overriding the
``cookie_name`` parameter instead of redefining the service
definition.
Protecting Parameters
~~~~~~~~~~~~~~~~~~~~~
Because Pimple sees anonymous functions as service definitions, you need to
wrap anonymous functions with the ``protect()`` method to store them as
parameters:
.. code-block:: php
$container['random_func'] = $container->protect(function () {
return rand();
});
Modifying Services after Definition
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In some cases you may want to modify a service definition after it has been
defined. You can use the ``extend()`` method to define additional code to be
run on your service just after it is created:
.. code-block:: php
$container['session_storage'] = function ($c) {
return new $c['session_storage_class']($c['cookie_name']);
};
$container->extend('session_storage', function ($storage, $c) {
$storage->...();
return $storage;
});
The first argument is the name of the service to extend, the second a function
that gets access to the object instance and the container.
Extending a Container
~~~~~~~~~~~~~~~~~~~~~
If you use the same libraries over and over, you might want to reuse some
services from one project to the next one; package your services into a
**provider** by implementing ``Pimple\ServiceProviderInterface``:
.. code-block:: php
use Pimple\Container;
class FooProvider implements Pimple\ServiceProviderInterface
{
public function register(Container $pimple)
{
// register some services and parameters
// on $pimple
}
}
Then, register the provider on a Container:
.. code-block:: php
$pimple->register(new FooProvider());
Fetching the Service Creation Function
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
When you access an object, Pimple automatically calls the anonymous function
that you defined, which creates the service object for you. If you want to get
raw access to this function, you can use the ``raw()`` method:
.. code-block:: php
$container['session'] = function ($c) {
return new Session($c['session_storage']);
};
$sessionFunction = $container->raw('session');
PSR-11 compatibility
--------------------
For historical reasons, the ``Container`` class does not implement the PSR-11
``ContainerInterface``. However, Pimple provides a helper class that will let
you decouple your code from the Pimple container class.
The PSR-11 container class
~~~~~~~~~~~~~~~~~~~~~~~~~~
The ``Pimple\Psr11\Container`` class lets you access the content of an
underlying Pimple container using ``Psr\Container\ContainerInterface``
methods:
.. code-block:: php
use Pimple\Container;
use Pimple\Psr11\Container as PsrContainer;
$container = new Container();
$container['service'] = function ($c) {
return new Service();
};
$psr11 = new PsrContainer($container);
$controller = function (PsrContainer $container) {
$service = $container->get('service');
};
$controller($psr11);
Using the PSR-11 ServiceLocator
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Sometimes, a service needs access to several other services without being sure
that all of them will actually be used. In those cases, you may want the
instantiation of the services to be lazy.
The traditional solution is to inject the entire service container to get only
the services really needed. However, this is not recommended because it gives
services a too broad access to the rest of the application and it hides their
actual dependencies.
The ``ServiceLocator`` is intended to solve this problem by giving access to a
set of predefined services while instantiating them only when actually needed.
It also allows you to make your services available under a different name than
the one used to register them. For instance, you may want to use an object
that expects an instance of ``EventDispatcherInterface`` to be available under
the name ``event_dispatcher`` while your event dispatcher has been
registered under the name ``dispatcher``:
.. code-block:: php
use Monolog\Logger;
use Pimple\Psr11\ServiceLocator;
use Psr\Container\ContainerInterface;
use Symfony\Component\EventDispatcher\EventDispatcher;
class MyService
{
/**
* "logger" must be an instance of Psr\Log\LoggerInterface
* "event_dispatcher" must be an instance of Symfony\Component\EventDispatcher\EventDispatcherInterface
*/
private $services;
public function __construct(ContainerInterface $services)
{
$this->services = $services;
}
}
$container['logger'] = function ($c) {
return new Monolog\Logger();
};
$container['dispatcher'] = function () {
return new EventDispatcher();
};
$container['service'] = function ($c) {
$locator = new ServiceLocator($c, array('logger', 'event_dispatcher' => 'dispatcher'));
return new MyService($locator);
};
Referencing a Collection of Services Lazily
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Passing a collection of services instances in an array may prove inefficient
if the class that consumes the collection only needs to iterate over it at a
later stage, when one of its method is called. It can also lead to problems
if there is a circular dependency between one of the services stored in the
collection and the class that consumes it.
The ``ServiceIterator`` class helps you solve these issues. It receives a
list of service names during instantiation and will retrieve the services
when iterated over:
.. code-block:: php
use Pimple\Container;
use Pimple\ServiceIterator;
class AuthorizationService
{
private $voters;
public function __construct($voters)
{
$this->voters = $voters;
}
public function canAccess($resource)
{
foreach ($this->voters as $voter) {
if (true === $voter->canAccess($resource) {
return true;
}
}
return false;
}
}
$container = new Container();
$container['voter1'] = function ($c) {
return new SomeVoter();
}
$container['voter2'] = function ($c) {
return new SomeOtherVoter($c['auth']);
}
$container['auth'] = function ($c) {
return new AuthorizationService(new ServiceIterator($c, array('voter1', 'voter2'));
}
.. _Pimple 1.x documentation: https://github.com/silexphp/Pimple/tree/1.1

View File

@ -0,0 +1,29 @@
{
"name": "pimple/pimple",
"type": "library",
"description": "Pimple, a simple Dependency Injection Container",
"keywords": ["dependency injection", "container"],
"homepage": "http://pimple.sensiolabs.org",
"license": "MIT",
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
}
],
"require": {
"php": ">=5.3.0",
"psr/container": "^1.0"
},
"require-dev": {
"symfony/phpunit-bridge": "^3.2"
},
"autoload": {
"psr-0": { "Pimple": "src/" }
},
"extra": {
"branch-alias": {
"dev-master": "3.2.x-dev"
}
}
}

View File

@ -0,0 +1,30 @@
*.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

@ -0,0 +1,12 @@
This is Pimple 2 implemented in C
* PHP >= 5.3
* Not tested under Windows, might work
Install
=======
> phpize
> ./configure
> make
> make install

View File

@ -0,0 +1,63 @@
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

@ -0,0 +1,13 @@
// $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

@ -0,0 +1,137 @@
/*
* 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.2.3-DEV"
#define PIMPLE_NS "Pimple"
#define PSR_CONTAINER_NS "Psr\\Container"
#define PIMPLE_EXCEPTION_NS "Pimple\\Exception"
#define PIMPLE_DEFAULT_ZVAL_CACHE_NUM 5
#define PIMPLE_DEFAULT_ZVAL_VALUES_NUM 10
#define PIMPLE_DEPRECATE do { \
int er = EG(error_reporting); \
EG(error_reporting) = 0;\
php_error(E_DEPRECATED, "The Pimple C extension is deprecated since version 3.1 and will be removed in 4.0."); \
EG(error_reporting) = er; \
} while (0);
zend_module_entry *get_module(void);
PHP_MINIT_FUNCTION(pimple);
PHP_MINFO_FUNCTION(pimple);
PHP_METHOD(FrozenServiceException, __construct);
PHP_METHOD(InvalidServiceIdentifierException, __construct);
PHP_METHOD(UnknownIdentifierException, __construct);
PHP_METHOD(Pimple, __construct);
PHP_METHOD(Pimple, factory);
PHP_METHOD(Pimple, protect);
PHP_METHOD(Pimple, raw);
PHP_METHOD(Pimple, extend);
PHP_METHOD(Pimple, keys);
PHP_METHOD(Pimple, register);
PHP_METHOD(Pimple, offsetSet);
PHP_METHOD(Pimple, offsetUnset);
PHP_METHOD(Pimple, offsetGet);
PHP_METHOD(Pimple, offsetExists);
PHP_METHOD(PimpleClosure, invoker);
typedef struct _pimple_bucket_value {
zval *value; /* Must be the first element */
zval *raw;
zend_object_handle handle_num;
enum {
PIMPLE_IS_PARAM = 0,
PIMPLE_IS_SERVICE = 2
} type;
zend_bool initialized;
zend_fcall_info_cache fcc;
} pimple_bucket_value;
typedef struct _pimple_object {
zend_object zobj;
HashTable values;
HashTable factories;
HashTable protected;
} pimple_object;
typedef struct _pimple_closure_object {
zend_object zobj;
zval *callable;
zval *factory;
} pimple_closure_object;
static const char sensiolabs_logo[] = "<img src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHYAAAAUCAMAAABvRTlyAAAAz1BMVEUAAAAAAAAAAAAsThWB5j4AAACD6T8AAACC6D+C6D6C6D+C6D4AAAAAAACC6D4AAAAAAACC6D8AAAAAAAAAAAAAAAAAAAAAAACC6D4AAAAAAAAAAACC6D4AAAAAAAAAAAAAAAAAAAAAAACC6D8AAACC6D4AAAAAAAAAAAAAAAAAAACC6D8AAACC6D6C6D+B6D+C6D+C6D+C6D8AAACC6D6C6D4AAACC6D/K/2KC6D+B6D6C6D6C6D+C6D8sTxUyWRhEeiEAAACC6D+C5z6B6D7drnEVAAAAQXRSTlMAE3oCNSUuDHFHzxaF9UFsu+irX+zlKzYimaJXktyOSFD6BolxqT7QGMMdarMIpuO28r9EolXKgR16OphfXYd4V14GtB4AAAMpSURBVEjHvVSJctowEF1jjME2RziMwUCoMfd9heZqG4n//6buLpJjkmYm03byZmxJa2nf6u2uQcG2bfhqRN4LoTKBzyGDm68M7mAwcOEdjo4zhA/Rf9Go/CVtTgiRhXfIC3EDH8F/eUX1/9KexRo+QgOdtHDsEe/sM7QT32/+K61Z1LFXcXJxN4pTbu1aTQUzuy2PIA0rDo0/0Aa5XFaJvKaVTrubywXvaa1Wq4Vu/Snr3Y7Aojh4VccwykW2N2oQ8wmjyut6+Q1t5ywIG5Npj1sh5E0B7YOzFDjfuRfaOh3O+MbbVNfTWS9COZk3Obd2su5d0a6IU9KLREbw8gEehWSr1r2sPWciXLG38r5NdW0xu9eioU87omjC9yNaMi5GNf6WppVSOqXCFkmCvMB3p9SROLoYQn5pDgQOujA1xjYvqH+plUdkwnmII8VxR/PKYkrfLLomhVlE3b/LhNbNr7hp0H2JaOc4v8dFB58HSsFTSafaqtY1sT3GO8wsy5rhokYPlRJdjPMajyYqTt1EHF/2uqSWQWmAjCUSmQ1MS3g8Btf1XOsy7YIC0CB1b5Xw1Vhba0zbxiCAQLH9TNPmHJXQUtJAN0KcDsoqLxsNvJrJExa7mKIdp2lRE2WexiS4pqWk/0jROlw6K6bV9YOBDGAuqMJ0bnuUKGB0L27bxgRhGEbzihbhxxXaQC88Vkwq8ldCi86RApWUb0Q+4VDosBCc+1s81lUdnBavH4Zp2mm3O44USwOfvSo9oBiwpFg71lMS1VKJLKljS3j9p+fOTvXXlsSNuEv6YPaZda9uRope0VJfKdo7fPiYfSmvFjXQbkhY0d9hCbBWIktRgEDieDhf1N3wbbkmNNgRy8hyl620yGQat/grV3HMpc2HDKTVmOPFz6ylPCKt/nXcAyV260jaAowwIW0YuBzrOgb/KrddZS9OmJaLgpWK4JX2DDuklcLZSDGcn8Vmx9YDNvT6UsjyBApRyFQVX7Vxm9TGxE16nmfRd8/zQoDmggQOTRh5Hv8pMt9Q/L2JmSwkMCE7dA4BuDjHJwfu0Om4QAhOjrN5XkIatglfiN/bUPdCQFjTYgAAAABJRU5ErkJggg==\">";
static void pimple_exception_call_parent_constructor(zval *this_ptr, const char *format, const char *arg1 TSRMLS_DC);
static int pimple_zval_to_pimpleval(zval *_zval, pimple_bucket_value *_pimple_bucket_value TSRMLS_DC);
static int pimple_zval_is_valid_callback(zval *_zval, pimple_bucket_value *_pimple_bucket_value TSRMLS_DC);
static void pimple_bucket_dtor(pimple_bucket_value *bucket);
static void pimple_free_bucket(pimple_bucket_value *bucket);
static zval *pimple_object_read_dimension(zval *object, zval *offset, int type TSRMLS_DC);
static void pimple_object_write_dimension(zval *object, zval *offset, zval *value TSRMLS_DC);
static int pimple_object_has_dimension(zval *object, zval *offset, int check_empty TSRMLS_DC);
static void pimple_object_unset_dimension(zval *object, zval *offset TSRMLS_DC);
static zend_object_value pimple_object_create(zend_class_entry *ce TSRMLS_DC);
static void pimple_free_object_storage(pimple_object *obj TSRMLS_DC);
static void pimple_closure_free_object_storage(pimple_closure_object *obj TSRMLS_DC);
static zend_object_value pimple_closure_object_create(zend_class_entry *ce TSRMLS_DC);
static zend_function *pimple_closure_get_constructor(zval * TSRMLS_DC);
static int pimple_closure_get_closure(zval *obj, zend_class_entry **ce_ptr, union _zend_function **fptr_ptr, zval **zobj_ptr TSRMLS_DC);
#ifdef ZTS
#define PIMPLE_G(v) TSRMG(pimple_globals_id, zend_pimple_globals *, v)
#else
#define PIMPLE_G(v) (pimple_globals.v)
#endif
#endif /* PHP_PIMPLE_H */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,81 @@
/*
* 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

@ -0,0 +1,45 @@
--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

@ -0,0 +1,15 @@
--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

@ -0,0 +1,16 @@
--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

@ -0,0 +1,30 @@
--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

@ -0,0 +1,27 @@
--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

@ -0,0 +1,51 @@
--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

@ -0,0 +1,22 @@
--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

@ -0,0 +1,29 @@
--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

@ -0,0 +1,13 @@
--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

@ -0,0 +1,45 @@
--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

@ -0,0 +1,19 @@
--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

@ -0,0 +1,28 @@
--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"

Some files were not shown because too many files have changed in this diff Show More