diff --git a/composer.json b/composer.json index a6e97bb57..b39cd3765 100644 --- a/composer.json +++ b/composer.json @@ -1,4 +1,6 @@ { + "type": "project", + "license": "AGPLv3", "require": { "php": ">=5.6.0", "ext-soap": "*", @@ -6,7 +8,19 @@ "ext-mysqli": "*", "ext-dom": "*", "ext-iconv": "*", - "ext-gd": "*" + "ext-gd": "*", + "ext-ctype": "*", + + "symfony/console": "3.4.*", + "symfony/dotenv": "3.4.*", + "symfony/framework-bundle": "3.4.*", + "symfony/twig-bundle": "3.4.*", + "symfony/yaml": "3.4.*", + "symfony/polyfill-php70": "1.*" + }, + "require-dev": { + "symfony/stopwatch": "3.4.*", + "symfony/web-profiler-bundle": "3.4.*" }, "suggest": { "ext-libsodium": "Required to use the AttributeEncryptedString.", @@ -19,6 +33,26 @@ "config": { "platform": { "php": "5.6.0" + }, + "vendor-dir": "lib", + "preferred-install": { + "*": "dist" + }, + "sort-packages": true + }, + "autoload": { + "classmap": [ + "core", + "application" + ] + }, + "conflict": { + "symfony/symfony": "*" + }, + "extra": { + "symfony": { + "allow-contrib": false, + "require": "3.4.*" } } } \ No newline at end of file diff --git a/composer.lock b/composer.lock index d48f8a184..d99a19380 100644 --- a/composer.lock +++ b/composer.lock @@ -4,1324 +4,56 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "45ec0e252f0ceab3e358672eb0944823", - "packages": [], - "packages-dev": [ + "content-hash": "41f51225471c2e18c3488910cdeabcf6", + "packages": [ { - "name": "cilex/cilex", - "version": "1.1.0", + "name": "paragonie/random_compat", + "version": "v2.0.18", "source": { "type": "git", - "url": "https://github.com/Cilex/Cilex.git", - "reference": "7acd965a609a56d0345e8b6071c261fbdb926cb5" + "url": "https://github.com/paragonie/random_compat.git", + "reference": "0a58ef6e3146256cc3dc7cc393927bcc7d1b72db" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Cilex/Cilex/zipball/7acd965a609a56d0345e8b6071c261fbdb926cb5", - "reference": "7acd965a609a56d0345e8b6071c261fbdb926cb5", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/0a58ef6e3146256cc3dc7cc393927bcc7d1b72db", + "reference": "0a58ef6e3146256cc3dc7cc393927bcc7d1b72db", "shasum": "" }, "require": { - "cilex/console-service-provider": "1.*", - "php": ">=5.3.3", - "pimple/pimple": "~1.0", - "symfony/finder": "~2.1", - "symfony/process": "~2.1" + "php": ">=5.2.0" }, "require-dev": { - "phpunit/phpunit": "3.7.*", - "symfony/validator": "~2.1" + "phpunit/phpunit": "4.*|5.*" }, "suggest": { - "monolog/monolog": ">=1.0.0", - "symfony/validator": ">=1.0.0", - "symfony/yaml": ">=1.0.0" + "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "psr-0": { - "Cilex": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "mike.vanriel@naenius.com" - } - ], - "description": "The PHP micro-framework for Command line tools based on the Symfony2 Components", - "homepage": "http://cilex.github.com", - "keywords": [ - "cli", - "microframework" - ], - "time": "2014-03-29T14:03:13+00:00" - }, - { - "name": "cilex/console-service-provider", - "version": "1.0.0", - "source": { - "type": "git", - "url": "https://github.com/Cilex/console-service-provider.git", - "reference": "25ee3d1875243d38e1a3448ff94bdf944f70d24e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Cilex/console-service-provider/zipball/25ee3d1875243d38e1a3448ff94bdf944f70d24e", - "reference": "25ee3d1875243d38e1a3448ff94bdf944f70d24e", - "shasum": "" - }, - "require": { - "php": ">=5.3.3", - "pimple/pimple": "1.*@dev", - "symfony/console": "~2.1" - }, - "require-dev": { - "cilex/cilex": "1.*@dev", - "silex/silex": "1.*@dev" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "psr-0": { - "Cilex\\Provider\\Console": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Beau Simensen", - "email": "beau@dflydev.com", - "homepage": "http://beausimensen.com" - }, - { - "name": "Mike van Riel", - "email": "mike.vanriel@naenius.com" - } - ], - "description": "Console Service Provider", - "keywords": [ - "cilex", - "console", - "pimple", - "service-provider", - "silex" - ], - "time": "2012-12-19T10:50:58+00:00" - }, - { - "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": "doctrine/annotations", - "version": "v1.4.0", - "source": { - "type": "git", - "url": "https://github.com/doctrine/annotations.git", - "reference": "54cacc9b81758b14e3ce750f205a393d52339e97" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/annotations/zipball/54cacc9b81758b14e3ce750f205a393d52339e97", - "reference": "54cacc9b81758b14e3ce750f205a393d52339e97", - "shasum": "" - }, - "require": { - "doctrine/lexer": "1.*", - "php": "^5.6 || ^7.0" - }, - "require-dev": { - "doctrine/cache": "1.*", - "phpunit/phpunit": "^5.7" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.4.x-dev" - } - }, - "autoload": { - "psr-4": { - "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" - } - ], - "description": "Docblock Annotations Parser", - "homepage": "http://www.doctrine-project.org", - "keywords": [ - "annotations", - "docblock", - "parser" - ], - "time": "2017-02-24T16:22:25+00:00" - }, - { - "name": "doctrine/instantiator", - "version": "1.0.5", - "source": { - "type": "git", - "url": "https://github.com/doctrine/instantiator.git", - "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d", - "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d", - "shasum": "" - }, - "require": { - "php": ">=5.3,<8.0-DEV" - }, - "require-dev": { - "athletic/athletic": "~0.1.8", - "ext-pdo": "*", - "ext-phar": "*", - "phpunit/phpunit": "~4.0", - "squizlabs/php_codesniffer": "~2.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com", - "homepage": "http://ocramius.github.com/" - } - ], - "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", - "homepage": "https://github.com/doctrine/instantiator", - "keywords": [ - "constructor", - "instantiate" - ], - "time": "2015-06-14T21:17:01+00:00" - }, - { - "name": "doctrine/lexer", - "version": "v1.0.1", - "source": { - "type": "git", - "url": "https://github.com/doctrine/lexer.git", - "reference": "83893c552fd2045dd78aef794c31e694c37c0b8c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/lexer/zipball/83893c552fd2045dd78aef794c31e694c37c0b8c", - "reference": "83893c552fd2045dd78aef794c31e694c37c0b8c", - "shasum": "" - }, - "require": { - "php": ">=5.3.2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-0": { - "Doctrine\\Common\\Lexer\\": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" - } - ], - "description": "Base library for a lexer that can be used in Top-Down, Recursive Descent Parsers.", - "homepage": "http://www.doctrine-project.org", - "keywords": [ - "lexer", - "parser" - ], - "time": "2014-09-09T13:34:57+00:00" - }, - { - "name": "erusev/parsedown", - "version": "1.7.1", - "source": { - "type": "git", - "url": "https://github.com/erusev/parsedown.git", - "reference": "92e9c27ba0e74b8b028b111d1b6f956a15c01fc1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/erusev/parsedown/zipball/92e9c27ba0e74b8b028b111d1b6f956a15c01fc1", - "reference": "92e9c27ba0e74b8b028b111d1b6f956a15c01fc1", - "shasum": "" - }, - "require": { - "ext-mbstring": "*", - "php": ">=5.3.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.8.35" - }, - "type": "library", - "autoload": { - "psr-0": { - "Parsedown": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Emanuil Rusev", - "email": "hello@erusev.com", - "homepage": "http://erusev.com" - } - ], - "description": "Parser for Markdown.", - "homepage": "http://parsedown.org", - "keywords": [ - "markdown", - "parser" - ], - "time": "2018-03-08T01:11:30+00:00" - }, - { - "name": "herrera-io/json", - "version": "1.0.3", - "source": { - "type": "git", - "url": "https://github.com/kherge-php/json.git", - "reference": "60c696c9370a1e5136816ca557c17f82a6fa83f1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/kherge-php/json/zipball/60c696c9370a1e5136816ca557c17f82a6fa83f1", - "reference": "60c696c9370a1e5136816ca557c17f82a6fa83f1", - "shasum": "" - }, - "require": { - "ext-json": "*", - "justinrainbow/json-schema": ">=1.0,<2.0-dev", - "php": ">=5.3.3", - "seld/jsonlint": ">=1.0,<2.0-dev" - }, - "require-dev": { - "herrera-io/phpunit-test-case": "1.*", - "mikey179/vfsstream": "1.1.0", - "phpunit/phpunit": "3.7.*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, "autoload": { "files": [ - "src/lib/json_version.php" - ], - "psr-0": { - "Herrera\\Json": "src/lib" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Kevin Herrera", - "email": "kevin@herrera.io", - "homepage": "http://kevin.herrera.io" - } - ], - "description": "A library for simplifying JSON linting and validation.", - "homepage": "http://herrera-io.github.com/php-json", - "keywords": [ - "json", - "lint", - "schema", - "validate" - ], - "abandoned": "kherge/json", - "time": "2013-10-30T16:51:34+00:00" - }, - { - "name": "herrera-io/phar-update", - "version": "1.0.3", - "source": { - "type": "git", - "url": "https://github.com/kherge-abandoned/php-phar-update.git", - "reference": "00a79e1d5b8cf3c080a2e3becf1ddf7a7fea025b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/kherge-abandoned/php-phar-update/zipball/00a79e1d5b8cf3c080a2e3becf1ddf7a7fea025b", - "reference": "00a79e1d5b8cf3c080a2e3becf1ddf7a7fea025b", - "shasum": "" - }, - "require": { - "herrera-io/json": "1.*", - "kherge/version": "1.*", - "php": ">=5.3.3" - }, - "require-dev": { - "herrera-io/phpunit-test-case": "1.*", - "mikey179/vfsstream": "1.1.0", - "phpunit/phpunit": "3.7.*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "files": [ - "src/lib/constants.php" - ], - "psr-0": { - "Herrera\\Phar\\Update": "src/lib" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Kevin Herrera", - "email": "kevin@herrera.io", - "homepage": "http://kevin.herrera.io" - } - ], - "description": "A library for self-updating Phars.", - "homepage": "http://herrera-io.github.com/php-phar-update", - "keywords": [ - "phar", - "update" - ], - "abandoned": true, - "time": "2013-10-30T17:23:01+00:00" - }, - { - "name": "jms/metadata", - "version": "1.7.0", - "source": { - "type": "git", - "url": "https://github.com/schmittjoh/metadata.git", - "reference": "e5854ab1aa643623dc64adde718a8eec32b957a8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/metadata/zipball/e5854ab1aa643623dc64adde718a8eec32b957a8", - "reference": "e5854ab1aa643623dc64adde718a8eec32b957a8", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "require-dev": { - "doctrine/cache": "~1.0", - "symfony/cache": "~3.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.5.x-dev" - } - }, - "autoload": { - "psr-0": { - "Metadata\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Asmir Mustafic", - "email": "goetas@gmail.com" - }, - { - "name": "Johannes M. Schmitt", - "email": "schmittjoh@gmail.com" - } - ], - "description": "Class/method/property metadata management in PHP", - "keywords": [ - "annotations", - "metadata", - "xml", - "yaml" - ], - "time": "2018-10-26T12:40:10+00:00" - }, - { - "name": "jms/parser-lib", - "version": "1.0.0", - "source": { - "type": "git", - "url": "https://github.com/schmittjoh/parser-lib.git", - "reference": "c509473bc1b4866415627af0e1c6cc8ac97fa51d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/parser-lib/zipball/c509473bc1b4866415627af0e1c6cc8ac97fa51d", - "reference": "c509473bc1b4866415627af0e1c6cc8ac97fa51d", - "shasum": "" - }, - "require": { - "phpoption/phpoption": ">=0.9,<2.0-dev" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "psr-0": { - "JMS\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache2" - ], - "description": "A library for easily creating recursive-descent parsers.", - "time": "2012-11-18T18:08:43+00:00" - }, - { - "name": "jms/serializer", - "version": "1.13.0", - "source": { - "type": "git", - "url": "https://github.com/schmittjoh/serializer.git", - "reference": "00863e1d55b411cc33ad3e1de09a4c8d3aae793c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/serializer/zipball/00863e1d55b411cc33ad3e1de09a4c8d3aae793c", - "reference": "00863e1d55b411cc33ad3e1de09a4c8d3aae793c", - "shasum": "" - }, - "require": { - "doctrine/annotations": "^1.0", - "doctrine/instantiator": "^1.0.3", - "jms/metadata": "^1.3", - "jms/parser-lib": "1.*", - "php": "^5.5|^7.0", - "phpcollection/phpcollection": "~0.1", - "phpoption/phpoption": "^1.1" - }, - "conflict": { - "twig/twig": "<1.12" - }, - "require-dev": { - "doctrine/orm": "~2.1", - "doctrine/phpcr-odm": "^1.3|^2.0", - "ext-pdo_sqlite": "*", - "jackalope/jackalope-doctrine-dbal": "^1.1.5", - "phpunit/phpunit": "^4.8|^5.0", - "propel/propel1": "~1.7", - "psr/container": "^1.0", - "symfony/dependency-injection": "^2.7|^3.3|^4.0", - "symfony/expression-language": "^2.6|^3.0", - "symfony/filesystem": "^2.1", - "symfony/form": "~2.1|^3.0", - "symfony/translation": "^2.1|^3.0", - "symfony/validator": "^2.2|^3.0", - "symfony/yaml": "^2.1|^3.0", - "twig/twig": "~1.12|~2.0" - }, - "suggest": { - "doctrine/cache": "Required if you like to use cache functionality.", - "doctrine/collections": "Required if you like to use doctrine collection types as ArrayCollection.", - "symfony/yaml": "Required if you'd like to serialize data to YAML format." - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-1.x": "1.13-dev" - } - }, - "autoload": { - "psr-0": { - "JMS\\Serializer": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Asmir Mustafic", - "email": "goetas@gmail.com" - }, - { - "name": "Johannes M. Schmitt", - "email": "schmittjoh@gmail.com" - } - ], - "description": "Library for (de-)serializing data of any complexity; supports XML, JSON, and YAML.", - "homepage": "http://jmsyst.com/libs/serializer", - "keywords": [ - "deserialization", - "jaxb", - "json", - "serialization", - "xml" - ], - "time": "2018-07-25T13:58:54+00:00" - }, - { - "name": "justinrainbow/json-schema", - "version": "1.6.1", - "source": { - "type": "git", - "url": "https://github.com/justinrainbow/json-schema.git", - "reference": "cc84765fb7317f6b07bd8ac78364747f95b86341" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/cc84765fb7317f6b07bd8ac78364747f95b86341", - "reference": "cc84765fb7317f6b07bd8ac78364747f95b86341", - "shasum": "" - }, - "require": { - "php": ">=5.3.29" - }, - "require-dev": { - "json-schema/json-schema-test-suite": "1.1.0", - "phpdocumentor/phpdocumentor": "~2", - "phpunit/phpunit": "~3.7" - }, - "bin": [ - "bin/validate-json" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.6.x-dev" - } - }, - "autoload": { - "psr-4": { - "JsonSchema\\": "src/JsonSchema/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Bruno Prieto Reis", - "email": "bruno.p.reis@gmail.com" - }, - { - "name": "Justin Rainbow", - "email": "justin.rainbow@gmail.com" - }, - { - "name": "Igor Wiedler", - "email": "igor@wiedler.ch" - }, - { - "name": "Robert Schönthal", - "email": "seroscho@googlemail.com" - } - ], - "description": "A library to validate a json schema.", - "homepage": "https://github.com/justinrainbow/json-schema", - "keywords": [ - "json", - "schema" - ], - "time": "2016-01-25T15:43:01+00:00" - }, - { - "name": "kherge/version", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/kherge-abandoned/Version.git", - "reference": "f07cf83f8ce533be8f93d2893d96d674bbeb7e30" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/kherge-abandoned/Version/zipball/f07cf83f8ce533be8f93d2893d96d674bbeb7e30", - "reference": "f07cf83f8ce533be8f93d2893d96d674bbeb7e30", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "psr-0": { - "KevinGH\\Version": "src/lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Kevin Herrera", - "email": "me@kevingh.com" - } - ], - "description": "A parsing and comparison library for semantic versioning.", - "homepage": "http://github.com/kherge/Version", - "abandoned": true, - "time": "2012-08-16T17:13:03+00:00" - }, - { - "name": "monolog/monolog", - "version": "1.24.0", - "source": { - "type": "git", - "url": "https://github.com/Seldaek/monolog.git", - "reference": "bfc9ebb28f97e7a24c45bdc3f0ff482e47bb0266" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/bfc9ebb28f97e7a24c45bdc3f0ff482e47bb0266", - "reference": "bfc9ebb28f97e7a24c45bdc3f0ff482e47bb0266", - "shasum": "" - }, - "require": { - "php": ">=5.3.0", - "psr/log": "~1.0" - }, - "provide": { - "psr/log-implementation": "1.0.0" - }, - "require-dev": { - "aws/aws-sdk-php": "^2.4.9 || ^3.0", - "doctrine/couchdb": "~1.0@dev", - "graylog2/gelf-php": "~1.0", - "jakub-onderka/php-parallel-lint": "0.9", - "php-amqplib/php-amqplib": "~2.4", - "php-console/php-console": "^3.1.3", - "phpunit/phpunit": "~4.5", - "phpunit/phpunit-mock-objects": "2.3.0", - "ruflin/elastica": ">=0.90 <3.0", - "sentry/sentry": "^0.13", - "swiftmailer/swiftmailer": "^5.3|^6.0" - }, - "suggest": { - "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", - "doctrine/couchdb": "Allow sending log messages to a CouchDB server", - "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", - "ext-mongo": "Allow sending log messages to a MongoDB server", - "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", - "mongodb/mongodb": "Allow sending log messages to a MongoDB server via PHP Driver", - "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", - "php-console/php-console": "Allow sending log messages to Google Chrome", - "rollbar/rollbar": "Allow sending log messages to Rollbar", - "ruflin/elastica": "Allow sending log messages to an Elastic Search server", - "sentry/sentry": "Allow sending log messages to a Sentry server" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Monolog\\": "src/Monolog" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" - } - ], - "description": "Sends your logs to files, sockets, inboxes, databases and various web services", - "homepage": "http://github.com/Seldaek/monolog", - "keywords": [ - "log", - "logging", - "psr-3" - ], - "time": "2018-11-05T09:00:11+00:00" - }, - { - "name": "nikic/php-parser", - "version": "v1.4.1", - "source": { - "type": "git", - "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "f78af2c9c86107aa1a34cd1dbb5bbe9eeb0d9f51" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/f78af2c9c86107aa1a34cd1dbb5bbe9eeb0d9f51", - "reference": "f78af2c9c86107aa1a34cd1dbb5bbe9eeb0d9f51", - "shasum": "" - }, - "require": { - "ext-tokenizer": "*", - "php": ">=5.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.4-dev" - } - }, - "autoload": { - "files": [ - "lib/bootstrap.php" + "lib/random.php" ] }, "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Nikita Popov" - } - ], - "description": "A PHP parser written in PHP", - "keywords": [ - "parser", - "php" - ], - "time": "2015-09-19T14:15:08+00:00" - }, - { - "name": "phpcollection/phpcollection", - "version": "0.5.0", - "source": { - "type": "git", - "url": "https://github.com/schmittjoh/php-collection.git", - "reference": "f2bcff45c0da7c27991bbc1f90f47c4b7fb434a6" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/php-collection/zipball/f2bcff45c0da7c27991bbc1f90f47c4b7fb434a6", - "reference": "f2bcff45c0da7c27991bbc1f90f47c4b7fb434a6", - "shasum": "" - }, - "require": { - "phpoption/phpoption": "1.*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "0.4-dev" - } - }, - "autoload": { - "psr-0": { - "PhpCollection": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache2" - ], - "authors": [ - { - "name": "Johannes M. Schmitt", - "email": "schmittjoh@gmail.com" - } - ], - "description": "General-Purpose Collection Library for PHP", - "keywords": [ - "collection", - "list", - "map", - "sequence", - "set" - ], - "time": "2015-05-17T12:39:23+00:00" - }, - { - "name": "phpdocumentor/fileset", - "version": "1.0.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/Fileset.git", - "reference": "bfa78d8fa9763dfce6d0e5d3730c1d8ab25d34b0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/Fileset/zipball/bfa78d8fa9763dfce6d0e5d3730c1d8ab25d34b0", - "reference": "bfa78d8fa9763dfce6d0e5d3730c1d8ab25d34b0", - "shasum": "" - }, - "require": { - "php": ">=5.3.3", - "symfony/finder": "~2.1" - }, - "require-dev": { - "phpunit/phpunit": "~3.7" - }, - "type": "library", - "autoload": { - "psr-0": { - "phpDocumentor": [ - "src/", - "tests/unit/" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Fileset component for collecting a set of files given directories and file paths", - "homepage": "http://www.phpdoc.org", - "keywords": [ - "files", - "fileset", - "phpdoc" - ], - "time": "2013-08-06T21:07:42+00:00" - }, - { - "name": "phpdocumentor/graphviz", - "version": "1.0.4", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/GraphViz.git", - "reference": "a906a90a9f230535f25ea31caf81b2323956283f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/GraphViz/zipball/a906a90a9f230535f25ea31caf81b2323956283f", - "reference": "a906a90a9f230535f25ea31caf81b2323956283f", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "require-dev": { - "phpunit/phpunit": "~4.0" - }, - "type": "library", - "autoload": { - "psr-0": { - "phpDocumentor": [ - "src/", - "tests/unit" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { - "name": "Mike van Riel", - "email": "mike.vanriel@naenius.com" + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com" } ], - "time": "2016-02-02T13:00:08+00:00" - }, - { - "name": "phpdocumentor/phpdocumentor", - "version": "v2.9.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/phpDocumentor2.git", - "reference": "be607da0eef9b9249c43c5b4820d25d631c73667" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/phpDocumentor2/zipball/be607da0eef9b9249c43c5b4820d25d631c73667", - "reference": "be607da0eef9b9249c43c5b4820d25d631c73667", - "shasum": "" - }, - "require": { - "cilex/cilex": "~1.0", - "erusev/parsedown": "~1.0", - "herrera-io/phar-update": "1.0.3", - "jms/serializer": ">=0.12", - "monolog/monolog": "~1.6", - "php": ">=5.3.3", - "phpdocumentor/fileset": "~1.0", - "phpdocumentor/graphviz": "~1.0", - "phpdocumentor/reflection": "^3.0", - "phpdocumentor/reflection-docblock": "~2.0", - "symfony/config": "~2.3", - "symfony/console": "~2.3", - "symfony/event-dispatcher": "~2.1", - "symfony/process": "~2.0", - "symfony/stopwatch": "~2.3", - "symfony/validator": "~2.2", - "twig/twig": "~1.3", - "zendframework/zend-cache": "~2.1", - "zendframework/zend-config": "~2.1", - "zendframework/zend-filter": "~2.1", - "zendframework/zend-i18n": "~2.1", - "zendframework/zend-serializer": "~2.1", - "zendframework/zend-servicemanager": "~2.1", - "zendframework/zend-stdlib": "~2.1", - "zetacomponents/document": ">=1.3.1" - }, - "require-dev": { - "behat/behat": "~3.0", - "mikey179/vfsstream": "~1.2", - "mockery/mockery": "~0.9@dev", - "phpunit/phpunit": "~4.0", - "squizlabs/php_codesniffer": "~1.4", - "symfony/expression-language": "~2.4" - }, - "suggest": { - "ext-twig": "Enabling the twig extension improves the generation of twig based templates.", - "ext-xslcache": "Enabling the XSLCache extension improves the generation of xml based templates." - }, - "bin": [ - "bin/phpdoc.php", - "bin/phpdoc" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-develop": "2.9-dev" - } - }, - "autoload": { - "psr-0": { - "phpDocumentor": [ - "src/", - "tests/unit/" - ], - "Cilex\\Provider": [ - "src/" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Documentation Generator for PHP", - "homepage": "http://www.phpdoc.org", + "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", "keywords": [ - "api", - "application", - "dga", - "documentation", - "phpdoc" + "csprng", + "polyfill", + "pseudorandom", + "random" ], - "time": "2016-05-22T09:50:56+00:00" - }, - { - "name": "phpdocumentor/reflection", - "version": "3.0.1", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/Reflection.git", - "reference": "793bfd92d9a0fc96ae9608fb3e947c3f59fb3a0d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/Reflection/zipball/793bfd92d9a0fc96ae9608fb3e947c3f59fb3a0d", - "reference": "793bfd92d9a0fc96ae9608fb3e947c3f59fb3a0d", - "shasum": "" - }, - "require": { - "nikic/php-parser": "^1.0", - "php": ">=5.3.3", - "phpdocumentor/reflection-docblock": "~2.0", - "psr/log": "~1.0" - }, - "require-dev": { - "behat/behat": "~2.4", - "mockery/mockery": "~0.8", - "phpunit/phpunit": "~4.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-0": { - "phpDocumentor": [ - "src/", - "tests/unit/", - "tests/mocks/" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Reflection library to do Static Analysis for PHP Projects", - "homepage": "http://www.phpdoc.org", - "keywords": [ - "phpDocumentor", - "phpdoc", - "reflection", - "static analysis" - ], - "time": "2016-05-21T08:42:32+00:00" - }, - { - "name": "phpdocumentor/reflection-docblock", - "version": "2.0.5", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "e6a969a640b00d8daa3c66518b0405fb41ae0c4b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/e6a969a640b00d8daa3c66518b0405fb41ae0c4b", - "reference": "e6a969a640b00d8daa3c66518b0405fb41ae0c4b", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "require-dev": { - "phpunit/phpunit": "~4.0" - }, - "suggest": { - "dflydev/markdown": "~1.0", - "erusev/parsedown": "~1.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "psr-0": { - "phpDocumentor": [ - "src/" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "mike.vanriel@naenius.com" - } - ], - "time": "2016-01-25T08:17:30+00:00" - }, - { - "name": "phpoption/phpoption", - "version": "1.5.0", - "source": { - "type": "git", - "url": "https://github.com/schmittjoh/php-option.git", - "reference": "94e644f7d2051a5f0fcf77d81605f152eecff0ed" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/94e644f7d2051a5f0fcf77d81605f152eecff0ed", - "reference": "94e644f7d2051a5f0fcf77d81605f152eecff0ed", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "require-dev": { - "phpunit/phpunit": "4.7.*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.3-dev" - } - }, - "autoload": { - "psr-0": { - "PhpOption\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache2" - ], - "authors": [ - { - "name": "Johannes M. Schmitt", - "email": "schmittjoh@gmail.com" - } - ], - "description": "Option Type for PHP", - "keywords": [ - "language", - "option", - "php", - "type" - ], - "time": "2015-07-25T16:39:46+00:00" - }, - { - "name": "pimple/pimple", - "version": "v1.1.1", - "source": { - "type": "git", - "url": "https://github.com/silexphp/Pimple.git", - "reference": "2019c145fe393923f3441b23f29bbdfaa5c58c4d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/silexphp/Pimple/zipball/2019c145fe393923f3441b23f29bbdfaa5c58c4d", - "reference": "2019c145fe393923f3441b23f29bbdfaa5c58c4d", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1.x-dev" - } - }, - "autoload": { - "psr-0": { - "Pimple": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - } - ], - "description": "Pimple is a simple Dependency Injection Container for PHP 5.3", - "homepage": "http://pimple.sensiolabs.org", - "keywords": [ - "container", - "dependency injection" - ], - "time": "2013-11-22T08:30:29+00:00" + "time": "2019-01-03T20:59:08+00:00" }, { "name": "psr/cache", @@ -1514,33 +246,52 @@ "time": "2017-10-23T01:57:42+00:00" }, { - "name": "seld/jsonlint", - "version": "1.7.1", + "name": "symfony/cache", + "version": "v3.4.30", "source": { "type": "git", - "url": "https://github.com/Seldaek/jsonlint.git", - "reference": "d15f59a67ff805a44c50ea0516d2341740f81a38" + "url": "https://github.com/symfony/cache.git", + "reference": "8c6e162f0f7626771edbfa0a0e45b46623bbae1c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/d15f59a67ff805a44c50ea0516d2341740f81a38", - "reference": "d15f59a67ff805a44c50ea0516d2341740f81a38", + "url": "https://api.github.com/repos/symfony/cache/zipball/8c6e162f0f7626771edbfa0a0e45b46623bbae1c", + "reference": "8c6e162f0f7626771edbfa0a0e45b46623bbae1c", "shasum": "" }, "require": { - "php": "^5.3 || ^7.0" + "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": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" + "cache/integration-tests": "dev-master", + "doctrine/cache": "~1.6", + "doctrine/dbal": "~2.4", + "predis/predis": "~1.0" }, - "bin": [ - "bin/jsonlint" - ], "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, "autoload": { "psr-4": { - "Seld\\JsonLint\\": "src/Seld/JsonLint/" - } + "Symfony\\Component\\Cache\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -1548,41 +299,106 @@ ], "authors": [ { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "JSON Linter", + "description": "Symfony Cache component with PSR-6, PSR-16, and tags", + "homepage": "https://symfony.com", "keywords": [ - "json", - "linter", - "parser", - "validator" + "caching", + "psr6" ], - "time": "2018-01-24T12:46:19+00:00" + "time": "2019-06-17T17:26:15+00:00" }, { - "name": "symfony/config", - "version": "v2.8.49", + "name": "symfony/class-loader", + "version": "v3.4.30", "source": { "type": "git", - "url": "https://github.com/symfony/config.git", - "reference": "7dd5f5040dc04c118d057fb5886563963eb70011" + "url": "https://github.com/symfony/class-loader.git", + "reference": "4459eef5298dedfb69f771186a580062b8516497" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/7dd5f5040dc04c118d057fb5886563963eb70011", - "reference": "7dd5f5040dc04c118d057fb5886563963eb70011", + "url": "https://api.github.com/repos/symfony/class-loader/zipball/4459eef5298dedfb69f771186a580062b8516497", + "reference": "4459eef5298dedfb69f771186a580062b8516497", "shasum": "" }, "require": { - "php": ">=5.3.9", - "symfony/filesystem": "~2.3|~3.0.0", - "symfony/polyfill-ctype": "~1.8" + "php": "^5.5.9|>=7.0.8" }, "require-dev": { - "symfony/yaml": "~2.7|~3.0.0" + "symfony/finder": "~2.8|~3.0|~4.0", + "symfony/polyfill-apcu": "~1.1" + }, + "suggest": { + "symfony/polyfill-apcu": "For using ApcClassLoader on HHVM" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\ClassLoader\\": "" + }, + "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 ClassLoader Component", + "homepage": "https://symfony.com", + "time": "2019-01-16T09:39:14+00:00" + }, + { + "name": "symfony/config", + "version": "v3.4.30", + "source": { + "type": "git", + "url": "https://github.com/symfony/config.git", + "reference": "623fd6be3e5d4112d667003488c8c3ec12b66f62" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/config/zipball/623fd6be3e5d4112d667003488c8c3ec12b66f62", + "reference": "623fd6be3e5d4112d667003488c8c3ec12b66f62", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8", + "symfony/filesystem": "~2.8|~3.0|~4.0", + "symfony/polyfill-ctype": "~1.8" + }, + "conflict": { + "symfony/dependency-injection": "<3.3", + "symfony/finder": "<3.3" + }, + "require-dev": { + "symfony/dependency-injection": "~3.3|~4.0", + "symfony/event-dispatcher": "~3.3|~4.0", + "symfony/finder": "~3.3|~4.0", + "symfony/yaml": "~3.0|~4.0" }, "suggest": { "symfony/yaml": "To use the yaml reference dumper" @@ -1590,7 +406,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.8-dev" + "dev-master": "3.4-dev" } }, "autoload": { @@ -1617,41 +433,52 @@ ], "description": "Symfony Config Component", "homepage": "https://symfony.com", - "time": "2018-11-26T09:38:12+00:00" + "time": "2019-07-17T15:23:18+00:00" }, { "name": "symfony/console", - "version": "v2.8.49", + "version": "v3.4.30", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "cbcf4b5e233af15cd2bbd50dee1ccc9b7927dc12" + "reference": "12940f20a816c978860fa4925b3f1bbb27e9ac46" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/cbcf4b5e233af15cd2bbd50dee1ccc9b7927dc12", - "reference": "cbcf4b5e233af15cd2bbd50dee1ccc9b7927dc12", + "url": "https://api.github.com/repos/symfony/console/zipball/12940f20a816c978860fa4925b3f1bbb27e9ac46", + "reference": "12940f20a816c978860fa4925b3f1bbb27e9ac46", "shasum": "" }, "require": { - "php": ">=5.3.9", - "symfony/debug": "^2.7.2|~3.0.0", + "php": "^5.5.9|>=7.0.8", + "symfony/debug": "~2.8|~3.0|~4.0", "symfony/polyfill-mbstring": "~1.0" }, + "conflict": { + "symfony/dependency-injection": "<3.4", + "symfony/process": "<3.3" + }, + "provide": { + "psr/log-implementation": "1.0" + }, "require-dev": { "psr/log": "~1.0", - "symfony/event-dispatcher": "~2.1|~3.0.0", - "symfony/process": "~2.1|~3.0.0" + "symfony/config": "~3.3|~4.0", + "symfony/dependency-injection": "~3.4|~4.0", + "symfony/event-dispatcher": "~2.8|~3.0|~4.0", + "symfony/lock": "~3.4|~4.0", + "symfony/process": "~3.3|~4.0" }, "suggest": { - "psr/log-implementation": "For using the console logger", + "psr/log": "For using the console logger", "symfony/event-dispatcher": "", + "symfony/lock": "", "symfony/process": "" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.8-dev" + "dev-master": "3.4-dev" } }, "autoload": { @@ -1678,37 +505,36 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", - "time": "2018-11-20T15:55:20+00:00" + "time": "2019-07-24T14:46:41+00:00" }, { "name": "symfony/debug", - "version": "v3.0.9", + "version": "v3.4.30", "source": { "type": "git", "url": "https://github.com/symfony/debug.git", - "reference": "697c527acd9ea1b2d3efac34d9806bf255278b0a" + "reference": "bc977cb2681d75988ab2d53d14c4245c6c04f82f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/debug/zipball/697c527acd9ea1b2d3efac34d9806bf255278b0a", - "reference": "697c527acd9ea1b2d3efac34d9806bf255278b0a", + "url": "https://api.github.com/repos/symfony/debug/zipball/bc977cb2681d75988ab2d53d14c4245c6c04f82f", + "reference": "bc977cb2681d75988ab2d53d14c4245c6c04f82f", "shasum": "" }, "require": { - "php": ">=5.5.9", + "php": "^5.5.9|>=7.0.8", "psr/log": "~1.0" }, "conflict": { "symfony/http-kernel": ">=2.3,<2.3.24|~2.4.0|>=2.5,<2.5.9|>=2.6,<2.6.2" }, "require-dev": { - "symfony/class-loader": "~2.8|~3.0", - "symfony/http-kernel": "~2.8|~3.0" + "symfony/http-kernel": "~2.8|~3.0|~4.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "3.4-dev" } }, "autoload": { @@ -1735,31 +561,162 @@ ], "description": "Symfony Debug Component", "homepage": "https://symfony.com", - "time": "2016-07-30T07:22:48+00:00" + "time": "2019-07-23T08:39:19+00:00" }, { - "name": "symfony/event-dispatcher", - "version": "v2.8.49", + "name": "symfony/dependency-injection", + "version": "v3.4.30", "source": { "type": "git", - "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "a77e974a5fecb4398833b0709210e3d5e334ffb0" + "url": "https://github.com/symfony/dependency-injection.git", + "reference": "ade939fe83d5ec5fcaa98628dc42d83232c8eb41" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/a77e974a5fecb4398833b0709210e3d5e334ffb0", - "reference": "a77e974a5fecb4398833b0709210e3d5e334ffb0", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/ade939fe83d5ec5fcaa98628dc42d83232c8eb41", + "reference": "ade939fe83d5ec5fcaa98628dc42d83232c8eb41", "shasum": "" }, "require": { - "php": ">=5.3.9" + "php": "^5.5.9|>=7.0.8", + "psr/container": "^1.0" + }, + "conflict": { + "symfony/config": "<3.3.7", + "symfony/finder": "<3.3", + "symfony/proxy-manager-bridge": "<3.4", + "symfony/yaml": "<3.4" + }, + "provide": { + "psr/container-implementation": "1.0" + }, + "require-dev": { + "symfony/config": "~3.3|~4.0", + "symfony/expression-language": "~2.8|~3.0|~4.0", + "symfony/yaml": "~3.4|~4.0" + }, + "suggest": { + "symfony/config": "", + "symfony/expression-language": "For using expressions in service container configuration", + "symfony/finder": "For using double-star glob patterns or when GLOB_BRACE portability is required", + "symfony/proxy-manager-bridge": "Generate service proxies to lazy load them", + "symfony/yaml": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\DependencyInjection\\": "" + }, + "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 DependencyInjection Component", + "homepage": "https://symfony.com", + "time": "2019-07-19T11:52:08+00:00" + }, + { + "name": "symfony/dotenv", + "version": "v3.4.30", + "source": { + "type": "git", + "url": "https://github.com/symfony/dotenv.git", + "reference": "3f4fdfb551bf36f2017d75cd2e6490fbe67f9d2d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/dotenv/zipball/3f4fdfb551bf36f2017d75cd2e6490fbe67f9d2d", + "reference": "3f4fdfb551bf36f2017d75cd2e6490fbe67f9d2d", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8" + }, + "require-dev": { + "symfony/process": "~3.2|~4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Dotenv\\": "" + }, + "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": "Registers environment variables from a .env file", + "homepage": "https://symfony.com", + "keywords": [ + "dotenv", + "env", + "environment" + ], + "time": "2019-06-23T08:10:04+00:00" + }, + { + "name": "symfony/event-dispatcher", + "version": "v3.4.30", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "f18fdd6cc7006441865e698420cee26bac94741f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/f18fdd6cc7006441865e698420cee26bac94741f", + "reference": "f18fdd6cc7006441865e698420cee26bac94741f", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8" + }, + "conflict": { + "symfony/dependency-injection": "<3.3" }, "require-dev": { "psr/log": "~1.0", - "symfony/config": "^2.0.5|~3.0.0", - "symfony/dependency-injection": "~2.6|~3.0.0", - "symfony/expression-language": "~2.6|~3.0.0", - "symfony/stopwatch": "~2.3|~3.0.0" + "symfony/config": "~2.8|~3.0|~4.0", + "symfony/dependency-injection": "~3.3|~4.0", + "symfony/expression-language": "~2.8|~3.0|~4.0", + "symfony/stopwatch": "~2.8|~3.0|~4.0" }, "suggest": { "symfony/dependency-injection": "", @@ -1768,7 +725,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.8-dev" + "dev-master": "3.4-dev" } }, "autoload": { @@ -1795,29 +752,30 @@ ], "description": "Symfony EventDispatcher Component", "homepage": "https://symfony.com", - "time": "2018-11-21T14:20:20+00:00" + "time": "2019-06-25T07:45:31+00:00" }, { "name": "symfony/filesystem", - "version": "v3.0.9", + "version": "v3.4.30", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "b2da5009d9bacbd91d83486aa1f44c793a8c380d" + "reference": "70adda061ef83bb7def63a17953dc41f203308a7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/b2da5009d9bacbd91d83486aa1f44c793a8c380d", - "reference": "b2da5009d9bacbd91d83486aa1f44c793a8c380d", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/70adda061ef83bb7def63a17953dc41f203308a7", + "reference": "70adda061ef83bb7def63a17953dc41f203308a7", "shasum": "" }, "require": { - "php": ">=5.5.9" + "php": "^5.5.9|>=7.0.8", + "symfony/polyfill-ctype": "~1.8" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "3.4-dev" } }, "autoload": { @@ -1844,29 +802,29 @@ ], "description": "Symfony Filesystem Component", "homepage": "https://symfony.com", - "time": "2016-07-20T05:43:46+00:00" + "time": "2019-06-23T09:29:17+00:00" }, { "name": "symfony/finder", - "version": "v2.8.49", + "version": "v3.4.30", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "1444eac52273e345d9b95129bf914639305a9ba4" + "reference": "1e762fdf73ace6ceb42ba5a6ca280be86082364a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/1444eac52273e345d9b95129bf914639305a9ba4", - "reference": "1444eac52273e345d9b95129bf914639305a9ba4", + "url": "https://api.github.com/repos/symfony/finder/zipball/1e762fdf73ace6ceb42ba5a6ca280be86082364a", + "reference": "1e762fdf73ace6ceb42ba5a6ca280be86082364a", "shasum": "" }, "require": { - "php": ">=5.3.9" + "php": "^5.5.9|>=7.0.8" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.8-dev" + "dev-master": "3.4-dev" } }, "autoload": { @@ -1893,20 +851,334 @@ ], "description": "Symfony Finder Component", "homepage": "https://symfony.com", - "time": "2018-11-11T11:18:13+00:00" + "time": "2019-06-28T08:02:59+00:00" }, { - "name": "symfony/polyfill-ctype", - "version": "v1.10.0", + "name": "symfony/framework-bundle", + "version": "v3.4.30", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "e3d826245268269cd66f8326bd8bc066687b4a19" + "url": "https://github.com/symfony/framework-bundle.git", + "reference": "0bb32eafe4adeb39faff1902dd9ec54e0ed2a265" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/e3d826245268269cd66f8326bd8bc066687b4a19", - "reference": "e3d826245268269cd66f8326bd8bc066687b4a19", + "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/0bb32eafe4adeb39faff1902dd9ec54e0ed2a265", + "reference": "0bb32eafe4adeb39faff1902dd9ec54e0ed2a265", + "shasum": "" + }, + "require": { + "ext-xml": "*", + "php": "^5.5.9|>=7.0.8", + "symfony/cache": "~3.4|~4.0", + "symfony/class-loader": "~3.2", + "symfony/config": "~3.4|~4.0", + "symfony/debug": "~2.8|~3.0|~4.0", + "symfony/dependency-injection": "^3.4.24|^4.2.5", + "symfony/event-dispatcher": "~3.4|~4.0", + "symfony/filesystem": "~2.8|~3.0|~4.0", + "symfony/finder": "~2.8|~3.0|~4.0", + "symfony/http-foundation": "^3.3.11|~4.0", + "symfony/http-kernel": "~3.4|~4.0", + "symfony/polyfill-mbstring": "~1.0", + "symfony/routing": "^3.4.5|^4.0.5" + }, + "conflict": { + "phpdocumentor/reflection-docblock": "<3.0", + "phpdocumentor/type-resolver": "<0.2.1", + "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0", + "symfony/asset": "<3.3", + "symfony/console": "<3.4", + "symfony/form": "<3.4", + "symfony/property-info": "<3.3", + "symfony/serializer": "<3.3", + "symfony/stopwatch": "<3.4", + "symfony/translation": "<3.4", + "symfony/validator": "<3.4", + "symfony/workflow": "<3.3" + }, + "require-dev": { + "doctrine/annotations": "~1.0", + "doctrine/cache": "~1.0", + "fig/link-util": "^1.0", + "phpdocumentor/reflection-docblock": "^3.0|^4.0", + "symfony/asset": "~3.3|~4.0", + "symfony/browser-kit": "~2.8|~3.0|~4.0", + "symfony/console": "~3.4|~4.0", + "symfony/css-selector": "~2.8|~3.0|~4.0", + "symfony/dom-crawler": "~2.8|~3.0|~4.0", + "symfony/expression-language": "~2.8|~3.0|~4.0", + "symfony/form": "^3.4.22|~4.1.11|^4.2.3", + "symfony/lock": "~3.4|~4.0", + "symfony/polyfill-intl-icu": "~1.0", + "symfony/process": "~2.8|~3.0|~4.0", + "symfony/property-info": "~3.3|~4.0", + "symfony/security-core": "~3.2|~4.0", + "symfony/security-csrf": "^2.8.31|^3.3.13|~4.0", + "symfony/serializer": "~3.3|~4.0", + "symfony/stopwatch": "~3.4|~4.0", + "symfony/templating": "~2.8|~3.0|~4.0", + "symfony/translation": "~3.4|~4.0", + "symfony/validator": "~3.4|~4.0", + "symfony/var-dumper": "~3.3|~4.0", + "symfony/web-link": "~3.3|~4.0", + "symfony/workflow": "~3.3|~4.0", + "symfony/yaml": "~3.2|~4.0", + "twig/twig": "~1.34|~2.4" + }, + "suggest": { + "ext-apcu": "For best performance of the system caches", + "symfony/console": "For using the console commands", + "symfony/form": "For using forms", + "symfony/property-info": "For using the property_info service", + "symfony/serializer": "For using the serializer service", + "symfony/validator": "For using validation", + "symfony/web-link": "For using web links, features such as preloading, prefetching or prerendering", + "symfony/yaml": "For using the debug:config and lint:yaml commands" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Bundle\\FrameworkBundle\\": "" + }, + "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 FrameworkBundle", + "homepage": "https://symfony.com", + "time": "2019-07-24T16:59:53+00:00" + }, + { + "name": "symfony/http-foundation", + "version": "v3.4.30", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-foundation.git", + "reference": "c450706851050ade2e1f30d012d50bb9173f7f3d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/c450706851050ade2e1f30d012d50bb9173f7f3d", + "reference": "c450706851050ade2e1f30d012d50bb9173f7f3d", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8", + "symfony/polyfill-mbstring": "~1.1", + "symfony/polyfill-php70": "~1.6" + }, + "require-dev": { + "symfony/expression-language": "~2.8|~3.0|~4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpFoundation\\": "" + }, + "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 HttpFoundation Component", + "homepage": "https://symfony.com", + "time": "2019-07-23T06:27:47+00:00" + }, + { + "name": "symfony/http-kernel", + "version": "v3.4.30", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-kernel.git", + "reference": "83a1b30c5dd02f5c3cd708a432071d0c99474eb3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/83a1b30c5dd02f5c3cd708a432071d0c99474eb3", + "reference": "83a1b30c5dd02f5c3cd708a432071d0c99474eb3", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8", + "psr/log": "~1.0", + "symfony/debug": "^3.3.3|~4.0", + "symfony/event-dispatcher": "~2.8|~3.0|~4.0", + "symfony/http-foundation": "~3.4.12|~4.0.12|^4.1.1", + "symfony/polyfill-ctype": "~1.8" + }, + "conflict": { + "symfony/config": "<2.8", + "symfony/dependency-injection": "<3.4.10|<4.0.10,>=4", + "symfony/var-dumper": "<3.3", + "twig/twig": "<1.34|<2.4,>=2" + }, + "provide": { + "psr/log-implementation": "1.0" + }, + "require-dev": { + "psr/cache": "~1.0", + "symfony/browser-kit": "~2.8|~3.0|~4.0", + "symfony/class-loader": "~2.8|~3.0", + "symfony/config": "~2.8|~3.0|~4.0", + "symfony/console": "~2.8|~3.0|~4.0", + "symfony/css-selector": "~2.8|~3.0|~4.0", + "symfony/dependency-injection": "^3.4.10|^4.0.10", + "symfony/dom-crawler": "~2.8|~3.0|~4.0", + "symfony/expression-language": "~2.8|~3.0|~4.0", + "symfony/finder": "~2.8|~3.0|~4.0", + "symfony/process": "~2.8|~3.0|~4.0", + "symfony/routing": "~3.4|~4.0", + "symfony/stopwatch": "~2.8|~3.0|~4.0", + "symfony/templating": "~2.8|~3.0|~4.0", + "symfony/translation": "~2.8|~3.0|~4.0", + "symfony/var-dumper": "~3.3|~4.0" + }, + "suggest": { + "symfony/browser-kit": "", + "symfony/config": "", + "symfony/console": "", + "symfony/dependency-injection": "", + "symfony/finder": "", + "symfony/var-dumper": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpKernel\\": "" + }, + "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 HttpKernel Component", + "homepage": "https://symfony.com", + "time": "2019-07-27T17:14:06+00:00" + }, + { + "name": "symfony/polyfill-apcu", + "version": "v1.12.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-apcu.git", + "reference": "71ce80635d5dcd67772b4dda00b86068595f64d5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-apcu/zipball/71ce80635d5dcd67772b4dda00b86068595f64d5", + "reference": "71ce80635d5dcd67772b4dda00b86068595f64d5", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.12-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": "2019-08-06T08:03:45+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.12.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "550ebaac289296ce228a706d0867afc34687e3f4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/550ebaac289296ce228a706d0867afc34687e3f4", + "reference": "550ebaac289296ce228a706d0867afc34687e3f4", "shasum": "" }, "require": { @@ -1918,7 +1190,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.9-dev" + "dev-master": "1.12-dev" } }, "autoload": { @@ -1935,12 +1207,12 @@ ], "authors": [ { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" }, { - "name": "Gert de Pagter", - "email": "backendtea@gmail.com" + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], "description": "Symfony polyfill for ctype functions", @@ -1951,20 +1223,20 @@ "polyfill", "portable" ], - "time": "2018-08-06T14:22:27+00:00" + "time": "2019-08-06T08:03:45+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.10.0", + "version": "v1.12.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "c79c051f5b3a46be09205c73b80b346e4153e494" + "reference": "b42a2f66e8f1b15ccf25652c3424265923eb4f17" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/c79c051f5b3a46be09205c73b80b346e4153e494", - "reference": "c79c051f5b3a46be09205c73b80b346e4153e494", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/b42a2f66e8f1b15ccf25652c3424265923eb4f17", + "reference": "b42a2f66e8f1b15ccf25652c3424265923eb4f17", "shasum": "" }, "require": { @@ -1976,7 +1248,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.9-dev" + "dev-master": "1.12-dev" } }, "autoload": { @@ -2010,34 +1282,114 @@ "portable", "shim" ], - "time": "2018-09-21T13:07:52+00:00" + "time": "2019-08-06T08:03:45+00:00" }, { - "name": "symfony/process", - "version": "v2.8.49", + "name": "symfony/polyfill-php70", + "version": "v1.12.0", "source": { "type": "git", - "url": "https://github.com/symfony/process.git", - "reference": "c3591a09c78639822b0b290d44edb69bf9f05dc8" + "url": "https://github.com/symfony/polyfill-php70.git", + "reference": "54b4c428a0054e254223797d2713c31e08610831" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/c3591a09c78639822b0b290d44edb69bf9f05dc8", - "reference": "c3591a09c78639822b0b290d44edb69bf9f05dc8", + "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/54b4c428a0054e254223797d2713c31e08610831", + "reference": "54b4c428a0054e254223797d2713c31e08610831", "shasum": "" }, "require": { - "php": ">=5.3.9" + "paragonie/random_compat": "~1.0|~2.0|~9.99", + "php": ">=5.3.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.8-dev" + "dev-master": "1.12-dev" } }, "autoload": { "psr-4": { - "Symfony\\Component\\Process\\": "" + "Symfony\\Polyfill\\Php70\\": "" + }, + "files": [ + "bootstrap.php" + ], + "classmap": [ + "Resources/stubs" + ] + }, + "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 some PHP 7.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "time": "2019-08-06T08:03:45+00:00" + }, + { + "name": "symfony/routing", + "version": "v3.4.30", + "source": { + "type": "git", + "url": "https://github.com/symfony/routing.git", + "reference": "8d804d8a65a26dc9de1aaf2ff3a421e581d050e6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/routing/zipball/8d804d8a65a26dc9de1aaf2ff3a421e581d050e6", + "reference": "8d804d8a65a26dc9de1aaf2ff3a421e581d050e6", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8" + }, + "conflict": { + "symfony/config": "<3.3.1", + "symfony/dependency-injection": "<3.3", + "symfony/yaml": "<3.4" + }, + "require-dev": { + "doctrine/annotations": "~1.0", + "psr/log": "~1.0", + "symfony/config": "^3.3.1|~4.0", + "symfony/dependency-injection": "~3.3|~4.0", + "symfony/expression-language": "~2.8|~3.0|~4.0", + "symfony/http-foundation": "~2.8|~3.0|~4.0", + "symfony/yaml": "~3.4|~4.0" + }, + "suggest": { + "doctrine/annotations": "For using the annotation loader", + "symfony/config": "For using the all-in-one router or any loader", + "symfony/expression-language": "For using expression matching", + "symfony/http-foundation": "For using a Symfony Request object", + "symfony/yaml": "For using the YAML loader" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Routing\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -2057,31 +1409,329 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Process Component", + "description": "Symfony Routing Component", "homepage": "https://symfony.com", - "time": "2018-11-11T11:18:13+00:00" + "keywords": [ + "router", + "routing", + "uri", + "url" + ], + "time": "2019-06-26T11:14:13+00:00" }, { - "name": "symfony/stopwatch", - "version": "v2.8.49", + "name": "symfony/twig-bridge", + "version": "v3.4.30", "source": { "type": "git", - "url": "https://github.com/symfony/stopwatch.git", - "reference": "752586c80af8a85aeb74d1ae8202411c68836663" + "url": "https://github.com/symfony/twig-bridge.git", + "reference": "bf136ba1fbd18126ab65a930ea0daebb0b0fd159" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/752586c80af8a85aeb74d1ae8202411c68836663", - "reference": "752586c80af8a85aeb74d1ae8202411c68836663", + "url": "https://api.github.com/repos/symfony/twig-bridge/zipball/bf136ba1fbd18126ab65a930ea0daebb0b0fd159", + "reference": "bf136ba1fbd18126ab65a930ea0daebb0b0fd159", "shasum": "" }, "require": { - "php": ">=5.3.9" + "php": "^5.5.9|>=7.0.8", + "twig/twig": "^1.40|^2.9" + }, + "conflict": { + "symfony/console": "<3.4", + "symfony/form": "<3.4.13|>=4.0,<4.0.13|>=4.1,<4.1.2" + }, + "require-dev": { + "symfony/asset": "~2.8|~3.0|~4.0", + "symfony/console": "~3.4|~4.0", + "symfony/dependency-injection": "~2.8|~3.0|~4.0", + "symfony/expression-language": "~2.8|~3.0|~4.0", + "symfony/finder": "~2.8|~3.0|~4.0", + "symfony/form": "^3.4.23|^4.2.4", + "symfony/http-foundation": "^3.3.11|~4.0", + "symfony/http-kernel": "~3.2|~4.0", + "symfony/polyfill-intl-icu": "~1.0", + "symfony/routing": "~2.8|~3.0|~4.0", + "symfony/security": "^2.8.31|^3.3.13|~4.0", + "symfony/security-acl": "~2.8|~3.0", + "symfony/stopwatch": "~2.8|~3.0|~4.0", + "symfony/templating": "~2.8|~3.0|~4.0", + "symfony/translation": "~2.8|~3.0|~4.0", + "symfony/var-dumper": "~2.8.10|~3.1.4|~3.2|~4.0", + "symfony/web-link": "~3.3|~4.0", + "symfony/workflow": "~3.3|~4.0", + "symfony/yaml": "~2.8|~3.0|~4.0" + }, + "suggest": { + "symfony/asset": "For using the AssetExtension", + "symfony/expression-language": "For using the ExpressionExtension", + "symfony/finder": "", + "symfony/form": "For using the FormExtension", + "symfony/http-kernel": "For using the HttpKernelExtension", + "symfony/routing": "For using the RoutingExtension", + "symfony/security": "For using the SecurityExtension", + "symfony/stopwatch": "For using the StopwatchExtension", + "symfony/templating": "For using the TwigEngine", + "symfony/translation": "For using the TranslationExtension", + "symfony/var-dumper": "For using the DumpExtension", + "symfony/web-link": "For using the WebLinkExtension", + "symfony/yaml": "For using the YamlExtension" + }, + "type": "symfony-bridge", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Bridge\\Twig\\": "" + }, + "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 Twig Bridge", + "homepage": "https://symfony.com", + "time": "2019-07-24T19:39:07+00:00" + }, + { + "name": "symfony/twig-bundle", + "version": "v3.4.30", + "source": { + "type": "git", + "url": "https://github.com/symfony/twig-bundle.git", + "reference": "fae76fd8d8cff50295282d752e9842324bd277e4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/twig-bundle/zipball/fae76fd8d8cff50295282d752e9842324bd277e4", + "reference": "fae76fd8d8cff50295282d752e9842324bd277e4", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8", + "symfony/config": "~3.2|~4.0", + "symfony/debug": "~2.8|~3.0|~4.0", + "symfony/http-foundation": "~2.8|~3.0|~4.0", + "symfony/http-kernel": "^3.3|~4.0", + "symfony/polyfill-ctype": "~1.8", + "symfony/twig-bridge": "^3.4.3|^4.0.3", + "twig/twig": "~1.40|~2.9" + }, + "conflict": { + "symfony/dependency-injection": "<3.4", + "symfony/event-dispatcher": "<3.3.1" + }, + "require-dev": { + "doctrine/annotations": "~1.0", + "doctrine/cache": "~1.0", + "symfony/asset": "~2.8|~3.0|~4.0", + "symfony/dependency-injection": "~3.4.24|^4.2.5", + "symfony/expression-language": "~2.8|~3.0|~4.0", + "symfony/finder": "~2.8|~3.0|~4.0", + "symfony/form": "~2.8|~3.0|~4.0", + "symfony/framework-bundle": "^3.3.11|~4.0", + "symfony/routing": "~2.8|~3.0|~4.0", + "symfony/stopwatch": "~2.8|~3.0|~4.0", + "symfony/templating": "~2.8|~3.0|~4.0", + "symfony/web-link": "~3.3|~4.0", + "symfony/yaml": "~2.8|~3.0|~4.0" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Bundle\\TwigBundle\\": "" + }, + "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 TwigBundle", + "homepage": "https://symfony.com", + "time": "2019-07-19T08:16:37+00:00" + }, + { + "name": "symfony/yaml", + "version": "v3.4.30", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "051d045c684148060ebfc9affb7e3f5e0899d40b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/051d045c684148060ebfc9affb7e3f5e0899d40b", + "reference": "051d045c684148060ebfc9affb7e3f5e0899d40b", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8", + "symfony/polyfill-ctype": "~1.8" + }, + "conflict": { + "symfony/console": "<3.4" + }, + "require-dev": { + "symfony/console": "~3.4|~4.0" + }, + "suggest": { + "symfony/console": "For validating YAML files using the lint command" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.8-dev" + "dev-master": "3.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Yaml\\": "" + }, + "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 Yaml Component", + "homepage": "https://symfony.com", + "time": "2019-07-24T13:01:31+00:00" + }, + { + "name": "twig/twig", + "version": "v1.42.2", + "source": { + "type": "git", + "url": "https://github.com/twigphp/Twig.git", + "reference": "21707d6ebd05476854805e4f91b836531941bcd4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/21707d6ebd05476854805e4f91b836531941bcd4", + "reference": "21707d6ebd05476854805e4f91b836531941bcd4", + "shasum": "" + }, + "require": { + "php": ">=5.4.0", + "symfony/polyfill-ctype": "^1.8" + }, + "require-dev": { + "psr/container": "^1.0", + "symfony/debug": "^2.7", + "symfony/phpunit-bridge": "^3.4.19|^4.1.8|^5.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.42-dev" + } + }, + "autoload": { + "psr-0": { + "Twig_": "lib/" + }, + "psr-4": { + "Twig\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Fabien Potencier", + "role": "Lead Developer", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org" + }, + { + "name": "Armin Ronacher", + "role": "Project Founder", + "email": "armin.ronacher@active-4.com" + }, + { + "name": "Twig Team", + "role": "Contributors", + "homepage": "https://twig.symfony.com/contributors" + } + ], + "description": "Twig, the flexible, fast, and secure template language for PHP", + "homepage": "https://twig.symfony.com", + "keywords": [ + "templating" + ], + "time": "2019-06-18T15:35:16+00:00" + } + ], + "packages-dev": [ + { + "name": "symfony/stopwatch", + "version": "v3.4.30", + "source": { + "type": "git", + "url": "https://github.com/symfony/stopwatch.git", + "reference": "2a651c2645c10bbedd21170771f122d935e0dd58" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/2a651c2645c10bbedd21170771f122d935e0dd58", + "reference": "2a651c2645c10bbedd21170771f122d935e0dd58", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" } }, "autoload": { @@ -2108,928 +1758,143 @@ ], "description": "Symfony Stopwatch Component", "homepage": "https://symfony.com", - "time": "2018-11-11T11:18:13+00:00" + "time": "2019-01-16T09:39:14+00:00" }, { - "name": "symfony/translation", - "version": "v3.0.9", + "name": "symfony/var-dumper", + "version": "v3.4.30", "source": { "type": "git", - "url": "https://github.com/symfony/translation.git", - "reference": "eee6c664853fd0576f21ae25725cfffeafe83f26" + "url": "https://github.com/symfony/var-dumper.git", + "reference": "b6a45abfe961183a4c26fad98a6112c487e983bf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/eee6c664853fd0576f21ae25725cfffeafe83f26", - "reference": "eee6c664853fd0576f21ae25725cfffeafe83f26", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/b6a45abfe961183a4c26fad98a6112c487e983bf", + "reference": "b6a45abfe961183a4c26fad98a6112c487e983bf", "shasum": "" }, "require": { - "php": ">=5.5.9", + "php": "^5.5.9|>=7.0.8", "symfony/polyfill-mbstring": "~1.0" }, "conflict": { - "symfony/config": "<2.8" + "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0" }, "require-dev": { - "psr/log": "~1.0", - "symfony/config": "~2.8|~3.0", - "symfony/intl": "~2.8|~3.0", - "symfony/yaml": "~2.8|~3.0" + "ext-iconv": "*", + "twig/twig": "~1.34|~2.4" }, "suggest": { - "psr/log": "To use logging capability in translator", - "symfony/config": "", - "symfony/yaml": "" + "ext-iconv": "To convert non-UTF-8 strings to UTF-8 (or symfony/polyfill-iconv in case ext-iconv cannot be used).", + "ext-intl": "To show region name in time zone dump", + "ext-symfony_debug": "" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Translation\\": "" - }, - "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 Translation Component", - "homepage": "https://symfony.com", - "time": "2016-07-30T07:22:48+00:00" - }, - { - "name": "symfony/validator", - "version": "v2.8.49", - "source": { - "type": "git", - "url": "https://github.com/symfony/validator.git", - "reference": "d5d2090bba3139d8ddb79959fbf516e87238fe3a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/validator/zipball/d5d2090bba3139d8ddb79959fbf516e87238fe3a", - "reference": "d5d2090bba3139d8ddb79959fbf516e87238fe3a", - "shasum": "" - }, - "require": { - "php": ">=5.3.9", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-mbstring": "~1.0", - "symfony/translation": "~2.4|~3.0.0" - }, - "require-dev": { - "doctrine/annotations": "~1.0", - "doctrine/cache": "~1.0", - "egulias/email-validator": "^1.2.1", - "symfony/config": "~2.2|~3.0.0", - "symfony/expression-language": "~2.4|~3.0.0", - "symfony/http-foundation": "~2.3|~3.0.0", - "symfony/intl": "~2.7.25|^2.8.18|~3.2.5", - "symfony/property-access": "~2.3|~3.0.0", - "symfony/yaml": "^2.0.5|~3.0.0" - }, - "suggest": { - "doctrine/annotations": "For using the annotation mapping. You will also need doctrine/cache.", - "doctrine/cache": "For using the default cached annotation reader and metadata cache.", - "egulias/email-validator": "Strict (RFC compliant) email validation", - "symfony/config": "", - "symfony/expression-language": "For using the 2.4 Expression validator", - "symfony/http-foundation": "", - "symfony/intl": "", - "symfony/property-access": "For using the 2.4 Validator API", - "symfony/yaml": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.8-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Validator\\": "" - }, - "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 Validator Component", - "homepage": "https://symfony.com", - "time": "2018-11-14T14:06:48+00:00" - }, - { - "name": "twig/twig", - "version": "v1.37.1", - "source": { - "type": "git", - "url": "https://github.com/twigphp/Twig.git", - "reference": "66be9366c76cbf23e82e7171d47cbfa54a057a62" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/66be9366c76cbf23e82e7171d47cbfa54a057a62", - "reference": "66be9366c76cbf23e82e7171d47cbfa54a057a62", - "shasum": "" - }, - "require": { - "php": ">=5.4.0", - "symfony/polyfill-ctype": "^1.8" - }, - "require-dev": { - "psr/container": "^1.0", - "symfony/debug": "^2.7", - "symfony/phpunit-bridge": "^3.4.19|^4.1.8" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.37-dev" - } - }, - "autoload": { - "psr-0": { - "Twig_": "lib/" - }, - "psr-4": { - "Twig\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com", - "homepage": "http://fabien.potencier.org", - "role": "Lead Developer" - }, - { - "name": "Armin Ronacher", - "email": "armin.ronacher@active-4.com", - "role": "Project Founder" - }, - { - "name": "Twig Team", - "homepage": "https://twig.symfony.com/contributors", - "role": "Contributors" - } - ], - "description": "Twig, the flexible, fast, and secure template language for PHP", - "homepage": "https://twig.symfony.com", - "keywords": [ - "templating" - ], - "time": "2019-01-14T14:59:29+00:00" - }, - { - "name": "zendframework/zend-cache", - "version": "2.8.2", - "source": { - "type": "git", - "url": "https://github.com/zendframework/zend-cache.git", - "reference": "4983dff629956490c78b88adcc8ece4711d7d8a3" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-cache/zipball/4983dff629956490c78b88adcc8ece4711d7d8a3", - "reference": "4983dff629956490c78b88adcc8ece4711d7d8a3", - "shasum": "" - }, - "require": { - "php": "^5.6 || ^7.0", - "psr/cache": "^1.0", - "psr/simple-cache": "^1.0", - "zendframework/zend-eventmanager": "^2.6.3 || ^3.2", - "zendframework/zend-servicemanager": "^2.7.8 || ^3.3", - "zendframework/zend-stdlib": "^2.7.7 || ^3.1" - }, - "provide": { - "psr/cache-implementation": "1.0", - "psr/simple-cache-implementation": "1.0" - }, - "require-dev": { - "cache/integration-tests": "^0.16", - "phpbench/phpbench": "^0.13", - "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-serializer": "^2.6", - "zendframework/zend-session": "^2.7.4" - }, - "suggest": { - "ext-apc": "APC or compatible extension, to use the APC storage adapter", - "ext-apcu": "APCU >= 5.1.0, to use the APCu storage adapter", - "ext-dba": "DBA, to use the DBA storage adapter", - "ext-memcache": "Memcache >= 2.0.0 to use the Memcache storage adapter", - "ext-memcached": "Memcached >= 1.0.0 to use the Memcached storage adapter", - "ext-mongo": "Mongo, to use MongoDb storage adapter", - "ext-mongodb": "MongoDB, to use the ExtMongoDb storage adapter", - "ext-redis": "Redis, to use Redis storage adapter", - "ext-wincache": "WinCache, to use the WinCache storage adapter", - "ext-xcache": "XCache, to use the XCache storage adapter", - "mongodb/mongodb": "Required for use with the ext-mongodb adapter", - "mongofill/mongofill": "Alternative to ext-mongo - a pure PHP implementation designed as a drop in replacement", - "zendframework/zend-serializer": "Zend\\Serializer component", - "zendframework/zend-session": "Zend\\Session component" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.8.x-dev", - "dev-develop": "2.9.x-dev" - }, - "zf": { - "component": "Zend\\Cache", - "config-provider": "Zend\\Cache\\ConfigProvider" + "dev-master": "3.4-dev" } }, "autoload": { "files": [ - "autoload/patternPluginManagerPolyfill.php" + "Resources/functions/dump.php" ], "psr-4": { - "Zend\\Cache\\": "src/" - } + "Symfony\\Component\\VarDumper\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "description": "Caching implementation with a variety of storage options, as well as codified caching strategies for callbacks, classes, and output", + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony mechanism for exploring and dumping PHP variables", + "homepage": "https://symfony.com", "keywords": [ - "ZendFramework", - "cache", - "psr-16", - "psr-6", - "zf" + "debug", + "dump" ], - "time": "2018-05-01T21:58:00+00:00" + "time": "2019-07-26T11:29:23+00:00" }, { - "name": "zendframework/zend-config", - "version": "2.6.0", + "name": "symfony/web-profiler-bundle", + "version": "v3.4.30", "source": { "type": "git", - "url": "https://github.com/zendframework/zend-config.git", - "reference": "2920e877a9f6dca9fa8f6bd3b1ffc2e19bb1e30d" + "url": "https://github.com/symfony/web-profiler-bundle.git", + "reference": "0e9627b1a3864949fa1d72eec78dab02830b5206" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-config/zipball/2920e877a9f6dca9fa8f6bd3b1ffc2e19bb1e30d", - "reference": "2920e877a9f6dca9fa8f6bd3b1ffc2e19bb1e30d", + "url": "https://api.github.com/repos/symfony/web-profiler-bundle/zipball/0e9627b1a3864949fa1d72eec78dab02830b5206", + "reference": "0e9627b1a3864949fa1d72eec78dab02830b5206", "shasum": "" }, "require": { - "php": "^5.5 || ^7.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" - }, - "require-dev": { - "fabpot/php-cs-fixer": "1.7.*", - "phpunit/phpunit": "~4.0", - "zendframework/zend-filter": "^2.6", - "zendframework/zend-i18n": "^2.5", - "zendframework/zend-json": "^2.6.1", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3" - }, - "suggest": { - "zendframework/zend-filter": "Zend\\Filter component", - "zendframework/zend-i18n": "Zend\\I18n component", - "zendframework/zend-json": "Zend\\Json to use the Json reader or writer classes", - "zendframework/zend-servicemanager": "Zend\\ServiceManager for use with the Config Factory to retrieve reader and writer instances" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.6-dev", - "dev-develop": "2.7-dev" - } - }, - "autoload": { - "psr-4": { - "Zend\\Config\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "description": "provides a nested object property based user interface for accessing this configuration data within application code", - "homepage": "https://github.com/zendframework/zend-config", - "keywords": [ - "config", - "zf2" - ], - "time": "2016-02-04T23:01:10+00:00" - }, - { - "name": "zendframework/zend-eventmanager", - "version": "3.2.1", - "source": { - "type": "git", - "url": "https://github.com/zendframework/zend-eventmanager.git", - "reference": "a5e2583a211f73604691586b8406ff7296a946dd" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-eventmanager/zipball/a5e2583a211f73604691586b8406ff7296a946dd", - "reference": "a5e2583a211f73604691586b8406ff7296a946dd", - "shasum": "" - }, - "require": { - "php": "^5.6 || ^7.0" - }, - "require-dev": { - "athletic/athletic": "^0.1", - "container-interop/container-interop": "^1.1.0", - "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-stdlib": "^2.7.3 || ^3.0" - }, - "suggest": { - "container-interop/container-interop": "^1.1.0, to use the lazy listeners feature", - "zendframework/zend-stdlib": "^2.7.3 || ^3.0, to use the FilterChain feature" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.2-dev", - "dev-develop": "3.3-dev" - } - }, - "autoload": { - "psr-4": { - "Zend\\EventManager\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "description": "Trigger and listen to events within a PHP application", - "homepage": "https://github.com/zendframework/zend-eventmanager", - "keywords": [ - "event", - "eventmanager", - "events", - "zf2" - ], - "time": "2018-04-25T15:33:34+00:00" - }, - { - "name": "zendframework/zend-filter", - "version": "2.9.1", - "source": { - "type": "git", - "url": "https://github.com/zendframework/zend-filter.git", - "reference": "1c3e6d02f9cd5f6c929c9859498f5efbe216e86f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-filter/zipball/1c3e6d02f9cd5f6c929c9859498f5efbe216e86f", - "reference": "1c3e6d02f9cd5f6c929c9859498f5efbe216e86f", - "shasum": "" - }, - "require": { - "php": "^5.6 || ^7.0", - "zendframework/zend-stdlib": "^2.7.7 || ^3.1" + "php": "^5.5.9|>=7.0.8", + "symfony/http-kernel": "~3.4.25|^4.2.6", + "symfony/polyfill-php70": "~1.0", + "symfony/routing": "~2.8|~3.0|~4.0", + "symfony/twig-bridge": "~2.8|~3.0|~4.0", + "symfony/var-dumper": "~3.3|~4.0", + "twig/twig": "~1.34|~2.4" }, "conflict": { - "zendframework/zend-validator": "<2.10.1" + "symfony/config": "<3.4", + "symfony/dependency-injection": "<3.4", + "symfony/event-dispatcher": "<3.3.1", + "symfony/var-dumper": "<3.3" }, "require-dev": { - "pear/archive_tar": "^1.4.3", - "phpunit/phpunit": "^5.7.23 || ^6.4.3", - "psr/http-factory": "^1.0", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-crypt": "^3.2.1", - "zendframework/zend-servicemanager": "^2.7.8 || ^3.3", - "zendframework/zend-uri": "^2.6" + "symfony/config": "~3.4|~4.0", + "symfony/console": "~2.8|~3.0|~4.0", + "symfony/dependency-injection": "~3.4|~4.0", + "symfony/stopwatch": "~2.8|~3.0|~4.0" }, - "suggest": { - "psr/http-factory-implementation": "psr/http-factory-implementation, for creating file upload instances when consuming PSR-7 in file upload filters", - "zendframework/zend-crypt": "Zend\\Crypt component, for encryption filters", - "zendframework/zend-i18n": "Zend\\I18n component for filters depending on i18n functionality", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component, for using the filter chain functionality", - "zendframework/zend-uri": "Zend\\Uri component, for the UriNormalize filter" - }, - "type": "library", + "type": "symfony-bundle", "extra": { "branch-alias": { - "dev-master": "2.9.x-dev", - "dev-develop": "2.10.x-dev" + "dev-master": "3.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Bundle\\WebProfilerBundle\\": "" }, - "zf": { - "component": "Zend\\Filter", - "config-provider": "Zend\\Filter\\ConfigProvider" - } - }, - "autoload": { - "psr-4": { - "Zend\\Filter\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "description": "provides a set of commonly needed data filters", - "keywords": [ - "ZendFramework", - "filter", - "zf" - ], - "time": "2018-12-17T16:00:04+00:00" - }, - { - "name": "zendframework/zend-hydrator", - "version": "1.1.0", - "source": { - "type": "git", - "url": "https://github.com/zendframework/zend-hydrator.git", - "reference": "22652e1661a5a10b3f564cf7824a2206cf5a4a65" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-hydrator/zipball/22652e1661a5a10b3f564cf7824a2206cf5a4a65", - "reference": "22652e1661a5a10b3f564cf7824a2206cf5a4a65", - "shasum": "" - }, - "require": { - "php": "^5.5 || ^7.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" - }, - "require-dev": { - "phpunit/phpunit": "~4.0", - "squizlabs/php_codesniffer": "^2.0@dev", - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", - "zendframework/zend-filter": "^2.6", - "zendframework/zend-inputfilter": "^2.6", - "zendframework/zend-serializer": "^2.6.1", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3" - }, - "suggest": { - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0, to support aggregate hydrator usage", - "zendframework/zend-filter": "^2.6, to support naming strategy hydrator usage", - "zendframework/zend-serializer": "^2.6.1, to use the SerializableStrategy", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3, to support hydrator plugin manager usage" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-release-1.0": "1.0-dev", - "dev-release-1.1": "1.1-dev", - "dev-master": "2.0-dev", - "dev-develop": "2.1-dev" - } - }, - "autoload": { - "psr-4": { - "Zend\\Hydrator\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "homepage": "https://github.com/zendframework/zend-hydrator", - "keywords": [ - "hydrator", - "zf2" - ], - "time": "2016-02-18T22:38:26+00:00" - }, - { - "name": "zendframework/zend-i18n", - "version": "2.9.0", - "source": { - "type": "git", - "url": "https://github.com/zendframework/zend-i18n.git", - "reference": "6d69af5a04e1a4de7250043cb1322f077a0cdb7f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-i18n/zipball/6d69af5a04e1a4de7250043cb1322f077a0cdb7f", - "reference": "6d69af5a04e1a4de7250043cb1322f077a0cdb7f", - "shasum": "" - }, - "require": { - "php": "^5.6 || ^7.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" - }, - "require-dev": { - "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2", - "zendframework/zend-cache": "^2.6.1", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-config": "^2.6", - "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", - "zendframework/zend-filter": "^2.6.1", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", - "zendframework/zend-validator": "^2.6", - "zendframework/zend-view": "^2.6.3" - }, - "suggest": { - "ext-intl": "Required for most features of Zend\\I18n; included in default builds of PHP", - "zendframework/zend-cache": "Zend\\Cache component", - "zendframework/zend-config": "Zend\\Config component", - "zendframework/zend-eventmanager": "You should install this package to use the events in the translator", - "zendframework/zend-filter": "You should install this package to use the provided filters", - "zendframework/zend-i18n-resources": "Translation resources", - "zendframework/zend-servicemanager": "Zend\\ServiceManager component", - "zendframework/zend-validator": "You should install this package to use the provided validators", - "zendframework/zend-view": "You should install this package to use the provided view helpers" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.9.x-dev", - "dev-develop": "2.10.x-dev" - }, - "zf": { - "component": "Zend\\I18n", - "config-provider": "Zend\\I18n\\ConfigProvider" - } - }, - "autoload": { - "psr-4": { - "Zend\\I18n\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "description": "Provide translations for your application, and filter and validate internationalized values", - "keywords": [ - "ZendFramework", - "i18n", - "zf" - ], - "time": "2018-05-16T16:39:13+00:00" - }, - { - "name": "zendframework/zend-json", - "version": "3.1.0", - "source": { - "type": "git", - "url": "https://github.com/zendframework/zend-json.git", - "reference": "4dd940e8e6f32f1d36ea6b0677ea57c540c7c19c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-json/zipball/4dd940e8e6f32f1d36ea6b0677ea57c540c7c19c", - "reference": "4dd940e8e6f32f1d36ea6b0677ea57c540c7c19c", - "shasum": "" - }, - "require": { - "php": "^5.6 || ^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^5.7.23 || ^6.4.3", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-stdlib": "^2.7.7 || ^3.1" - }, - "suggest": { - "zendframework/zend-json-server": "For implementing JSON-RPC servers", - "zendframework/zend-xml2json": "For converting XML documents to JSON" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.1.x-dev", - "dev-develop": "3.2.x-dev" - } - }, - "autoload": { - "psr-4": { - "Zend\\Json\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "description": "provides convenience methods for serializing native PHP to JSON and decoding JSON to native PHP", - "keywords": [ - "ZendFramework", - "json", - "zf" - ], - "time": "2018-01-04T17:51:34+00:00" - }, - { - "name": "zendframework/zend-serializer", - "version": "2.9.0", - "source": { - "type": "git", - "url": "https://github.com/zendframework/zend-serializer.git", - "reference": "0172690db48d8935edaf625c4cba38b79719892c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-serializer/zipball/0172690db48d8935edaf625c4cba38b79719892c", - "reference": "0172690db48d8935edaf625c4cba38b79719892c", - "shasum": "" - }, - "require": { - "php": "^5.6 || ^7.0", - "zendframework/zend-json": "^2.5 || ^3.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" - }, - "require-dev": { - "phpunit/phpunit": "^5.7.25 || ^6.4.4", - "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-math": "^2.6 || ^3.0", - "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3" - }, - "suggest": { - "zendframework/zend-math": "(^2.6 || ^3.0) To support Python Pickle serialization", - "zendframework/zend-servicemanager": "(^2.7.5 || ^3.0.3) To support plugin manager support" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.9.x-dev", - "dev-develop": "2.10.x-dev" - }, - "zf": { - "component": "Zend\\Serializer", - "config-provider": "Zend\\Serializer\\ConfigProvider" - } - }, - "autoload": { - "psr-4": { - "Zend\\Serializer\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "description": "provides an adapter based interface to simply generate storable representation of PHP types by different facilities, and recover", - "keywords": [ - "ZendFramework", - "serializer", - "zf" - ], - "time": "2018-05-14T18:45:18+00:00" - }, - { - "name": "zendframework/zend-servicemanager", - "version": "2.7.11", - "source": { - "type": "git", - "url": "https://github.com/zendframework/zend-servicemanager.git", - "reference": "99ec9ed5d0f15aed9876433c74c2709eb933d4c7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-servicemanager/zipball/99ec9ed5d0f15aed9876433c74c2709eb933d4c7", - "reference": "99ec9ed5d0f15aed9876433c74c2709eb933d4c7", - "shasum": "" - }, - "require": { - "container-interop/container-interop": "~1.0", - "php": "^5.5 || ^7.0" - }, - "require-dev": { - "athletic/athletic": "dev-master", - "fabpot/php-cs-fixer": "1.7.*", - "phpunit/phpunit": "~4.0", - "zendframework/zend-di": "~2.5", - "zendframework/zend-mvc": "~2.5" - }, - "suggest": { - "ocramius/proxy-manager": "ProxyManager 0.5.* to handle lazy initialization of services", - "zendframework/zend-di": "Zend\\Di component" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.7-dev", - "dev-develop": "3.0-dev" - } - }, - "autoload": { - "psr-4": { - "Zend\\ServiceManager\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "homepage": "https://github.com/zendframework/zend-servicemanager", - "keywords": [ - "servicemanager", - "zf2" - ], - "time": "2018-06-22T14:49:54+00:00" - }, - { - "name": "zendframework/zend-stdlib", - "version": "2.7.7", - "source": { - "type": "git", - "url": "https://github.com/zendframework/zend-stdlib.git", - "reference": "0e44eb46788f65e09e077eb7f44d2659143bcc1f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-stdlib/zipball/0e44eb46788f65e09e077eb7f44d2659143bcc1f", - "reference": "0e44eb46788f65e09e077eb7f44d2659143bcc1f", - "shasum": "" - }, - "require": { - "php": "^5.5 || ^7.0", - "zendframework/zend-hydrator": "~1.1" - }, - "require-dev": { - "athletic/athletic": "~0.1", - "fabpot/php-cs-fixer": "1.7.*", - "phpunit/phpunit": "~4.0", - "zendframework/zend-config": "~2.5", - "zendframework/zend-eventmanager": "~2.5", - "zendframework/zend-filter": "~2.5", - "zendframework/zend-inputfilter": "~2.5", - "zendframework/zend-serializer": "~2.5", - "zendframework/zend-servicemanager": "~2.5" - }, - "suggest": { - "zendframework/zend-eventmanager": "To support aggregate hydrator usage", - "zendframework/zend-filter": "To support naming strategy hydrator usage", - "zendframework/zend-serializer": "Zend\\Serializer component", - "zendframework/zend-servicemanager": "To support hydrator plugin manager usage" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-release-2.7": "2.7-dev", - "dev-master": "3.0-dev", - "dev-develop": "3.1-dev" - } - }, - "autoload": { - "psr-4": { - "Zend\\Stdlib\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "homepage": "https://github.com/zendframework/zend-stdlib", - "keywords": [ - "stdlib", - "zf2" - ], - "time": "2016-04-12T21:17:31+00:00" - }, - { - "name": "zetacomponents/base", - "version": "1.9.1", - "source": { - "type": "git", - "url": "https://github.com/zetacomponents/Base.git", - "reference": "489e20235989ddc97fdd793af31ac803972454f1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/zetacomponents/Base/zipball/489e20235989ddc97fdd793af31ac803972454f1", - "reference": "489e20235989ddc97fdd793af31ac803972454f1", - "shasum": "" - }, - "require-dev": { - "phpunit/phpunit": "~5.7", - "zetacomponents/unit-test": "*" - }, - "type": "library", - "autoload": { - "classmap": [ - "src" + "exclude-from-classmap": [ + "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "Apache-2.0" + "MIT" ], "authors": [ { - "name": "Sergey Alexeev" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" }, { - "name": "Sebastian Bergmann" - }, - { - "name": "Jan Borsodi" - }, - { - "name": "Raymond Bosman" - }, - { - "name": "Frederik Holljen" - }, - { - "name": "Kore Nordmann" - }, - { - "name": "Derick Rethans" - }, - { - "name": "Vadym Savchuk" - }, - { - "name": "Tobias Schlitt" - }, - { - "name": "Alexandru Stanoi" + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "The Base package provides the basic infrastructure that all packages rely on. Therefore every component relies on this package.", - "homepage": "https://github.com/zetacomponents", - "time": "2017-11-28T11:30:00+00:00" - }, - { - "name": "zetacomponents/document", - "version": "1.3.1", - "source": { - "type": "git", - "url": "https://github.com/zetacomponents/Document.git", - "reference": "688abfde573cf3fe0730f82538fbd7aa9fc95bc8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/zetacomponents/Document/zipball/688abfde573cf3fe0730f82538fbd7aa9fc95bc8", - "reference": "688abfde573cf3fe0730f82538fbd7aa9fc95bc8", - "shasum": "" - }, - "require": { - "zetacomponents/base": "*" - }, - "require-dev": { - "zetacomponents/unit-test": "dev-master" - }, - "type": "library", - "autoload": { - "classmap": [ - "src" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0" - ], - "authors": [ - { - "name": "Sebastian Bergmann" - }, - { - "name": "Kore Nordmann" - }, - { - "name": "Derick Rethans" - }, - { - "name": "Tobias Schlitt" - }, - { - "name": "Alexandru Stanoi" - } - ], - "description": "The Document components provides a general conversion framework for different semantic document markup languages like XHTML, Docbook, RST and similar.", - "homepage": "https://github.com/zetacomponents", - "time": "2013-12-19T11:40:00+00:00" + "description": "Symfony WebProfilerBundle", + "homepage": "https://symfony.com", + "time": "2019-07-24T10:33:03+00:00" } ], "aliases": [], @@ -3041,11 +1906,11 @@ "php": ">=5.6.0", "ext-soap": "*", "ext-json": "*", - "ext-zip": "*", "ext-mysqli": "*", "ext-dom": "*", "ext-iconv": "*", - "ext-gd": "*" + "ext-gd": "*", + "ext-ctype": "*" }, "platform-dev": [], "platform-overrides": { diff --git a/datamodels/2.x/itop-portal-base/module.itop-portal-base.php b/datamodels/2.x/itop-portal-base/module.itop-portal-base.php index b8e2a5a7c..f2c0b642a 100644 --- a/datamodels/2.x/itop-portal-base/module.itop-portal-base.php +++ b/datamodels/2.x/itop-portal-base/module.itop-portal-base.php @@ -1,5 +1,6 @@ true, // Components 'datamodel' => array( + // Note: The autoloader is there instead of portal/config/bootstrap.php in order to be available for other modules with a dependency on this one. + // eg. If a module has a class extending \Combodo\iTop\Portal\Controller\AbstractController, it needs to find it even if the portal kernel is not loaded. + 'portal/vendor/autoload.php', 'model.itop-portal-base.php', ), 'webservice' => array( //'webservices.itop-portal-base.php', ), 'dictionary' => array( - 'fr.dict.itop-portal-base.php', - //'de.dict.itop-portal-base.php', ), 'data.struct' => array( //'data.struct.itop-portal-base.xml', diff --git a/datamodels/2.x/itop-portal-base/portal/.gitignore b/datamodels/2.x/itop-portal-base/portal/.gitignore index 136901bf9..7f570fabc 100644 --- a/datamodels/2.x/itop-portal-base/portal/.gitignore +++ b/datamodels/2.x/itop-portal-base/portal/.gitignore @@ -1,9 +1,4 @@ - -###> symfony/framework-bundle ### /.env.local /.env.local.php /.env.*.local /public/bundles/ -/var/ -/vendor/ -###< symfony/framework-bundle ### diff --git a/datamodels/2.x/itop-portal-base/portal/composer.json b/datamodels/2.x/itop-portal-base/portal/composer.json index 2d965875a..92a2d0e13 100644 --- a/datamodels/2.x/itop-portal-base/portal/composer.json +++ b/datamodels/2.x/itop-portal-base/portal/composer.json @@ -1,52 +1,13 @@ { - "type": "project", "license": "AGPLv3", - "require": { - "php": ">=5.6.0", - "ext-ctype": "*", - "ext-iconv": "*", - "symfony/console": "3.4.*", - "symfony/dotenv": "3.4.*", - "symfony/framework-bundle": "3.4.*", - "symfony/twig-bundle": "3.4.*", - "symfony/yaml": "3.4.*", - "symfony/polyfill-php70": "1.*" - }, - "require-dev": { - "symfony/stopwatch": "3.4.*", - "symfony/web-profiler-bundle": "3.4.*" - }, - "config": { - "vendor-dir": "../../../../lib/composer-vendor", - "preferred-install": { - "*": "dist" - }, - "sort-packages": true, - "platform": { - "php": "5.6.0" - } - }, "autoload": { "psr-4": { "Combodo\\iTop\\Portal\\": "src/" - }, - "classmap": [ - "../../../../core", - "../../../../application" - ] + } }, "autoload-dev": { "psr-4": { "Combodo\\iTop\\Portal\\Tests\\": "tests/" } - }, - "conflict": { - "symfony/symfony": "*" - }, - "extra": { - "symfony": { - "allow-contrib": false, - "require": "3.4.*" - } } } diff --git a/datamodels/2.x/itop-portal-base/portal/composer.lock b/datamodels/2.x/itop-portal-base/portal/composer.lock deleted file mode 100644 index 60ca80bdc..000000000 --- a/datamodels/2.x/itop-portal-base/portal/composer.lock +++ /dev/null @@ -1,1912 +0,0 @@ -{ - "_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#installing-dependencies", - "This file is @generated automatically" - ], - "content-hash": "fb4688a395450a984cceeb3491181bbd", - "packages": [ - { - "name": "paragonie/random_compat", - "version": "v2.0.18", - "source": { - "type": "git", - "url": "https://github.com/paragonie/random_compat.git", - "reference": "0a58ef6e3146256cc3dc7cc393927bcc7d1b72db" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/paragonie/random_compat/zipball/0a58ef6e3146256cc3dc7cc393927bcc7d1b72db", - "reference": "0a58ef6e3146256cc3dc7cc393927bcc7d1b72db", - "shasum": "" - }, - "require": { - "php": ">=5.2.0" - }, - "require-dev": { - "phpunit/phpunit": "4.*|5.*" - }, - "suggest": { - "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." - }, - "type": "library", - "autoload": { - "files": [ - "lib/random.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Paragon Initiative Enterprises", - "email": "security@paragonie.com", - "homepage": "https://paragonie.com" - } - ], - "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", - "keywords": [ - "csprng", - "polyfill", - "pseudorandom", - "random" - ], - "time": "2019-01-03T20:59:08+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/log", - "version": "1.1.0", - "source": { - "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd", - "reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd", - "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": "2018-11-20T15:27:04+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": "symfony/cache", - "version": "v3.4.29", - "source": { - "type": "git", - "url": "https://github.com/symfony/cache.git", - "reference": "8c6e162f0f7626771edbfa0a0e45b46623bbae1c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/cache/zipball/8c6e162f0f7626771edbfa0a0e45b46623bbae1c", - "reference": "8c6e162f0f7626771edbfa0a0e45b46623bbae1c", - "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": "2019-06-17T17:26:15+00:00" - }, - { - "name": "symfony/class-loader", - "version": "v3.4.29", - "source": { - "type": "git", - "url": "https://github.com/symfony/class-loader.git", - "reference": "4459eef5298dedfb69f771186a580062b8516497" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/class-loader/zipball/4459eef5298dedfb69f771186a580062b8516497", - "reference": "4459eef5298dedfb69f771186a580062b8516497", - "shasum": "" - }, - "require": { - "php": "^5.5.9|>=7.0.8" - }, - "require-dev": { - "symfony/finder": "~2.8|~3.0|~4.0", - "symfony/polyfill-apcu": "~1.1" - }, - "suggest": { - "symfony/polyfill-apcu": "For using ApcClassLoader on HHVM" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.4-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\ClassLoader\\": "" - }, - "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 ClassLoader Component", - "homepage": "https://symfony.com", - "time": "2019-01-16T09:39:14+00:00" - }, - { - "name": "symfony/config", - "version": "v3.4.29", - "source": { - "type": "git", - "url": "https://github.com/symfony/config.git", - "reference": "29a33f66194fbe2ed4555981810f15fd8440e4a8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/29a33f66194fbe2ed4555981810f15fd8440e4a8", - "reference": "29a33f66194fbe2ed4555981810f15fd8440e4a8", - "shasum": "" - }, - "require": { - "php": "^5.5.9|>=7.0.8", - "symfony/filesystem": "~2.8|~3.0|~4.0", - "symfony/polyfill-ctype": "~1.8" - }, - "conflict": { - "symfony/dependency-injection": "<3.3", - "symfony/finder": "<3.3" - }, - "require-dev": { - "symfony/dependency-injection": "~3.3|~4.0", - "symfony/event-dispatcher": "~3.3|~4.0", - "symfony/finder": "~3.3|~4.0", - "symfony/yaml": "~3.0|~4.0" - }, - "suggest": { - "symfony/yaml": "To use the yaml reference dumper" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.4-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Config\\": "" - }, - "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 Config Component", - "homepage": "https://symfony.com", - "time": "2019-05-30T15:47:52+00:00" - }, - { - "name": "symfony/console", - "version": "v3.4.29", - "source": { - "type": "git", - "url": "https://github.com/symfony/console.git", - "reference": "c4d2f3529755ffc0be9fb823583b28d8744eeb3d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/c4d2f3529755ffc0be9fb823583b28d8744eeb3d", - "reference": "c4d2f3529755ffc0be9fb823583b28d8744eeb3d", - "shasum": "" - }, - "require": { - "php": "^5.5.9|>=7.0.8", - "symfony/debug": "~2.8|~3.0|~4.0", - "symfony/polyfill-mbstring": "~1.0" - }, - "conflict": { - "symfony/dependency-injection": "<3.4", - "symfony/process": "<3.3" - }, - "provide": { - "psr/log-implementation": "1.0" - }, - "require-dev": { - "psr/log": "~1.0", - "symfony/config": "~3.3|~4.0", - "symfony/dependency-injection": "~3.4|~4.0", - "symfony/event-dispatcher": "~2.8|~3.0|~4.0", - "symfony/lock": "~3.4|~4.0", - "symfony/process": "~3.3|~4.0" - }, - "suggest": { - "psr/log": "For using the console logger", - "symfony/event-dispatcher": "", - "symfony/lock": "", - "symfony/process": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.4-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Console\\": "" - }, - "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 Console Component", - "homepage": "https://symfony.com", - "time": "2019-06-05T11:33:52+00:00" - }, - { - "name": "symfony/debug", - "version": "v3.4.29", - "source": { - "type": "git", - "url": "https://github.com/symfony/debug.git", - "reference": "1172dc1abe44dfadd162239153818b074e6e53bf" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/debug/zipball/1172dc1abe44dfadd162239153818b074e6e53bf", - "reference": "1172dc1abe44dfadd162239153818b074e6e53bf", - "shasum": "" - }, - "require": { - "php": "^5.5.9|>=7.0.8", - "psr/log": "~1.0" - }, - "conflict": { - "symfony/http-kernel": ">=2.3,<2.3.24|~2.4.0|>=2.5,<2.5.9|>=2.6,<2.6.2" - }, - "require-dev": { - "symfony/http-kernel": "~2.8|~3.0|~4.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.4-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Debug\\": "" - }, - "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 Debug Component", - "homepage": "https://symfony.com", - "time": "2019-06-18T21:26:03+00:00" - }, - { - "name": "symfony/dependency-injection", - "version": "v3.4.29", - "source": { - "type": "git", - "url": "https://github.com/symfony/dependency-injection.git", - "reference": "76857ce235ba1866b66a1d5be34c6794c8895435" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/76857ce235ba1866b66a1d5be34c6794c8895435", - "reference": "76857ce235ba1866b66a1d5be34c6794c8895435", - "shasum": "" - }, - "require": { - "php": "^5.5.9|>=7.0.8", - "psr/container": "^1.0" - }, - "conflict": { - "symfony/config": "<3.3.7", - "symfony/finder": "<3.3", - "symfony/proxy-manager-bridge": "<3.4", - "symfony/yaml": "<3.4" - }, - "provide": { - "psr/container-implementation": "1.0" - }, - "require-dev": { - "symfony/config": "~3.3|~4.0", - "symfony/expression-language": "~2.8|~3.0|~4.0", - "symfony/yaml": "~3.4|~4.0" - }, - "suggest": { - "symfony/config": "", - "symfony/expression-language": "For using expressions in service container configuration", - "symfony/finder": "For using double-star glob patterns or when GLOB_BRACE portability is required", - "symfony/proxy-manager-bridge": "Generate service proxies to lazy load them", - "symfony/yaml": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.4-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\DependencyInjection\\": "" - }, - "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 DependencyInjection Component", - "homepage": "https://symfony.com", - "time": "2019-05-30T15:47:52+00:00" - }, - { - "name": "symfony/dotenv", - "version": "v3.4.29", - "source": { - "type": "git", - "url": "https://github.com/symfony/dotenv.git", - "reference": "3f4fdfb551bf36f2017d75cd2e6490fbe67f9d2d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/dotenv/zipball/3f4fdfb551bf36f2017d75cd2e6490fbe67f9d2d", - "reference": "3f4fdfb551bf36f2017d75cd2e6490fbe67f9d2d", - "shasum": "" - }, - "require": { - "php": "^5.5.9|>=7.0.8" - }, - "require-dev": { - "symfony/process": "~3.2|~4.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.4-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Dotenv\\": "" - }, - "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": "Registers environment variables from a .env file", - "homepage": "https://symfony.com", - "keywords": [ - "dotenv", - "env", - "environment" - ], - "time": "2019-06-23T08:10:04+00:00" - }, - { - "name": "symfony/event-dispatcher", - "version": "v3.4.29", - "source": { - "type": "git", - "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "f18fdd6cc7006441865e698420cee26bac94741f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/f18fdd6cc7006441865e698420cee26bac94741f", - "reference": "f18fdd6cc7006441865e698420cee26bac94741f", - "shasum": "" - }, - "require": { - "php": "^5.5.9|>=7.0.8" - }, - "conflict": { - "symfony/dependency-injection": "<3.3" - }, - "require-dev": { - "psr/log": "~1.0", - "symfony/config": "~2.8|~3.0|~4.0", - "symfony/dependency-injection": "~3.3|~4.0", - "symfony/expression-language": "~2.8|~3.0|~4.0", - "symfony/stopwatch": "~2.8|~3.0|~4.0" - }, - "suggest": { - "symfony/dependency-injection": "", - "symfony/http-kernel": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.4-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\EventDispatcher\\": "" - }, - "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 EventDispatcher Component", - "homepage": "https://symfony.com", - "time": "2019-06-25T07:45:31+00:00" - }, - { - "name": "symfony/filesystem", - "version": "v3.4.29", - "source": { - "type": "git", - "url": "https://github.com/symfony/filesystem.git", - "reference": "70adda061ef83bb7def63a17953dc41f203308a7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/70adda061ef83bb7def63a17953dc41f203308a7", - "reference": "70adda061ef83bb7def63a17953dc41f203308a7", - "shasum": "" - }, - "require": { - "php": "^5.5.9|>=7.0.8", - "symfony/polyfill-ctype": "~1.8" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.4-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Filesystem\\": "" - }, - "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 Filesystem Component", - "homepage": "https://symfony.com", - "time": "2019-06-23T09:29:17+00:00" - }, - { - "name": "symfony/finder", - "version": "v3.4.29", - "source": { - "type": "git", - "url": "https://github.com/symfony/finder.git", - "reference": "5f80266a729e30bbcc37f8bf0e62c3d5a38c8208" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/5f80266a729e30bbcc37f8bf0e62c3d5a38c8208", - "reference": "5f80266a729e30bbcc37f8bf0e62c3d5a38c8208", - "shasum": "" - }, - "require": { - "php": "^5.5.9|>=7.0.8" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.4-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Finder\\": "" - }, - "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 Finder Component", - "homepage": "https://symfony.com", - "time": "2019-05-30T15:47:52+00:00" - }, - { - "name": "symfony/framework-bundle", - "version": "v3.4.29", - "source": { - "type": "git", - "url": "https://github.com/symfony/framework-bundle.git", - "reference": "ed26f0f9cef6ff47aa26766349272df653638f31" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/ed26f0f9cef6ff47aa26766349272df653638f31", - "reference": "ed26f0f9cef6ff47aa26766349272df653638f31", - "shasum": "" - }, - "require": { - "ext-xml": "*", - "php": "^5.5.9|>=7.0.8", - "symfony/cache": "~3.4|~4.0", - "symfony/class-loader": "~3.2", - "symfony/config": "~3.4|~4.0", - "symfony/dependency-injection": "^3.4.24|^4.2.5", - "symfony/event-dispatcher": "~3.4|~4.0", - "symfony/filesystem": "~2.8|~3.0|~4.0", - "symfony/finder": "~2.8|~3.0|~4.0", - "symfony/http-foundation": "^3.3.11|~4.0", - "symfony/http-kernel": "~3.4|~4.0", - "symfony/polyfill-mbstring": "~1.0", - "symfony/routing": "^3.4.5|^4.0.5" - }, - "conflict": { - "phpdocumentor/reflection-docblock": "<3.0", - "phpdocumentor/type-resolver": "<0.2.1", - "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0", - "symfony/asset": "<3.3", - "symfony/console": "<3.4", - "symfony/form": "<3.4", - "symfony/property-info": "<3.3", - "symfony/serializer": "<3.3", - "symfony/stopwatch": "<3.4", - "symfony/translation": "<3.4", - "symfony/validator": "<3.4", - "symfony/workflow": "<3.3" - }, - "require-dev": { - "doctrine/annotations": "~1.0", - "doctrine/cache": "~1.0", - "fig/link-util": "^1.0", - "phpdocumentor/reflection-docblock": "^3.0|^4.0", - "symfony/asset": "~3.3|~4.0", - "symfony/browser-kit": "~2.8|~3.0|~4.0", - "symfony/console": "~3.4|~4.0", - "symfony/css-selector": "~2.8|~3.0|~4.0", - "symfony/dom-crawler": "~2.8|~3.0|~4.0", - "symfony/expression-language": "~2.8|~3.0|~4.0", - "symfony/form": "^3.4.22|~4.1.11|^4.2.3", - "symfony/lock": "~3.4|~4.0", - "symfony/polyfill-intl-icu": "~1.0", - "symfony/process": "~2.8|~3.0|~4.0", - "symfony/property-info": "~3.3|~4.0", - "symfony/security-core": "~3.2|~4.0", - "symfony/security-csrf": "^2.8.31|^3.3.13|~4.0", - "symfony/serializer": "~3.3|~4.0", - "symfony/stopwatch": "~3.4|~4.0", - "symfony/templating": "~2.8|~3.0|~4.0", - "symfony/translation": "~3.4|~4.0", - "symfony/validator": "~3.4|~4.0", - "symfony/var-dumper": "~3.3|~4.0", - "symfony/web-link": "~3.3|~4.0", - "symfony/workflow": "~3.3|~4.0", - "symfony/yaml": "~3.2|~4.0", - "twig/twig": "~1.34|~2.4" - }, - "suggest": { - "ext-apcu": "For best performance of the system caches", - "symfony/console": "For using the console commands", - "symfony/form": "For using forms", - "symfony/property-info": "For using the property_info service", - "symfony/serializer": "For using the serializer service", - "symfony/validator": "For using validation", - "symfony/web-link": "For using web links, features such as preloading, prefetching or prerendering", - "symfony/yaml": "For using the debug:config and lint:yaml commands" - }, - "type": "symfony-bundle", - "extra": { - "branch-alias": { - "dev-master": "3.4-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Bundle\\FrameworkBundle\\": "" - }, - "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 FrameworkBundle", - "homepage": "https://symfony.com", - "time": "2019-06-25T15:43:39+00:00" - }, - { - "name": "symfony/http-foundation", - "version": "v3.4.29", - "source": { - "type": "git", - "url": "https://github.com/symfony/http-foundation.git", - "reference": "8cfbf75bb3a72963b12c513a73e9247891df24f8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/8cfbf75bb3a72963b12c513a73e9247891df24f8", - "reference": "8cfbf75bb3a72963b12c513a73e9247891df24f8", - "shasum": "" - }, - "require": { - "php": "^5.5.9|>=7.0.8", - "symfony/polyfill-mbstring": "~1.1", - "symfony/polyfill-php70": "~1.6" - }, - "require-dev": { - "symfony/expression-language": "~2.8|~3.0|~4.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.4-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\HttpFoundation\\": "" - }, - "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 HttpFoundation Component", - "homepage": "https://symfony.com", - "time": "2019-06-22T20:10:25+00:00" - }, - { - "name": "symfony/http-kernel", - "version": "v3.4.29", - "source": { - "type": "git", - "url": "https://github.com/symfony/http-kernel.git", - "reference": "abbb38dbab652ddc40a86d0c3b0e14ca52d58ed2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/abbb38dbab652ddc40a86d0c3b0e14ca52d58ed2", - "reference": "abbb38dbab652ddc40a86d0c3b0e14ca52d58ed2", - "shasum": "" - }, - "require": { - "php": "^5.5.9|>=7.0.8", - "psr/log": "~1.0", - "symfony/debug": "^3.3.3|~4.0", - "symfony/event-dispatcher": "~2.8|~3.0|~4.0", - "symfony/http-foundation": "~3.4.12|~4.0.12|^4.1.1", - "symfony/polyfill-ctype": "~1.8" - }, - "conflict": { - "symfony/config": "<2.8", - "symfony/dependency-injection": "<3.4.10|<4.0.10,>=4", - "symfony/var-dumper": "<3.3", - "twig/twig": "<1.34|<2.4,>=2" - }, - "provide": { - "psr/log-implementation": "1.0" - }, - "require-dev": { - "psr/cache": "~1.0", - "symfony/browser-kit": "~2.8|~3.0|~4.0", - "symfony/class-loader": "~2.8|~3.0", - "symfony/config": "~2.8|~3.0|~4.0", - "symfony/console": "~2.8|~3.0|~4.0", - "symfony/css-selector": "~2.8|~3.0|~4.0", - "symfony/dependency-injection": "^3.4.10|^4.0.10", - "symfony/dom-crawler": "~2.8|~3.0|~4.0", - "symfony/expression-language": "~2.8|~3.0|~4.0", - "symfony/finder": "~2.8|~3.0|~4.0", - "symfony/process": "~2.8|~3.0|~4.0", - "symfony/routing": "~3.4|~4.0", - "symfony/stopwatch": "~2.8|~3.0|~4.0", - "symfony/templating": "~2.8|~3.0|~4.0", - "symfony/translation": "~2.8|~3.0|~4.0", - "symfony/var-dumper": "~3.3|~4.0" - }, - "suggest": { - "symfony/browser-kit": "", - "symfony/config": "", - "symfony/console": "", - "symfony/dependency-injection": "", - "symfony/finder": "", - "symfony/var-dumper": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.4-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\HttpKernel\\": "" - }, - "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 HttpKernel Component", - "homepage": "https://symfony.com", - "time": "2019-06-26T13:56:39+00:00" - }, - { - "name": "symfony/polyfill-apcu", - "version": "v1.11.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-apcu.git", - "reference": "a502face1da6a53289480166f24de2c3c68e5c3c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-apcu/zipball/a502face1da6a53289480166f24de2c3c68e5c3c", - "reference": "a502face1da6a53289480166f24de2c3c68e5c3c", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.11-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": "2019-02-06T07:57:58+00:00" - }, - { - "name": "symfony/polyfill-ctype", - "version": "v1.11.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "82ebae02209c21113908c229e9883c419720738a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/82ebae02209c21113908c229e9883c419720738a", - "reference": "82ebae02209c21113908c229e9883c419720738a", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "suggest": { - "ext-ctype": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.11-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Ctype\\": "" - }, - "files": [ - "bootstrap.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - }, - { - "name": "Gert de Pagter", - "email": "BackEndTea@gmail.com" - } - ], - "description": "Symfony polyfill for ctype functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "ctype", - "polyfill", - "portable" - ], - "time": "2019-02-06T07:57:58+00:00" - }, - { - "name": "symfony/polyfill-mbstring", - "version": "v1.11.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "fe5e94c604826c35a32fa832f35bd036b6799609" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fe5e94c604826c35a32fa832f35bd036b6799609", - "reference": "fe5e94c604826c35a32fa832f35bd036b6799609", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "suggest": { - "ext-mbstring": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.11-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" - }, - "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 for the Mbstring extension", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "mbstring", - "polyfill", - "portable", - "shim" - ], - "time": "2019-02-06T07:57:58+00:00" - }, - { - "name": "symfony/polyfill-php70", - "version": "v1.11.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php70.git", - "reference": "bc4858fb611bda58719124ca079baff854149c89" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/bc4858fb611bda58719124ca079baff854149c89", - "reference": "bc4858fb611bda58719124ca079baff854149c89", - "shasum": "" - }, - "require": { - "paragonie/random_compat": "~1.0|~2.0|~9.99", - "php": ">=5.3.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.11-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Php70\\": "" - }, - "files": [ - "bootstrap.php" - ], - "classmap": [ - "Resources/stubs" - ] - }, - "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 some PHP 7.0+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "time": "2019-02-06T07:57:58+00:00" - }, - { - "name": "symfony/routing", - "version": "v3.4.29", - "source": { - "type": "git", - "url": "https://github.com/symfony/routing.git", - "reference": "8d804d8a65a26dc9de1aaf2ff3a421e581d050e6" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/8d804d8a65a26dc9de1aaf2ff3a421e581d050e6", - "reference": "8d804d8a65a26dc9de1aaf2ff3a421e581d050e6", - "shasum": "" - }, - "require": { - "php": "^5.5.9|>=7.0.8" - }, - "conflict": { - "symfony/config": "<3.3.1", - "symfony/dependency-injection": "<3.3", - "symfony/yaml": "<3.4" - }, - "require-dev": { - "doctrine/annotations": "~1.0", - "psr/log": "~1.0", - "symfony/config": "^3.3.1|~4.0", - "symfony/dependency-injection": "~3.3|~4.0", - "symfony/expression-language": "~2.8|~3.0|~4.0", - "symfony/http-foundation": "~2.8|~3.0|~4.0", - "symfony/yaml": "~3.4|~4.0" - }, - "suggest": { - "doctrine/annotations": "For using the annotation loader", - "symfony/config": "For using the all-in-one router or any loader", - "symfony/expression-language": "For using expression matching", - "symfony/http-foundation": "For using a Symfony Request object", - "symfony/yaml": "For using the YAML loader" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.4-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Routing\\": "" - }, - "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 Routing Component", - "homepage": "https://symfony.com", - "keywords": [ - "router", - "routing", - "uri", - "url" - ], - "time": "2019-06-26T11:14:13+00:00" - }, - { - "name": "symfony/twig-bridge", - "version": "v3.4.29", - "source": { - "type": "git", - "url": "https://github.com/symfony/twig-bridge.git", - "reference": "c5cda7f836ccfa80a84c27aec10aa16591333829" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/twig-bridge/zipball/c5cda7f836ccfa80a84c27aec10aa16591333829", - "reference": "c5cda7f836ccfa80a84c27aec10aa16591333829", - "shasum": "" - }, - "require": { - "php": "^5.5.9|>=7.0.8", - "twig/twig": "^1.40|^2.9" - }, - "conflict": { - "symfony/console": "<3.4", - "symfony/form": "<3.4.13|>=4.0,<4.0.13|>=4.1,<4.1.2" - }, - "require-dev": { - "symfony/asset": "~2.8|~3.0|~4.0", - "symfony/console": "~3.4|~4.0", - "symfony/dependency-injection": "~2.8|~3.0|~4.0", - "symfony/expression-language": "~2.8|~3.0|~4.0", - "symfony/finder": "~2.8|~3.0|~4.0", - "symfony/form": "^3.4.23|^4.2.4", - "symfony/http-foundation": "^3.3.11|~4.0", - "symfony/http-kernel": "~3.2|~4.0", - "symfony/polyfill-intl-icu": "~1.0", - "symfony/routing": "~2.8|~3.0|~4.0", - "symfony/security": "^2.8.31|^3.3.13|~4.0", - "symfony/security-acl": "~2.8|~3.0", - "symfony/stopwatch": "~2.8|~3.0|~4.0", - "symfony/templating": "~2.8|~3.0|~4.0", - "symfony/translation": "~2.8|~3.0|~4.0", - "symfony/var-dumper": "~2.8.10|~3.1.4|~3.2|~4.0", - "symfony/web-link": "~3.3|~4.0", - "symfony/workflow": "~3.3|~4.0", - "symfony/yaml": "~2.8|~3.0|~4.0" - }, - "suggest": { - "symfony/asset": "For using the AssetExtension", - "symfony/expression-language": "For using the ExpressionExtension", - "symfony/finder": "", - "symfony/form": "For using the FormExtension", - "symfony/http-kernel": "For using the HttpKernelExtension", - "symfony/routing": "For using the RoutingExtension", - "symfony/security": "For using the SecurityExtension", - "symfony/stopwatch": "For using the StopwatchExtension", - "symfony/templating": "For using the TwigEngine", - "symfony/translation": "For using the TranslationExtension", - "symfony/var-dumper": "For using the DumpExtension", - "symfony/web-link": "For using the WebLinkExtension", - "symfony/yaml": "For using the YamlExtension" - }, - "type": "symfony-bridge", - "extra": { - "branch-alias": { - "dev-master": "3.4-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Bridge\\Twig\\": "" - }, - "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 Twig Bridge", - "homepage": "https://symfony.com", - "time": "2019-06-13T10:34:15+00:00" - }, - { - "name": "symfony/twig-bundle", - "version": "v3.4.29", - "source": { - "type": "git", - "url": "https://github.com/symfony/twig-bundle.git", - "reference": "94c558cc915a6ac2e2de7d7f9a6b6e8a739a7c53" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/twig-bundle/zipball/94c558cc915a6ac2e2de7d7f9a6b6e8a739a7c53", - "reference": "94c558cc915a6ac2e2de7d7f9a6b6e8a739a7c53", - "shasum": "" - }, - "require": { - "php": "^5.5.9|>=7.0.8", - "symfony/config": "~3.2|~4.0", - "symfony/http-foundation": "~2.8|~3.0|~4.0", - "symfony/http-kernel": "^3.3|~4.0", - "symfony/polyfill-ctype": "~1.8", - "symfony/twig-bridge": "^3.4.3|^4.0.3", - "twig/twig": "~1.40|~2.9" - }, - "conflict": { - "symfony/dependency-injection": "<3.4", - "symfony/event-dispatcher": "<3.3.1" - }, - "require-dev": { - "doctrine/annotations": "~1.0", - "doctrine/cache": "~1.0", - "symfony/asset": "~2.8|~3.0|~4.0", - "symfony/dependency-injection": "~3.4.24|^4.2.5", - "symfony/expression-language": "~2.8|~3.0|~4.0", - "symfony/finder": "~2.8|~3.0|~4.0", - "symfony/form": "~2.8|~3.0|~4.0", - "symfony/framework-bundle": "^3.3.11|~4.0", - "symfony/routing": "~2.8|~3.0|~4.0", - "symfony/stopwatch": "~2.8|~3.0|~4.0", - "symfony/templating": "~2.8|~3.0|~4.0", - "symfony/web-link": "~3.3|~4.0", - "symfony/yaml": "~2.8|~3.0|~4.0" - }, - "type": "symfony-bundle", - "extra": { - "branch-alias": { - "dev-master": "3.4-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Bundle\\TwigBundle\\": "" - }, - "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 TwigBundle", - "homepage": "https://symfony.com", - "time": "2019-05-30T15:47:52+00:00" - }, - { - "name": "symfony/yaml", - "version": "v3.4.29", - "source": { - "type": "git", - "url": "https://github.com/symfony/yaml.git", - "reference": "212a27b731e5bfb735679d1ffaac82bd6a1dc996" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/212a27b731e5bfb735679d1ffaac82bd6a1dc996", - "reference": "212a27b731e5bfb735679d1ffaac82bd6a1dc996", - "shasum": "" - }, - "require": { - "php": "^5.5.9|>=7.0.8", - "symfony/polyfill-ctype": "~1.8" - }, - "conflict": { - "symfony/console": "<3.4" - }, - "require-dev": { - "symfony/console": "~3.4|~4.0" - }, - "suggest": { - "symfony/console": "For validating YAML files using the lint command" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.4-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Yaml\\": "" - }, - "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 Yaml Component", - "homepage": "https://symfony.com", - "time": "2019-03-25T07:48:46+00:00" - }, - { - "name": "twig/twig", - "version": "v1.42.2", - "source": { - "type": "git", - "url": "https://github.com/twigphp/Twig.git", - "reference": "21707d6ebd05476854805e4f91b836531941bcd4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/21707d6ebd05476854805e4f91b836531941bcd4", - "reference": "21707d6ebd05476854805e4f91b836531941bcd4", - "shasum": "" - }, - "require": { - "php": ">=5.4.0", - "symfony/polyfill-ctype": "^1.8" - }, - "require-dev": { - "psr/container": "^1.0", - "symfony/debug": "^2.7", - "symfony/phpunit-bridge": "^3.4.19|^4.1.8|^5.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.42-dev" - } - }, - "autoload": { - "psr-0": { - "Twig_": "lib/" - }, - "psr-4": { - "Twig\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com", - "homepage": "http://fabien.potencier.org", - "role": "Lead Developer" - }, - { - "name": "Armin Ronacher", - "email": "armin.ronacher@active-4.com", - "role": "Project Founder" - }, - { - "name": "Twig Team", - "homepage": "https://twig.symfony.com/contributors", - "role": "Contributors" - } - ], - "description": "Twig, the flexible, fast, and secure template language for PHP", - "homepage": "https://twig.symfony.com", - "keywords": [ - "templating" - ], - "time": "2019-06-18T15:35:16+00:00" - } - ], - "packages-dev": [ - { - "name": "symfony/stopwatch", - "version": "v3.4.29", - "source": { - "type": "git", - "url": "https://github.com/symfony/stopwatch.git", - "reference": "2a651c2645c10bbedd21170771f122d935e0dd58" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/2a651c2645c10bbedd21170771f122d935e0dd58", - "reference": "2a651c2645c10bbedd21170771f122d935e0dd58", - "shasum": "" - }, - "require": { - "php": "^5.5.9|>=7.0.8" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.4-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Stopwatch\\": "" - }, - "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 Stopwatch Component", - "homepage": "https://symfony.com", - "time": "2019-01-16T09:39:14+00:00" - }, - { - "name": "symfony/var-dumper", - "version": "v3.4.29", - "source": { - "type": "git", - "url": "https://github.com/symfony/var-dumper.git", - "reference": "7b92618169c44af4bb226f69dbac42b56b1a7745" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/7b92618169c44af4bb226f69dbac42b56b1a7745", - "reference": "7b92618169c44af4bb226f69dbac42b56b1a7745", - "shasum": "" - }, - "require": { - "php": "^5.5.9|>=7.0.8", - "symfony/polyfill-mbstring": "~1.0" - }, - "conflict": { - "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0" - }, - "require-dev": { - "ext-iconv": "*", - "twig/twig": "~1.34|~2.4" - }, - "suggest": { - "ext-iconv": "To convert non-UTF-8 strings to UTF-8 (or symfony/polyfill-iconv in case ext-iconv cannot be used).", - "ext-intl": "To show region name in time zone dump", - "ext-symfony_debug": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.4-dev" - } - }, - "autoload": { - "files": [ - "Resources/functions/dump.php" - ], - "psr-4": { - "Symfony\\Component\\VarDumper\\": "" - }, - "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 mechanism for exploring and dumping PHP variables", - "homepage": "https://symfony.com", - "keywords": [ - "debug", - "dump" - ], - "time": "2019-06-13T16:26:35+00:00" - }, - { - "name": "symfony/web-profiler-bundle", - "version": "v3.4.29", - "source": { - "type": "git", - "url": "https://github.com/symfony/web-profiler-bundle.git", - "reference": "6e835f66ca6052bf410a44a41449d1985c06aed3" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/web-profiler-bundle/zipball/6e835f66ca6052bf410a44a41449d1985c06aed3", - "reference": "6e835f66ca6052bf410a44a41449d1985c06aed3", - "shasum": "" - }, - "require": { - "php": "^5.5.9|>=7.0.8", - "symfony/http-kernel": "~3.4.25|^4.2.6", - "symfony/polyfill-php70": "~1.0", - "symfony/routing": "~2.8|~3.0|~4.0", - "symfony/twig-bridge": "~2.8|~3.0|~4.0", - "symfony/var-dumper": "~3.3|~4.0", - "twig/twig": "~1.34|~2.4" - }, - "conflict": { - "symfony/config": "<3.4", - "symfony/dependency-injection": "<3.4", - "symfony/event-dispatcher": "<3.3.1", - "symfony/var-dumper": "<3.3" - }, - "require-dev": { - "symfony/config": "~3.4|~4.0", - "symfony/console": "~2.8|~3.0|~4.0", - "symfony/dependency-injection": "~3.4|~4.0", - "symfony/stopwatch": "~2.8|~3.0|~4.0" - }, - "type": "symfony-bundle", - "extra": { - "branch-alias": { - "dev-master": "3.4-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Bundle\\WebProfilerBundle\\": "" - }, - "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 WebProfilerBundle", - "homepage": "https://symfony.com", - "time": "2019-06-09T16:36:33+00:00" - } - ], - "aliases": [], - "minimum-stability": "stable", - "stability-flags": [], - "prefer-stable": false, - "prefer-lowest": false, - "platform": { - "php": ">=5.6.0", - "ext-ctype": "*", - "ext-iconv": "*" - }, - "platform-dev": [], - "platform-overrides": { - "php": "5.6.0" - } -} diff --git a/datamodels/2.x/itop-portal-base/portal/config/bootstrap.php b/datamodels/2.x/itop-portal-base/portal/config/bootstrap.php index 15d55a844..407f6f1a2 100644 --- a/datamodels/2.x/itop-portal-base/portal/config/bootstrap.php +++ b/datamodels/2.x/itop-portal-base/portal/config/bootstrap.php @@ -26,9 +26,10 @@ use Symfony\Component\Debug\Debug; use Symfony\Component\Dotenv\Dotenv; -require_once APPROOT.'/lib/composer-vendor/autoload.php'; +// Global autoloader (portal autoloader is already required through module.itop-portal-base.php) +require_once APPROOT.'/lib/autoload.php'; -// Load current environment if necessary +// Load current environment if necessary (typically from CLI as the app is not started yet) if (!defined('MODULESROOT')) { if (file_exists(__DIR__.'/../../../../approot.inc.php')) diff --git a/datamodels/2.x/itop-portal-base/portal/symfony.lock b/datamodels/2.x/itop-portal-base/portal/symfony.lock deleted file mode 100644 index 2e26d4a2d..000000000 --- a/datamodels/2.x/itop-portal-base/portal/symfony.lock +++ /dev/null @@ -1,135 +0,0 @@ -{ - "psr/cache": { - "version": "1.0.1" - }, - "psr/container": { - "version": "1.0.0" - }, - "psr/log": { - "version": "1.1.0" - }, - "psr/simple-cache": { - "version": "1.0.1" - }, - "symfony/cache": { - "version": "v3.4.27" - }, - "symfony/class-loader": { - "version": "v3.4.27" - }, - "symfony/config": { - "version": "v3.4.27" - }, - "symfony/console": { - "version": "3.3", - "recipe": { - "repo": "github.com/symfony/recipes", - "branch": "master", - "version": "3.3", - "ref": "482d233eb8de91ebd042992077bbd5838858890c" - }, - "files": [ - "./bin/console", - "./config/bootstrap.php" - ] - }, - "symfony/debug": { - "version": "v3.4.27" - }, - "symfony/dependency-injection": { - "version": "v3.4.27" - }, - "symfony/dotenv": { - "version": "v3.4.27" - }, - "symfony/event-dispatcher": { - "version": "v3.4.27" - }, - "symfony/filesystem": { - "version": "v3.4.27" - }, - "symfony/finder": { - "version": "v3.4.27" - }, - "symfony/flex": { - "version": "1.0", - "recipe": { - "repo": "github.com/symfony/recipes", - "branch": "master", - "version": "1.0", - "ref": "dc3fc2e0334a4137c47cfd5a3ececc601fa61a0b" - }, - "files": [ - "./.env" - ] - }, - "symfony/framework-bundle": { - "version": "3.3", - "recipe": { - "repo": "github.com/symfony/recipes", - "branch": "master", - "version": "3.3", - "ref": "0a20c266736ca946b4ebb088c767f0cb340e41c7" - }, - "files": [ - "./config/bootstrap.php", - "./config/packages/cache.yaml", - "./config/packages/framework.yaml", - "./config/packages/test/framework.yaml", - "./config/services.yaml", - "./public/index.php", - "./src/Controller/.gitignore", - "./src/Kernel.php" - ] - }, - "symfony/http-foundation": { - "version": "v3.4.27" - }, - "symfony/http-kernel": { - "version": "v3.4.27" - }, - "symfony/polyfill-apcu": { - "version": "v1.11.0" - }, - "symfony/polyfill-mbstring": { - "version": "v1.11.0" - }, - "symfony/routing": { - "version": "3.3", - "recipe": { - "repo": "github.com/symfony/recipes", - "branch": "master", - "version": "3.3", - "ref": "4d36f55d8edd7bd88b730754c2d3f3dfc2cd316b" - }, - "files": [ - "./config/packages/dev/routing.yaml", - "./config/packages/routing.yaml", - "./config/packages/test/routing.yaml", - "./config/routes.yaml" - ] - }, - "symfony/twig-bridge": { - "version": "v3.4.28" - }, - "symfony/twig-bundle": { - "version": "3.3", - "recipe": { - "repo": "github.com/symfony/recipes", - "branch": "master", - "version": "3.3", - "ref": "369b5b29dc52b2c190002825ae7ec24ab6f962dd" - }, - "files": [ - "./config/packages/twig.yaml", - "./config/routes/dev/twig.yaml", - "./templates/base.html.twig" - ] - }, - "symfony/yaml": { - "version": "v3.4.27" - }, - "twig/twig": { - "version": "v2.11.3" - } -} diff --git a/datamodels/2.x/itop-portal-base/portal/vendor/autoload.php b/datamodels/2.x/itop-portal-base/portal/vendor/autoload.php new file mode 100644 index 000000000..7383562f4 --- /dev/null +++ b/datamodels/2.x/itop-portal-base/portal/vendor/autoload.php @@ -0,0 +1,7 @@ + + * Jordi Boggiano + * + * 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 + * @author Jordi Boggiano + * @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; +} diff --git a/datamodels/2.x/itop-portal-base/portal/vendor/composer/LICENSE b/datamodels/2.x/itop-portal-base/portal/vendor/composer/LICENSE new file mode 100644 index 000000000..f27399a04 --- /dev/null +++ b/datamodels/2.x/itop-portal-base/portal/vendor/composer/LICENSE @@ -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. + diff --git a/datamodels/2.x/itop-portal-base/portal/vendor/composer/autoload_classmap.php b/datamodels/2.x/itop-portal-base/portal/vendor/composer/autoload_classmap.php new file mode 100644 index 000000000..0a6148450 --- /dev/null +++ b/datamodels/2.x/itop-portal-base/portal/vendor/composer/autoload_classmap.php @@ -0,0 +1,57 @@ + $baseDir . '/src/Brick/AbstractBrick.php', + 'Combodo\\iTop\\Portal\\Brick\\AggregatePageBrick' => $baseDir . '/src/Brick/AggregatePageBrick.php', + 'Combodo\\iTop\\Portal\\Brick\\BrickCollection' => $baseDir . '/src/Brick/BrickCollection.php', + 'Combodo\\iTop\\Portal\\Brick\\BrickNotFoundException' => $baseDir . '/src/Brick/BrickNotFoundException.php', + 'Combodo\\iTop\\Portal\\Brick\\BrowseBrick' => $baseDir . '/src/Brick/BrowseBrick.php', + 'Combodo\\iTop\\Portal\\Brick\\CreateBrick' => $baseDir . '/src/Brick/CreateBrick.php', + 'Combodo\\iTop\\Portal\\Brick\\FilterBrick' => $baseDir . '/src/Brick/FilterBrick.php', + 'Combodo\\iTop\\Portal\\Brick\\ManageBrick' => $baseDir . '/src/Brick/ManageBrick.php', + 'Combodo\\iTop\\Portal\\Brick\\PortalBrick' => $baseDir . '/src/Brick/PortalBrick.php', + 'Combodo\\iTop\\Portal\\Brick\\PropertyNotFoundException' => $baseDir . '/src/Brick/PropertyNotFoundException.php', + 'Combodo\\iTop\\Portal\\Brick\\UserProfileBrick' => $baseDir . '/src/Brick/UserProfileBrick.php', + 'Combodo\\iTop\\Portal\\Controller\\AbstractController' => $baseDir . '/src/Controller/AbstractController.php', + 'Combodo\\iTop\\Portal\\Controller\\AggregatePageBrickController' => $baseDir . '/src/Controller/AggregatePageBrickController.class.inc.php', + 'Combodo\\iTop\\Portal\\Controller\\BrickController' => $baseDir . '/src/Controller/BrickController.php', + 'Combodo\\iTop\\Portal\\Controller\\BrowseBrickController' => $baseDir . '/src/Controller/BrowseBrickController.php', + 'Combodo\\iTop\\Portal\\Controller\\CreateBrickController' => $baseDir . '/src/Controller/CreateBrickController.php', + 'Combodo\\iTop\\Portal\\Controller\\DefaultController' => $baseDir . '/src/Controller/DefaultController.php', + 'Combodo\\iTop\\Portal\\Controller\\ManageBrickController' => $baseDir . '/src/Controller/ManageBrickController.php', + 'Combodo\\iTop\\Portal\\Controller\\ObjectController' => $baseDir . '/src/Controller/ObjectController.php', + 'Combodo\\iTop\\Portal\\Controller\\UserProfileBrickController' => $baseDir . '/src/Controller/UserProfileBrickController.php', + 'Combodo\\iTop\\Portal\\DependencyInjection\\SilexCompatBootstrap\\PortalXmlConfiguration\\AbstractConfiguration' => $baseDir . '/src/DependencyInjection/SilexCompatBootstrap/PortalXmlConfiguration/AbstractConfiguration.php', + 'Combodo\\iTop\\Portal\\DependencyInjection\\SilexCompatBootstrap\\PortalXmlConfiguration\\Basic' => $baseDir . '/src/DependencyInjection/SilexCompatBootstrap/PortalXmlConfiguration/Basic.php', + 'Combodo\\iTop\\Portal\\DependencyInjection\\SilexCompatBootstrap\\PortalXmlConfiguration\\Forms' => $baseDir . '/src/DependencyInjection/SilexCompatBootstrap/PortalXmlConfiguration/Forms.php', + 'Combodo\\iTop\\Portal\\DependencyInjection\\SilexCompatBootstrap\\PortalXmlConfiguration\\Lists' => $baseDir . '/src/DependencyInjection/SilexCompatBootstrap/PortalXmlConfiguration/Lists.php', + 'Combodo\\iTop\\Portal\\EventListener\\ApplicationContextSetUrlMakerClass' => $baseDir . '/src/EventListener/ApplicationContextSetUrlMakerClass.php', + 'Combodo\\iTop\\Portal\\EventListener\\UserProvider' => $baseDir . '/src/EventListener/UserProvider.php', + 'Combodo\\iTop\\Portal\\Form\\ObjectFormManager' => $baseDir . '/src/Form/ObjectFormManager.php', + 'Combodo\\iTop\\Portal\\Form\\PasswordFormManager' => $baseDir . '/src/Form/PasswordFormManager.php', + 'Combodo\\iTop\\Portal\\Form\\PreferencesFormManager' => $baseDir . '/src/Form/PreferencesFormManager.php', + 'Combodo\\iTop\\Portal\\Helper\\ApplicationHelper' => $baseDir . '/src/Helper/ApplicationHelper.php', + 'Combodo\\iTop\\Portal\\Helper\\BrickControllerHelper' => $baseDir . '/src/Helper/BrickControllerHelper.php', + 'Combodo\\iTop\\Portal\\Helper\\BrowseBrickHelper' => $baseDir . '/src/Helper/BrowseBrickHelper.php', + 'Combodo\\iTop\\Portal\\Helper\\ContextManipulatorHelper' => $baseDir . '/src/Helper/ContextManipulatorHelper.php', + 'Combodo\\iTop\\Portal\\Helper\\LifecycleValidatorHelper' => $baseDir . '/src/Helper/LifecycleValidatorHelper.php', + 'Combodo\\iTop\\Portal\\Helper\\ObjectFormHandlerHelper' => $baseDir . '/src/Helper/ObjectFormHandlerHelper.php', + 'Combodo\\iTop\\Portal\\Helper\\RequestManipulatorHelper' => $baseDir . '/src/Helper/RequestManipulatorHelper.php', + 'Combodo\\iTop\\Portal\\Helper\\ScopeValidatorHelper' => $baseDir . '/src/Helper/ScopeValidatorHelper.php', + 'Combodo\\iTop\\Portal\\Helper\\SecurityHelper' => $baseDir . '/src/Helper/SecurityHelper.php', + 'Combodo\\iTop\\Portal\\Kernel' => $baseDir . '/src/Kernel.php', + 'Combodo\\iTop\\Portal\\Routing\\ItopExtensionsExtraRoutes' => $baseDir . '/src/Routing/ItopExtensionsExtraRoutes.php', + 'Combodo\\iTop\\Portal\\Routing\\UrlGenerator' => $baseDir . '/src/Routing/UrlGenerator.php', + 'Combodo\\iTop\\Portal\\Twig\\AppExtension' => $baseDir . '/src/Twig/AppExtension.php', + 'Combodo\\iTop\\Portal\\Twig\\AppVariable' => $baseDir . '/src/Twig/AppVariable.php', + 'Combodo\\iTop\\Portal\\UrlMaker\\AbstractPortalUrlMaker' => $baseDir . '/src/UrlMaker/AbstractPortalUrlMaker.php', + 'Combodo\\iTop\\Portal\\VariableAccessor\\AbstractStringVariableAccessor' => $baseDir . '/src/VariableAccessor/AbstractStringVariableAccessor.php', + 'Combodo\\iTop\\Portal\\VariableAccessor\\AbstractVariableAccessor' => $baseDir . '/src/VariableAccessor/AbstractVariableAccessor.php', + 'Combodo\\iTop\\Portal\\VariableAccessor\\CombodoCurrentContactPhotoUrl' => $baseDir . '/src/VariableAccessor/CombodoCurrentContactPhotoUrl.php', + 'Combodo\\iTop\\Portal\\VariableAccessor\\CombodoPortalInstanceConf' => $baseDir . '/src/VariableAccessor/CombodoPortalInstanceConf.php', +); diff --git a/datamodels/2.x/itop-portal-base/portal/vendor/composer/autoload_namespaces.php b/datamodels/2.x/itop-portal-base/portal/vendor/composer/autoload_namespaces.php new file mode 100644 index 000000000..b7fc0125d --- /dev/null +++ b/datamodels/2.x/itop-portal-base/portal/vendor/composer/autoload_namespaces.php @@ -0,0 +1,9 @@ + array($baseDir . '/tests'), + 'Combodo\\iTop\\Portal\\' => array($baseDir . '/src'), +); diff --git a/datamodels/2.x/itop-portal-base/portal/vendor/composer/autoload_real.php b/datamodels/2.x/itop-portal-base/portal/vendor/composer/autoload_real.php new file mode 100644 index 000000000..089756ac6 --- /dev/null +++ b/datamodels/2.x/itop-portal-base/portal/vendor/composer/autoload_real.php @@ -0,0 +1,43 @@ += 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\ComposerStaticInitdf408f3f8ea034d298269cdf7647358b::getInitializer($loader)); + } else { + $classMap = require __DIR__ . '/autoload_classmap.php'; + if ($classMap) { + $loader->addClassMap($classMap); + } + } + + $loader->setClassMapAuthoritative(true); + $loader->register(true); + + return $loader; + } +} diff --git a/datamodels/2.x/itop-portal-base/portal/vendor/composer/autoload_static.php b/datamodels/2.x/itop-portal-base/portal/vendor/composer/autoload_static.php new file mode 100644 index 000000000..79163bd9f --- /dev/null +++ b/datamodels/2.x/itop-portal-base/portal/vendor/composer/autoload_static.php @@ -0,0 +1,88 @@ + + array ( + 'Combodo\\iTop\\Portal\\Tests\\' => 26, + 'Combodo\\iTop\\Portal\\' => 20, + ), + ); + + public static $prefixDirsPsr4 = array ( + 'Combodo\\iTop\\Portal\\Tests\\' => + array ( + 0 => __DIR__ . '/../..' . '/tests', + ), + 'Combodo\\iTop\\Portal\\' => + array ( + 0 => __DIR__ . '/../..' . '/src', + ), + ); + + public static $classMap = array ( + 'Combodo\\iTop\\Portal\\Brick\\AbstractBrick' => __DIR__ . '/../..' . '/src/Brick/AbstractBrick.php', + 'Combodo\\iTop\\Portal\\Brick\\AggregatePageBrick' => __DIR__ . '/../..' . '/src/Brick/AggregatePageBrick.php', + 'Combodo\\iTop\\Portal\\Brick\\BrickCollection' => __DIR__ . '/../..' . '/src/Brick/BrickCollection.php', + 'Combodo\\iTop\\Portal\\Brick\\BrickNotFoundException' => __DIR__ . '/../..' . '/src/Brick/BrickNotFoundException.php', + 'Combodo\\iTop\\Portal\\Brick\\BrowseBrick' => __DIR__ . '/../..' . '/src/Brick/BrowseBrick.php', + 'Combodo\\iTop\\Portal\\Brick\\CreateBrick' => __DIR__ . '/../..' . '/src/Brick/CreateBrick.php', + 'Combodo\\iTop\\Portal\\Brick\\FilterBrick' => __DIR__ . '/../..' . '/src/Brick/FilterBrick.php', + 'Combodo\\iTop\\Portal\\Brick\\ManageBrick' => __DIR__ . '/../..' . '/src/Brick/ManageBrick.php', + 'Combodo\\iTop\\Portal\\Brick\\PortalBrick' => __DIR__ . '/../..' . '/src/Brick/PortalBrick.php', + 'Combodo\\iTop\\Portal\\Brick\\PropertyNotFoundException' => __DIR__ . '/../..' . '/src/Brick/PropertyNotFoundException.php', + 'Combodo\\iTop\\Portal\\Brick\\UserProfileBrick' => __DIR__ . '/../..' . '/src/Brick/UserProfileBrick.php', + 'Combodo\\iTop\\Portal\\Controller\\AbstractController' => __DIR__ . '/../..' . '/src/Controller/AbstractController.php', + 'Combodo\\iTop\\Portal\\Controller\\AggregatePageBrickController' => __DIR__ . '/../..' . '/src/Controller/AggregatePageBrickController.class.inc.php', + 'Combodo\\iTop\\Portal\\Controller\\BrickController' => __DIR__ . '/../..' . '/src/Controller/BrickController.php', + 'Combodo\\iTop\\Portal\\Controller\\BrowseBrickController' => __DIR__ . '/../..' . '/src/Controller/BrowseBrickController.php', + 'Combodo\\iTop\\Portal\\Controller\\CreateBrickController' => __DIR__ . '/../..' . '/src/Controller/CreateBrickController.php', + 'Combodo\\iTop\\Portal\\Controller\\DefaultController' => __DIR__ . '/../..' . '/src/Controller/DefaultController.php', + 'Combodo\\iTop\\Portal\\Controller\\ManageBrickController' => __DIR__ . '/../..' . '/src/Controller/ManageBrickController.php', + 'Combodo\\iTop\\Portal\\Controller\\ObjectController' => __DIR__ . '/../..' . '/src/Controller/ObjectController.php', + 'Combodo\\iTop\\Portal\\Controller\\UserProfileBrickController' => __DIR__ . '/../..' . '/src/Controller/UserProfileBrickController.php', + 'Combodo\\iTop\\Portal\\DependencyInjection\\SilexCompatBootstrap\\PortalXmlConfiguration\\AbstractConfiguration' => __DIR__ . '/../..' . '/src/DependencyInjection/SilexCompatBootstrap/PortalXmlConfiguration/AbstractConfiguration.php', + 'Combodo\\iTop\\Portal\\DependencyInjection\\SilexCompatBootstrap\\PortalXmlConfiguration\\Basic' => __DIR__ . '/../..' . '/src/DependencyInjection/SilexCompatBootstrap/PortalXmlConfiguration/Basic.php', + 'Combodo\\iTop\\Portal\\DependencyInjection\\SilexCompatBootstrap\\PortalXmlConfiguration\\Forms' => __DIR__ . '/../..' . '/src/DependencyInjection/SilexCompatBootstrap/PortalXmlConfiguration/Forms.php', + 'Combodo\\iTop\\Portal\\DependencyInjection\\SilexCompatBootstrap\\PortalXmlConfiguration\\Lists' => __DIR__ . '/../..' . '/src/DependencyInjection/SilexCompatBootstrap/PortalXmlConfiguration/Lists.php', + 'Combodo\\iTop\\Portal\\EventListener\\ApplicationContextSetUrlMakerClass' => __DIR__ . '/../..' . '/src/EventListener/ApplicationContextSetUrlMakerClass.php', + 'Combodo\\iTop\\Portal\\EventListener\\UserProvider' => __DIR__ . '/../..' . '/src/EventListener/UserProvider.php', + 'Combodo\\iTop\\Portal\\Form\\ObjectFormManager' => __DIR__ . '/../..' . '/src/Form/ObjectFormManager.php', + 'Combodo\\iTop\\Portal\\Form\\PasswordFormManager' => __DIR__ . '/../..' . '/src/Form/PasswordFormManager.php', + 'Combodo\\iTop\\Portal\\Form\\PreferencesFormManager' => __DIR__ . '/../..' . '/src/Form/PreferencesFormManager.php', + 'Combodo\\iTop\\Portal\\Helper\\ApplicationHelper' => __DIR__ . '/../..' . '/src/Helper/ApplicationHelper.php', + 'Combodo\\iTop\\Portal\\Helper\\BrickControllerHelper' => __DIR__ . '/../..' . '/src/Helper/BrickControllerHelper.php', + 'Combodo\\iTop\\Portal\\Helper\\BrowseBrickHelper' => __DIR__ . '/../..' . '/src/Helper/BrowseBrickHelper.php', + 'Combodo\\iTop\\Portal\\Helper\\ContextManipulatorHelper' => __DIR__ . '/../..' . '/src/Helper/ContextManipulatorHelper.php', + 'Combodo\\iTop\\Portal\\Helper\\LifecycleValidatorHelper' => __DIR__ . '/../..' . '/src/Helper/LifecycleValidatorHelper.php', + 'Combodo\\iTop\\Portal\\Helper\\ObjectFormHandlerHelper' => __DIR__ . '/../..' . '/src/Helper/ObjectFormHandlerHelper.php', + 'Combodo\\iTop\\Portal\\Helper\\RequestManipulatorHelper' => __DIR__ . '/../..' . '/src/Helper/RequestManipulatorHelper.php', + 'Combodo\\iTop\\Portal\\Helper\\ScopeValidatorHelper' => __DIR__ . '/../..' . '/src/Helper/ScopeValidatorHelper.php', + 'Combodo\\iTop\\Portal\\Helper\\SecurityHelper' => __DIR__ . '/../..' . '/src/Helper/SecurityHelper.php', + 'Combodo\\iTop\\Portal\\Kernel' => __DIR__ . '/../..' . '/src/Kernel.php', + 'Combodo\\iTop\\Portal\\Routing\\ItopExtensionsExtraRoutes' => __DIR__ . '/../..' . '/src/Routing/ItopExtensionsExtraRoutes.php', + 'Combodo\\iTop\\Portal\\Routing\\UrlGenerator' => __DIR__ . '/../..' . '/src/Routing/UrlGenerator.php', + 'Combodo\\iTop\\Portal\\Twig\\AppExtension' => __DIR__ . '/../..' . '/src/Twig/AppExtension.php', + 'Combodo\\iTop\\Portal\\Twig\\AppVariable' => __DIR__ . '/../..' . '/src/Twig/AppVariable.php', + 'Combodo\\iTop\\Portal\\UrlMaker\\AbstractPortalUrlMaker' => __DIR__ . '/../..' . '/src/UrlMaker/AbstractPortalUrlMaker.php', + 'Combodo\\iTop\\Portal\\VariableAccessor\\AbstractStringVariableAccessor' => __DIR__ . '/../..' . '/src/VariableAccessor/AbstractStringVariableAccessor.php', + 'Combodo\\iTop\\Portal\\VariableAccessor\\AbstractVariableAccessor' => __DIR__ . '/../..' . '/src/VariableAccessor/AbstractVariableAccessor.php', + 'Combodo\\iTop\\Portal\\VariableAccessor\\CombodoCurrentContactPhotoUrl' => __DIR__ . '/../..' . '/src/VariableAccessor/CombodoCurrentContactPhotoUrl.php', + 'Combodo\\iTop\\Portal\\VariableAccessor\\CombodoPortalInstanceConf' => __DIR__ . '/../..' . '/src/VariableAccessor/CombodoPortalInstanceConf.php', + ); + + public static function getInitializer(ClassLoader $loader) + { + return \Closure::bind(function () use ($loader) { + $loader->prefixLengthsPsr4 = ComposerStaticInitdf408f3f8ea034d298269cdf7647358b::$prefixLengthsPsr4; + $loader->prefixDirsPsr4 = ComposerStaticInitdf408f3f8ea034d298269cdf7647358b::$prefixDirsPsr4; + $loader->classMap = ComposerStaticInitdf408f3f8ea034d298269cdf7647358b::$classMap; + + }, null, ClassLoader::class); + } +} diff --git a/datamodels/2.x/itop-portal-base/portal/vendor/composer/installed.json b/datamodels/2.x/itop-portal-base/portal/vendor/composer/installed.json new file mode 100644 index 000000000..fe51488c7 --- /dev/null +++ b/datamodels/2.x/itop-portal-base/portal/vendor/composer/installed.json @@ -0,0 +1 @@ +[] diff --git a/datamodels/2.x/itop-portal/main.itop-portal.php b/datamodels/2.x/itop-portal/main.itop-portal.php index bdd5cd5b0..135272cbd 100644 --- a/datamodels/2.x/itop-portal/main.itop-portal.php +++ b/datamodels/2.x/itop-portal/main.itop-portal.php @@ -21,7 +21,8 @@ use Combodo\iTop\Portal\UrlMaker\AbstractPortalUrlMaker; -require_once APPROOT.'/lib/composer-vendor/autoload.php'; +// Global autoloader (portal autoloader is already required through itop-portal-base/module.itop-portal-base.php) +require_once APPROOT.'/lib/autoload.php'; /** * iTopPortalEditUrlMaker diff --git a/lib/autoload.php b/lib/autoload.php new file mode 100644 index 000000000..79c1600b5 --- /dev/null +++ b/lib/autoload.php @@ -0,0 +1,7 @@ + + * Jordi Boggiano + * + * 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 + * @author Jordi Boggiano + * @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; +} diff --git a/lib/composer/LICENSE b/lib/composer/LICENSE new file mode 100644 index 000000000..f27399a04 --- /dev/null +++ b/lib/composer/LICENSE @@ -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. + diff --git a/lib/composer/autoload_classmap.php b/lib/composer/autoload_classmap.php new file mode 100644 index 000000000..edcf9e812 --- /dev/null +++ b/lib/composer/autoload_classmap.php @@ -0,0 +1,1816 @@ + $baseDir . '/application/applicationextension.inc.php', + 'Action' => $baseDir . '/core/action.class.inc.php', + 'ActionChecker' => $baseDir . '/core/userrights.class.inc.php', + 'ActionEmail' => $baseDir . '/core/action.class.inc.php', + 'ActionNotification' => $baseDir . '/core/action.class.inc.php', + 'ApplicationContext' => $baseDir . '/application/applicationcontext.class.inc.php', + 'ApplicationException' => $baseDir . '/application/application.inc.php', + 'ApplicationMenu' => $baseDir . '/application/menunode.class.inc.php', + 'ApplicationPopupMenuItem' => $baseDir . '/application/applicationextension.inc.php', + 'ArchivedObjectException' => $baseDir . '/core/coreexception.class.inc.php', + 'ArithmeticError' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/ArithmeticError.php', + 'AssertionError' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/AssertionError.php', + 'AsyncSendEmail' => $baseDir . '/core/asynctask.class.inc.php', + 'AsyncTask' => $baseDir . '/core/asynctask.class.inc.php', + 'AttributeApplicationLanguage' => $baseDir . '/core/attributedef.class.inc.php', + 'AttributeArchiveDate' => $baseDir . '/core/attributedef.class.inc.php', + 'AttributeArchiveFlag' => $baseDir . '/core/attributedef.class.inc.php', + 'AttributeBlob' => $baseDir . '/core/attributedef.class.inc.php', + 'AttributeBoolean' => $baseDir . '/core/attributedef.class.inc.php', + 'AttributeCaseLog' => $baseDir . '/core/attributedef.class.inc.php', + 'AttributeClass' => $baseDir . '/core/attributedef.class.inc.php', + 'AttributeClassAttCodeSet' => $baseDir . '/core/attributedef.class.inc.php', + 'AttributeClassState' => $baseDir . '/core/attributedef.class.inc.php', + 'AttributeCustomFields' => $baseDir . '/core/attributedef.class.inc.php', + 'AttributeDBField' => $baseDir . '/core/attributedef.class.inc.php', + 'AttributeDBFieldVoid' => $baseDir . '/core/attributedef.class.inc.php', + 'AttributeDashboard' => $baseDir . '/core/attributedef.class.inc.php', + 'AttributeDate' => $baseDir . '/core/attributedef.class.inc.php', + 'AttributeDateTime' => $baseDir . '/core/attributedef.class.inc.php', + 'AttributeDeadline' => $baseDir . '/core/attributedef.class.inc.php', + 'AttributeDecimal' => $baseDir . '/core/attributedef.class.inc.php', + 'AttributeDefinition' => $baseDir . '/core/attributedef.class.inc.php', + 'AttributeDuration' => $baseDir . '/core/attributedef.class.inc.php', + 'AttributeEmailAddress' => $baseDir . '/core/attributedef.class.inc.php', + 'AttributeEncryptedString' => $baseDir . '/core/attributedef.class.inc.php', + 'AttributeEnum' => $baseDir . '/core/attributedef.class.inc.php', + 'AttributeExternalField' => $baseDir . '/core/attributedef.class.inc.php', + 'AttributeExternalKey' => $baseDir . '/core/attributedef.class.inc.php', + 'AttributeFinalClass' => $baseDir . '/core/attributedef.class.inc.php', + 'AttributeFriendlyName' => $baseDir . '/core/attributedef.class.inc.php', + 'AttributeHTML' => $baseDir . '/core/attributedef.class.inc.php', + 'AttributeHierarchicalKey' => $baseDir . '/core/attributedef.class.inc.php', + 'AttributeIPAddress' => $baseDir . '/core/attributedef.class.inc.php', + 'AttributeImage' => $baseDir . '/core/attributedef.class.inc.php', + 'AttributeInteger' => $baseDir . '/core/attributedef.class.inc.php', + 'AttributeLinkedSet' => $baseDir . '/core/attributedef.class.inc.php', + 'AttributeLinkedSetIndirect' => $baseDir . '/core/attributedef.class.inc.php', + 'AttributeLongText' => $baseDir . '/core/attributedef.class.inc.php', + 'AttributeMetaEnum' => $baseDir . '/core/attributedef.class.inc.php', + 'AttributeOQL' => $baseDir . '/core/attributedef.class.inc.php', + 'AttributeObjectKey' => $baseDir . '/core/attributedef.class.inc.php', + 'AttributeObsolescenceDate' => $baseDir . '/core/attributedef.class.inc.php', + 'AttributeObsolescenceFlag' => $baseDir . '/core/attributedef.class.inc.php', + 'AttributeOneWayPassword' => $baseDir . '/core/attributedef.class.inc.php', + 'AttributePassword' => $baseDir . '/core/attributedef.class.inc.php', + 'AttributePercentage' => $baseDir . '/core/attributedef.class.inc.php', + 'AttributePhoneNumber' => $baseDir . '/core/attributedef.class.inc.php', + 'AttributePropertySet' => $baseDir . '/core/attributedef.class.inc.php', + 'AttributeQueryAttCodeSet' => $baseDir . '/core/attributedef.class.inc.php', + 'AttributeRedundancySettings' => $baseDir . '/core/attributedef.class.inc.php', + 'AttributeSet' => $baseDir . '/core/attributedef.class.inc.php', + 'AttributeStopWatch' => $baseDir . '/core/attributedef.class.inc.php', + 'AttributeString' => $baseDir . '/core/attributedef.class.inc.php', + 'AttributeSubItem' => $baseDir . '/core/attributedef.class.inc.php', + 'AttributeTable' => $baseDir . '/core/attributedef.class.inc.php', + 'AttributeTagSet' => $baseDir . '/core/attributedef.class.inc.php', + 'AttributeTemplateHTML' => $baseDir . '/core/attributedef.class.inc.php', + 'AttributeTemplateString' => $baseDir . '/core/attributedef.class.inc.php', + 'AttributeTemplateText' => $baseDir . '/core/attributedef.class.inc.php', + 'AttributeText' => $baseDir . '/core/attributedef.class.inc.php', + 'AttributeURL' => $baseDir . '/core/attributedef.class.inc.php', + 'AuditCategory' => $baseDir . '/application/audit.category.class.inc.php', + 'AuditRule' => $baseDir . '/application/audit.rule.class.inc.php', + 'BackgroundTask' => $baseDir . '/core/backgroundtask.class.inc.php', + 'BinaryExpression' => $baseDir . '/core/oql/expression.class.inc.php', + 'BinaryOqlExpression' => $baseDir . '/core/oql/oqlquery.class.inc.php', + 'BulkChange' => $baseDir . '/core/bulkchange.class.inc.php', + 'BulkChangeException' => $baseDir . '/core/bulkchange.class.inc.php', + 'BulkExport' => $baseDir . '/core/bulkexport.class.inc.php', + 'BulkExportException' => $baseDir . '/core/bulkexport.class.inc.php', + 'BulkExportMissingParameterException' => $baseDir . '/core/bulkexport.class.inc.php', + 'BulkExportResult' => $baseDir . '/core/bulkexport.class.inc.php', + 'BulkExportResultGC' => $baseDir . '/core/bulkexport.class.inc.php', + 'CAS_SelfRegister' => $baseDir . '/core/userrights.class.inc.php', + 'CLIPage' => $baseDir . '/application/clipage.class.inc.php', + 'CMDBChange' => $baseDir . '/core/cmdbchange.class.inc.php', + 'CMDBChangeOp' => $baseDir . '/core/cmdbchangeop.class.inc.php', + 'CMDBChangeOpCreate' => $baseDir . '/core/cmdbchangeop.class.inc.php', + 'CMDBChangeOpDelete' => $baseDir . '/core/cmdbchangeop.class.inc.php', + 'CMDBChangeOpPlugin' => $baseDir . '/core/cmdbchangeop.class.inc.php', + 'CMDBChangeOpSetAttribute' => $baseDir . '/core/cmdbchangeop.class.inc.php', + 'CMDBChangeOpSetAttributeBlob' => $baseDir . '/core/cmdbchangeop.class.inc.php', + 'CMDBChangeOpSetAttributeCaseLog' => $baseDir . '/core/cmdbchangeop.class.inc.php', + 'CMDBChangeOpSetAttributeCustomFields' => $baseDir . '/core/cmdbchangeop.class.inc.php', + 'CMDBChangeOpSetAttributeEncrypted' => $baseDir . '/core/cmdbchangeop.class.inc.php', + 'CMDBChangeOpSetAttributeHTML' => $baseDir . '/core/cmdbchangeop.class.inc.php', + 'CMDBChangeOpSetAttributeLinks' => $baseDir . '/core/cmdbchangeop.class.inc.php', + 'CMDBChangeOpSetAttributeLinksAddRemove' => $baseDir . '/core/cmdbchangeop.class.inc.php', + 'CMDBChangeOpSetAttributeLinksTune' => $baseDir . '/core/cmdbchangeop.class.inc.php', + 'CMDBChangeOpSetAttributeLongText' => $baseDir . '/core/cmdbchangeop.class.inc.php', + 'CMDBChangeOpSetAttributeOneWayPassword' => $baseDir . '/core/cmdbchangeop.class.inc.php', + 'CMDBChangeOpSetAttributeScalar' => $baseDir . '/core/cmdbchangeop.class.inc.php', + 'CMDBChangeOpSetAttributeTagSet' => $baseDir . '/core/cmdbchangeop.class.inc.php', + 'CMDBChangeOpSetAttributeText' => $baseDir . '/core/cmdbchangeop.class.inc.php', + 'CMDBChangeOpSetAttributeURL' => $baseDir . '/core/cmdbchangeop.class.inc.php', + 'CMDBObject' => $baseDir . '/core/cmdbobject.class.inc.php', + 'CMDBObjectSet' => $baseDir . '/core/cmdbobject.class.inc.php', + 'CMDBSource' => $baseDir . '/core/cmdbsource.class.inc.php', + 'CSVBulkExport' => $baseDir . '/core/csvbulkexport.class.inc.php', + 'CSVPage' => $baseDir . '/application/csvpage.class.inc.php', + 'CSVParser' => $baseDir . '/core/csvparser.class.inc.php', + 'CSVParserException' => $baseDir . '/core/csvparser.class.inc.php', + 'CaptureWebPage' => $baseDir . '/application/capturewebpage.class.inc.php', + 'CellChangeSpec' => $baseDir . '/core/bulkchange.class.inc.php', + 'CellStatus_Ambiguous' => $baseDir . '/core/bulkchange.class.inc.php', + 'CellStatus_Issue' => $baseDir . '/core/bulkchange.class.inc.php', + 'CellStatus_Modify' => $baseDir . '/core/bulkchange.class.inc.php', + 'CellStatus_NullIssue' => $baseDir . '/core/bulkchange.class.inc.php', + 'CellStatus_SearchIssue' => $baseDir . '/core/bulkchange.class.inc.php', + 'CellStatus_Void' => $baseDir . '/core/bulkchange.class.inc.php', + 'CharConcatExpression' => $baseDir . '/core/oql/expression.class.inc.php', + 'CharConcatWSExpression' => $baseDir . '/core/oql/expression.class.inc.php', + 'CheckStopWatchThresholds' => $baseDir . '/core/ormstopwatch.class.inc.php', + 'CheckableExpression' => $baseDir . '/core/oql/oqlquery.class.inc.php', + 'Combodo\\iTop\\DesignDocument' => $baseDir . '/core/designdocument.class.inc.php', + 'Combodo\\iTop\\DesignElement' => $baseDir . '/core/designdocument.class.inc.php', + 'Config' => $baseDir . '/core/config.class.inc.php', + 'ConfigException' => $baseDir . '/core/config.class.inc.php', + 'ContextTag' => $baseDir . '/core/contexttag.class.inc.php', + 'CoreCannotSaveObjectException' => $baseDir . '/core/coreexception.class.inc.php', + 'CoreException' => $baseDir . '/core/coreexception.class.inc.php', + 'CoreServices' => $baseDir . '/core/restservices.class.inc.php', + 'CoreUnexpectedValue' => $baseDir . '/core/coreexception.class.inc.php', + 'CoreWarning' => $baseDir . '/core/coreexception.class.inc.php', + 'CryptEngine' => $baseDir . '/core/simplecrypt.class.inc.php', + 'CustomFieldsHandler' => $baseDir . '/core/customfieldshandler.class.inc.php', + 'DBObject' => $baseDir . '/core/dbobject.class.php', + 'DBObjectSearch' => $baseDir . '/core/dbobjectsearch.class.php', + 'DBObjectSet' => $baseDir . '/core/dbobjectset.class.php', + 'DBObjectSetComparator' => $baseDir . '/core/dbobjectset.class.php', + 'DBProperty' => $baseDir . '/core/dbproperty.class.inc.php', + 'DBSearch' => $baseDir . '/core/dbsearch.class.php', + 'DBUnionSearch' => $baseDir . '/core/dbunionsearch.class.php', + 'Dashboard' => $baseDir . '/application/dashboard.class.inc.php', + 'DashboardLayout' => $baseDir . '/application/dashboardlayout.class.inc.php', + 'DashboardLayoutMultiCol' => $baseDir . '/application/dashboardlayout.class.inc.php', + 'DashboardLayoutOneCol' => $baseDir . '/application/dashboardlayout.class.inc.php', + 'DashboardLayoutThreeCols' => $baseDir . '/application/dashboardlayout.class.inc.php', + 'DashboardLayoutTwoCols' => $baseDir . '/application/dashboardlayout.class.inc.php', + 'DashboardMenuNode' => $baseDir . '/application/menunode.class.inc.php', + 'Dashlet' => $baseDir . '/application/dashlet.class.inc.php', + 'DashletBadge' => $baseDir . '/application/dashlet.class.inc.php', + 'DashletEmptyCell' => $baseDir . '/application/dashlet.class.inc.php', + 'DashletGroupBy' => $baseDir . '/application/dashlet.class.inc.php', + 'DashletGroupByBars' => $baseDir . '/application/dashlet.class.inc.php', + 'DashletGroupByPie' => $baseDir . '/application/dashlet.class.inc.php', + 'DashletGroupByTable' => $baseDir . '/application/dashlet.class.inc.php', + 'DashletHeaderDynamic' => $baseDir . '/application/dashlet.class.inc.php', + 'DashletHeaderStatic' => $baseDir . '/application/dashlet.class.inc.php', + 'DashletObjectList' => $baseDir . '/application/dashlet.class.inc.php', + 'DashletPlainText' => $baseDir . '/application/dashlet.class.inc.php', + 'DashletProxy' => $baseDir . '/application/dashlet.class.inc.php', + 'DashletUnknown' => $baseDir . '/application/dashlet.class.inc.php', + 'DataTable' => $baseDir . '/application/datatable.class.inc.php', + 'DataTableSettings' => $baseDir . '/application/datatable.class.inc.php', + 'DateTimeFormat' => $baseDir . '/core/datetimeformat.class.inc.php', + 'DefaultMetricComputer' => $baseDir . '/core/computing.inc.php', + 'DefaultWorkingTimeComputer' => $baseDir . '/core/computing.inc.php', + 'DeleteException' => $baseDir . '/core/deletionplan.class.inc.php', + 'DeletionPlan' => $baseDir . '/core/deletionplan.class.inc.php', + 'DesignerBooleanField' => $baseDir . '/application/forms.class.inc.php', + 'DesignerComboField' => $baseDir . '/application/forms.class.inc.php', + 'DesignerForm' => $baseDir . '/application/forms.class.inc.php', + 'DesignerFormField' => $baseDir . '/application/forms.class.inc.php', + 'DesignerFormSelectorField' => $baseDir . '/application/forms.class.inc.php', + 'DesignerHiddenField' => $baseDir . '/application/forms.class.inc.php', + 'DesignerIconSelectionField' => $baseDir . '/application/forms.class.inc.php', + 'DesignerIntegerField' => $baseDir . '/application/forms.class.inc.php', + 'DesignerLabelField' => $baseDir . '/application/forms.class.inc.php', + 'DesignerLongTextField' => $baseDir . '/application/forms.class.inc.php', + 'DesignerSortableField' => $baseDir . '/application/forms.class.inc.php', + 'DesignerStaticTextField' => $baseDir . '/application/forms.class.inc.php', + 'DesignerSubFormField' => $baseDir . '/application/forms.class.inc.php', + 'DesignerTabularForm' => $baseDir . '/application/forms.class.inc.php', + 'DesignerTextField' => $baseDir . '/application/forms.class.inc.php', + 'Dict' => $baseDir . '/core/dict.class.inc.php', + 'DictException' => $baseDir . '/core/dict.class.inc.php', + 'DictExceptionMissingString' => $baseDir . '/core/dict.class.inc.php', + 'DictExceptionUnknownLanguage' => $baseDir . '/core/dict.class.inc.php', + 'DisplayBlock' => $baseDir . '/application/displayblock.class.inc.php', + 'DisplayTemplate' => $baseDir . '/application/template.class.inc.php', + 'DisplayableEdge' => $baseDir . '/core/displayablegraph.class.inc.php', + 'DisplayableGraph' => $baseDir . '/core/displayablegraph.class.inc.php', + 'DisplayableGroupNode' => $baseDir . '/core/displayablegraph.class.inc.php', + 'DisplayableNode' => $baseDir . '/core/displayablegraph.class.inc.php', + 'DisplayableRedundancyNode' => $baseDir . '/core/displayablegraph.class.inc.php', + 'DivisionByZeroError' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/DivisionByZeroError.php', + 'EMail' => $baseDir . '/core/email.class.inc.php', + 'Error' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/Error.php', + 'Event' => $baseDir . '/core/event.class.inc.php', + 'EventIssue' => $baseDir . '/core/event.class.inc.php', + 'EventLoginUsage' => $baseDir . '/core/event.class.inc.php', + 'EventNotification' => $baseDir . '/core/event.class.inc.php', + 'EventNotificationEmail' => $baseDir . '/core/event.class.inc.php', + 'EventOnObject' => $baseDir . '/core/event.class.inc.php', + 'EventRestService' => $baseDir . '/core/event.class.inc.php', + 'EventWebService' => $baseDir . '/core/event.class.inc.php', + 'ExcelBulkExport' => $baseDir . '/core/excelbulkexport.class.inc.php', + 'ExcelExporter' => $baseDir . '/application/excelexporter.class.inc.php', + 'ExecAsyncTask' => $baseDir . '/core/asynctask.class.inc.php', + 'ExecutionKPI' => $baseDir . '/core/kpi.class.inc.php', + 'Expression' => $baseDir . '/core/oql/expression.class.inc.php', + 'ExpressionCache' => $baseDir . '/core/expressioncache.class.inc.php', + 'FalseExpression' => $baseDir . '/core/oql/expression.class.inc.php', + 'FieldExpression' => $baseDir . '/core/oql/expression.class.inc.php', + 'FieldExpressionResolved' => $baseDir . '/core/oql/expression.class.inc.php', + 'FieldOqlExpression' => $baseDir . '/core/oql/oqlquery.class.inc.php', + 'FileLog' => $baseDir . '/core/log.class.inc.php', + 'FileUploadException' => $baseDir . '/application/utils.inc.php', + 'FilterDefinition' => $baseDir . '/core/filterdef.class.inc.php', + 'FilterFromAttribute' => $baseDir . '/core/filterdef.class.inc.php', + 'FilterPrivateKey' => $baseDir . '/core/filterdef.class.inc.php', + 'FunctionExpression' => $baseDir . '/core/oql/expression.class.inc.php', + 'FunctionOqlExpression' => $baseDir . '/core/oql/oqlquery.class.inc.php', + 'GraphEdge' => $baseDir . '/core/simplegraph.class.inc.php', + 'GraphElement' => $baseDir . '/core/simplegraph.class.inc.php', + 'GraphNode' => $baseDir . '/core/simplegraph.class.inc.php', + 'HTMLBulkExport' => $baseDir . '/core/htmlbulkexport.class.inc.php', + 'HTMLDOMSanitizer' => $baseDir . '/core/htmlsanitizer.class.inc.php', + 'HTMLNullSanitizer' => $baseDir . '/core/htmlsanitizer.class.inc.php', + 'HTMLSanitizer' => $baseDir . '/core/htmlsanitizer.class.inc.php', + 'HistoryBlock' => $baseDir . '/application/displayblock.class.inc.php', + 'Html2Text\\Html2Text' => $baseDir . '/application/Html2Text.php', + 'Html2Text\\Html2TextException' => $baseDir . '/application/Html2TextException.php', + 'ITopArchiveTar' => $baseDir . '/core/tar-itop.class.inc.php', + 'InlineImage' => $baseDir . '/core/inlineimage.class.inc.php', + 'InlineImageGC' => $baseDir . '/core/inlineimage.class.inc.php', + 'InputOutputTask' => $baseDir . '/application/iotask.class.inc.php', + 'IntervalExpression' => $baseDir . '/core/oql/expression.class.inc.php', + 'IntervalOqlExpression' => $baseDir . '/core/oql/oqlquery.class.inc.php', + 'Introspection' => $baseDir . '/core/introspection.class.inc.php', + 'InvalidConfigParamException' => $baseDir . '/core/coreexception.class.inc.php', + 'IssueLog' => $baseDir . '/core/log.class.inc.php', + 'JSButtonItem' => $baseDir . '/application/applicationextension.inc.php', + 'JSPopupMenuItem' => $baseDir . '/application/applicationextension.inc.php', + 'ListExpression' => $baseDir . '/core/oql/expression.class.inc.php', + 'ListOqlExpression' => $baseDir . '/core/oql/oqlquery.class.inc.php', + 'LogAPI' => $baseDir . '/core/log.class.inc.php', + 'LoginWebPage' => $baseDir . '/application/loginwebpage.class.inc.php', + 'MatchExpression' => $baseDir . '/core/oql/expression.class.inc.php', + 'MatchOqlExpression' => $baseDir . '/core/oql/oqlquery.class.inc.php', + 'MenuBlock' => $baseDir . '/application/displayblock.class.inc.php', + 'MenuGroup' => $baseDir . '/application/menunode.class.inc.php', + 'MenuNode' => $baseDir . '/application/menunode.class.inc.php', + 'MetaModel' => $baseDir . '/core/metamodel.class.php', + 'MissingColumnException' => $baseDir . '/core/attributedef.class.inc.php', + 'MissingQueryArgument' => $baseDir . '/core/oql/expression.class.inc.php', + 'ModelReflection' => $baseDir . '/core/modelreflection.class.inc.php', + 'ModelReflectionRuntime' => $baseDir . '/core/modelreflection.class.inc.php', + 'ModuleDesign' => $baseDir . '/core/moduledesign.class.inc.php', + 'ModuleHandlerAPI' => $baseDir . '/core/modulehandler.class.inc.php', + 'ModuleHandlerApiInterface' => $baseDir . '/core/modulehandler.class.inc.php', + 'MyHelpers' => $baseDir . '/core/MyHelpers.class.inc.php', + 'MySQLException' => $baseDir . '/core/cmdbsource.class.inc.php', + 'MySQLHasGoneAwayException' => $baseDir . '/core/cmdbsource.class.inc.php', + 'MySQLQueryHasNoResultException' => $baseDir . '/core/cmdbsource.class.inc.php', + 'NewObjectMenuNode' => $baseDir . '/application/menunode.class.inc.php', + 'NewsroomProviderBase' => $baseDir . '/application/newsroomprovider.class.inc.php', + 'NiceWebPage' => $baseDir . '/application/nicewebpage.class.inc.php', + 'OQLException' => $baseDir . '/core/oql/oqlexception.class.inc.php', + 'OQLLexer' => $baseDir . '/core/oql/oql-lexer.php', + 'OQLLexerException' => $baseDir . '/core/oql/oql-lexer.php', + 'OQLLexerRaw' => $baseDir . '/core/oql/oql-lexer.php', + 'OQLMenuNode' => $baseDir . '/application/menunode.class.inc.php', + 'OQLParser' => $baseDir . '/core/oql/oql-parser.php', + 'OQLParserException' => $baseDir . '/core/oql/oql-parser.php', + 'OQLParserRaw' => $baseDir . '/core/oql/oql-parser.php', + 'OQLParser_yyStackEntry' => $baseDir . '/core/oql/oql-parser.php', + 'OQLParser_yyToken' => $baseDir . '/core/oql/oql-parser.php', + 'ObjectDetailsTemplate' => $baseDir . '/application/template.class.inc.php', + 'ObjectResult' => $baseDir . '/core/restservices.class.inc.php', + 'ObjectStimulus' => $baseDir . '/core/stimulus.class.inc.php', + 'ObsolescenceDateUpdater' => $baseDir . '/core/background.inc.php', + 'OqlHexValue' => $baseDir . '/core/oql/oqlquery.class.inc.php', + 'OqlInterpreter' => $baseDir . '/core/oql/oqlinterpreter.class.inc.php', + 'OqlInterpreterException' => $baseDir . '/core/oql/oqlinterpreter.class.inc.php', + 'OqlJoinSpec' => $baseDir . '/core/oql/oqlquery.class.inc.php', + 'OqlName' => $baseDir . '/core/oql/oqlquery.class.inc.php', + 'OqlNormalizeException' => $baseDir . '/core/oql/oqlinterpreter.class.inc.php', + 'OqlObjectQuery' => $baseDir . '/core/oql/oqlquery.class.inc.php', + 'OqlQuery' => $baseDir . '/core/oql/oqlquery.class.inc.php', + 'OqlUnionQuery' => $baseDir . '/core/oql/oqlquery.class.inc.php', + 'PDFBulkExport' => $baseDir . '/core/pdfbulkexport.class.inc.php', + 'PDFPage' => $baseDir . '/application/pdfpage.class.inc.php', + 'PHP_LexerGenerator' => $baseDir . '/core/oql/build/PHP/LexerGenerator.php', + 'PHP_LexerGenerator_Exception' => $baseDir . '/core/oql/build/PHP/LexerGenerator/Exception.php', + 'PHP_LexerGenerator_Lexer' => $baseDir . '/core/oql/build/PHP/LexerGenerator/Lexer.php', + 'PHP_LexerGenerator_Parser' => $baseDir . '/core/oql/build/PHP/LexerGenerator/Parser.php', + 'PHP_LexerGenerator_ParseryyStackEntry' => $baseDir . '/core/oql/build/PHP/LexerGenerator/Parser.php', + 'PHP_LexerGenerator_ParseryyToken' => $baseDir . '/core/oql/build/PHP/LexerGenerator/Parser.php', + 'PHP_LexerGenerator_Regex_Lexer' => $baseDir . '/core/oql/build/PHP/LexerGenerator/Regex/Lexer.php', + 'PHP_LexerGenerator_Regex_Parser' => $baseDir . '/core/oql/build/PHP/LexerGenerator/Regex/Parser.php', + 'PHP_LexerGenerator_Regex_yyStackEntry' => $baseDir . '/core/oql/build/PHP/LexerGenerator/Regex/Parser.php', + 'PHP_LexerGenerator_Regex_yyToken' => $baseDir . '/core/oql/build/PHP/LexerGenerator/Regex/Parser.php', + 'PHP_ParserGenerator' => $baseDir . '/core/oql/build/PHP/ParserGenerator.php', + 'PHP_ParserGenerator_Action' => $baseDir . '/core/oql/build/PHP/ParserGenerator/Action.php', + 'PHP_ParserGenerator_ActionTable' => $baseDir . '/core/oql/build/PHP/ParserGenerator/ActionTable.php', + 'PHP_ParserGenerator_Config' => $baseDir . '/core/oql/build/PHP/ParserGenerator/Config.php', + 'PHP_ParserGenerator_Data' => $baseDir . '/core/oql/build/PHP/ParserGenerator/Data.php', + 'PHP_ParserGenerator_Parser' => $baseDir . '/core/oql/build/PHP/ParserGenerator/Parser.php', + 'PHP_ParserGenerator_PropagationLink' => $baseDir . '/core/oql/build/PHP/ParserGenerator/PropagationLink.php', + 'PHP_ParserGenerator_Rule' => $baseDir . '/core/oql/build/PHP/ParserGenerator/Rule.php', + 'PHP_ParserGenerator_State' => $baseDir . '/core/oql/build/PHP/ParserGenerator/State.php', + 'PHP_ParserGenerator_StateNode' => $baseDir . '/core/oql/build/PHP/ParserGenerator/State.php', + 'PHP_ParserGenerator_Symbol' => $baseDir . '/core/oql/build/PHP/ParserGenerator/Symbol.php', + 'Page' => $baseDir . '/application/webpage.class.inc.php', + 'ParseError' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/ParseError.php', + 'ParseyyStackEntry' => $baseDir . '/core/oql/build/PHP/Lempar.php', + 'ParseyyToken' => $baseDir . '/core/oql/build/PHP/Lempar.php', + 'PortalDispatcher' => $baseDir . '/application/portaldispatcher.class.inc.php', + 'PortalURLMaker' => $baseDir . '/application/applicationcontext.class.inc.php', + 'PrintableDataTable' => $baseDir . '/application/datatable.class.inc.php', + 'ProcessException' => $baseDir . '/core/backgroundprocess.inc.php', + 'ProcessFatalException' => $baseDir . '/core/backgroundprocess.inc.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\\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\\Log\\Test\\TestLogger' => $vendorDir . '/psr/log/Psr/Log/Test/TestLogger.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', + 'Query' => $baseDir . '/application/query.class.inc.php', + 'QueryBuilderContext' => $baseDir . '/core/querybuildercontext.class.inc.php', + 'QueryBuilderExpressions' => $baseDir . '/core/oql/expression.class.inc.php', + 'QueryOQL' => $baseDir . '/application/query.class.inc.php', + 'QueryReflection' => $baseDir . '/core/modelreflection.class.inc.php', + 'QueryReflectionRuntime' => $baseDir . '/core/modelreflection.class.inc.php', + 'RelationEdge' => $baseDir . '/core/relationgraph.class.inc.php', + 'RelationGraph' => $baseDir . '/core/relationgraph.class.inc.php', + 'RelationObjectNode' => $baseDir . '/core/relationgraph.class.inc.php', + 'RelationRedundancyNode' => $baseDir . '/core/relationgraph.class.inc.php', + 'RelationTypeIterator' => $baseDir . '/core/simplegraph.class.inc.php', + 'RestDelete' => $baseDir . '/core/restservices.class.inc.php', + 'RestResult' => $baseDir . '/application/applicationextension.inc.php', + 'RestResultWithObjects' => $baseDir . '/core/restservices.class.inc.php', + 'RestResultWithRelations' => $baseDir . '/core/restservices.class.inc.php', + 'RestUtils' => $baseDir . '/application/applicationextension.inc.php', + 'RowStatus' => $baseDir . '/core/bulkchange.class.inc.php', + 'RowStatus_Disappeared' => $baseDir . '/core/bulkchange.class.inc.php', + 'RowStatus_Issue' => $baseDir . '/core/bulkchange.class.inc.php', + 'RowStatus_Modify' => $baseDir . '/core/bulkchange.class.inc.php', + 'RowStatus_NewObj' => $baseDir . '/core/bulkchange.class.inc.php', + 'RowStatus_NoChange' => $baseDir . '/core/bulkchange.class.inc.php', + 'RunTimeIconSelectionField' => $baseDir . '/application/forms.class.inc.php', + 'RuntimeDashboard' => $baseDir . '/application/dashboard.class.inc.php', + 'SQLExpression' => $baseDir . '/core/oql/expression.class.inc.php', + 'SQLObjectQuery' => $baseDir . '/core/sqlobjectquery.class.inc.php', + 'SQLQuery' => $baseDir . '/core/sqlquery.class.inc.php', + 'SQLUnionQuery' => $baseDir . '/core/sqlunionquery.class.inc.php', + 'ScalarExpression' => $baseDir . '/core/oql/expression.class.inc.php', + 'ScalarOqlExpression' => $baseDir . '/core/oql/oqlquery.class.inc.php', + 'SearchMenuNode' => $baseDir . '/application/menunode.class.inc.php', + 'SecurityException' => $baseDir . '/core/coreexception.class.inc.php', + 'SeparatorPopupMenuItem' => $baseDir . '/application/applicationextension.inc.php', + 'SessionUpdateTimestampHandlerInterface' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/SessionUpdateTimestampHandlerInterface.php', + 'SetupLog' => $baseDir . '/core/log.class.inc.php', + 'Shortcut' => $baseDir . '/application/shortcut.class.inc.php', + 'ShortcutContainerMenuNode' => $baseDir . '/application/menunode.class.inc.php', + 'ShortcutMenuNode' => $baseDir . '/application/menunode.class.inc.php', + 'ShortcutOQL' => $baseDir . '/application/shortcut.class.inc.php', + 'SimpleCrypt' => $baseDir . '/core/simplecrypt.class.inc.php', + 'SimpleCryptMcryptEngine' => $baseDir . '/core/simplecrypt.class.inc.php', + 'SimpleCryptOpenSSLEngine' => $baseDir . '/core/simplecrypt.class.inc.php', + 'SimpleCryptOpenSSLMcryptCompatibilityEngine' => $baseDir . '/core/simplecrypt.class.inc.php', + 'SimpleCryptSimpleEngine' => $baseDir . '/core/simplecrypt.class.inc.php', + 'SimpleCryptSodiumEngine' => $baseDir . '/core/simplecrypt.class.inc.php', + 'SimpleGraph' => $baseDir . '/core/simplegraph.class.inc.php', + 'SimpleGraphException' => $baseDir . '/core/simplegraph.class.inc.php', + 'SpreadsheetBulkExport' => $baseDir . '/core/spreadsheetbulkexport.class.inc.php', + 'StimulusChecker' => $baseDir . '/core/userrights.class.inc.php', + 'StimulusInternal' => $baseDir . '/core/stimulus.class.inc.php', + 'StimulusUserAction' => $baseDir . '/core/stimulus.class.inc.php', + 'Str' => $baseDir . '/core/MyHelpers.class.inc.php', + 'Swift_LogFileTransport' => $baseDir . '/core/email.class.inc.php', + 'Swift_Transport_LogFileTransport' => $baseDir . '/core/email.class.inc.php', + 'Symfony\\Bridge\\Twig\\AppVariable' => $vendorDir . '/symfony/twig-bridge/AppVariable.php', + 'Symfony\\Bridge\\Twig\\Command\\DebugCommand' => $vendorDir . '/symfony/twig-bridge/Command/DebugCommand.php', + 'Symfony\\Bridge\\Twig\\Command\\LintCommand' => $vendorDir . '/symfony/twig-bridge/Command/LintCommand.php', + 'Symfony\\Bridge\\Twig\\DataCollector\\TwigDataCollector' => $vendorDir . '/symfony/twig-bridge/DataCollector/TwigDataCollector.php', + 'Symfony\\Bridge\\Twig\\Extension\\AssetExtension' => $vendorDir . '/symfony/twig-bridge/Extension/AssetExtension.php', + 'Symfony\\Bridge\\Twig\\Extension\\CodeExtension' => $vendorDir . '/symfony/twig-bridge/Extension/CodeExtension.php', + 'Symfony\\Bridge\\Twig\\Extension\\DumpExtension' => $vendorDir . '/symfony/twig-bridge/Extension/DumpExtension.php', + 'Symfony\\Bridge\\Twig\\Extension\\ExpressionExtension' => $vendorDir . '/symfony/twig-bridge/Extension/ExpressionExtension.php', + 'Symfony\\Bridge\\Twig\\Extension\\FormExtension' => $vendorDir . '/symfony/twig-bridge/Extension/FormExtension.php', + 'Symfony\\Bridge\\Twig\\Extension\\HttpFoundationExtension' => $vendorDir . '/symfony/twig-bridge/Extension/HttpFoundationExtension.php', + 'Symfony\\Bridge\\Twig\\Extension\\HttpKernelExtension' => $vendorDir . '/symfony/twig-bridge/Extension/HttpKernelExtension.php', + 'Symfony\\Bridge\\Twig\\Extension\\HttpKernelRuntime' => $vendorDir . '/symfony/twig-bridge/Extension/HttpKernelRuntime.php', + 'Symfony\\Bridge\\Twig\\Extension\\InitRuntimeInterface' => $vendorDir . '/symfony/twig-bridge/Extension/InitRuntimeInterface.php', + 'Symfony\\Bridge\\Twig\\Extension\\LogoutUrlExtension' => $vendorDir . '/symfony/twig-bridge/Extension/LogoutUrlExtension.php', + 'Symfony\\Bridge\\Twig\\Extension\\ProfilerExtension' => $vendorDir . '/symfony/twig-bridge/Extension/ProfilerExtension.php', + 'Symfony\\Bridge\\Twig\\Extension\\RoutingExtension' => $vendorDir . '/symfony/twig-bridge/Extension/RoutingExtension.php', + 'Symfony\\Bridge\\Twig\\Extension\\SecurityExtension' => $vendorDir . '/symfony/twig-bridge/Extension/SecurityExtension.php', + 'Symfony\\Bridge\\Twig\\Extension\\StopwatchExtension' => $vendorDir . '/symfony/twig-bridge/Extension/StopwatchExtension.php', + 'Symfony\\Bridge\\Twig\\Extension\\TranslationExtension' => $vendorDir . '/symfony/twig-bridge/Extension/TranslationExtension.php', + 'Symfony\\Bridge\\Twig\\Extension\\WebLinkExtension' => $vendorDir . '/symfony/twig-bridge/Extension/WebLinkExtension.php', + 'Symfony\\Bridge\\Twig\\Extension\\WorkflowExtension' => $vendorDir . '/symfony/twig-bridge/Extension/WorkflowExtension.php', + 'Symfony\\Bridge\\Twig\\Extension\\YamlExtension' => $vendorDir . '/symfony/twig-bridge/Extension/YamlExtension.php', + 'Symfony\\Bridge\\Twig\\Form\\TwigRenderer' => $vendorDir . '/symfony/twig-bridge/Form/TwigRenderer.php', + 'Symfony\\Bridge\\Twig\\Form\\TwigRendererEngine' => $vendorDir . '/symfony/twig-bridge/Form/TwigRendererEngine.php', + 'Symfony\\Bridge\\Twig\\Form\\TwigRendererEngineInterface' => $vendorDir . '/symfony/twig-bridge/Form/TwigRendererEngineInterface.php', + 'Symfony\\Bridge\\Twig\\Form\\TwigRendererInterface' => $vendorDir . '/symfony/twig-bridge/Form/TwigRendererInterface.php', + 'Symfony\\Bridge\\Twig\\NodeVisitor\\Scope' => $vendorDir . '/symfony/twig-bridge/NodeVisitor/Scope.php', + 'Symfony\\Bridge\\Twig\\NodeVisitor\\TranslationDefaultDomainNodeVisitor' => $vendorDir . '/symfony/twig-bridge/NodeVisitor/TranslationDefaultDomainNodeVisitor.php', + 'Symfony\\Bridge\\Twig\\NodeVisitor\\TranslationNodeVisitor' => $vendorDir . '/symfony/twig-bridge/NodeVisitor/TranslationNodeVisitor.php', + 'Symfony\\Bridge\\Twig\\Node\\DumpNode' => $vendorDir . '/symfony/twig-bridge/Node/DumpNode.php', + 'Symfony\\Bridge\\Twig\\Node\\FormThemeNode' => $vendorDir . '/symfony/twig-bridge/Node/FormThemeNode.php', + 'Symfony\\Bridge\\Twig\\Node\\RenderBlockNode' => $vendorDir . '/symfony/twig-bridge/Node/RenderBlockNode.php', + 'Symfony\\Bridge\\Twig\\Node\\SearchAndRenderBlockNode' => $vendorDir . '/symfony/twig-bridge/Node/SearchAndRenderBlockNode.php', + 'Symfony\\Bridge\\Twig\\Node\\StopwatchNode' => $vendorDir . '/symfony/twig-bridge/Node/StopwatchNode.php', + 'Symfony\\Bridge\\Twig\\Node\\TransDefaultDomainNode' => $vendorDir . '/symfony/twig-bridge/Node/TransDefaultDomainNode.php', + 'Symfony\\Bridge\\Twig\\Node\\TransNode' => $vendorDir . '/symfony/twig-bridge/Node/TransNode.php', + 'Symfony\\Bridge\\Twig\\TokenParser\\DumpTokenParser' => $vendorDir . '/symfony/twig-bridge/TokenParser/DumpTokenParser.php', + 'Symfony\\Bridge\\Twig\\TokenParser\\FormThemeTokenParser' => $vendorDir . '/symfony/twig-bridge/TokenParser/FormThemeTokenParser.php', + 'Symfony\\Bridge\\Twig\\TokenParser\\StopwatchTokenParser' => $vendorDir . '/symfony/twig-bridge/TokenParser/StopwatchTokenParser.php', + 'Symfony\\Bridge\\Twig\\TokenParser\\TransChoiceTokenParser' => $vendorDir . '/symfony/twig-bridge/TokenParser/TransChoiceTokenParser.php', + 'Symfony\\Bridge\\Twig\\TokenParser\\TransDefaultDomainTokenParser' => $vendorDir . '/symfony/twig-bridge/TokenParser/TransDefaultDomainTokenParser.php', + 'Symfony\\Bridge\\Twig\\TokenParser\\TransTokenParser' => $vendorDir . '/symfony/twig-bridge/TokenParser/TransTokenParser.php', + 'Symfony\\Bridge\\Twig\\Translation\\TwigExtractor' => $vendorDir . '/symfony/twig-bridge/Translation/TwigExtractor.php', + 'Symfony\\Bridge\\Twig\\TwigEngine' => $vendorDir . '/symfony/twig-bridge/TwigEngine.php', + 'Symfony\\Bridge\\Twig\\UndefinedCallableHandler' => $vendorDir . '/symfony/twig-bridge/UndefinedCallableHandler.php', + 'Symfony\\Bundle\\FrameworkBundle\\CacheWarmer\\AbstractPhpFileCacheWarmer' => $vendorDir . '/symfony/framework-bundle/CacheWarmer/AbstractPhpFileCacheWarmer.php', + 'Symfony\\Bundle\\FrameworkBundle\\CacheWarmer\\AnnotationsCacheWarmer' => $vendorDir . '/symfony/framework-bundle/CacheWarmer/AnnotationsCacheWarmer.php', + 'Symfony\\Bundle\\FrameworkBundle\\CacheWarmer\\ClassCacheCacheWarmer' => $vendorDir . '/symfony/framework-bundle/CacheWarmer/ClassCacheCacheWarmer.php', + 'Symfony\\Bundle\\FrameworkBundle\\CacheWarmer\\RouterCacheWarmer' => $vendorDir . '/symfony/framework-bundle/CacheWarmer/RouterCacheWarmer.php', + 'Symfony\\Bundle\\FrameworkBundle\\CacheWarmer\\SerializerCacheWarmer' => $vendorDir . '/symfony/framework-bundle/CacheWarmer/SerializerCacheWarmer.php', + 'Symfony\\Bundle\\FrameworkBundle\\CacheWarmer\\TemplateFinder' => $vendorDir . '/symfony/framework-bundle/CacheWarmer/TemplateFinder.php', + 'Symfony\\Bundle\\FrameworkBundle\\CacheWarmer\\TemplateFinderInterface' => $vendorDir . '/symfony/framework-bundle/CacheWarmer/TemplateFinderInterface.php', + 'Symfony\\Bundle\\FrameworkBundle\\CacheWarmer\\TemplatePathsCacheWarmer' => $vendorDir . '/symfony/framework-bundle/CacheWarmer/TemplatePathsCacheWarmer.php', + 'Symfony\\Bundle\\FrameworkBundle\\CacheWarmer\\TranslationsCacheWarmer' => $vendorDir . '/symfony/framework-bundle/CacheWarmer/TranslationsCacheWarmer.php', + 'Symfony\\Bundle\\FrameworkBundle\\CacheWarmer\\ValidatorCacheWarmer' => $vendorDir . '/symfony/framework-bundle/CacheWarmer/ValidatorCacheWarmer.php', + 'Symfony\\Bundle\\FrameworkBundle\\Client' => $vendorDir . '/symfony/framework-bundle/Client.php', + 'Symfony\\Bundle\\FrameworkBundle\\Command\\AboutCommand' => $vendorDir . '/symfony/framework-bundle/Command/AboutCommand.php', + 'Symfony\\Bundle\\FrameworkBundle\\Command\\AbstractConfigCommand' => $vendorDir . '/symfony/framework-bundle/Command/AbstractConfigCommand.php', + 'Symfony\\Bundle\\FrameworkBundle\\Command\\AssetsInstallCommand' => $vendorDir . '/symfony/framework-bundle/Command/AssetsInstallCommand.php', + 'Symfony\\Bundle\\FrameworkBundle\\Command\\CacheClearCommand' => $vendorDir . '/symfony/framework-bundle/Command/CacheClearCommand.php', + 'Symfony\\Bundle\\FrameworkBundle\\Command\\CachePoolClearCommand' => $vendorDir . '/symfony/framework-bundle/Command/CachePoolClearCommand.php', + 'Symfony\\Bundle\\FrameworkBundle\\Command\\CachePoolPruneCommand' => $vendorDir . '/symfony/framework-bundle/Command/CachePoolPruneCommand.php', + 'Symfony\\Bundle\\FrameworkBundle\\Command\\CacheWarmupCommand' => $vendorDir . '/symfony/framework-bundle/Command/CacheWarmupCommand.php', + 'Symfony\\Bundle\\FrameworkBundle\\Command\\ConfigDebugCommand' => $vendorDir . '/symfony/framework-bundle/Command/ConfigDebugCommand.php', + 'Symfony\\Bundle\\FrameworkBundle\\Command\\ConfigDumpReferenceCommand' => $vendorDir . '/symfony/framework-bundle/Command/ConfigDumpReferenceCommand.php', + 'Symfony\\Bundle\\FrameworkBundle\\Command\\ContainerAwareCommand' => $vendorDir . '/symfony/framework-bundle/Command/ContainerAwareCommand.php', + 'Symfony\\Bundle\\FrameworkBundle\\Command\\ContainerDebugCommand' => $vendorDir . '/symfony/framework-bundle/Command/ContainerDebugCommand.php', + 'Symfony\\Bundle\\FrameworkBundle\\Command\\DebugAutowiringCommand' => $vendorDir . '/symfony/framework-bundle/Command/DebugAutowiringCommand.php', + 'Symfony\\Bundle\\FrameworkBundle\\Command\\EventDispatcherDebugCommand' => $vendorDir . '/symfony/framework-bundle/Command/EventDispatcherDebugCommand.php', + 'Symfony\\Bundle\\FrameworkBundle\\Command\\RouterDebugCommand' => $vendorDir . '/symfony/framework-bundle/Command/RouterDebugCommand.php', + 'Symfony\\Bundle\\FrameworkBundle\\Command\\RouterMatchCommand' => $vendorDir . '/symfony/framework-bundle/Command/RouterMatchCommand.php', + 'Symfony\\Bundle\\FrameworkBundle\\Command\\TranslationDebugCommand' => $vendorDir . '/symfony/framework-bundle/Command/TranslationDebugCommand.php', + 'Symfony\\Bundle\\FrameworkBundle\\Command\\TranslationUpdateCommand' => $vendorDir . '/symfony/framework-bundle/Command/TranslationUpdateCommand.php', + 'Symfony\\Bundle\\FrameworkBundle\\Command\\WorkflowDumpCommand' => $vendorDir . '/symfony/framework-bundle/Command/WorkflowDumpCommand.php', + 'Symfony\\Bundle\\FrameworkBundle\\Command\\XliffLintCommand' => $vendorDir . '/symfony/framework-bundle/Command/XliffLintCommand.php', + 'Symfony\\Bundle\\FrameworkBundle\\Command\\YamlLintCommand' => $vendorDir . '/symfony/framework-bundle/Command/YamlLintCommand.php', + 'Symfony\\Bundle\\FrameworkBundle\\Console\\Application' => $vendorDir . '/symfony/framework-bundle/Console/Application.php', + 'Symfony\\Bundle\\FrameworkBundle\\Console\\Descriptor\\Descriptor' => $vendorDir . '/symfony/framework-bundle/Console/Descriptor/Descriptor.php', + 'Symfony\\Bundle\\FrameworkBundle\\Console\\Descriptor\\JsonDescriptor' => $vendorDir . '/symfony/framework-bundle/Console/Descriptor/JsonDescriptor.php', + 'Symfony\\Bundle\\FrameworkBundle\\Console\\Descriptor\\MarkdownDescriptor' => $vendorDir . '/symfony/framework-bundle/Console/Descriptor/MarkdownDescriptor.php', + 'Symfony\\Bundle\\FrameworkBundle\\Console\\Descriptor\\TextDescriptor' => $vendorDir . '/symfony/framework-bundle/Console/Descriptor/TextDescriptor.php', + 'Symfony\\Bundle\\FrameworkBundle\\Console\\Descriptor\\XmlDescriptor' => $vendorDir . '/symfony/framework-bundle/Console/Descriptor/XmlDescriptor.php', + 'Symfony\\Bundle\\FrameworkBundle\\Console\\Helper\\DescriptorHelper' => $vendorDir . '/symfony/framework-bundle/Console/Helper/DescriptorHelper.php', + 'Symfony\\Bundle\\FrameworkBundle\\Controller\\AbstractController' => $vendorDir . '/symfony/framework-bundle/Controller/AbstractController.php', + 'Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller' => $vendorDir . '/symfony/framework-bundle/Controller/Controller.php', + 'Symfony\\Bundle\\FrameworkBundle\\Controller\\ControllerNameParser' => $vendorDir . '/symfony/framework-bundle/Controller/ControllerNameParser.php', + 'Symfony\\Bundle\\FrameworkBundle\\Controller\\ControllerResolver' => $vendorDir . '/symfony/framework-bundle/Controller/ControllerResolver.php', + 'Symfony\\Bundle\\FrameworkBundle\\Controller\\ControllerTrait' => $vendorDir . '/symfony/framework-bundle/Controller/ControllerTrait.php', + 'Symfony\\Bundle\\FrameworkBundle\\Controller\\RedirectController' => $vendorDir . '/symfony/framework-bundle/Controller/RedirectController.php', + 'Symfony\\Bundle\\FrameworkBundle\\Controller\\TemplateController' => $vendorDir . '/symfony/framework-bundle/Controller/TemplateController.php', + 'Symfony\\Bundle\\FrameworkBundle\\DataCollector\\RequestDataCollector' => $vendorDir . '/symfony/framework-bundle/DataCollector/RequestDataCollector.php', + 'Symfony\\Bundle\\FrameworkBundle\\DataCollector\\RouterDataCollector' => $vendorDir . '/symfony/framework-bundle/DataCollector/RouterDataCollector.php', + 'Symfony\\Bundle\\FrameworkBundle\\DependencyInjection\\Compiler\\AddAnnotationsCachedReaderPass' => $vendorDir . '/symfony/framework-bundle/DependencyInjection/Compiler/AddAnnotationsCachedReaderPass.php', + 'Symfony\\Bundle\\FrameworkBundle\\DependencyInjection\\Compiler\\AddCacheClearerPass' => $vendorDir . '/symfony/framework-bundle/DependencyInjection/Compiler/AddCacheClearerPass.php', + 'Symfony\\Bundle\\FrameworkBundle\\DependencyInjection\\Compiler\\AddCacheWarmerPass' => $vendorDir . '/symfony/framework-bundle/DependencyInjection/Compiler/AddCacheWarmerPass.php', + 'Symfony\\Bundle\\FrameworkBundle\\DependencyInjection\\Compiler\\AddConsoleCommandPass' => $vendorDir . '/symfony/framework-bundle/DependencyInjection/Compiler/AddConsoleCommandPass.php', + 'Symfony\\Bundle\\FrameworkBundle\\DependencyInjection\\Compiler\\AddConstraintValidatorsPass' => $vendorDir . '/symfony/framework-bundle/DependencyInjection/Compiler/AddConstraintValidatorsPass.php', + 'Symfony\\Bundle\\FrameworkBundle\\DependencyInjection\\Compiler\\AddDebugLogProcessorPass' => $vendorDir . '/symfony/framework-bundle/DependencyInjection/Compiler/AddDebugLogProcessorPass.php', + 'Symfony\\Bundle\\FrameworkBundle\\DependencyInjection\\Compiler\\AddExpressionLanguageProvidersPass' => $vendorDir . '/symfony/framework-bundle/DependencyInjection/Compiler/AddExpressionLanguageProvidersPass.php', + 'Symfony\\Bundle\\FrameworkBundle\\DependencyInjection\\Compiler\\AddValidatorInitializersPass' => $vendorDir . '/symfony/framework-bundle/DependencyInjection/Compiler/AddValidatorInitializersPass.php', + 'Symfony\\Bundle\\FrameworkBundle\\DependencyInjection\\Compiler\\CacheCollectorPass' => $vendorDir . '/symfony/framework-bundle/DependencyInjection/Compiler/CacheCollectorPass.php', + 'Symfony\\Bundle\\FrameworkBundle\\DependencyInjection\\Compiler\\CachePoolClearerPass' => $vendorDir . '/symfony/framework-bundle/DependencyInjection/Compiler/CachePoolClearerPass.php', + 'Symfony\\Bundle\\FrameworkBundle\\DependencyInjection\\Compiler\\CachePoolPass' => $vendorDir . '/symfony/framework-bundle/DependencyInjection/Compiler/CachePoolPass.php', + 'Symfony\\Bundle\\FrameworkBundle\\DependencyInjection\\Compiler\\CachePoolPrunerPass' => $vendorDir . '/symfony/framework-bundle/DependencyInjection/Compiler/CachePoolPrunerPass.php', + 'Symfony\\Bundle\\FrameworkBundle\\DependencyInjection\\Compiler\\CompilerDebugDumpPass' => $vendorDir . '/symfony/framework-bundle/DependencyInjection/Compiler/CompilerDebugDumpPass.php', + 'Symfony\\Bundle\\FrameworkBundle\\DependencyInjection\\Compiler\\ConfigCachePass' => $vendorDir . '/symfony/framework-bundle/DependencyInjection/Compiler/ConfigCachePass.php', + 'Symfony\\Bundle\\FrameworkBundle\\DependencyInjection\\Compiler\\ContainerBuilderDebugDumpPass' => $vendorDir . '/symfony/framework-bundle/DependencyInjection/Compiler/ContainerBuilderDebugDumpPass.php', + 'Symfony\\Bundle\\FrameworkBundle\\DependencyInjection\\Compiler\\ControllerArgumentValueResolverPass' => $vendorDir . '/symfony/framework-bundle/DependencyInjection/Compiler/ControllerArgumentValueResolverPass.php', + 'Symfony\\Bundle\\FrameworkBundle\\DependencyInjection\\Compiler\\DataCollectorTranslatorPass' => $vendorDir . '/symfony/framework-bundle/DependencyInjection/Compiler/DataCollectorTranslatorPass.php', + 'Symfony\\Bundle\\FrameworkBundle\\DependencyInjection\\Compiler\\FormPass' => $vendorDir . '/symfony/framework-bundle/DependencyInjection/Compiler/FormPass.php', + 'Symfony\\Bundle\\FrameworkBundle\\DependencyInjection\\Compiler\\LoggingTranslatorPass' => $vendorDir . '/symfony/framework-bundle/DependencyInjection/Compiler/LoggingTranslatorPass.php', + 'Symfony\\Bundle\\FrameworkBundle\\DependencyInjection\\Compiler\\ProfilerPass' => $vendorDir . '/symfony/framework-bundle/DependencyInjection/Compiler/ProfilerPass.php', + 'Symfony\\Bundle\\FrameworkBundle\\DependencyInjection\\Compiler\\PropertyInfoPass' => $vendorDir . '/symfony/framework-bundle/DependencyInjection/Compiler/PropertyInfoPass.php', + 'Symfony\\Bundle\\FrameworkBundle\\DependencyInjection\\Compiler\\RoutingResolverPass' => $vendorDir . '/symfony/framework-bundle/DependencyInjection/Compiler/RoutingResolverPass.php', + 'Symfony\\Bundle\\FrameworkBundle\\DependencyInjection\\Compiler\\SerializerPass' => $vendorDir . '/symfony/framework-bundle/DependencyInjection/Compiler/SerializerPass.php', + 'Symfony\\Bundle\\FrameworkBundle\\DependencyInjection\\Compiler\\TemplatingPass' => $vendorDir . '/symfony/framework-bundle/DependencyInjection/Compiler/TemplatingPass.php', + 'Symfony\\Bundle\\FrameworkBundle\\DependencyInjection\\Compiler\\TranslationDumperPass' => $vendorDir . '/symfony/framework-bundle/DependencyInjection/Compiler/TranslationDumperPass.php', + 'Symfony\\Bundle\\FrameworkBundle\\DependencyInjection\\Compiler\\TranslationExtractorPass' => $vendorDir . '/symfony/framework-bundle/DependencyInjection/Compiler/TranslationExtractorPass.php', + 'Symfony\\Bundle\\FrameworkBundle\\DependencyInjection\\Compiler\\TranslatorPass' => $vendorDir . '/symfony/framework-bundle/DependencyInjection/Compiler/TranslatorPass.php', + 'Symfony\\Bundle\\FrameworkBundle\\DependencyInjection\\Compiler\\UnusedTagsPass' => $vendorDir . '/symfony/framework-bundle/DependencyInjection/Compiler/UnusedTagsPass.php', + 'Symfony\\Bundle\\FrameworkBundle\\DependencyInjection\\Compiler\\ValidateWorkflowsPass' => $vendorDir . '/symfony/framework-bundle/DependencyInjection/Compiler/ValidateWorkflowsPass.php', + 'Symfony\\Bundle\\FrameworkBundle\\DependencyInjection\\Compiler\\WorkflowGuardListenerPass' => $vendorDir . '/symfony/framework-bundle/DependencyInjection/Compiler/WorkflowGuardListenerPass.php', + 'Symfony\\Bundle\\FrameworkBundle\\DependencyInjection\\Configuration' => $vendorDir . '/symfony/framework-bundle/DependencyInjection/Configuration.php', + 'Symfony\\Bundle\\FrameworkBundle\\DependencyInjection\\FrameworkExtension' => $vendorDir . '/symfony/framework-bundle/DependencyInjection/FrameworkExtension.php', + 'Symfony\\Bundle\\FrameworkBundle\\EventListener\\ResolveControllerNameSubscriber' => $vendorDir . '/symfony/framework-bundle/EventListener/ResolveControllerNameSubscriber.php', + 'Symfony\\Bundle\\FrameworkBundle\\EventListener\\SessionListener' => $vendorDir . '/symfony/framework-bundle/EventListener/SessionListener.php', + 'Symfony\\Bundle\\FrameworkBundle\\EventListener\\TestSessionListener' => $vendorDir . '/symfony/framework-bundle/EventListener/TestSessionListener.php', + 'Symfony\\Bundle\\FrameworkBundle\\FrameworkBundle' => $vendorDir . '/symfony/framework-bundle/FrameworkBundle.php', + 'Symfony\\Bundle\\FrameworkBundle\\HttpCache\\HttpCache' => $vendorDir . '/symfony/framework-bundle/HttpCache/HttpCache.php', + 'Symfony\\Bundle\\FrameworkBundle\\Kernel\\MicroKernelTrait' => $vendorDir . '/symfony/framework-bundle/Kernel/MicroKernelTrait.php', + 'Symfony\\Bundle\\FrameworkBundle\\Routing\\AnnotatedRouteControllerLoader' => $vendorDir . '/symfony/framework-bundle/Routing/AnnotatedRouteControllerLoader.php', + 'Symfony\\Bundle\\FrameworkBundle\\Routing\\DelegatingLoader' => $vendorDir . '/symfony/framework-bundle/Routing/DelegatingLoader.php', + 'Symfony\\Bundle\\FrameworkBundle\\Routing\\RedirectableUrlMatcher' => $vendorDir . '/symfony/framework-bundle/Routing/RedirectableUrlMatcher.php', + 'Symfony\\Bundle\\FrameworkBundle\\Routing\\Router' => $vendorDir . '/symfony/framework-bundle/Routing/Router.php', + 'Symfony\\Bundle\\FrameworkBundle\\Templating\\DelegatingEngine' => $vendorDir . '/symfony/framework-bundle/Templating/DelegatingEngine.php', + 'Symfony\\Bundle\\FrameworkBundle\\Templating\\EngineInterface' => $vendorDir . '/symfony/framework-bundle/Templating/EngineInterface.php', + 'Symfony\\Bundle\\FrameworkBundle\\Templating\\GlobalVariables' => $vendorDir . '/symfony/framework-bundle/Templating/GlobalVariables.php', + 'Symfony\\Bundle\\FrameworkBundle\\Templating\\Helper\\ActionsHelper' => $vendorDir . '/symfony/framework-bundle/Templating/Helper/ActionsHelper.php', + 'Symfony\\Bundle\\FrameworkBundle\\Templating\\Helper\\AssetsHelper' => $vendorDir . '/symfony/framework-bundle/Templating/Helper/AssetsHelper.php', + 'Symfony\\Bundle\\FrameworkBundle\\Templating\\Helper\\CodeHelper' => $vendorDir . '/symfony/framework-bundle/Templating/Helper/CodeHelper.php', + 'Symfony\\Bundle\\FrameworkBundle\\Templating\\Helper\\FormHelper' => $vendorDir . '/symfony/framework-bundle/Templating/Helper/FormHelper.php', + 'Symfony\\Bundle\\FrameworkBundle\\Templating\\Helper\\RequestHelper' => $vendorDir . '/symfony/framework-bundle/Templating/Helper/RequestHelper.php', + 'Symfony\\Bundle\\FrameworkBundle\\Templating\\Helper\\RouterHelper' => $vendorDir . '/symfony/framework-bundle/Templating/Helper/RouterHelper.php', + 'Symfony\\Bundle\\FrameworkBundle\\Templating\\Helper\\SessionHelper' => $vendorDir . '/symfony/framework-bundle/Templating/Helper/SessionHelper.php', + 'Symfony\\Bundle\\FrameworkBundle\\Templating\\Helper\\StopwatchHelper' => $vendorDir . '/symfony/framework-bundle/Templating/Helper/StopwatchHelper.php', + 'Symfony\\Bundle\\FrameworkBundle\\Templating\\Helper\\TranslatorHelper' => $vendorDir . '/symfony/framework-bundle/Templating/Helper/TranslatorHelper.php', + 'Symfony\\Bundle\\FrameworkBundle\\Templating\\Loader\\FilesystemLoader' => $vendorDir . '/symfony/framework-bundle/Templating/Loader/FilesystemLoader.php', + 'Symfony\\Bundle\\FrameworkBundle\\Templating\\Loader\\TemplateLocator' => $vendorDir . '/symfony/framework-bundle/Templating/Loader/TemplateLocator.php', + 'Symfony\\Bundle\\FrameworkBundle\\Templating\\PhpEngine' => $vendorDir . '/symfony/framework-bundle/Templating/PhpEngine.php', + 'Symfony\\Bundle\\FrameworkBundle\\Templating\\TemplateFilenameParser' => $vendorDir . '/symfony/framework-bundle/Templating/TemplateFilenameParser.php', + 'Symfony\\Bundle\\FrameworkBundle\\Templating\\TemplateNameParser' => $vendorDir . '/symfony/framework-bundle/Templating/TemplateNameParser.php', + 'Symfony\\Bundle\\FrameworkBundle\\Templating\\TemplateReference' => $vendorDir . '/symfony/framework-bundle/Templating/TemplateReference.php', + 'Symfony\\Bundle\\FrameworkBundle\\Templating\\TimedPhpEngine' => $vendorDir . '/symfony/framework-bundle/Templating/TimedPhpEngine.php', + 'Symfony\\Bundle\\FrameworkBundle\\Test\\KernelShutdownOnTearDownTrait' => $vendorDir . '/symfony/framework-bundle/Test/KernelShutdownOnTearDownTrait.php', + 'Symfony\\Bundle\\FrameworkBundle\\Test\\KernelTestCase' => $vendorDir . '/symfony/framework-bundle/Test/KernelTestCase.php', + 'Symfony\\Bundle\\FrameworkBundle\\Test\\WebTestCase' => $vendorDir . '/symfony/framework-bundle/Test/WebTestCase.php', + 'Symfony\\Bundle\\FrameworkBundle\\Translation\\PhpExtractor' => $vendorDir . '/symfony/framework-bundle/Translation/PhpExtractor.php', + 'Symfony\\Bundle\\FrameworkBundle\\Translation\\PhpStringTokenParser' => $vendorDir . '/symfony/framework-bundle/Translation/PhpStringTokenParser.php', + 'Symfony\\Bundle\\FrameworkBundle\\Translation\\TranslationLoader' => $vendorDir . '/symfony/framework-bundle/Translation/TranslationLoader.php', + 'Symfony\\Bundle\\FrameworkBundle\\Translation\\Translator' => $vendorDir . '/symfony/framework-bundle/Translation/Translator.php', + 'Symfony\\Bundle\\FrameworkBundle\\Validator\\ConstraintValidatorFactory' => $vendorDir . '/symfony/framework-bundle/Validator/ConstraintValidatorFactory.php', + 'Symfony\\Bundle\\TwigBundle\\CacheWarmer\\TemplateCacheCacheWarmer' => $vendorDir . '/symfony/twig-bundle/CacheWarmer/TemplateCacheCacheWarmer.php', + 'Symfony\\Bundle\\TwigBundle\\CacheWarmer\\TemplateCacheWarmer' => $vendorDir . '/symfony/twig-bundle/CacheWarmer/TemplateCacheWarmer.php', + 'Symfony\\Bundle\\TwigBundle\\Command\\DebugCommand' => $vendorDir . '/symfony/twig-bundle/Command/DebugCommand.php', + 'Symfony\\Bundle\\TwigBundle\\Command\\LintCommand' => $vendorDir . '/symfony/twig-bundle/Command/LintCommand.php', + 'Symfony\\Bundle\\TwigBundle\\ContainerAwareRuntimeLoader' => $vendorDir . '/symfony/twig-bundle/ContainerAwareRuntimeLoader.php', + 'Symfony\\Bundle\\TwigBundle\\Controller\\ExceptionController' => $vendorDir . '/symfony/twig-bundle/Controller/ExceptionController.php', + 'Symfony\\Bundle\\TwigBundle\\Controller\\PreviewErrorController' => $vendorDir . '/symfony/twig-bundle/Controller/PreviewErrorController.php', + 'Symfony\\Bundle\\TwigBundle\\DependencyInjection\\Compiler\\ExceptionListenerPass' => $vendorDir . '/symfony/twig-bundle/DependencyInjection/Compiler/ExceptionListenerPass.php', + 'Symfony\\Bundle\\TwigBundle\\DependencyInjection\\Compiler\\ExtensionPass' => $vendorDir . '/symfony/twig-bundle/DependencyInjection/Compiler/ExtensionPass.php', + 'Symfony\\Bundle\\TwigBundle\\DependencyInjection\\Compiler\\RuntimeLoaderPass' => $vendorDir . '/symfony/twig-bundle/DependencyInjection/Compiler/RuntimeLoaderPass.php', + 'Symfony\\Bundle\\TwigBundle\\DependencyInjection\\Compiler\\TwigEnvironmentPass' => $vendorDir . '/symfony/twig-bundle/DependencyInjection/Compiler/TwigEnvironmentPass.php', + 'Symfony\\Bundle\\TwigBundle\\DependencyInjection\\Compiler\\TwigLoaderPass' => $vendorDir . '/symfony/twig-bundle/DependencyInjection/Compiler/TwigLoaderPass.php', + 'Symfony\\Bundle\\TwigBundle\\DependencyInjection\\Configuration' => $vendorDir . '/symfony/twig-bundle/DependencyInjection/Configuration.php', + 'Symfony\\Bundle\\TwigBundle\\DependencyInjection\\Configurator\\EnvironmentConfigurator' => $vendorDir . '/symfony/twig-bundle/DependencyInjection/Configurator/EnvironmentConfigurator.php', + 'Symfony\\Bundle\\TwigBundle\\DependencyInjection\\TwigExtension' => $vendorDir . '/symfony/twig-bundle/DependencyInjection/TwigExtension.php', + 'Symfony\\Bundle\\TwigBundle\\Loader\\FilesystemLoader' => $vendorDir . '/symfony/twig-bundle/Loader/FilesystemLoader.php', + 'Symfony\\Bundle\\TwigBundle\\TemplateIterator' => $vendorDir . '/symfony/twig-bundle/TemplateIterator.php', + 'Symfony\\Bundle\\TwigBundle\\TwigBundle' => $vendorDir . '/symfony/twig-bundle/TwigBundle.php', + 'Symfony\\Bundle\\TwigBundle\\TwigEngine' => $vendorDir . '/symfony/twig-bundle/TwigEngine.php', + 'Symfony\\Bundle\\WebProfilerBundle\\Controller\\ExceptionController' => $vendorDir . '/symfony/web-profiler-bundle/Controller/ExceptionController.php', + 'Symfony\\Bundle\\WebProfilerBundle\\Controller\\ProfilerController' => $vendorDir . '/symfony/web-profiler-bundle/Controller/ProfilerController.php', + 'Symfony\\Bundle\\WebProfilerBundle\\Controller\\RouterController' => $vendorDir . '/symfony/web-profiler-bundle/Controller/RouterController.php', + 'Symfony\\Bundle\\WebProfilerBundle\\Csp\\ContentSecurityPolicyHandler' => $vendorDir . '/symfony/web-profiler-bundle/Csp/ContentSecurityPolicyHandler.php', + 'Symfony\\Bundle\\WebProfilerBundle\\Csp\\NonceGenerator' => $vendorDir . '/symfony/web-profiler-bundle/Csp/NonceGenerator.php', + 'Symfony\\Bundle\\WebProfilerBundle\\DependencyInjection\\Configuration' => $vendorDir . '/symfony/web-profiler-bundle/DependencyInjection/Configuration.php', + 'Symfony\\Bundle\\WebProfilerBundle\\DependencyInjection\\WebProfilerExtension' => $vendorDir . '/symfony/web-profiler-bundle/DependencyInjection/WebProfilerExtension.php', + 'Symfony\\Bundle\\WebProfilerBundle\\EventListener\\WebDebugToolbarListener' => $vendorDir . '/symfony/web-profiler-bundle/EventListener/WebDebugToolbarListener.php', + 'Symfony\\Bundle\\WebProfilerBundle\\Profiler\\TemplateManager' => $vendorDir . '/symfony/web-profiler-bundle/Profiler/TemplateManager.php', + 'Symfony\\Bundle\\WebProfilerBundle\\Twig\\WebProfilerExtension' => $vendorDir . '/symfony/web-profiler-bundle/Twig/WebProfilerExtension.php', + 'Symfony\\Bundle\\WebProfilerBundle\\WebProfilerBundle' => $vendorDir . '/symfony/web-profiler-bundle/WebProfilerBundle.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\\ClassLoader\\ApcClassLoader' => $vendorDir . '/symfony/class-loader/ApcClassLoader.php', + 'Symfony\\Component\\ClassLoader\\ClassCollectionLoader' => $vendorDir . '/symfony/class-loader/ClassCollectionLoader.php', + 'Symfony\\Component\\ClassLoader\\ClassLoader' => $vendorDir . '/symfony/class-loader/ClassLoader.php', + 'Symfony\\Component\\ClassLoader\\ClassMapGenerator' => $vendorDir . '/symfony/class-loader/ClassMapGenerator.php', + 'Symfony\\Component\\ClassLoader\\MapClassLoader' => $vendorDir . '/symfony/class-loader/MapClassLoader.php', + 'Symfony\\Component\\ClassLoader\\Psr4ClassLoader' => $vendorDir . '/symfony/class-loader/Psr4ClassLoader.php', + 'Symfony\\Component\\ClassLoader\\WinCacheClassLoader' => $vendorDir . '/symfony/class-loader/WinCacheClassLoader.php', + 'Symfony\\Component\\ClassLoader\\XcacheClassLoader' => $vendorDir . '/symfony/class-loader/XcacheClassLoader.php', + 'Symfony\\Component\\Config\\ConfigCache' => $vendorDir . '/symfony/config/ConfigCache.php', + 'Symfony\\Component\\Config\\ConfigCacheFactory' => $vendorDir . '/symfony/config/ConfigCacheFactory.php', + 'Symfony\\Component\\Config\\ConfigCacheFactoryInterface' => $vendorDir . '/symfony/config/ConfigCacheFactoryInterface.php', + 'Symfony\\Component\\Config\\ConfigCacheInterface' => $vendorDir . '/symfony/config/ConfigCacheInterface.php', + 'Symfony\\Component\\Config\\Definition\\ArrayNode' => $vendorDir . '/symfony/config/Definition/ArrayNode.php', + 'Symfony\\Component\\Config\\Definition\\BaseNode' => $vendorDir . '/symfony/config/Definition/BaseNode.php', + 'Symfony\\Component\\Config\\Definition\\BooleanNode' => $vendorDir . '/symfony/config/Definition/BooleanNode.php', + 'Symfony\\Component\\Config\\Definition\\Builder\\ArrayNodeDefinition' => $vendorDir . '/symfony/config/Definition/Builder/ArrayNodeDefinition.php', + 'Symfony\\Component\\Config\\Definition\\Builder\\BooleanNodeDefinition' => $vendorDir . '/symfony/config/Definition/Builder/BooleanNodeDefinition.php', + 'Symfony\\Component\\Config\\Definition\\Builder\\EnumNodeDefinition' => $vendorDir . '/symfony/config/Definition/Builder/EnumNodeDefinition.php', + 'Symfony\\Component\\Config\\Definition\\Builder\\ExprBuilder' => $vendorDir . '/symfony/config/Definition/Builder/ExprBuilder.php', + 'Symfony\\Component\\Config\\Definition\\Builder\\FloatNodeDefinition' => $vendorDir . '/symfony/config/Definition/Builder/FloatNodeDefinition.php', + 'Symfony\\Component\\Config\\Definition\\Builder\\IntegerNodeDefinition' => $vendorDir . '/symfony/config/Definition/Builder/IntegerNodeDefinition.php', + 'Symfony\\Component\\Config\\Definition\\Builder\\MergeBuilder' => $vendorDir . '/symfony/config/Definition/Builder/MergeBuilder.php', + 'Symfony\\Component\\Config\\Definition\\Builder\\NodeBuilder' => $vendorDir . '/symfony/config/Definition/Builder/NodeBuilder.php', + 'Symfony\\Component\\Config\\Definition\\Builder\\NodeDefinition' => $vendorDir . '/symfony/config/Definition/Builder/NodeDefinition.php', + 'Symfony\\Component\\Config\\Definition\\Builder\\NodeParentInterface' => $vendorDir . '/symfony/config/Definition/Builder/NodeParentInterface.php', + 'Symfony\\Component\\Config\\Definition\\Builder\\NormalizationBuilder' => $vendorDir . '/symfony/config/Definition/Builder/NormalizationBuilder.php', + 'Symfony\\Component\\Config\\Definition\\Builder\\NumericNodeDefinition' => $vendorDir . '/symfony/config/Definition/Builder/NumericNodeDefinition.php', + 'Symfony\\Component\\Config\\Definition\\Builder\\ParentNodeDefinitionInterface' => $vendorDir . '/symfony/config/Definition/Builder/ParentNodeDefinitionInterface.php', + 'Symfony\\Component\\Config\\Definition\\Builder\\ScalarNodeDefinition' => $vendorDir . '/symfony/config/Definition/Builder/ScalarNodeDefinition.php', + 'Symfony\\Component\\Config\\Definition\\Builder\\TreeBuilder' => $vendorDir . '/symfony/config/Definition/Builder/TreeBuilder.php', + 'Symfony\\Component\\Config\\Definition\\Builder\\ValidationBuilder' => $vendorDir . '/symfony/config/Definition/Builder/ValidationBuilder.php', + 'Symfony\\Component\\Config\\Definition\\Builder\\VariableNodeDefinition' => $vendorDir . '/symfony/config/Definition/Builder/VariableNodeDefinition.php', + 'Symfony\\Component\\Config\\Definition\\ConfigurationInterface' => $vendorDir . '/symfony/config/Definition/ConfigurationInterface.php', + 'Symfony\\Component\\Config\\Definition\\Dumper\\XmlReferenceDumper' => $vendorDir . '/symfony/config/Definition/Dumper/XmlReferenceDumper.php', + 'Symfony\\Component\\Config\\Definition\\Dumper\\YamlReferenceDumper' => $vendorDir . '/symfony/config/Definition/Dumper/YamlReferenceDumper.php', + 'Symfony\\Component\\Config\\Definition\\EnumNode' => $vendorDir . '/symfony/config/Definition/EnumNode.php', + 'Symfony\\Component\\Config\\Definition\\Exception\\DuplicateKeyException' => $vendorDir . '/symfony/config/Definition/Exception/DuplicateKeyException.php', + 'Symfony\\Component\\Config\\Definition\\Exception\\Exception' => $vendorDir . '/symfony/config/Definition/Exception/Exception.php', + 'Symfony\\Component\\Config\\Definition\\Exception\\ForbiddenOverwriteException' => $vendorDir . '/symfony/config/Definition/Exception/ForbiddenOverwriteException.php', + 'Symfony\\Component\\Config\\Definition\\Exception\\InvalidConfigurationException' => $vendorDir . '/symfony/config/Definition/Exception/InvalidConfigurationException.php', + 'Symfony\\Component\\Config\\Definition\\Exception\\InvalidDefinitionException' => $vendorDir . '/symfony/config/Definition/Exception/InvalidDefinitionException.php', + 'Symfony\\Component\\Config\\Definition\\Exception\\InvalidTypeException' => $vendorDir . '/symfony/config/Definition/Exception/InvalidTypeException.php', + 'Symfony\\Component\\Config\\Definition\\Exception\\UnsetKeyException' => $vendorDir . '/symfony/config/Definition/Exception/UnsetKeyException.php', + 'Symfony\\Component\\Config\\Definition\\FloatNode' => $vendorDir . '/symfony/config/Definition/FloatNode.php', + 'Symfony\\Component\\Config\\Definition\\IntegerNode' => $vendorDir . '/symfony/config/Definition/IntegerNode.php', + 'Symfony\\Component\\Config\\Definition\\NodeInterface' => $vendorDir . '/symfony/config/Definition/NodeInterface.php', + 'Symfony\\Component\\Config\\Definition\\NumericNode' => $vendorDir . '/symfony/config/Definition/NumericNode.php', + 'Symfony\\Component\\Config\\Definition\\Processor' => $vendorDir . '/symfony/config/Definition/Processor.php', + 'Symfony\\Component\\Config\\Definition\\PrototypeNodeInterface' => $vendorDir . '/symfony/config/Definition/PrototypeNodeInterface.php', + 'Symfony\\Component\\Config\\Definition\\PrototypedArrayNode' => $vendorDir . '/symfony/config/Definition/PrototypedArrayNode.php', + 'Symfony\\Component\\Config\\Definition\\ScalarNode' => $vendorDir . '/symfony/config/Definition/ScalarNode.php', + 'Symfony\\Component\\Config\\Definition\\VariableNode' => $vendorDir . '/symfony/config/Definition/VariableNode.php', + 'Symfony\\Component\\Config\\DependencyInjection\\ConfigCachePass' => $vendorDir . '/symfony/config/DependencyInjection/ConfigCachePass.php', + 'Symfony\\Component\\Config\\Exception\\FileLoaderImportCircularReferenceException' => $vendorDir . '/symfony/config/Exception/FileLoaderImportCircularReferenceException.php', + 'Symfony\\Component\\Config\\Exception\\FileLoaderLoadException' => $vendorDir . '/symfony/config/Exception/FileLoaderLoadException.php', + 'Symfony\\Component\\Config\\Exception\\FileLocatorFileNotFoundException' => $vendorDir . '/symfony/config/Exception/FileLocatorFileNotFoundException.php', + 'Symfony\\Component\\Config\\FileLocator' => $vendorDir . '/symfony/config/FileLocator.php', + 'Symfony\\Component\\Config\\FileLocatorInterface' => $vendorDir . '/symfony/config/FileLocatorInterface.php', + 'Symfony\\Component\\Config\\Loader\\DelegatingLoader' => $vendorDir . '/symfony/config/Loader/DelegatingLoader.php', + 'Symfony\\Component\\Config\\Loader\\FileLoader' => $vendorDir . '/symfony/config/Loader/FileLoader.php', + 'Symfony\\Component\\Config\\Loader\\GlobFileLoader' => $vendorDir . '/symfony/config/Loader/GlobFileLoader.php', + 'Symfony\\Component\\Config\\Loader\\Loader' => $vendorDir . '/symfony/config/Loader/Loader.php', + 'Symfony\\Component\\Config\\Loader\\LoaderInterface' => $vendorDir . '/symfony/config/Loader/LoaderInterface.php', + 'Symfony\\Component\\Config\\Loader\\LoaderResolver' => $vendorDir . '/symfony/config/Loader/LoaderResolver.php', + 'Symfony\\Component\\Config\\Loader\\LoaderResolverInterface' => $vendorDir . '/symfony/config/Loader/LoaderResolverInterface.php', + 'Symfony\\Component\\Config\\ResourceCheckerConfigCache' => $vendorDir . '/symfony/config/ResourceCheckerConfigCache.php', + 'Symfony\\Component\\Config\\ResourceCheckerConfigCacheFactory' => $vendorDir . '/symfony/config/ResourceCheckerConfigCacheFactory.php', + 'Symfony\\Component\\Config\\ResourceCheckerInterface' => $vendorDir . '/symfony/config/ResourceCheckerInterface.php', + 'Symfony\\Component\\Config\\Resource\\ClassExistenceResource' => $vendorDir . '/symfony/config/Resource/ClassExistenceResource.php', + 'Symfony\\Component\\Config\\Resource\\ComposerResource' => $vendorDir . '/symfony/config/Resource/ComposerResource.php', + 'Symfony\\Component\\Config\\Resource\\DirectoryResource' => $vendorDir . '/symfony/config/Resource/DirectoryResource.php', + 'Symfony\\Component\\Config\\Resource\\FileExistenceResource' => $vendorDir . '/symfony/config/Resource/FileExistenceResource.php', + 'Symfony\\Component\\Config\\Resource\\FileResource' => $vendorDir . '/symfony/config/Resource/FileResource.php', + 'Symfony\\Component\\Config\\Resource\\GlobResource' => $vendorDir . '/symfony/config/Resource/GlobResource.php', + 'Symfony\\Component\\Config\\Resource\\ReflectionClassResource' => $vendorDir . '/symfony/config/Resource/ReflectionClassResource.php', + 'Symfony\\Component\\Config\\Resource\\ReflectionMethodHhvmWrapper' => $vendorDir . '/symfony/config/Resource/ReflectionClassResource.php', + 'Symfony\\Component\\Config\\Resource\\ReflectionParameterHhvmWrapper' => $vendorDir . '/symfony/config/Resource/ReflectionClassResource.php', + 'Symfony\\Component\\Config\\Resource\\ResourceInterface' => $vendorDir . '/symfony/config/Resource/ResourceInterface.php', + 'Symfony\\Component\\Config\\Resource\\SelfCheckingResourceChecker' => $vendorDir . '/symfony/config/Resource/SelfCheckingResourceChecker.php', + 'Symfony\\Component\\Config\\Resource\\SelfCheckingResourceInterface' => $vendorDir . '/symfony/config/Resource/SelfCheckingResourceInterface.php', + 'Symfony\\Component\\Config\\Util\\Exception\\InvalidXmlException' => $vendorDir . '/symfony/config/Util/Exception/InvalidXmlException.php', + 'Symfony\\Component\\Config\\Util\\Exception\\XmlParsingException' => $vendorDir . '/symfony/config/Util/Exception/XmlParsingException.php', + 'Symfony\\Component\\Config\\Util\\XmlUtils' => $vendorDir . '/symfony/config/Util/XmlUtils.php', + 'Symfony\\Component\\Console\\Application' => $vendorDir . '/symfony/console/Application.php', + 'Symfony\\Component\\Console\\CommandLoader\\CommandLoaderInterface' => $vendorDir . '/symfony/console/CommandLoader/CommandLoaderInterface.php', + 'Symfony\\Component\\Console\\CommandLoader\\ContainerCommandLoader' => $vendorDir . '/symfony/console/CommandLoader/ContainerCommandLoader.php', + 'Symfony\\Component\\Console\\CommandLoader\\FactoryCommandLoader' => $vendorDir . '/symfony/console/CommandLoader/FactoryCommandLoader.php', + 'Symfony\\Component\\Console\\Command\\Command' => $vendorDir . '/symfony/console/Command/Command.php', + 'Symfony\\Component\\Console\\Command\\HelpCommand' => $vendorDir . '/symfony/console/Command/HelpCommand.php', + 'Symfony\\Component\\Console\\Command\\ListCommand' => $vendorDir . '/symfony/console/Command/ListCommand.php', + 'Symfony\\Component\\Console\\Command\\LockableTrait' => $vendorDir . '/symfony/console/Command/LockableTrait.php', + 'Symfony\\Component\\Console\\ConsoleEvents' => $vendorDir . '/symfony/console/ConsoleEvents.php', + 'Symfony\\Component\\Console\\DependencyInjection\\AddConsoleCommandPass' => $vendorDir . '/symfony/console/DependencyInjection/AddConsoleCommandPass.php', + 'Symfony\\Component\\Console\\Descriptor\\ApplicationDescription' => $vendorDir . '/symfony/console/Descriptor/ApplicationDescription.php', + 'Symfony\\Component\\Console\\Descriptor\\Descriptor' => $vendorDir . '/symfony/console/Descriptor/Descriptor.php', + 'Symfony\\Component\\Console\\Descriptor\\DescriptorInterface' => $vendorDir . '/symfony/console/Descriptor/DescriptorInterface.php', + 'Symfony\\Component\\Console\\Descriptor\\JsonDescriptor' => $vendorDir . '/symfony/console/Descriptor/JsonDescriptor.php', + 'Symfony\\Component\\Console\\Descriptor\\MarkdownDescriptor' => $vendorDir . '/symfony/console/Descriptor/MarkdownDescriptor.php', + 'Symfony\\Component\\Console\\Descriptor\\TextDescriptor' => $vendorDir . '/symfony/console/Descriptor/TextDescriptor.php', + 'Symfony\\Component\\Console\\Descriptor\\XmlDescriptor' => $vendorDir . '/symfony/console/Descriptor/XmlDescriptor.php', + 'Symfony\\Component\\Console\\EventListener\\ErrorListener' => $vendorDir . '/symfony/console/EventListener/ErrorListener.php', + 'Symfony\\Component\\Console\\Event\\ConsoleCommandEvent' => $vendorDir . '/symfony/console/Event/ConsoleCommandEvent.php', + 'Symfony\\Component\\Console\\Event\\ConsoleErrorEvent' => $vendorDir . '/symfony/console/Event/ConsoleErrorEvent.php', + 'Symfony\\Component\\Console\\Event\\ConsoleEvent' => $vendorDir . '/symfony/console/Event/ConsoleEvent.php', + 'Symfony\\Component\\Console\\Event\\ConsoleExceptionEvent' => $vendorDir . '/symfony/console/Event/ConsoleExceptionEvent.php', + 'Symfony\\Component\\Console\\Event\\ConsoleTerminateEvent' => $vendorDir . '/symfony/console/Event/ConsoleTerminateEvent.php', + 'Symfony\\Component\\Console\\Exception\\CommandNotFoundException' => $vendorDir . '/symfony/console/Exception/CommandNotFoundException.php', + 'Symfony\\Component\\Console\\Exception\\ExceptionInterface' => $vendorDir . '/symfony/console/Exception/ExceptionInterface.php', + 'Symfony\\Component\\Console\\Exception\\InvalidArgumentException' => $vendorDir . '/symfony/console/Exception/InvalidArgumentException.php', + 'Symfony\\Component\\Console\\Exception\\InvalidOptionException' => $vendorDir . '/symfony/console/Exception/InvalidOptionException.php', + 'Symfony\\Component\\Console\\Exception\\LogicException' => $vendorDir . '/symfony/console/Exception/LogicException.php', + 'Symfony\\Component\\Console\\Exception\\RuntimeException' => $vendorDir . '/symfony/console/Exception/RuntimeException.php', + 'Symfony\\Component\\Console\\Formatter\\OutputFormatter' => $vendorDir . '/symfony/console/Formatter/OutputFormatter.php', + 'Symfony\\Component\\Console\\Formatter\\OutputFormatterInterface' => $vendorDir . '/symfony/console/Formatter/OutputFormatterInterface.php', + 'Symfony\\Component\\Console\\Formatter\\OutputFormatterStyle' => $vendorDir . '/symfony/console/Formatter/OutputFormatterStyle.php', + 'Symfony\\Component\\Console\\Formatter\\OutputFormatterStyleInterface' => $vendorDir . '/symfony/console/Formatter/OutputFormatterStyleInterface.php', + 'Symfony\\Component\\Console\\Formatter\\OutputFormatterStyleStack' => $vendorDir . '/symfony/console/Formatter/OutputFormatterStyleStack.php', + 'Symfony\\Component\\Console\\Helper\\DebugFormatterHelper' => $vendorDir . '/symfony/console/Helper/DebugFormatterHelper.php', + 'Symfony\\Component\\Console\\Helper\\DescriptorHelper' => $vendorDir . '/symfony/console/Helper/DescriptorHelper.php', + 'Symfony\\Component\\Console\\Helper\\FormatterHelper' => $vendorDir . '/symfony/console/Helper/FormatterHelper.php', + 'Symfony\\Component\\Console\\Helper\\Helper' => $vendorDir . '/symfony/console/Helper/Helper.php', + 'Symfony\\Component\\Console\\Helper\\HelperInterface' => $vendorDir . '/symfony/console/Helper/HelperInterface.php', + 'Symfony\\Component\\Console\\Helper\\HelperSet' => $vendorDir . '/symfony/console/Helper/HelperSet.php', + 'Symfony\\Component\\Console\\Helper\\InputAwareHelper' => $vendorDir . '/symfony/console/Helper/InputAwareHelper.php', + 'Symfony\\Component\\Console\\Helper\\ProcessHelper' => $vendorDir . '/symfony/console/Helper/ProcessHelper.php', + 'Symfony\\Component\\Console\\Helper\\ProgressBar' => $vendorDir . '/symfony/console/Helper/ProgressBar.php', + 'Symfony\\Component\\Console\\Helper\\ProgressIndicator' => $vendorDir . '/symfony/console/Helper/ProgressIndicator.php', + 'Symfony\\Component\\Console\\Helper\\QuestionHelper' => $vendorDir . '/symfony/console/Helper/QuestionHelper.php', + 'Symfony\\Component\\Console\\Helper\\SymfonyQuestionHelper' => $vendorDir . '/symfony/console/Helper/SymfonyQuestionHelper.php', + 'Symfony\\Component\\Console\\Helper\\Table' => $vendorDir . '/symfony/console/Helper/Table.php', + 'Symfony\\Component\\Console\\Helper\\TableCell' => $vendorDir . '/symfony/console/Helper/TableCell.php', + 'Symfony\\Component\\Console\\Helper\\TableSeparator' => $vendorDir . '/symfony/console/Helper/TableSeparator.php', + 'Symfony\\Component\\Console\\Helper\\TableStyle' => $vendorDir . '/symfony/console/Helper/TableStyle.php', + 'Symfony\\Component\\Console\\Input\\ArgvInput' => $vendorDir . '/symfony/console/Input/ArgvInput.php', + 'Symfony\\Component\\Console\\Input\\ArrayInput' => $vendorDir . '/symfony/console/Input/ArrayInput.php', + 'Symfony\\Component\\Console\\Input\\Input' => $vendorDir . '/symfony/console/Input/Input.php', + 'Symfony\\Component\\Console\\Input\\InputArgument' => $vendorDir . '/symfony/console/Input/InputArgument.php', + 'Symfony\\Component\\Console\\Input\\InputAwareInterface' => $vendorDir . '/symfony/console/Input/InputAwareInterface.php', + 'Symfony\\Component\\Console\\Input\\InputDefinition' => $vendorDir . '/symfony/console/Input/InputDefinition.php', + 'Symfony\\Component\\Console\\Input\\InputInterface' => $vendorDir . '/symfony/console/Input/InputInterface.php', + 'Symfony\\Component\\Console\\Input\\InputOption' => $vendorDir . '/symfony/console/Input/InputOption.php', + 'Symfony\\Component\\Console\\Input\\StreamableInputInterface' => $vendorDir . '/symfony/console/Input/StreamableInputInterface.php', + 'Symfony\\Component\\Console\\Input\\StringInput' => $vendorDir . '/symfony/console/Input/StringInput.php', + 'Symfony\\Component\\Console\\Logger\\ConsoleLogger' => $vendorDir . '/symfony/console/Logger/ConsoleLogger.php', + 'Symfony\\Component\\Console\\Output\\BufferedOutput' => $vendorDir . '/symfony/console/Output/BufferedOutput.php', + 'Symfony\\Component\\Console\\Output\\ConsoleOutput' => $vendorDir . '/symfony/console/Output/ConsoleOutput.php', + 'Symfony\\Component\\Console\\Output\\ConsoleOutputInterface' => $vendorDir . '/symfony/console/Output/ConsoleOutputInterface.php', + 'Symfony\\Component\\Console\\Output\\NullOutput' => $vendorDir . '/symfony/console/Output/NullOutput.php', + 'Symfony\\Component\\Console\\Output\\Output' => $vendorDir . '/symfony/console/Output/Output.php', + 'Symfony\\Component\\Console\\Output\\OutputInterface' => $vendorDir . '/symfony/console/Output/OutputInterface.php', + 'Symfony\\Component\\Console\\Output\\StreamOutput' => $vendorDir . '/symfony/console/Output/StreamOutput.php', + 'Symfony\\Component\\Console\\Question\\ChoiceQuestion' => $vendorDir . '/symfony/console/Question/ChoiceQuestion.php', + 'Symfony\\Component\\Console\\Question\\ConfirmationQuestion' => $vendorDir . '/symfony/console/Question/ConfirmationQuestion.php', + 'Symfony\\Component\\Console\\Question\\Question' => $vendorDir . '/symfony/console/Question/Question.php', + 'Symfony\\Component\\Console\\Style\\OutputStyle' => $vendorDir . '/symfony/console/Style/OutputStyle.php', + 'Symfony\\Component\\Console\\Style\\StyleInterface' => $vendorDir . '/symfony/console/Style/StyleInterface.php', + 'Symfony\\Component\\Console\\Style\\SymfonyStyle' => $vendorDir . '/symfony/console/Style/SymfonyStyle.php', + 'Symfony\\Component\\Console\\Terminal' => $vendorDir . '/symfony/console/Terminal.php', + 'Symfony\\Component\\Console\\Tester\\ApplicationTester' => $vendorDir . '/symfony/console/Tester/ApplicationTester.php', + 'Symfony\\Component\\Console\\Tester\\CommandTester' => $vendorDir . '/symfony/console/Tester/CommandTester.php', + 'Symfony\\Component\\Debug\\BufferingLogger' => $vendorDir . '/symfony/debug/BufferingLogger.php', + 'Symfony\\Component\\Debug\\Debug' => $vendorDir . '/symfony/debug/Debug.php', + 'Symfony\\Component\\Debug\\DebugClassLoader' => $vendorDir . '/symfony/debug/DebugClassLoader.php', + 'Symfony\\Component\\Debug\\ErrorHandler' => $vendorDir . '/symfony/debug/ErrorHandler.php', + 'Symfony\\Component\\Debug\\ExceptionHandler' => $vendorDir . '/symfony/debug/ExceptionHandler.php', + 'Symfony\\Component\\Debug\\Exception\\ClassNotFoundException' => $vendorDir . '/symfony/debug/Exception/ClassNotFoundException.php', + 'Symfony\\Component\\Debug\\Exception\\ContextErrorException' => $vendorDir . '/symfony/debug/Exception/ContextErrorException.php', + 'Symfony\\Component\\Debug\\Exception\\FatalErrorException' => $vendorDir . '/symfony/debug/Exception/FatalErrorException.php', + 'Symfony\\Component\\Debug\\Exception\\FatalThrowableError' => $vendorDir . '/symfony/debug/Exception/FatalThrowableError.php', + 'Symfony\\Component\\Debug\\Exception\\FlattenException' => $vendorDir . '/symfony/debug/Exception/FlattenException.php', + 'Symfony\\Component\\Debug\\Exception\\OutOfMemoryException' => $vendorDir . '/symfony/debug/Exception/OutOfMemoryException.php', + 'Symfony\\Component\\Debug\\Exception\\SilencedErrorContext' => $vendorDir . '/symfony/debug/Exception/SilencedErrorContext.php', + 'Symfony\\Component\\Debug\\Exception\\UndefinedFunctionException' => $vendorDir . '/symfony/debug/Exception/UndefinedFunctionException.php', + 'Symfony\\Component\\Debug\\Exception\\UndefinedMethodException' => $vendorDir . '/symfony/debug/Exception/UndefinedMethodException.php', + 'Symfony\\Component\\Debug\\FatalErrorHandler\\ClassNotFoundFatalErrorHandler' => $vendorDir . '/symfony/debug/FatalErrorHandler/ClassNotFoundFatalErrorHandler.php', + 'Symfony\\Component\\Debug\\FatalErrorHandler\\FatalErrorHandlerInterface' => $vendorDir . '/symfony/debug/FatalErrorHandler/FatalErrorHandlerInterface.php', + 'Symfony\\Component\\Debug\\FatalErrorHandler\\UndefinedFunctionFatalErrorHandler' => $vendorDir . '/symfony/debug/FatalErrorHandler/UndefinedFunctionFatalErrorHandler.php', + 'Symfony\\Component\\Debug\\FatalErrorHandler\\UndefinedMethodFatalErrorHandler' => $vendorDir . '/symfony/debug/FatalErrorHandler/UndefinedMethodFatalErrorHandler.php', + 'Symfony\\Component\\DependencyInjection\\Alias' => $vendorDir . '/symfony/dependency-injection/Alias.php', + 'Symfony\\Component\\DependencyInjection\\Argument\\ArgumentInterface' => $vendorDir . '/symfony/dependency-injection/Argument/ArgumentInterface.php', + 'Symfony\\Component\\DependencyInjection\\Argument\\BoundArgument' => $vendorDir . '/symfony/dependency-injection/Argument/BoundArgument.php', + 'Symfony\\Component\\DependencyInjection\\Argument\\IteratorArgument' => $vendorDir . '/symfony/dependency-injection/Argument/IteratorArgument.php', + 'Symfony\\Component\\DependencyInjection\\Argument\\RewindableGenerator' => $vendorDir . '/symfony/dependency-injection/Argument/RewindableGenerator.php', + 'Symfony\\Component\\DependencyInjection\\Argument\\ServiceClosureArgument' => $vendorDir . '/symfony/dependency-injection/Argument/ServiceClosureArgument.php', + 'Symfony\\Component\\DependencyInjection\\Argument\\TaggedIteratorArgument' => $vendorDir . '/symfony/dependency-injection/Argument/TaggedIteratorArgument.php', + 'Symfony\\Component\\DependencyInjection\\ChildDefinition' => $vendorDir . '/symfony/dependency-injection/ChildDefinition.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\AbstractRecursivePass' => $vendorDir . '/symfony/dependency-injection/Compiler/AbstractRecursivePass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\AnalyzeServiceReferencesPass' => $vendorDir . '/symfony/dependency-injection/Compiler/AnalyzeServiceReferencesPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\AutoAliasServicePass' => $vendorDir . '/symfony/dependency-injection/Compiler/AutoAliasServicePass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\AutowireExceptionPass' => $vendorDir . '/symfony/dependency-injection/Compiler/AutowireExceptionPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\AutowirePass' => $vendorDir . '/symfony/dependency-injection/Compiler/AutowirePass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\AutowireRequiredMethodsPass' => $vendorDir . '/symfony/dependency-injection/Compiler/AutowireRequiredMethodsPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\CheckArgumentsValidityPass' => $vendorDir . '/symfony/dependency-injection/Compiler/CheckArgumentsValidityPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\CheckCircularReferencesPass' => $vendorDir . '/symfony/dependency-injection/Compiler/CheckCircularReferencesPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\CheckDefinitionValidityPass' => $vendorDir . '/symfony/dependency-injection/Compiler/CheckDefinitionValidityPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\CheckExceptionOnInvalidReferenceBehaviorPass' => $vendorDir . '/symfony/dependency-injection/Compiler/CheckExceptionOnInvalidReferenceBehaviorPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\CheckReferenceValidityPass' => $vendorDir . '/symfony/dependency-injection/Compiler/CheckReferenceValidityPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\Compiler' => $vendorDir . '/symfony/dependency-injection/Compiler/Compiler.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\CompilerPassInterface' => $vendorDir . '/symfony/dependency-injection/Compiler/CompilerPassInterface.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\DecoratorServicePass' => $vendorDir . '/symfony/dependency-injection/Compiler/DecoratorServicePass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\DefinitionErrorExceptionPass' => $vendorDir . '/symfony/dependency-injection/Compiler/DefinitionErrorExceptionPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\ExtensionCompilerPass' => $vendorDir . '/symfony/dependency-injection/Compiler/ExtensionCompilerPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\FactoryReturnTypePass' => $vendorDir . '/symfony/dependency-injection/Compiler/FactoryReturnTypePass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\InlineServiceDefinitionsPass' => $vendorDir . '/symfony/dependency-injection/Compiler/InlineServiceDefinitionsPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\LoggingFormatter' => $vendorDir . '/symfony/dependency-injection/Compiler/LoggingFormatter.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\MergeExtensionConfigurationContainerBuilder' => $vendorDir . '/symfony/dependency-injection/Compiler/MergeExtensionConfigurationPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\MergeExtensionConfigurationParameterBag' => $vendorDir . '/symfony/dependency-injection/Compiler/MergeExtensionConfigurationPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\MergeExtensionConfigurationPass' => $vendorDir . '/symfony/dependency-injection/Compiler/MergeExtensionConfigurationPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\PassConfig' => $vendorDir . '/symfony/dependency-injection/Compiler/PassConfig.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\PriorityTaggedServiceTrait' => $vendorDir . '/symfony/dependency-injection/Compiler/PriorityTaggedServiceTrait.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\RegisterEnvVarProcessorsPass' => $vendorDir . '/symfony/dependency-injection/Compiler/RegisterEnvVarProcessorsPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\RegisterServiceSubscribersPass' => $vendorDir . '/symfony/dependency-injection/Compiler/RegisterServiceSubscribersPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\RemoveAbstractDefinitionsPass' => $vendorDir . '/symfony/dependency-injection/Compiler/RemoveAbstractDefinitionsPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\RemovePrivateAliasesPass' => $vendorDir . '/symfony/dependency-injection/Compiler/RemovePrivateAliasesPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\RemoveUnusedDefinitionsPass' => $vendorDir . '/symfony/dependency-injection/Compiler/RemoveUnusedDefinitionsPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\RepeatablePassInterface' => $vendorDir . '/symfony/dependency-injection/Compiler/RepeatablePassInterface.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\RepeatedPass' => $vendorDir . '/symfony/dependency-injection/Compiler/RepeatedPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\ReplaceAliasByActualDefinitionPass' => $vendorDir . '/symfony/dependency-injection/Compiler/ReplaceAliasByActualDefinitionPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\ResolveBindingsPass' => $vendorDir . '/symfony/dependency-injection/Compiler/ResolveBindingsPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\ResolveChildDefinitionsPass' => $vendorDir . '/symfony/dependency-injection/Compiler/ResolveChildDefinitionsPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\ResolveClassPass' => $vendorDir . '/symfony/dependency-injection/Compiler/ResolveClassPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\ResolveDefinitionTemplatesPass' => $vendorDir . '/symfony/dependency-injection/Compiler/ResolveDefinitionTemplatesPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\ResolveEnvPlaceholdersPass' => $vendorDir . '/symfony/dependency-injection/Compiler/ResolveEnvPlaceholdersPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\ResolveFactoryClassPass' => $vendorDir . '/symfony/dependency-injection/Compiler/ResolveFactoryClassPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\ResolveHotPathPass' => $vendorDir . '/symfony/dependency-injection/Compiler/ResolveHotPathPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\ResolveInstanceofConditionalsPass' => $vendorDir . '/symfony/dependency-injection/Compiler/ResolveInstanceofConditionalsPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\ResolveInvalidReferencesPass' => $vendorDir . '/symfony/dependency-injection/Compiler/ResolveInvalidReferencesPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\ResolveNamedArgumentsPass' => $vendorDir . '/symfony/dependency-injection/Compiler/ResolveNamedArgumentsPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\ResolveParameterPlaceHoldersPass' => $vendorDir . '/symfony/dependency-injection/Compiler/ResolveParameterPlaceHoldersPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\ResolvePrivatesPass' => $vendorDir . '/symfony/dependency-injection/Compiler/ResolvePrivatesPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\ResolveReferencesToAliasesPass' => $vendorDir . '/symfony/dependency-injection/Compiler/ResolveReferencesToAliasesPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\ResolveServiceSubscribersPass' => $vendorDir . '/symfony/dependency-injection/Compiler/ResolveServiceSubscribersPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\ResolveTaggedIteratorArgumentPass' => $vendorDir . '/symfony/dependency-injection/Compiler/ResolveTaggedIteratorArgumentPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\ServiceLocatorTagPass' => $vendorDir . '/symfony/dependency-injection/Compiler/ServiceLocatorTagPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\ServiceReferenceGraph' => $vendorDir . '/symfony/dependency-injection/Compiler/ServiceReferenceGraph.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\ServiceReferenceGraphEdge' => $vendorDir . '/symfony/dependency-injection/Compiler/ServiceReferenceGraphEdge.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\ServiceReferenceGraphNode' => $vendorDir . '/symfony/dependency-injection/Compiler/ServiceReferenceGraphNode.php', + 'Symfony\\Component\\DependencyInjection\\Config\\AutowireServiceResource' => $vendorDir . '/symfony/dependency-injection/Config/AutowireServiceResource.php', + 'Symfony\\Component\\DependencyInjection\\Config\\ContainerParametersResource' => $vendorDir . '/symfony/dependency-injection/Config/ContainerParametersResource.php', + 'Symfony\\Component\\DependencyInjection\\Config\\ContainerParametersResourceChecker' => $vendorDir . '/symfony/dependency-injection/Config/ContainerParametersResourceChecker.php', + 'Symfony\\Component\\DependencyInjection\\Container' => $vendorDir . '/symfony/dependency-injection/Container.php', + 'Symfony\\Component\\DependencyInjection\\ContainerAwareInterface' => $vendorDir . '/symfony/dependency-injection/ContainerAwareInterface.php', + 'Symfony\\Component\\DependencyInjection\\ContainerAwareTrait' => $vendorDir . '/symfony/dependency-injection/ContainerAwareTrait.php', + 'Symfony\\Component\\DependencyInjection\\ContainerBuilder' => $vendorDir . '/symfony/dependency-injection/ContainerBuilder.php', + 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => $vendorDir . '/symfony/dependency-injection/ContainerInterface.php', + 'Symfony\\Component\\DependencyInjection\\Definition' => $vendorDir . '/symfony/dependency-injection/Definition.php', + 'Symfony\\Component\\DependencyInjection\\DefinitionDecorator' => $vendorDir . '/symfony/dependency-injection/DefinitionDecorator.php', + 'Symfony\\Component\\DependencyInjection\\Dumper\\Dumper' => $vendorDir . '/symfony/dependency-injection/Dumper/Dumper.php', + 'Symfony\\Component\\DependencyInjection\\Dumper\\DumperInterface' => $vendorDir . '/symfony/dependency-injection/Dumper/DumperInterface.php', + 'Symfony\\Component\\DependencyInjection\\Dumper\\GraphvizDumper' => $vendorDir . '/symfony/dependency-injection/Dumper/GraphvizDumper.php', + 'Symfony\\Component\\DependencyInjection\\Dumper\\PhpDumper' => $vendorDir . '/symfony/dependency-injection/Dumper/PhpDumper.php', + 'Symfony\\Component\\DependencyInjection\\Dumper\\XmlDumper' => $vendorDir . '/symfony/dependency-injection/Dumper/XmlDumper.php', + 'Symfony\\Component\\DependencyInjection\\Dumper\\YamlDumper' => $vendorDir . '/symfony/dependency-injection/Dumper/YamlDumper.php', + 'Symfony\\Component\\DependencyInjection\\EnvVarProcessor' => $vendorDir . '/symfony/dependency-injection/EnvVarProcessor.php', + 'Symfony\\Component\\DependencyInjection\\EnvVarProcessorInterface' => $vendorDir . '/symfony/dependency-injection/EnvVarProcessorInterface.php', + 'Symfony\\Component\\DependencyInjection\\Exception\\AutowiringFailedException' => $vendorDir . '/symfony/dependency-injection/Exception/AutowiringFailedException.php', + 'Symfony\\Component\\DependencyInjection\\Exception\\BadMethodCallException' => $vendorDir . '/symfony/dependency-injection/Exception/BadMethodCallException.php', + 'Symfony\\Component\\DependencyInjection\\Exception\\EnvNotFoundException' => $vendorDir . '/symfony/dependency-injection/Exception/EnvNotFoundException.php', + 'Symfony\\Component\\DependencyInjection\\Exception\\EnvParameterException' => $vendorDir . '/symfony/dependency-injection/Exception/EnvParameterException.php', + 'Symfony\\Component\\DependencyInjection\\Exception\\ExceptionInterface' => $vendorDir . '/symfony/dependency-injection/Exception/ExceptionInterface.php', + 'Symfony\\Component\\DependencyInjection\\Exception\\InvalidArgumentException' => $vendorDir . '/symfony/dependency-injection/Exception/InvalidArgumentException.php', + 'Symfony\\Component\\DependencyInjection\\Exception\\LogicException' => $vendorDir . '/symfony/dependency-injection/Exception/LogicException.php', + 'Symfony\\Component\\DependencyInjection\\Exception\\OutOfBoundsException' => $vendorDir . '/symfony/dependency-injection/Exception/OutOfBoundsException.php', + 'Symfony\\Component\\DependencyInjection\\Exception\\ParameterCircularReferenceException' => $vendorDir . '/symfony/dependency-injection/Exception/ParameterCircularReferenceException.php', + 'Symfony\\Component\\DependencyInjection\\Exception\\ParameterNotFoundException' => $vendorDir . '/symfony/dependency-injection/Exception/ParameterNotFoundException.php', + 'Symfony\\Component\\DependencyInjection\\Exception\\RuntimeException' => $vendorDir . '/symfony/dependency-injection/Exception/RuntimeException.php', + 'Symfony\\Component\\DependencyInjection\\Exception\\ServiceCircularReferenceException' => $vendorDir . '/symfony/dependency-injection/Exception/ServiceCircularReferenceException.php', + 'Symfony\\Component\\DependencyInjection\\Exception\\ServiceNotFoundException' => $vendorDir . '/symfony/dependency-injection/Exception/ServiceNotFoundException.php', + 'Symfony\\Component\\DependencyInjection\\ExpressionLanguage' => $vendorDir . '/symfony/dependency-injection/ExpressionLanguage.php', + 'Symfony\\Component\\DependencyInjection\\ExpressionLanguageProvider' => $vendorDir . '/symfony/dependency-injection/ExpressionLanguageProvider.php', + 'Symfony\\Component\\DependencyInjection\\Extension\\ConfigurationExtensionInterface' => $vendorDir . '/symfony/dependency-injection/Extension/ConfigurationExtensionInterface.php', + 'Symfony\\Component\\DependencyInjection\\Extension\\Extension' => $vendorDir . '/symfony/dependency-injection/Extension/Extension.php', + 'Symfony\\Component\\DependencyInjection\\Extension\\ExtensionInterface' => $vendorDir . '/symfony/dependency-injection/Extension/ExtensionInterface.php', + 'Symfony\\Component\\DependencyInjection\\Extension\\PrependExtensionInterface' => $vendorDir . '/symfony/dependency-injection/Extension/PrependExtensionInterface.php', + 'Symfony\\Component\\DependencyInjection\\LazyProxy\\Instantiator\\InstantiatorInterface' => $vendorDir . '/symfony/dependency-injection/LazyProxy/Instantiator/InstantiatorInterface.php', + 'Symfony\\Component\\DependencyInjection\\LazyProxy\\Instantiator\\RealServiceInstantiator' => $vendorDir . '/symfony/dependency-injection/LazyProxy/Instantiator/RealServiceInstantiator.php', + 'Symfony\\Component\\DependencyInjection\\LazyProxy\\PhpDumper\\DumperInterface' => $vendorDir . '/symfony/dependency-injection/LazyProxy/PhpDumper/DumperInterface.php', + 'Symfony\\Component\\DependencyInjection\\LazyProxy\\PhpDumper\\NullDumper' => $vendorDir . '/symfony/dependency-injection/LazyProxy/PhpDumper/NullDumper.php', + 'Symfony\\Component\\DependencyInjection\\LazyProxy\\ProxyHelper' => $vendorDir . '/symfony/dependency-injection/LazyProxy/ProxyHelper.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\ClosureLoader' => $vendorDir . '/symfony/dependency-injection/Loader/ClosureLoader.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\AbstractConfigurator' => $vendorDir . '/symfony/dependency-injection/Loader/Configurator/AbstractConfigurator.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\AbstractServiceConfigurator' => $vendorDir . '/symfony/dependency-injection/Loader/Configurator/AbstractServiceConfigurator.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\AliasConfigurator' => $vendorDir . '/symfony/dependency-injection/Loader/Configurator/AliasConfigurator.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\ContainerConfigurator' => $vendorDir . '/symfony/dependency-injection/Loader/Configurator/ContainerConfigurator.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\DefaultsConfigurator' => $vendorDir . '/symfony/dependency-injection/Loader/Configurator/DefaultsConfigurator.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\InlineServiceConfigurator' => $vendorDir . '/symfony/dependency-injection/Loader/Configurator/InlineServiceConfigurator.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\InstanceofConfigurator' => $vendorDir . '/symfony/dependency-injection/Loader/Configurator/InstanceofConfigurator.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\ParametersConfigurator' => $vendorDir . '/symfony/dependency-injection/Loader/Configurator/ParametersConfigurator.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\PrototypeConfigurator' => $vendorDir . '/symfony/dependency-injection/Loader/Configurator/PrototypeConfigurator.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\ReferenceConfigurator' => $vendorDir . '/symfony/dependency-injection/Loader/Configurator/ReferenceConfigurator.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\ServiceConfigurator' => $vendorDir . '/symfony/dependency-injection/Loader/Configurator/ServiceConfigurator.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\ServicesConfigurator' => $vendorDir . '/symfony/dependency-injection/Loader/Configurator/ServicesConfigurator.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\Traits\\AbstractTrait' => $vendorDir . '/symfony/dependency-injection/Loader/Configurator/Traits/AbstractTrait.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\Traits\\ArgumentTrait' => $vendorDir . '/symfony/dependency-injection/Loader/Configurator/Traits/ArgumentTrait.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\Traits\\AutoconfigureTrait' => $vendorDir . '/symfony/dependency-injection/Loader/Configurator/Traits/AutoconfigureTrait.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\Traits\\AutowireTrait' => $vendorDir . '/symfony/dependency-injection/Loader/Configurator/Traits/AutowireTrait.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\Traits\\BindTrait' => $vendorDir . '/symfony/dependency-injection/Loader/Configurator/Traits/BindTrait.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\Traits\\CallTrait' => $vendorDir . '/symfony/dependency-injection/Loader/Configurator/Traits/CallTrait.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\Traits\\ClassTrait' => $vendorDir . '/symfony/dependency-injection/Loader/Configurator/Traits/ClassTrait.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\Traits\\ConfiguratorTrait' => $vendorDir . '/symfony/dependency-injection/Loader/Configurator/Traits/ConfiguratorTrait.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\Traits\\DecorateTrait' => $vendorDir . '/symfony/dependency-injection/Loader/Configurator/Traits/DecorateTrait.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\Traits\\DeprecateTrait' => $vendorDir . '/symfony/dependency-injection/Loader/Configurator/Traits/DeprecateTrait.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\Traits\\FactoryTrait' => $vendorDir . '/symfony/dependency-injection/Loader/Configurator/Traits/FactoryTrait.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\Traits\\FileTrait' => $vendorDir . '/symfony/dependency-injection/Loader/Configurator/Traits/FileTrait.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\Traits\\LazyTrait' => $vendorDir . '/symfony/dependency-injection/Loader/Configurator/Traits/LazyTrait.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\Traits\\ParentTrait' => $vendorDir . '/symfony/dependency-injection/Loader/Configurator/Traits/ParentTrait.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\Traits\\PropertyTrait' => $vendorDir . '/symfony/dependency-injection/Loader/Configurator/Traits/PropertyTrait.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\Traits\\PublicTrait' => $vendorDir . '/symfony/dependency-injection/Loader/Configurator/Traits/PublicTrait.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\Traits\\ShareTrait' => $vendorDir . '/symfony/dependency-injection/Loader/Configurator/Traits/ShareTrait.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\Traits\\SyntheticTrait' => $vendorDir . '/symfony/dependency-injection/Loader/Configurator/Traits/SyntheticTrait.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\Traits\\TagTrait' => $vendorDir . '/symfony/dependency-injection/Loader/Configurator/Traits/TagTrait.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\DirectoryLoader' => $vendorDir . '/symfony/dependency-injection/Loader/DirectoryLoader.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\FileLoader' => $vendorDir . '/symfony/dependency-injection/Loader/FileLoader.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\GlobFileLoader' => $vendorDir . '/symfony/dependency-injection/Loader/GlobFileLoader.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\IniFileLoader' => $vendorDir . '/symfony/dependency-injection/Loader/IniFileLoader.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\PhpFileLoader' => $vendorDir . '/symfony/dependency-injection/Loader/PhpFileLoader.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\ProtectedPhpFileLoader' => $vendorDir . '/symfony/dependency-injection/Loader/PhpFileLoader.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\XmlFileLoader' => $vendorDir . '/symfony/dependency-injection/Loader/XmlFileLoader.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\YamlFileLoader' => $vendorDir . '/symfony/dependency-injection/Loader/YamlFileLoader.php', + 'Symfony\\Component\\DependencyInjection\\Parameter' => $vendorDir . '/symfony/dependency-injection/Parameter.php', + 'Symfony\\Component\\DependencyInjection\\ParameterBag\\EnvPlaceholderParameterBag' => $vendorDir . '/symfony/dependency-injection/ParameterBag/EnvPlaceholderParameterBag.php', + 'Symfony\\Component\\DependencyInjection\\ParameterBag\\FrozenParameterBag' => $vendorDir . '/symfony/dependency-injection/ParameterBag/FrozenParameterBag.php', + 'Symfony\\Component\\DependencyInjection\\ParameterBag\\ParameterBag' => $vendorDir . '/symfony/dependency-injection/ParameterBag/ParameterBag.php', + 'Symfony\\Component\\DependencyInjection\\ParameterBag\\ParameterBagInterface' => $vendorDir . '/symfony/dependency-injection/ParameterBag/ParameterBagInterface.php', + 'Symfony\\Component\\DependencyInjection\\Reference' => $vendorDir . '/symfony/dependency-injection/Reference.php', + 'Symfony\\Component\\DependencyInjection\\ResettableContainerInterface' => $vendorDir . '/symfony/dependency-injection/ResettableContainerInterface.php', + 'Symfony\\Component\\DependencyInjection\\ServiceLocator' => $vendorDir . '/symfony/dependency-injection/ServiceLocator.php', + 'Symfony\\Component\\DependencyInjection\\ServiceSubscriberInterface' => $vendorDir . '/symfony/dependency-injection/ServiceSubscriberInterface.php', + 'Symfony\\Component\\DependencyInjection\\TaggedContainerInterface' => $vendorDir . '/symfony/dependency-injection/TaggedContainerInterface.php', + 'Symfony\\Component\\DependencyInjection\\TypedReference' => $vendorDir . '/symfony/dependency-injection/TypedReference.php', + 'Symfony\\Component\\DependencyInjection\\Variable' => $vendorDir . '/symfony/dependency-injection/Variable.php', + 'Symfony\\Component\\Dotenv\\Dotenv' => $vendorDir . '/symfony/dotenv/Dotenv.php', + 'Symfony\\Component\\Dotenv\\Exception\\ExceptionInterface' => $vendorDir . '/symfony/dotenv/Exception/ExceptionInterface.php', + 'Symfony\\Component\\Dotenv\\Exception\\FormatException' => $vendorDir . '/symfony/dotenv/Exception/FormatException.php', + 'Symfony\\Component\\Dotenv\\Exception\\FormatExceptionContext' => $vendorDir . '/symfony/dotenv/Exception/FormatExceptionContext.php', + 'Symfony\\Component\\Dotenv\\Exception\\PathException' => $vendorDir . '/symfony/dotenv/Exception/PathException.php', + 'Symfony\\Component\\EventDispatcher\\ContainerAwareEventDispatcher' => $vendorDir . '/symfony/event-dispatcher/ContainerAwareEventDispatcher.php', + 'Symfony\\Component\\EventDispatcher\\Debug\\TraceableEventDispatcher' => $vendorDir . '/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php', + 'Symfony\\Component\\EventDispatcher\\Debug\\TraceableEventDispatcherInterface' => $vendorDir . '/symfony/event-dispatcher/Debug/TraceableEventDispatcherInterface.php', + 'Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener' => $vendorDir . '/symfony/event-dispatcher/Debug/WrappedListener.php', + 'Symfony\\Component\\EventDispatcher\\DependencyInjection\\ExtractingEventDispatcher' => $vendorDir . '/symfony/event-dispatcher/DependencyInjection/RegisterListenersPass.php', + 'Symfony\\Component\\EventDispatcher\\DependencyInjection\\RegisterListenersPass' => $vendorDir . '/symfony/event-dispatcher/DependencyInjection/RegisterListenersPass.php', + 'Symfony\\Component\\EventDispatcher\\Event' => $vendorDir . '/symfony/event-dispatcher/Event.php', + 'Symfony\\Component\\EventDispatcher\\EventDispatcher' => $vendorDir . '/symfony/event-dispatcher/EventDispatcher.php', + 'Symfony\\Component\\EventDispatcher\\EventDispatcherInterface' => $vendorDir . '/symfony/event-dispatcher/EventDispatcherInterface.php', + 'Symfony\\Component\\EventDispatcher\\EventSubscriberInterface' => $vendorDir . '/symfony/event-dispatcher/EventSubscriberInterface.php', + 'Symfony\\Component\\EventDispatcher\\GenericEvent' => $vendorDir . '/symfony/event-dispatcher/GenericEvent.php', + 'Symfony\\Component\\EventDispatcher\\ImmutableEventDispatcher' => $vendorDir . '/symfony/event-dispatcher/ImmutableEventDispatcher.php', + 'Symfony\\Component\\Filesystem\\Exception\\ExceptionInterface' => $vendorDir . '/symfony/filesystem/Exception/ExceptionInterface.php', + 'Symfony\\Component\\Filesystem\\Exception\\FileNotFoundException' => $vendorDir . '/symfony/filesystem/Exception/FileNotFoundException.php', + 'Symfony\\Component\\Filesystem\\Exception\\IOException' => $vendorDir . '/symfony/filesystem/Exception/IOException.php', + 'Symfony\\Component\\Filesystem\\Exception\\IOExceptionInterface' => $vendorDir . '/symfony/filesystem/Exception/IOExceptionInterface.php', + 'Symfony\\Component\\Filesystem\\Filesystem' => $vendorDir . '/symfony/filesystem/Filesystem.php', + 'Symfony\\Component\\Filesystem\\LockHandler' => $vendorDir . '/symfony/filesystem/LockHandler.php', + 'Symfony\\Component\\Finder\\Comparator\\Comparator' => $vendorDir . '/symfony/finder/Comparator/Comparator.php', + 'Symfony\\Component\\Finder\\Comparator\\DateComparator' => $vendorDir . '/symfony/finder/Comparator/DateComparator.php', + 'Symfony\\Component\\Finder\\Comparator\\NumberComparator' => $vendorDir . '/symfony/finder/Comparator/NumberComparator.php', + 'Symfony\\Component\\Finder\\Exception\\AccessDeniedException' => $vendorDir . '/symfony/finder/Exception/AccessDeniedException.php', + 'Symfony\\Component\\Finder\\Exception\\ExceptionInterface' => $vendorDir . '/symfony/finder/Exception/ExceptionInterface.php', + 'Symfony\\Component\\Finder\\Finder' => $vendorDir . '/symfony/finder/Finder.php', + 'Symfony\\Component\\Finder\\Glob' => $vendorDir . '/symfony/finder/Glob.php', + 'Symfony\\Component\\Finder\\Iterator\\CustomFilterIterator' => $vendorDir . '/symfony/finder/Iterator/CustomFilterIterator.php', + 'Symfony\\Component\\Finder\\Iterator\\DateRangeFilterIterator' => $vendorDir . '/symfony/finder/Iterator/DateRangeFilterIterator.php', + 'Symfony\\Component\\Finder\\Iterator\\DepthRangeFilterIterator' => $vendorDir . '/symfony/finder/Iterator/DepthRangeFilterIterator.php', + 'Symfony\\Component\\Finder\\Iterator\\ExcludeDirectoryFilterIterator' => $vendorDir . '/symfony/finder/Iterator/ExcludeDirectoryFilterIterator.php', + 'Symfony\\Component\\Finder\\Iterator\\FileTypeFilterIterator' => $vendorDir . '/symfony/finder/Iterator/FileTypeFilterIterator.php', + 'Symfony\\Component\\Finder\\Iterator\\FilecontentFilterIterator' => $vendorDir . '/symfony/finder/Iterator/FilecontentFilterIterator.php', + 'Symfony\\Component\\Finder\\Iterator\\FilenameFilterIterator' => $vendorDir . '/symfony/finder/Iterator/FilenameFilterIterator.php', + 'Symfony\\Component\\Finder\\Iterator\\FilterIterator' => $vendorDir . '/symfony/finder/Iterator/FilterIterator.php', + 'Symfony\\Component\\Finder\\Iterator\\MultiplePcreFilterIterator' => $vendorDir . '/symfony/finder/Iterator/MultiplePcreFilterIterator.php', + 'Symfony\\Component\\Finder\\Iterator\\PathFilterIterator' => $vendorDir . '/symfony/finder/Iterator/PathFilterIterator.php', + 'Symfony\\Component\\Finder\\Iterator\\RecursiveDirectoryIterator' => $vendorDir . '/symfony/finder/Iterator/RecursiveDirectoryIterator.php', + 'Symfony\\Component\\Finder\\Iterator\\SizeRangeFilterIterator' => $vendorDir . '/symfony/finder/Iterator/SizeRangeFilterIterator.php', + 'Symfony\\Component\\Finder\\Iterator\\SortableIterator' => $vendorDir . '/symfony/finder/Iterator/SortableIterator.php', + 'Symfony\\Component\\Finder\\SplFileInfo' => $vendorDir . '/symfony/finder/SplFileInfo.php', + 'Symfony\\Component\\HttpFoundation\\AcceptHeader' => $vendorDir . '/symfony/http-foundation/AcceptHeader.php', + 'Symfony\\Component\\HttpFoundation\\AcceptHeaderItem' => $vendorDir . '/symfony/http-foundation/AcceptHeaderItem.php', + 'Symfony\\Component\\HttpFoundation\\ApacheRequest' => $vendorDir . '/symfony/http-foundation/ApacheRequest.php', + 'Symfony\\Component\\HttpFoundation\\BinaryFileResponse' => $vendorDir . '/symfony/http-foundation/BinaryFileResponse.php', + 'Symfony\\Component\\HttpFoundation\\Cookie' => $vendorDir . '/symfony/http-foundation/Cookie.php', + 'Symfony\\Component\\HttpFoundation\\Exception\\ConflictingHeadersException' => $vendorDir . '/symfony/http-foundation/Exception/ConflictingHeadersException.php', + 'Symfony\\Component\\HttpFoundation\\Exception\\RequestExceptionInterface' => $vendorDir . '/symfony/http-foundation/Exception/RequestExceptionInterface.php', + 'Symfony\\Component\\HttpFoundation\\Exception\\SuspiciousOperationException' => $vendorDir . '/symfony/http-foundation/Exception/SuspiciousOperationException.php', + 'Symfony\\Component\\HttpFoundation\\ExpressionRequestMatcher' => $vendorDir . '/symfony/http-foundation/ExpressionRequestMatcher.php', + 'Symfony\\Component\\HttpFoundation\\FileBag' => $vendorDir . '/symfony/http-foundation/FileBag.php', + 'Symfony\\Component\\HttpFoundation\\File\\Exception\\AccessDeniedException' => $vendorDir . '/symfony/http-foundation/File/Exception/AccessDeniedException.php', + 'Symfony\\Component\\HttpFoundation\\File\\Exception\\FileException' => $vendorDir . '/symfony/http-foundation/File/Exception/FileException.php', + 'Symfony\\Component\\HttpFoundation\\File\\Exception\\FileNotFoundException' => $vendorDir . '/symfony/http-foundation/File/Exception/FileNotFoundException.php', + 'Symfony\\Component\\HttpFoundation\\File\\Exception\\UnexpectedTypeException' => $vendorDir . '/symfony/http-foundation/File/Exception/UnexpectedTypeException.php', + 'Symfony\\Component\\HttpFoundation\\File\\Exception\\UploadException' => $vendorDir . '/symfony/http-foundation/File/Exception/UploadException.php', + 'Symfony\\Component\\HttpFoundation\\File\\File' => $vendorDir . '/symfony/http-foundation/File/File.php', + 'Symfony\\Component\\HttpFoundation\\File\\MimeType\\ExtensionGuesser' => $vendorDir . '/symfony/http-foundation/File/MimeType/ExtensionGuesser.php', + 'Symfony\\Component\\HttpFoundation\\File\\MimeType\\ExtensionGuesserInterface' => $vendorDir . '/symfony/http-foundation/File/MimeType/ExtensionGuesserInterface.php', + 'Symfony\\Component\\HttpFoundation\\File\\MimeType\\FileBinaryMimeTypeGuesser' => $vendorDir . '/symfony/http-foundation/File/MimeType/FileBinaryMimeTypeGuesser.php', + 'Symfony\\Component\\HttpFoundation\\File\\MimeType\\FileinfoMimeTypeGuesser' => $vendorDir . '/symfony/http-foundation/File/MimeType/FileinfoMimeTypeGuesser.php', + 'Symfony\\Component\\HttpFoundation\\File\\MimeType\\MimeTypeExtensionGuesser' => $vendorDir . '/symfony/http-foundation/File/MimeType/MimeTypeExtensionGuesser.php', + 'Symfony\\Component\\HttpFoundation\\File\\MimeType\\MimeTypeGuesser' => $vendorDir . '/symfony/http-foundation/File/MimeType/MimeTypeGuesser.php', + 'Symfony\\Component\\HttpFoundation\\File\\MimeType\\MimeTypeGuesserInterface' => $vendorDir . '/symfony/http-foundation/File/MimeType/MimeTypeGuesserInterface.php', + 'Symfony\\Component\\HttpFoundation\\File\\Stream' => $vendorDir . '/symfony/http-foundation/File/Stream.php', + 'Symfony\\Component\\HttpFoundation\\File\\UploadedFile' => $vendorDir . '/symfony/http-foundation/File/UploadedFile.php', + 'Symfony\\Component\\HttpFoundation\\HeaderBag' => $vendorDir . '/symfony/http-foundation/HeaderBag.php', + 'Symfony\\Component\\HttpFoundation\\IpUtils' => $vendorDir . '/symfony/http-foundation/IpUtils.php', + 'Symfony\\Component\\HttpFoundation\\JsonResponse' => $vendorDir . '/symfony/http-foundation/JsonResponse.php', + 'Symfony\\Component\\HttpFoundation\\ParameterBag' => $vendorDir . '/symfony/http-foundation/ParameterBag.php', + 'Symfony\\Component\\HttpFoundation\\RedirectResponse' => $vendorDir . '/symfony/http-foundation/RedirectResponse.php', + 'Symfony\\Component\\HttpFoundation\\Request' => $vendorDir . '/symfony/http-foundation/Request.php', + 'Symfony\\Component\\HttpFoundation\\RequestMatcher' => $vendorDir . '/symfony/http-foundation/RequestMatcher.php', + 'Symfony\\Component\\HttpFoundation\\RequestMatcherInterface' => $vendorDir . '/symfony/http-foundation/RequestMatcherInterface.php', + 'Symfony\\Component\\HttpFoundation\\RequestStack' => $vendorDir . '/symfony/http-foundation/RequestStack.php', + 'Symfony\\Component\\HttpFoundation\\Response' => $vendorDir . '/symfony/http-foundation/Response.php', + 'Symfony\\Component\\HttpFoundation\\ResponseHeaderBag' => $vendorDir . '/symfony/http-foundation/ResponseHeaderBag.php', + 'Symfony\\Component\\HttpFoundation\\ServerBag' => $vendorDir . '/symfony/http-foundation/ServerBag.php', + 'Symfony\\Component\\HttpFoundation\\Session\\Attribute\\AttributeBag' => $vendorDir . '/symfony/http-foundation/Session/Attribute/AttributeBag.php', + 'Symfony\\Component\\HttpFoundation\\Session\\Attribute\\AttributeBagInterface' => $vendorDir . '/symfony/http-foundation/Session/Attribute/AttributeBagInterface.php', + 'Symfony\\Component\\HttpFoundation\\Session\\Attribute\\NamespacedAttributeBag' => $vendorDir . '/symfony/http-foundation/Session/Attribute/NamespacedAttributeBag.php', + 'Symfony\\Component\\HttpFoundation\\Session\\Flash\\AutoExpireFlashBag' => $vendorDir . '/symfony/http-foundation/Session/Flash/AutoExpireFlashBag.php', + 'Symfony\\Component\\HttpFoundation\\Session\\Flash\\FlashBag' => $vendorDir . '/symfony/http-foundation/Session/Flash/FlashBag.php', + 'Symfony\\Component\\HttpFoundation\\Session\\Flash\\FlashBagInterface' => $vendorDir . '/symfony/http-foundation/Session/Flash/FlashBagInterface.php', + 'Symfony\\Component\\HttpFoundation\\Session\\Session' => $vendorDir . '/symfony/http-foundation/Session/Session.php', + 'Symfony\\Component\\HttpFoundation\\Session\\SessionBagInterface' => $vendorDir . '/symfony/http-foundation/Session/SessionBagInterface.php', + 'Symfony\\Component\\HttpFoundation\\Session\\SessionBagProxy' => $vendorDir . '/symfony/http-foundation/Session/SessionBagProxy.php', + 'Symfony\\Component\\HttpFoundation\\Session\\SessionInterface' => $vendorDir . '/symfony/http-foundation/Session/SessionInterface.php', + 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\AbstractSessionHandler' => $vendorDir . '/symfony/http-foundation/Session/Storage/Handler/AbstractSessionHandler.php', + 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\MemcacheSessionHandler' => $vendorDir . '/symfony/http-foundation/Session/Storage/Handler/MemcacheSessionHandler.php', + 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\MemcachedSessionHandler' => $vendorDir . '/symfony/http-foundation/Session/Storage/Handler/MemcachedSessionHandler.php', + 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\MongoDbSessionHandler' => $vendorDir . '/symfony/http-foundation/Session/Storage/Handler/MongoDbSessionHandler.php', + 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\NativeFileSessionHandler' => $vendorDir . '/symfony/http-foundation/Session/Storage/Handler/NativeFileSessionHandler.php', + 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\NativeSessionHandler' => $vendorDir . '/symfony/http-foundation/Session/Storage/Handler/NativeSessionHandler.php', + 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\NullSessionHandler' => $vendorDir . '/symfony/http-foundation/Session/Storage/Handler/NullSessionHandler.php', + 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\PdoSessionHandler' => $vendorDir . '/symfony/http-foundation/Session/Storage/Handler/PdoSessionHandler.php', + 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\StrictSessionHandler' => $vendorDir . '/symfony/http-foundation/Session/Storage/Handler/StrictSessionHandler.php', + 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\WriteCheckSessionHandler' => $vendorDir . '/symfony/http-foundation/Session/Storage/Handler/WriteCheckSessionHandler.php', + 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\MetadataBag' => $vendorDir . '/symfony/http-foundation/Session/Storage/MetadataBag.php', + 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\MockArraySessionStorage' => $vendorDir . '/symfony/http-foundation/Session/Storage/MockArraySessionStorage.php', + 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\MockFileSessionStorage' => $vendorDir . '/symfony/http-foundation/Session/Storage/MockFileSessionStorage.php', + 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\NativeSessionStorage' => $vendorDir . '/symfony/http-foundation/Session/Storage/NativeSessionStorage.php', + 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\PhpBridgeSessionStorage' => $vendorDir . '/symfony/http-foundation/Session/Storage/PhpBridgeSessionStorage.php', + 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Proxy\\AbstractProxy' => $vendorDir . '/symfony/http-foundation/Session/Storage/Proxy/AbstractProxy.php', + 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Proxy\\NativeProxy' => $vendorDir . '/symfony/http-foundation/Session/Storage/Proxy/NativeProxy.php', + 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Proxy\\SessionHandlerProxy' => $vendorDir . '/symfony/http-foundation/Session/Storage/Proxy/SessionHandlerProxy.php', + 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\SessionStorageInterface' => $vendorDir . '/symfony/http-foundation/Session/Storage/SessionStorageInterface.php', + 'Symfony\\Component\\HttpFoundation\\StreamedResponse' => $vendorDir . '/symfony/http-foundation/StreamedResponse.php', + 'Symfony\\Component\\HttpKernel\\Bundle\\Bundle' => $vendorDir . '/symfony/http-kernel/Bundle/Bundle.php', + 'Symfony\\Component\\HttpKernel\\Bundle\\BundleInterface' => $vendorDir . '/symfony/http-kernel/Bundle/BundleInterface.php', + 'Symfony\\Component\\HttpKernel\\CacheClearer\\CacheClearerInterface' => $vendorDir . '/symfony/http-kernel/CacheClearer/CacheClearerInterface.php', + 'Symfony\\Component\\HttpKernel\\CacheClearer\\ChainCacheClearer' => $vendorDir . '/symfony/http-kernel/CacheClearer/ChainCacheClearer.php', + 'Symfony\\Component\\HttpKernel\\CacheClearer\\Psr6CacheClearer' => $vendorDir . '/symfony/http-kernel/CacheClearer/Psr6CacheClearer.php', + 'Symfony\\Component\\HttpKernel\\CacheWarmer\\CacheWarmer' => $vendorDir . '/symfony/http-kernel/CacheWarmer/CacheWarmer.php', + 'Symfony\\Component\\HttpKernel\\CacheWarmer\\CacheWarmerAggregate' => $vendorDir . '/symfony/http-kernel/CacheWarmer/CacheWarmerAggregate.php', + 'Symfony\\Component\\HttpKernel\\CacheWarmer\\CacheWarmerInterface' => $vendorDir . '/symfony/http-kernel/CacheWarmer/CacheWarmerInterface.php', + 'Symfony\\Component\\HttpKernel\\CacheWarmer\\WarmableInterface' => $vendorDir . '/symfony/http-kernel/CacheWarmer/WarmableInterface.php', + 'Symfony\\Component\\HttpKernel\\Client' => $vendorDir . '/symfony/http-kernel/Client.php', + 'Symfony\\Component\\HttpKernel\\Config\\EnvParametersResource' => $vendorDir . '/symfony/http-kernel/Config/EnvParametersResource.php', + 'Symfony\\Component\\HttpKernel\\Config\\FileLocator' => $vendorDir . '/symfony/http-kernel/Config/FileLocator.php', + 'Symfony\\Component\\HttpKernel\\ControllerMetadata\\ArgumentMetadata' => $vendorDir . '/symfony/http-kernel/ControllerMetadata/ArgumentMetadata.php', + 'Symfony\\Component\\HttpKernel\\ControllerMetadata\\ArgumentMetadataFactory' => $vendorDir . '/symfony/http-kernel/ControllerMetadata/ArgumentMetadataFactory.php', + 'Symfony\\Component\\HttpKernel\\ControllerMetadata\\ArgumentMetadataFactoryInterface' => $vendorDir . '/symfony/http-kernel/ControllerMetadata/ArgumentMetadataFactoryInterface.php', + 'Symfony\\Component\\HttpKernel\\Controller\\ArgumentResolver' => $vendorDir . '/symfony/http-kernel/Controller/ArgumentResolver.php', + 'Symfony\\Component\\HttpKernel\\Controller\\ArgumentResolverInterface' => $vendorDir . '/symfony/http-kernel/Controller/ArgumentResolverInterface.php', + 'Symfony\\Component\\HttpKernel\\Controller\\ArgumentResolver\\DefaultValueResolver' => $vendorDir . '/symfony/http-kernel/Controller/ArgumentResolver/DefaultValueResolver.php', + 'Symfony\\Component\\HttpKernel\\Controller\\ArgumentResolver\\RequestAttributeValueResolver' => $vendorDir . '/symfony/http-kernel/Controller/ArgumentResolver/RequestAttributeValueResolver.php', + 'Symfony\\Component\\HttpKernel\\Controller\\ArgumentResolver\\RequestValueResolver' => $vendorDir . '/symfony/http-kernel/Controller/ArgumentResolver/RequestValueResolver.php', + 'Symfony\\Component\\HttpKernel\\Controller\\ArgumentResolver\\ServiceValueResolver' => $vendorDir . '/symfony/http-kernel/Controller/ArgumentResolver/ServiceValueResolver.php', + 'Symfony\\Component\\HttpKernel\\Controller\\ArgumentResolver\\SessionValueResolver' => $vendorDir . '/symfony/http-kernel/Controller/ArgumentResolver/SessionValueResolver.php', + 'Symfony\\Component\\HttpKernel\\Controller\\ArgumentResolver\\VariadicValueResolver' => $vendorDir . '/symfony/http-kernel/Controller/ArgumentResolver/VariadicValueResolver.php', + 'Symfony\\Component\\HttpKernel\\Controller\\ArgumentValueResolverInterface' => $vendorDir . '/symfony/http-kernel/Controller/ArgumentValueResolverInterface.php', + 'Symfony\\Component\\HttpKernel\\Controller\\ContainerControllerResolver' => $vendorDir . '/symfony/http-kernel/Controller/ContainerControllerResolver.php', + 'Symfony\\Component\\HttpKernel\\Controller\\ControllerReference' => $vendorDir . '/symfony/http-kernel/Controller/ControllerReference.php', + 'Symfony\\Component\\HttpKernel\\Controller\\ControllerResolver' => $vendorDir . '/symfony/http-kernel/Controller/ControllerResolver.php', + 'Symfony\\Component\\HttpKernel\\Controller\\ControllerResolverInterface' => $vendorDir . '/symfony/http-kernel/Controller/ControllerResolverInterface.php', + 'Symfony\\Component\\HttpKernel\\Controller\\TraceableArgumentResolver' => $vendorDir . '/symfony/http-kernel/Controller/TraceableArgumentResolver.php', + 'Symfony\\Component\\HttpKernel\\Controller\\TraceableControllerResolver' => $vendorDir . '/symfony/http-kernel/Controller/TraceableControllerResolver.php', + 'Symfony\\Component\\HttpKernel\\DataCollector\\AjaxDataCollector' => $vendorDir . '/symfony/http-kernel/DataCollector/AjaxDataCollector.php', + 'Symfony\\Component\\HttpKernel\\DataCollector\\ConfigDataCollector' => $vendorDir . '/symfony/http-kernel/DataCollector/ConfigDataCollector.php', + 'Symfony\\Component\\HttpKernel\\DataCollector\\DataCollector' => $vendorDir . '/symfony/http-kernel/DataCollector/DataCollector.php', + 'Symfony\\Component\\HttpKernel\\DataCollector\\DataCollectorInterface' => $vendorDir . '/symfony/http-kernel/DataCollector/DataCollectorInterface.php', + 'Symfony\\Component\\HttpKernel\\DataCollector\\DumpDataCollector' => $vendorDir . '/symfony/http-kernel/DataCollector/DumpDataCollector.php', + 'Symfony\\Component\\HttpKernel\\DataCollector\\EventDataCollector' => $vendorDir . '/symfony/http-kernel/DataCollector/EventDataCollector.php', + 'Symfony\\Component\\HttpKernel\\DataCollector\\ExceptionDataCollector' => $vendorDir . '/symfony/http-kernel/DataCollector/ExceptionDataCollector.php', + 'Symfony\\Component\\HttpKernel\\DataCollector\\LateDataCollectorInterface' => $vendorDir . '/symfony/http-kernel/DataCollector/LateDataCollectorInterface.php', + 'Symfony\\Component\\HttpKernel\\DataCollector\\LoggerDataCollector' => $vendorDir . '/symfony/http-kernel/DataCollector/LoggerDataCollector.php', + 'Symfony\\Component\\HttpKernel\\DataCollector\\MemoryDataCollector' => $vendorDir . '/symfony/http-kernel/DataCollector/MemoryDataCollector.php', + 'Symfony\\Component\\HttpKernel\\DataCollector\\RequestDataCollector' => $vendorDir . '/symfony/http-kernel/DataCollector/RequestDataCollector.php', + 'Symfony\\Component\\HttpKernel\\DataCollector\\RouterDataCollector' => $vendorDir . '/symfony/http-kernel/DataCollector/RouterDataCollector.php', + 'Symfony\\Component\\HttpKernel\\DataCollector\\TimeDataCollector' => $vendorDir . '/symfony/http-kernel/DataCollector/TimeDataCollector.php', + 'Symfony\\Component\\HttpKernel\\DataCollector\\Util\\ValueExporter' => $vendorDir . '/symfony/http-kernel/DataCollector/Util/ValueExporter.php', + 'Symfony\\Component\\HttpKernel\\Debug\\FileLinkFormatter' => $vendorDir . '/symfony/http-kernel/Debug/FileLinkFormatter.php', + 'Symfony\\Component\\HttpKernel\\Debug\\TraceableEventDispatcher' => $vendorDir . '/symfony/http-kernel/Debug/TraceableEventDispatcher.php', + 'Symfony\\Component\\HttpKernel\\DependencyInjection\\AddAnnotatedClassesToCachePass' => $vendorDir . '/symfony/http-kernel/DependencyInjection/AddAnnotatedClassesToCachePass.php', + 'Symfony\\Component\\HttpKernel\\DependencyInjection\\AddClassesToCachePass' => $vendorDir . '/symfony/http-kernel/DependencyInjection/AddClassesToCachePass.php', + 'Symfony\\Component\\HttpKernel\\DependencyInjection\\ConfigurableExtension' => $vendorDir . '/symfony/http-kernel/DependencyInjection/ConfigurableExtension.php', + 'Symfony\\Component\\HttpKernel\\DependencyInjection\\ControllerArgumentValueResolverPass' => $vendorDir . '/symfony/http-kernel/DependencyInjection/ControllerArgumentValueResolverPass.php', + 'Symfony\\Component\\HttpKernel\\DependencyInjection\\Extension' => $vendorDir . '/symfony/http-kernel/DependencyInjection/Extension.php', + 'Symfony\\Component\\HttpKernel\\DependencyInjection\\FragmentRendererPass' => $vendorDir . '/symfony/http-kernel/DependencyInjection/FragmentRendererPass.php', + 'Symfony\\Component\\HttpKernel\\DependencyInjection\\LazyLoadingFragmentHandler' => $vendorDir . '/symfony/http-kernel/DependencyInjection/LazyLoadingFragmentHandler.php', + 'Symfony\\Component\\HttpKernel\\DependencyInjection\\LoggerPass' => $vendorDir . '/symfony/http-kernel/DependencyInjection/LoggerPass.php', + 'Symfony\\Component\\HttpKernel\\DependencyInjection\\MergeExtensionConfigurationPass' => $vendorDir . '/symfony/http-kernel/DependencyInjection/MergeExtensionConfigurationPass.php', + 'Symfony\\Component\\HttpKernel\\DependencyInjection\\RegisterControllerArgumentLocatorsPass' => $vendorDir . '/symfony/http-kernel/DependencyInjection/RegisterControllerArgumentLocatorsPass.php', + 'Symfony\\Component\\HttpKernel\\DependencyInjection\\RemoveEmptyControllerArgumentLocatorsPass' => $vendorDir . '/symfony/http-kernel/DependencyInjection/RemoveEmptyControllerArgumentLocatorsPass.php', + 'Symfony\\Component\\HttpKernel\\DependencyInjection\\ResettableServicePass' => $vendorDir . '/symfony/http-kernel/DependencyInjection/ResettableServicePass.php', + 'Symfony\\Component\\HttpKernel\\DependencyInjection\\ServicesResetter' => $vendorDir . '/symfony/http-kernel/DependencyInjection/ServicesResetter.php', + 'Symfony\\Component\\HttpKernel\\EventListener\\AbstractSessionListener' => $vendorDir . '/symfony/http-kernel/EventListener/AbstractSessionListener.php', + 'Symfony\\Component\\HttpKernel\\EventListener\\AbstractTestSessionListener' => $vendorDir . '/symfony/http-kernel/EventListener/AbstractTestSessionListener.php', + 'Symfony\\Component\\HttpKernel\\EventListener\\AddRequestFormatsListener' => $vendorDir . '/symfony/http-kernel/EventListener/AddRequestFormatsListener.php', + 'Symfony\\Component\\HttpKernel\\EventListener\\DebugHandlersListener' => $vendorDir . '/symfony/http-kernel/EventListener/DebugHandlersListener.php', + 'Symfony\\Component\\HttpKernel\\EventListener\\DumpListener' => $vendorDir . '/symfony/http-kernel/EventListener/DumpListener.php', + 'Symfony\\Component\\HttpKernel\\EventListener\\ExceptionListener' => $vendorDir . '/symfony/http-kernel/EventListener/ExceptionListener.php', + 'Symfony\\Component\\HttpKernel\\EventListener\\FragmentListener' => $vendorDir . '/symfony/http-kernel/EventListener/FragmentListener.php', + 'Symfony\\Component\\HttpKernel\\EventListener\\LocaleListener' => $vendorDir . '/symfony/http-kernel/EventListener/LocaleListener.php', + 'Symfony\\Component\\HttpKernel\\EventListener\\ProfilerListener' => $vendorDir . '/symfony/http-kernel/EventListener/ProfilerListener.php', + 'Symfony\\Component\\HttpKernel\\EventListener\\ResponseListener' => $vendorDir . '/symfony/http-kernel/EventListener/ResponseListener.php', + 'Symfony\\Component\\HttpKernel\\EventListener\\RouterListener' => $vendorDir . '/symfony/http-kernel/EventListener/RouterListener.php', + 'Symfony\\Component\\HttpKernel\\EventListener\\SaveSessionListener' => $vendorDir . '/symfony/http-kernel/EventListener/SaveSessionListener.php', + 'Symfony\\Component\\HttpKernel\\EventListener\\SessionListener' => $vendorDir . '/symfony/http-kernel/EventListener/SessionListener.php', + 'Symfony\\Component\\HttpKernel\\EventListener\\StreamedResponseListener' => $vendorDir . '/symfony/http-kernel/EventListener/StreamedResponseListener.php', + 'Symfony\\Component\\HttpKernel\\EventListener\\SurrogateListener' => $vendorDir . '/symfony/http-kernel/EventListener/SurrogateListener.php', + 'Symfony\\Component\\HttpKernel\\EventListener\\TestSessionListener' => $vendorDir . '/symfony/http-kernel/EventListener/TestSessionListener.php', + 'Symfony\\Component\\HttpKernel\\EventListener\\TranslatorListener' => $vendorDir . '/symfony/http-kernel/EventListener/TranslatorListener.php', + 'Symfony\\Component\\HttpKernel\\EventListener\\ValidateRequestListener' => $vendorDir . '/symfony/http-kernel/EventListener/ValidateRequestListener.php', + 'Symfony\\Component\\HttpKernel\\Event\\FilterControllerArgumentsEvent' => $vendorDir . '/symfony/http-kernel/Event/FilterControllerArgumentsEvent.php', + 'Symfony\\Component\\HttpKernel\\Event\\FilterControllerEvent' => $vendorDir . '/symfony/http-kernel/Event/FilterControllerEvent.php', + 'Symfony\\Component\\HttpKernel\\Event\\FilterResponseEvent' => $vendorDir . '/symfony/http-kernel/Event/FilterResponseEvent.php', + 'Symfony\\Component\\HttpKernel\\Event\\FinishRequestEvent' => $vendorDir . '/symfony/http-kernel/Event/FinishRequestEvent.php', + 'Symfony\\Component\\HttpKernel\\Event\\GetResponseEvent' => $vendorDir . '/symfony/http-kernel/Event/GetResponseEvent.php', + 'Symfony\\Component\\HttpKernel\\Event\\GetResponseForControllerResultEvent' => $vendorDir . '/symfony/http-kernel/Event/GetResponseForControllerResultEvent.php', + 'Symfony\\Component\\HttpKernel\\Event\\GetResponseForExceptionEvent' => $vendorDir . '/symfony/http-kernel/Event/GetResponseForExceptionEvent.php', + 'Symfony\\Component\\HttpKernel\\Event\\KernelEvent' => $vendorDir . '/symfony/http-kernel/Event/KernelEvent.php', + 'Symfony\\Component\\HttpKernel\\Event\\PostResponseEvent' => $vendorDir . '/symfony/http-kernel/Event/PostResponseEvent.php', + 'Symfony\\Component\\HttpKernel\\Exception\\AccessDeniedHttpException' => $vendorDir . '/symfony/http-kernel/Exception/AccessDeniedHttpException.php', + 'Symfony\\Component\\HttpKernel\\Exception\\BadRequestHttpException' => $vendorDir . '/symfony/http-kernel/Exception/BadRequestHttpException.php', + 'Symfony\\Component\\HttpKernel\\Exception\\ConflictHttpException' => $vendorDir . '/symfony/http-kernel/Exception/ConflictHttpException.php', + 'Symfony\\Component\\HttpKernel\\Exception\\GoneHttpException' => $vendorDir . '/symfony/http-kernel/Exception/GoneHttpException.php', + 'Symfony\\Component\\HttpKernel\\Exception\\HttpException' => $vendorDir . '/symfony/http-kernel/Exception/HttpException.php', + 'Symfony\\Component\\HttpKernel\\Exception\\HttpExceptionInterface' => $vendorDir . '/symfony/http-kernel/Exception/HttpExceptionInterface.php', + 'Symfony\\Component\\HttpKernel\\Exception\\LengthRequiredHttpException' => $vendorDir . '/symfony/http-kernel/Exception/LengthRequiredHttpException.php', + 'Symfony\\Component\\HttpKernel\\Exception\\MethodNotAllowedHttpException' => $vendorDir . '/symfony/http-kernel/Exception/MethodNotAllowedHttpException.php', + 'Symfony\\Component\\HttpKernel\\Exception\\NotAcceptableHttpException' => $vendorDir . '/symfony/http-kernel/Exception/NotAcceptableHttpException.php', + 'Symfony\\Component\\HttpKernel\\Exception\\NotFoundHttpException' => $vendorDir . '/symfony/http-kernel/Exception/NotFoundHttpException.php', + 'Symfony\\Component\\HttpKernel\\Exception\\PreconditionFailedHttpException' => $vendorDir . '/symfony/http-kernel/Exception/PreconditionFailedHttpException.php', + 'Symfony\\Component\\HttpKernel\\Exception\\PreconditionRequiredHttpException' => $vendorDir . '/symfony/http-kernel/Exception/PreconditionRequiredHttpException.php', + 'Symfony\\Component\\HttpKernel\\Exception\\ServiceUnavailableHttpException' => $vendorDir . '/symfony/http-kernel/Exception/ServiceUnavailableHttpException.php', + 'Symfony\\Component\\HttpKernel\\Exception\\TooManyRequestsHttpException' => $vendorDir . '/symfony/http-kernel/Exception/TooManyRequestsHttpException.php', + 'Symfony\\Component\\HttpKernel\\Exception\\UnauthorizedHttpException' => $vendorDir . '/symfony/http-kernel/Exception/UnauthorizedHttpException.php', + 'Symfony\\Component\\HttpKernel\\Exception\\UnprocessableEntityHttpException' => $vendorDir . '/symfony/http-kernel/Exception/UnprocessableEntityHttpException.php', + 'Symfony\\Component\\HttpKernel\\Exception\\UnsupportedMediaTypeHttpException' => $vendorDir . '/symfony/http-kernel/Exception/UnsupportedMediaTypeHttpException.php', + 'Symfony\\Component\\HttpKernel\\Fragment\\AbstractSurrogateFragmentRenderer' => $vendorDir . '/symfony/http-kernel/Fragment/AbstractSurrogateFragmentRenderer.php', + 'Symfony\\Component\\HttpKernel\\Fragment\\EsiFragmentRenderer' => $vendorDir . '/symfony/http-kernel/Fragment/EsiFragmentRenderer.php', + 'Symfony\\Component\\HttpKernel\\Fragment\\FragmentHandler' => $vendorDir . '/symfony/http-kernel/Fragment/FragmentHandler.php', + 'Symfony\\Component\\HttpKernel\\Fragment\\FragmentRendererInterface' => $vendorDir . '/symfony/http-kernel/Fragment/FragmentRendererInterface.php', + 'Symfony\\Component\\HttpKernel\\Fragment\\HIncludeFragmentRenderer' => $vendorDir . '/symfony/http-kernel/Fragment/HIncludeFragmentRenderer.php', + 'Symfony\\Component\\HttpKernel\\Fragment\\InlineFragmentRenderer' => $vendorDir . '/symfony/http-kernel/Fragment/InlineFragmentRenderer.php', + 'Symfony\\Component\\HttpKernel\\Fragment\\RoutableFragmentRenderer' => $vendorDir . '/symfony/http-kernel/Fragment/RoutableFragmentRenderer.php', + 'Symfony\\Component\\HttpKernel\\Fragment\\SsiFragmentRenderer' => $vendorDir . '/symfony/http-kernel/Fragment/SsiFragmentRenderer.php', + 'Symfony\\Component\\HttpKernel\\HttpCache\\AbstractSurrogate' => $vendorDir . '/symfony/http-kernel/HttpCache/AbstractSurrogate.php', + 'Symfony\\Component\\HttpKernel\\HttpCache\\Esi' => $vendorDir . '/symfony/http-kernel/HttpCache/Esi.php', + 'Symfony\\Component\\HttpKernel\\HttpCache\\HttpCache' => $vendorDir . '/symfony/http-kernel/HttpCache/HttpCache.php', + 'Symfony\\Component\\HttpKernel\\HttpCache\\ResponseCacheStrategy' => $vendorDir . '/symfony/http-kernel/HttpCache/ResponseCacheStrategy.php', + 'Symfony\\Component\\HttpKernel\\HttpCache\\ResponseCacheStrategyInterface' => $vendorDir . '/symfony/http-kernel/HttpCache/ResponseCacheStrategyInterface.php', + 'Symfony\\Component\\HttpKernel\\HttpCache\\Ssi' => $vendorDir . '/symfony/http-kernel/HttpCache/Ssi.php', + 'Symfony\\Component\\HttpKernel\\HttpCache\\Store' => $vendorDir . '/symfony/http-kernel/HttpCache/Store.php', + 'Symfony\\Component\\HttpKernel\\HttpCache\\StoreInterface' => $vendorDir . '/symfony/http-kernel/HttpCache/StoreInterface.php', + 'Symfony\\Component\\HttpKernel\\HttpCache\\SubRequestHandler' => $vendorDir . '/symfony/http-kernel/HttpCache/SubRequestHandler.php', + 'Symfony\\Component\\HttpKernel\\HttpCache\\SurrogateInterface' => $vendorDir . '/symfony/http-kernel/HttpCache/SurrogateInterface.php', + 'Symfony\\Component\\HttpKernel\\HttpKernel' => $vendorDir . '/symfony/http-kernel/HttpKernel.php', + 'Symfony\\Component\\HttpKernel\\HttpKernelInterface' => $vendorDir . '/symfony/http-kernel/HttpKernelInterface.php', + 'Symfony\\Component\\HttpKernel\\Kernel' => $vendorDir . '/symfony/http-kernel/Kernel.php', + 'Symfony\\Component\\HttpKernel\\KernelEvents' => $vendorDir . '/symfony/http-kernel/KernelEvents.php', + 'Symfony\\Component\\HttpKernel\\KernelInterface' => $vendorDir . '/symfony/http-kernel/KernelInterface.php', + 'Symfony\\Component\\HttpKernel\\Log\\DebugLoggerInterface' => $vendorDir . '/symfony/http-kernel/Log/DebugLoggerInterface.php', + 'Symfony\\Component\\HttpKernel\\Log\\Logger' => $vendorDir . '/symfony/http-kernel/Log/Logger.php', + 'Symfony\\Component\\HttpKernel\\Profiler\\FileProfilerStorage' => $vendorDir . '/symfony/http-kernel/Profiler/FileProfilerStorage.php', + 'Symfony\\Component\\HttpKernel\\Profiler\\Profile' => $vendorDir . '/symfony/http-kernel/Profiler/Profile.php', + 'Symfony\\Component\\HttpKernel\\Profiler\\Profiler' => $vendorDir . '/symfony/http-kernel/Profiler/Profiler.php', + 'Symfony\\Component\\HttpKernel\\Profiler\\ProfilerStorageInterface' => $vendorDir . '/symfony/http-kernel/Profiler/ProfilerStorageInterface.php', + 'Symfony\\Component\\HttpKernel\\RebootableInterface' => $vendorDir . '/symfony/http-kernel/RebootableInterface.php', + 'Symfony\\Component\\HttpKernel\\TerminableInterface' => $vendorDir . '/symfony/http-kernel/TerminableInterface.php', + 'Symfony\\Component\\HttpKernel\\UriSigner' => $vendorDir . '/symfony/http-kernel/UriSigner.php', + 'Symfony\\Component\\Routing\\Annotation\\Route' => $vendorDir . '/symfony/routing/Annotation/Route.php', + 'Symfony\\Component\\Routing\\CompiledRoute' => $vendorDir . '/symfony/routing/CompiledRoute.php', + 'Symfony\\Component\\Routing\\DependencyInjection\\RoutingResolverPass' => $vendorDir . '/symfony/routing/DependencyInjection/RoutingResolverPass.php', + 'Symfony\\Component\\Routing\\Exception\\ExceptionInterface' => $vendorDir . '/symfony/routing/Exception/ExceptionInterface.php', + 'Symfony\\Component\\Routing\\Exception\\InvalidParameterException' => $vendorDir . '/symfony/routing/Exception/InvalidParameterException.php', + 'Symfony\\Component\\Routing\\Exception\\MethodNotAllowedException' => $vendorDir . '/symfony/routing/Exception/MethodNotAllowedException.php', + 'Symfony\\Component\\Routing\\Exception\\MissingMandatoryParametersException' => $vendorDir . '/symfony/routing/Exception/MissingMandatoryParametersException.php', + 'Symfony\\Component\\Routing\\Exception\\NoConfigurationException' => $vendorDir . '/symfony/routing/Exception/NoConfigurationException.php', + 'Symfony\\Component\\Routing\\Exception\\ResourceNotFoundException' => $vendorDir . '/symfony/routing/Exception/ResourceNotFoundException.php', + 'Symfony\\Component\\Routing\\Exception\\RouteNotFoundException' => $vendorDir . '/symfony/routing/Exception/RouteNotFoundException.php', + 'Symfony\\Component\\Routing\\Generator\\ConfigurableRequirementsInterface' => $vendorDir . '/symfony/routing/Generator/ConfigurableRequirementsInterface.php', + 'Symfony\\Component\\Routing\\Generator\\Dumper\\GeneratorDumper' => $vendorDir . '/symfony/routing/Generator/Dumper/GeneratorDumper.php', + 'Symfony\\Component\\Routing\\Generator\\Dumper\\GeneratorDumperInterface' => $vendorDir . '/symfony/routing/Generator/Dumper/GeneratorDumperInterface.php', + 'Symfony\\Component\\Routing\\Generator\\Dumper\\PhpGeneratorDumper' => $vendorDir . '/symfony/routing/Generator/Dumper/PhpGeneratorDumper.php', + 'Symfony\\Component\\Routing\\Generator\\UrlGenerator' => $vendorDir . '/symfony/routing/Generator/UrlGenerator.php', + 'Symfony\\Component\\Routing\\Generator\\UrlGeneratorInterface' => $vendorDir . '/symfony/routing/Generator/UrlGeneratorInterface.php', + 'Symfony\\Component\\Routing\\Loader\\AnnotationClassLoader' => $vendorDir . '/symfony/routing/Loader/AnnotationClassLoader.php', + 'Symfony\\Component\\Routing\\Loader\\AnnotationDirectoryLoader' => $vendorDir . '/symfony/routing/Loader/AnnotationDirectoryLoader.php', + 'Symfony\\Component\\Routing\\Loader\\AnnotationFileLoader' => $vendorDir . '/symfony/routing/Loader/AnnotationFileLoader.php', + 'Symfony\\Component\\Routing\\Loader\\ClosureLoader' => $vendorDir . '/symfony/routing/Loader/ClosureLoader.php', + 'Symfony\\Component\\Routing\\Loader\\Configurator\\CollectionConfigurator' => $vendorDir . '/symfony/routing/Loader/Configurator/CollectionConfigurator.php', + 'Symfony\\Component\\Routing\\Loader\\Configurator\\ImportConfigurator' => $vendorDir . '/symfony/routing/Loader/Configurator/ImportConfigurator.php', + 'Symfony\\Component\\Routing\\Loader\\Configurator\\RouteConfigurator' => $vendorDir . '/symfony/routing/Loader/Configurator/RouteConfigurator.php', + 'Symfony\\Component\\Routing\\Loader\\Configurator\\RoutingConfigurator' => $vendorDir . '/symfony/routing/Loader/Configurator/RoutingConfigurator.php', + 'Symfony\\Component\\Routing\\Loader\\Configurator\\Traits\\AddTrait' => $vendorDir . '/symfony/routing/Loader/Configurator/Traits/AddTrait.php', + 'Symfony\\Component\\Routing\\Loader\\Configurator\\Traits\\RouteTrait' => $vendorDir . '/symfony/routing/Loader/Configurator/Traits/RouteTrait.php', + 'Symfony\\Component\\Routing\\Loader\\DependencyInjection\\ServiceRouterLoader' => $vendorDir . '/symfony/routing/Loader/DependencyInjection/ServiceRouterLoader.php', + 'Symfony\\Component\\Routing\\Loader\\DirectoryLoader' => $vendorDir . '/symfony/routing/Loader/DirectoryLoader.php', + 'Symfony\\Component\\Routing\\Loader\\GlobFileLoader' => $vendorDir . '/symfony/routing/Loader/GlobFileLoader.php', + 'Symfony\\Component\\Routing\\Loader\\ObjectRouteLoader' => $vendorDir . '/symfony/routing/Loader/ObjectRouteLoader.php', + 'Symfony\\Component\\Routing\\Loader\\PhpFileLoader' => $vendorDir . '/symfony/routing/Loader/PhpFileLoader.php', + 'Symfony\\Component\\Routing\\Loader\\ProtectedPhpFileLoader' => $vendorDir . '/symfony/routing/Loader/PhpFileLoader.php', + 'Symfony\\Component\\Routing\\Loader\\XmlFileLoader' => $vendorDir . '/symfony/routing/Loader/XmlFileLoader.php', + 'Symfony\\Component\\Routing\\Loader\\YamlFileLoader' => $vendorDir . '/symfony/routing/Loader/YamlFileLoader.php', + 'Symfony\\Component\\Routing\\Matcher\\Dumper\\DumperCollection' => $vendorDir . '/symfony/routing/Matcher/Dumper/DumperCollection.php', + 'Symfony\\Component\\Routing\\Matcher\\Dumper\\DumperRoute' => $vendorDir . '/symfony/routing/Matcher/Dumper/DumperRoute.php', + 'Symfony\\Component\\Routing\\Matcher\\Dumper\\MatcherDumper' => $vendorDir . '/symfony/routing/Matcher/Dumper/MatcherDumper.php', + 'Symfony\\Component\\Routing\\Matcher\\Dumper\\MatcherDumperInterface' => $vendorDir . '/symfony/routing/Matcher/Dumper/MatcherDumperInterface.php', + 'Symfony\\Component\\Routing\\Matcher\\Dumper\\PhpMatcherDumper' => $vendorDir . '/symfony/routing/Matcher/Dumper/PhpMatcherDumper.php', + 'Symfony\\Component\\Routing\\Matcher\\Dumper\\StaticPrefixCollection' => $vendorDir . '/symfony/routing/Matcher/Dumper/StaticPrefixCollection.php', + 'Symfony\\Component\\Routing\\Matcher\\RedirectableUrlMatcher' => $vendorDir . '/symfony/routing/Matcher/RedirectableUrlMatcher.php', + 'Symfony\\Component\\Routing\\Matcher\\RedirectableUrlMatcherInterface' => $vendorDir . '/symfony/routing/Matcher/RedirectableUrlMatcherInterface.php', + 'Symfony\\Component\\Routing\\Matcher\\RequestMatcherInterface' => $vendorDir . '/symfony/routing/Matcher/RequestMatcherInterface.php', + 'Symfony\\Component\\Routing\\Matcher\\TraceableUrlMatcher' => $vendorDir . '/symfony/routing/Matcher/TraceableUrlMatcher.php', + 'Symfony\\Component\\Routing\\Matcher\\UrlMatcher' => $vendorDir . '/symfony/routing/Matcher/UrlMatcher.php', + 'Symfony\\Component\\Routing\\Matcher\\UrlMatcherInterface' => $vendorDir . '/symfony/routing/Matcher/UrlMatcherInterface.php', + 'Symfony\\Component\\Routing\\RequestContext' => $vendorDir . '/symfony/routing/RequestContext.php', + 'Symfony\\Component\\Routing\\RequestContextAwareInterface' => $vendorDir . '/symfony/routing/RequestContextAwareInterface.php', + 'Symfony\\Component\\Routing\\Route' => $vendorDir . '/symfony/routing/Route.php', + 'Symfony\\Component\\Routing\\RouteCollection' => $vendorDir . '/symfony/routing/RouteCollection.php', + 'Symfony\\Component\\Routing\\RouteCollectionBuilder' => $vendorDir . '/symfony/routing/RouteCollectionBuilder.php', + 'Symfony\\Component\\Routing\\RouteCompiler' => $vendorDir . '/symfony/routing/RouteCompiler.php', + 'Symfony\\Component\\Routing\\RouteCompilerInterface' => $vendorDir . '/symfony/routing/RouteCompilerInterface.php', + 'Symfony\\Component\\Routing\\Router' => $vendorDir . '/symfony/routing/Router.php', + 'Symfony\\Component\\Routing\\RouterInterface' => $vendorDir . '/symfony/routing/RouterInterface.php', + 'Symfony\\Component\\Stopwatch\\Section' => $vendorDir . '/symfony/stopwatch/Section.php', + 'Symfony\\Component\\Stopwatch\\Stopwatch' => $vendorDir . '/symfony/stopwatch/Stopwatch.php', + 'Symfony\\Component\\Stopwatch\\StopwatchEvent' => $vendorDir . '/symfony/stopwatch/StopwatchEvent.php', + 'Symfony\\Component\\Stopwatch\\StopwatchPeriod' => $vendorDir . '/symfony/stopwatch/StopwatchPeriod.php', + 'Symfony\\Component\\VarDumper\\Caster\\AmqpCaster' => $vendorDir . '/symfony/var-dumper/Caster/AmqpCaster.php', + 'Symfony\\Component\\VarDumper\\Caster\\ArgsStub' => $vendorDir . '/symfony/var-dumper/Caster/ArgsStub.php', + 'Symfony\\Component\\VarDumper\\Caster\\Caster' => $vendorDir . '/symfony/var-dumper/Caster/Caster.php', + 'Symfony\\Component\\VarDumper\\Caster\\ClassStub' => $vendorDir . '/symfony/var-dumper/Caster/ClassStub.php', + 'Symfony\\Component\\VarDumper\\Caster\\ConstStub' => $vendorDir . '/symfony/var-dumper/Caster/ConstStub.php', + 'Symfony\\Component\\VarDumper\\Caster\\CutArrayStub' => $vendorDir . '/symfony/var-dumper/Caster/CutArrayStub.php', + 'Symfony\\Component\\VarDumper\\Caster\\CutStub' => $vendorDir . '/symfony/var-dumper/Caster/CutStub.php', + 'Symfony\\Component\\VarDumper\\Caster\\DOMCaster' => $vendorDir . '/symfony/var-dumper/Caster/DOMCaster.php', + 'Symfony\\Component\\VarDumper\\Caster\\DateCaster' => $vendorDir . '/symfony/var-dumper/Caster/DateCaster.php', + 'Symfony\\Component\\VarDumper\\Caster\\DoctrineCaster' => $vendorDir . '/symfony/var-dumper/Caster/DoctrineCaster.php', + 'Symfony\\Component\\VarDumper\\Caster\\EnumStub' => $vendorDir . '/symfony/var-dumper/Caster/EnumStub.php', + 'Symfony\\Component\\VarDumper\\Caster\\ExceptionCaster' => $vendorDir . '/symfony/var-dumper/Caster/ExceptionCaster.php', + 'Symfony\\Component\\VarDumper\\Caster\\FrameStub' => $vendorDir . '/symfony/var-dumper/Caster/FrameStub.php', + 'Symfony\\Component\\VarDumper\\Caster\\LinkStub' => $vendorDir . '/symfony/var-dumper/Caster/LinkStub.php', + 'Symfony\\Component\\VarDumper\\Caster\\MongoCaster' => $vendorDir . '/symfony/var-dumper/Caster/MongoCaster.php', + 'Symfony\\Component\\VarDumper\\Caster\\PdoCaster' => $vendorDir . '/symfony/var-dumper/Caster/PdoCaster.php', + 'Symfony\\Component\\VarDumper\\Caster\\PgSqlCaster' => $vendorDir . '/symfony/var-dumper/Caster/PgSqlCaster.php', + 'Symfony\\Component\\VarDumper\\Caster\\RedisCaster' => $vendorDir . '/symfony/var-dumper/Caster/RedisCaster.php', + 'Symfony\\Component\\VarDumper\\Caster\\ReflectionCaster' => $vendorDir . '/symfony/var-dumper/Caster/ReflectionCaster.php', + 'Symfony\\Component\\VarDumper\\Caster\\ResourceCaster' => $vendorDir . '/symfony/var-dumper/Caster/ResourceCaster.php', + 'Symfony\\Component\\VarDumper\\Caster\\SplCaster' => $vendorDir . '/symfony/var-dumper/Caster/SplCaster.php', + 'Symfony\\Component\\VarDumper\\Caster\\StubCaster' => $vendorDir . '/symfony/var-dumper/Caster/StubCaster.php', + 'Symfony\\Component\\VarDumper\\Caster\\SymfonyCaster' => $vendorDir . '/symfony/var-dumper/Caster/SymfonyCaster.php', + 'Symfony\\Component\\VarDumper\\Caster\\TraceStub' => $vendorDir . '/symfony/var-dumper/Caster/TraceStub.php', + 'Symfony\\Component\\VarDumper\\Caster\\XmlReaderCaster' => $vendorDir . '/symfony/var-dumper/Caster/XmlReaderCaster.php', + 'Symfony\\Component\\VarDumper\\Caster\\XmlResourceCaster' => $vendorDir . '/symfony/var-dumper/Caster/XmlResourceCaster.php', + 'Symfony\\Component\\VarDumper\\Cloner\\AbstractCloner' => $vendorDir . '/symfony/var-dumper/Cloner/AbstractCloner.php', + 'Symfony\\Component\\VarDumper\\Cloner\\ClonerInterface' => $vendorDir . '/symfony/var-dumper/Cloner/ClonerInterface.php', + 'Symfony\\Component\\VarDumper\\Cloner\\Cursor' => $vendorDir . '/symfony/var-dumper/Cloner/Cursor.php', + 'Symfony\\Component\\VarDumper\\Cloner\\Data' => $vendorDir . '/symfony/var-dumper/Cloner/Data.php', + 'Symfony\\Component\\VarDumper\\Cloner\\DumperInterface' => $vendorDir . '/symfony/var-dumper/Cloner/DumperInterface.php', + 'Symfony\\Component\\VarDumper\\Cloner\\Stub' => $vendorDir . '/symfony/var-dumper/Cloner/Stub.php', + 'Symfony\\Component\\VarDumper\\Cloner\\VarCloner' => $vendorDir . '/symfony/var-dumper/Cloner/VarCloner.php', + 'Symfony\\Component\\VarDumper\\Dumper\\AbstractDumper' => $vendorDir . '/symfony/var-dumper/Dumper/AbstractDumper.php', + 'Symfony\\Component\\VarDumper\\Dumper\\CliDumper' => $vendorDir . '/symfony/var-dumper/Dumper/CliDumper.php', + 'Symfony\\Component\\VarDumper\\Dumper\\DataDumperInterface' => $vendorDir . '/symfony/var-dumper/Dumper/DataDumperInterface.php', + 'Symfony\\Component\\VarDumper\\Dumper\\HtmlDumper' => $vendorDir . '/symfony/var-dumper/Dumper/HtmlDumper.php', + 'Symfony\\Component\\VarDumper\\Exception\\ThrowingCasterException' => $vendorDir . '/symfony/var-dumper/Exception/ThrowingCasterException.php', + 'Symfony\\Component\\VarDumper\\Test\\VarDumperTestTrait' => $vendorDir . '/symfony/var-dumper/Test/VarDumperTestTrait.php', + 'Symfony\\Component\\VarDumper\\VarDumper' => $vendorDir . '/symfony/var-dumper/VarDumper.php', + 'Symfony\\Component\\Yaml\\Command\\LintCommand' => $vendorDir . '/symfony/yaml/Command/LintCommand.php', + 'Symfony\\Component\\Yaml\\Dumper' => $vendorDir . '/symfony/yaml/Dumper.php', + 'Symfony\\Component\\Yaml\\Escaper' => $vendorDir . '/symfony/yaml/Escaper.php', + 'Symfony\\Component\\Yaml\\Exception\\DumpException' => $vendorDir . '/symfony/yaml/Exception/DumpException.php', + 'Symfony\\Component\\Yaml\\Exception\\ExceptionInterface' => $vendorDir . '/symfony/yaml/Exception/ExceptionInterface.php', + 'Symfony\\Component\\Yaml\\Exception\\ParseException' => $vendorDir . '/symfony/yaml/Exception/ParseException.php', + 'Symfony\\Component\\Yaml\\Exception\\RuntimeException' => $vendorDir . '/symfony/yaml/Exception/RuntimeException.php', + 'Symfony\\Component\\Yaml\\Inline' => $vendorDir . '/symfony/yaml/Inline.php', + 'Symfony\\Component\\Yaml\\Parser' => $vendorDir . '/symfony/yaml/Parser.php', + 'Symfony\\Component\\Yaml\\Tag\\TaggedValue' => $vendorDir . '/symfony/yaml/Tag/TaggedValue.php', + 'Symfony\\Component\\Yaml\\Unescaper' => $vendorDir . '/symfony/yaml/Unescaper.php', + 'Symfony\\Component\\Yaml\\Yaml' => $vendorDir . '/symfony/yaml/Yaml.php', + 'Symfony\\Polyfill\\Apcu\\Apcu' => $vendorDir . '/symfony/polyfill-apcu/Apcu.php', + 'Symfony\\Polyfill\\Ctype\\Ctype' => $vendorDir . '/symfony/polyfill-ctype/Ctype.php', + 'Symfony\\Polyfill\\Mbstring\\Mbstring' => $vendorDir . '/symfony/polyfill-mbstring/Mbstring.php', + 'Symfony\\Polyfill\\Php70\\Php70' => $vendorDir . '/symfony/polyfill-php70/Php70.php', + 'TabManager' => $baseDir . '/application/webpage.class.inc.php', + 'TabularBulkExport' => $baseDir . '/core/tabularbulkexport.class.inc.php', + 'TagSetFieldData' => $baseDir . '/core/tagsetfield.class.inc.php', + 'TemplateMenuNode' => $baseDir . '/application/menunode.class.inc.php', + 'TemplateString' => $baseDir . '/core/templatestring.class.inc.php', + 'TemplateStringPlaceholder' => $baseDir . '/core/templatestring.class.inc.php', + 'ToolsLog' => $baseDir . '/core/log.class.inc.php', + 'Trigger' => $baseDir . '/core/trigger.class.inc.php', + 'TriggerOnObject' => $baseDir . '/core/trigger.class.inc.php', + 'TriggerOnObjectCreate' => $baseDir . '/core/trigger.class.inc.php', + 'TriggerOnObjectDelete' => $baseDir . '/core/trigger.class.inc.php', + 'TriggerOnObjectUpdate' => $baseDir . '/core/trigger.class.inc.php', + 'TriggerOnPortalUpdate' => $baseDir . '/core/trigger.class.inc.php', + 'TriggerOnStateChange' => $baseDir . '/core/trigger.class.inc.php', + 'TriggerOnStateEnter' => $baseDir . '/core/trigger.class.inc.php', + 'TriggerOnStateLeave' => $baseDir . '/core/trigger.class.inc.php', + 'TriggerOnThresholdReached' => $baseDir . '/core/trigger.class.inc.php', + 'TrueExpression' => $baseDir . '/core/oql/expression.class.inc.php', + 'Twig\\Cache\\CacheInterface' => $vendorDir . '/twig/twig/src/Cache/CacheInterface.php', + 'Twig\\Cache\\FilesystemCache' => $vendorDir . '/twig/twig/src/Cache/FilesystemCache.php', + 'Twig\\Cache\\NullCache' => $vendorDir . '/twig/twig/src/Cache/NullCache.php', + 'Twig\\Compiler' => $vendorDir . '/twig/twig/src/Compiler.php', + 'Twig\\Environment' => $vendorDir . '/twig/twig/src/Environment.php', + 'Twig\\Error\\Error' => $vendorDir . '/twig/twig/src/Error/Error.php', + 'Twig\\Error\\LoaderError' => $vendorDir . '/twig/twig/src/Error/LoaderError.php', + 'Twig\\Error\\RuntimeError' => $vendorDir . '/twig/twig/src/Error/RuntimeError.php', + 'Twig\\Error\\SyntaxError' => $vendorDir . '/twig/twig/src/Error/SyntaxError.php', + 'Twig\\ExpressionParser' => $vendorDir . '/twig/twig/src/ExpressionParser.php', + 'Twig\\Extension\\AbstractExtension' => $vendorDir . '/twig/twig/src/Extension/AbstractExtension.php', + 'Twig\\Extension\\CoreExtension' => $vendorDir . '/twig/twig/src/Extension/CoreExtension.php', + 'Twig\\Extension\\DebugExtension' => $vendorDir . '/twig/twig/src/Extension/DebugExtension.php', + 'Twig\\Extension\\EscaperExtension' => $vendorDir . '/twig/twig/src/Extension/EscaperExtension.php', + 'Twig\\Extension\\ExtensionInterface' => $vendorDir . '/twig/twig/src/Extension/ExtensionInterface.php', + 'Twig\\Extension\\GlobalsInterface' => $vendorDir . '/twig/twig/src/Extension/GlobalsInterface.php', + 'Twig\\Extension\\InitRuntimeInterface' => $vendorDir . '/twig/twig/src/Extension/InitRuntimeInterface.php', + 'Twig\\Extension\\OptimizerExtension' => $vendorDir . '/twig/twig/src/Extension/OptimizerExtension.php', + 'Twig\\Extension\\ProfilerExtension' => $vendorDir . '/twig/twig/src/Extension/ProfilerExtension.php', + 'Twig\\Extension\\RuntimeExtensionInterface' => $vendorDir . '/twig/twig/src/Extension/RuntimeExtensionInterface.php', + 'Twig\\Extension\\SandboxExtension' => $vendorDir . '/twig/twig/src/Extension/SandboxExtension.php', + 'Twig\\Extension\\StagingExtension' => $vendorDir . '/twig/twig/src/Extension/StagingExtension.php', + 'Twig\\Extension\\StringLoaderExtension' => $vendorDir . '/twig/twig/src/Extension/StringLoaderExtension.php', + 'Twig\\FileExtensionEscapingStrategy' => $vendorDir . '/twig/twig/src/FileExtensionEscapingStrategy.php', + 'Twig\\Lexer' => $vendorDir . '/twig/twig/src/Lexer.php', + 'Twig\\Loader\\ArrayLoader' => $vendorDir . '/twig/twig/src/Loader/ArrayLoader.php', + 'Twig\\Loader\\ChainLoader' => $vendorDir . '/twig/twig/src/Loader/ChainLoader.php', + 'Twig\\Loader\\ExistsLoaderInterface' => $vendorDir . '/twig/twig/src/Loader/ExistsLoaderInterface.php', + 'Twig\\Loader\\FilesystemLoader' => $vendorDir . '/twig/twig/src/Loader/FilesystemLoader.php', + 'Twig\\Loader\\LoaderInterface' => $vendorDir . '/twig/twig/src/Loader/LoaderInterface.php', + 'Twig\\Loader\\SourceContextLoaderInterface' => $vendorDir . '/twig/twig/src/Loader/SourceContextLoaderInterface.php', + 'Twig\\Markup' => $vendorDir . '/twig/twig/src/Markup.php', + 'Twig\\NodeTraverser' => $vendorDir . '/twig/twig/src/NodeTraverser.php', + 'Twig\\NodeVisitor\\AbstractNodeVisitor' => $vendorDir . '/twig/twig/src/NodeVisitor/AbstractNodeVisitor.php', + 'Twig\\NodeVisitor\\EscaperNodeVisitor' => $vendorDir . '/twig/twig/src/NodeVisitor/EscaperNodeVisitor.php', + 'Twig\\NodeVisitor\\NodeVisitorInterface' => $vendorDir . '/twig/twig/src/NodeVisitor/NodeVisitorInterface.php', + 'Twig\\NodeVisitor\\OptimizerNodeVisitor' => $vendorDir . '/twig/twig/src/NodeVisitor/OptimizerNodeVisitor.php', + 'Twig\\NodeVisitor\\SafeAnalysisNodeVisitor' => $vendorDir . '/twig/twig/src/NodeVisitor/SafeAnalysisNodeVisitor.php', + 'Twig\\NodeVisitor\\SandboxNodeVisitor' => $vendorDir . '/twig/twig/src/NodeVisitor/SandboxNodeVisitor.php', + 'Twig\\Node\\AutoEscapeNode' => $vendorDir . '/twig/twig/src/Node/AutoEscapeNode.php', + 'Twig\\Node\\BlockNode' => $vendorDir . '/twig/twig/src/Node/BlockNode.php', + 'Twig\\Node\\BlockReferenceNode' => $vendorDir . '/twig/twig/src/Node/BlockReferenceNode.php', + 'Twig\\Node\\BodyNode' => $vendorDir . '/twig/twig/src/Node/BodyNode.php', + 'Twig\\Node\\CheckSecurityNode' => $vendorDir . '/twig/twig/src/Node/CheckSecurityNode.php', + 'Twig\\Node\\CheckToStringNode' => $vendorDir . '/twig/twig/src/Node/CheckToStringNode.php', + 'Twig\\Node\\DeprecatedNode' => $vendorDir . '/twig/twig/src/Node/DeprecatedNode.php', + 'Twig\\Node\\DoNode' => $vendorDir . '/twig/twig/src/Node/DoNode.php', + 'Twig\\Node\\EmbedNode' => $vendorDir . '/twig/twig/src/Node/EmbedNode.php', + 'Twig\\Node\\Expression\\AbstractExpression' => $vendorDir . '/twig/twig/src/Node/Expression/AbstractExpression.php', + 'Twig\\Node\\Expression\\ArrayExpression' => $vendorDir . '/twig/twig/src/Node/Expression/ArrayExpression.php', + 'Twig\\Node\\Expression\\ArrowFunctionExpression' => $vendorDir . '/twig/twig/src/Node/Expression/ArrowFunctionExpression.php', + 'Twig\\Node\\Expression\\AssignNameExpression' => $vendorDir . '/twig/twig/src/Node/Expression/AssignNameExpression.php', + 'Twig\\Node\\Expression\\Binary\\AbstractBinary' => $vendorDir . '/twig/twig/src/Node/Expression/Binary/AbstractBinary.php', + 'Twig\\Node\\Expression\\Binary\\AddBinary' => $vendorDir . '/twig/twig/src/Node/Expression/Binary/AddBinary.php', + 'Twig\\Node\\Expression\\Binary\\AndBinary' => $vendorDir . '/twig/twig/src/Node/Expression/Binary/AndBinary.php', + 'Twig\\Node\\Expression\\Binary\\BitwiseAndBinary' => $vendorDir . '/twig/twig/src/Node/Expression/Binary/BitwiseAndBinary.php', + 'Twig\\Node\\Expression\\Binary\\BitwiseOrBinary' => $vendorDir . '/twig/twig/src/Node/Expression/Binary/BitwiseOrBinary.php', + 'Twig\\Node\\Expression\\Binary\\BitwiseXorBinary' => $vendorDir . '/twig/twig/src/Node/Expression/Binary/BitwiseXorBinary.php', + 'Twig\\Node\\Expression\\Binary\\ConcatBinary' => $vendorDir . '/twig/twig/src/Node/Expression/Binary/ConcatBinary.php', + 'Twig\\Node\\Expression\\Binary\\DivBinary' => $vendorDir . '/twig/twig/src/Node/Expression/Binary/DivBinary.php', + 'Twig\\Node\\Expression\\Binary\\EndsWithBinary' => $vendorDir . '/twig/twig/src/Node/Expression/Binary/EndsWithBinary.php', + 'Twig\\Node\\Expression\\Binary\\EqualBinary' => $vendorDir . '/twig/twig/src/Node/Expression/Binary/EqualBinary.php', + 'Twig\\Node\\Expression\\Binary\\FloorDivBinary' => $vendorDir . '/twig/twig/src/Node/Expression/Binary/FloorDivBinary.php', + 'Twig\\Node\\Expression\\Binary\\GreaterBinary' => $vendorDir . '/twig/twig/src/Node/Expression/Binary/GreaterBinary.php', + 'Twig\\Node\\Expression\\Binary\\GreaterEqualBinary' => $vendorDir . '/twig/twig/src/Node/Expression/Binary/GreaterEqualBinary.php', + 'Twig\\Node\\Expression\\Binary\\InBinary' => $vendorDir . '/twig/twig/src/Node/Expression/Binary/InBinary.php', + 'Twig\\Node\\Expression\\Binary\\LessBinary' => $vendorDir . '/twig/twig/src/Node/Expression/Binary/LessBinary.php', + 'Twig\\Node\\Expression\\Binary\\LessEqualBinary' => $vendorDir . '/twig/twig/src/Node/Expression/Binary/LessEqualBinary.php', + 'Twig\\Node\\Expression\\Binary\\MatchesBinary' => $vendorDir . '/twig/twig/src/Node/Expression/Binary/MatchesBinary.php', + 'Twig\\Node\\Expression\\Binary\\ModBinary' => $vendorDir . '/twig/twig/src/Node/Expression/Binary/ModBinary.php', + 'Twig\\Node\\Expression\\Binary\\MulBinary' => $vendorDir . '/twig/twig/src/Node/Expression/Binary/MulBinary.php', + 'Twig\\Node\\Expression\\Binary\\NotEqualBinary' => $vendorDir . '/twig/twig/src/Node/Expression/Binary/NotEqualBinary.php', + 'Twig\\Node\\Expression\\Binary\\NotInBinary' => $vendorDir . '/twig/twig/src/Node/Expression/Binary/NotInBinary.php', + 'Twig\\Node\\Expression\\Binary\\OrBinary' => $vendorDir . '/twig/twig/src/Node/Expression/Binary/OrBinary.php', + 'Twig\\Node\\Expression\\Binary\\PowerBinary' => $vendorDir . '/twig/twig/src/Node/Expression/Binary/PowerBinary.php', + 'Twig\\Node\\Expression\\Binary\\RangeBinary' => $vendorDir . '/twig/twig/src/Node/Expression/Binary/RangeBinary.php', + 'Twig\\Node\\Expression\\Binary\\StartsWithBinary' => $vendorDir . '/twig/twig/src/Node/Expression/Binary/StartsWithBinary.php', + 'Twig\\Node\\Expression\\Binary\\SubBinary' => $vendorDir . '/twig/twig/src/Node/Expression/Binary/SubBinary.php', + 'Twig\\Node\\Expression\\BlockReferenceExpression' => $vendorDir . '/twig/twig/src/Node/Expression/BlockReferenceExpression.php', + 'Twig\\Node\\Expression\\CallExpression' => $vendorDir . '/twig/twig/src/Node/Expression/CallExpression.php', + 'Twig\\Node\\Expression\\ConditionalExpression' => $vendorDir . '/twig/twig/src/Node/Expression/ConditionalExpression.php', + 'Twig\\Node\\Expression\\ConstantExpression' => $vendorDir . '/twig/twig/src/Node/Expression/ConstantExpression.php', + 'Twig\\Node\\Expression\\FilterExpression' => $vendorDir . '/twig/twig/src/Node/Expression/FilterExpression.php', + 'Twig\\Node\\Expression\\Filter\\DefaultFilter' => $vendorDir . '/twig/twig/src/Node/Expression/Filter/DefaultFilter.php', + 'Twig\\Node\\Expression\\FunctionExpression' => $vendorDir . '/twig/twig/src/Node/Expression/FunctionExpression.php', + 'Twig\\Node\\Expression\\GetAttrExpression' => $vendorDir . '/twig/twig/src/Node/Expression/GetAttrExpression.php', + 'Twig\\Node\\Expression\\InlinePrint' => $vendorDir . '/twig/twig/src/Node/Expression/InlinePrint.php', + 'Twig\\Node\\Expression\\MethodCallExpression' => $vendorDir . '/twig/twig/src/Node/Expression/MethodCallExpression.php', + 'Twig\\Node\\Expression\\NameExpression' => $vendorDir . '/twig/twig/src/Node/Expression/NameExpression.php', + 'Twig\\Node\\Expression\\NullCoalesceExpression' => $vendorDir . '/twig/twig/src/Node/Expression/NullCoalesceExpression.php', + 'Twig\\Node\\Expression\\ParentExpression' => $vendorDir . '/twig/twig/src/Node/Expression/ParentExpression.php', + 'Twig\\Node\\Expression\\TempNameExpression' => $vendorDir . '/twig/twig/src/Node/Expression/TempNameExpression.php', + 'Twig\\Node\\Expression\\TestExpression' => $vendorDir . '/twig/twig/src/Node/Expression/TestExpression.php', + 'Twig\\Node\\Expression\\Test\\ConstantTest' => $vendorDir . '/twig/twig/src/Node/Expression/Test/ConstantTest.php', + 'Twig\\Node\\Expression\\Test\\DefinedTest' => $vendorDir . '/twig/twig/src/Node/Expression/Test/DefinedTest.php', + 'Twig\\Node\\Expression\\Test\\DivisiblebyTest' => $vendorDir . '/twig/twig/src/Node/Expression/Test/DivisiblebyTest.php', + 'Twig\\Node\\Expression\\Test\\EvenTest' => $vendorDir . '/twig/twig/src/Node/Expression/Test/EvenTest.php', + 'Twig\\Node\\Expression\\Test\\NullTest' => $vendorDir . '/twig/twig/src/Node/Expression/Test/NullTest.php', + 'Twig\\Node\\Expression\\Test\\OddTest' => $vendorDir . '/twig/twig/src/Node/Expression/Test/OddTest.php', + 'Twig\\Node\\Expression\\Test\\SameasTest' => $vendorDir . '/twig/twig/src/Node/Expression/Test/SameasTest.php', + 'Twig\\Node\\Expression\\Unary\\AbstractUnary' => $vendorDir . '/twig/twig/src/Node/Expression/Unary/AbstractUnary.php', + 'Twig\\Node\\Expression\\Unary\\NegUnary' => $vendorDir . '/twig/twig/src/Node/Expression/Unary/NegUnary.php', + 'Twig\\Node\\Expression\\Unary\\NotUnary' => $vendorDir . '/twig/twig/src/Node/Expression/Unary/NotUnary.php', + 'Twig\\Node\\Expression\\Unary\\PosUnary' => $vendorDir . '/twig/twig/src/Node/Expression/Unary/PosUnary.php', + 'Twig\\Node\\FlushNode' => $vendorDir . '/twig/twig/src/Node/FlushNode.php', + 'Twig\\Node\\ForLoopNode' => $vendorDir . '/twig/twig/src/Node/ForLoopNode.php', + 'Twig\\Node\\ForNode' => $vendorDir . '/twig/twig/src/Node/ForNode.php', + 'Twig\\Node\\IfNode' => $vendorDir . '/twig/twig/src/Node/IfNode.php', + 'Twig\\Node\\ImportNode' => $vendorDir . '/twig/twig/src/Node/ImportNode.php', + 'Twig\\Node\\IncludeNode' => $vendorDir . '/twig/twig/src/Node/IncludeNode.php', + 'Twig\\Node\\MacroNode' => $vendorDir . '/twig/twig/src/Node/MacroNode.php', + 'Twig\\Node\\ModuleNode' => $vendorDir . '/twig/twig/src/Node/ModuleNode.php', + 'Twig\\Node\\Node' => $vendorDir . '/twig/twig/src/Node/Node.php', + 'Twig\\Node\\NodeCaptureInterface' => $vendorDir . '/twig/twig/src/Node/NodeCaptureInterface.php', + 'Twig\\Node\\NodeOutputInterface' => $vendorDir . '/twig/twig/src/Node/NodeOutputInterface.php', + 'Twig\\Node\\PrintNode' => $vendorDir . '/twig/twig/src/Node/PrintNode.php', + 'Twig\\Node\\SandboxNode' => $vendorDir . '/twig/twig/src/Node/SandboxNode.php', + 'Twig\\Node\\SandboxedPrintNode' => $vendorDir . '/twig/twig/src/Node/SandboxedPrintNode.php', + 'Twig\\Node\\SetNode' => $vendorDir . '/twig/twig/src/Node/SetNode.php', + 'Twig\\Node\\SetTempNode' => $vendorDir . '/twig/twig/src/Node/SetTempNode.php', + 'Twig\\Node\\SpacelessNode' => $vendorDir . '/twig/twig/src/Node/SpacelessNode.php', + 'Twig\\Node\\TextNode' => $vendorDir . '/twig/twig/src/Node/TextNode.php', + 'Twig\\Node\\WithNode' => $vendorDir . '/twig/twig/src/Node/WithNode.php', + 'Twig\\Parser' => $vendorDir . '/twig/twig/src/Parser.php', + 'Twig\\Profiler\\Dumper\\BaseDumper' => $vendorDir . '/twig/twig/src/Profiler/Dumper/BaseDumper.php', + 'Twig\\Profiler\\Dumper\\BlackfireDumper' => $vendorDir . '/twig/twig/src/Profiler/Dumper/BlackfireDumper.php', + 'Twig\\Profiler\\Dumper\\HtmlDumper' => $vendorDir . '/twig/twig/src/Profiler/Dumper/HtmlDumper.php', + 'Twig\\Profiler\\Dumper\\TextDumper' => $vendorDir . '/twig/twig/src/Profiler/Dumper/TextDumper.php', + 'Twig\\Profiler\\NodeVisitor\\ProfilerNodeVisitor' => $vendorDir . '/twig/twig/src/Profiler/NodeVisitor/ProfilerNodeVisitor.php', + 'Twig\\Profiler\\Node\\EnterProfileNode' => $vendorDir . '/twig/twig/src/Profiler/Node/EnterProfileNode.php', + 'Twig\\Profiler\\Node\\LeaveProfileNode' => $vendorDir . '/twig/twig/src/Profiler/Node/LeaveProfileNode.php', + 'Twig\\Profiler\\Profile' => $vendorDir . '/twig/twig/src/Profiler/Profile.php', + 'Twig\\RuntimeLoader\\ContainerRuntimeLoader' => $vendorDir . '/twig/twig/src/RuntimeLoader/ContainerRuntimeLoader.php', + 'Twig\\RuntimeLoader\\FactoryRuntimeLoader' => $vendorDir . '/twig/twig/src/RuntimeLoader/FactoryRuntimeLoader.php', + 'Twig\\RuntimeLoader\\RuntimeLoaderInterface' => $vendorDir . '/twig/twig/src/RuntimeLoader/RuntimeLoaderInterface.php', + 'Twig\\Sandbox\\SecurityError' => $vendorDir . '/twig/twig/src/Sandbox/SecurityError.php', + 'Twig\\Sandbox\\SecurityNotAllowedFilterError' => $vendorDir . '/twig/twig/src/Sandbox/SecurityNotAllowedFilterError.php', + 'Twig\\Sandbox\\SecurityNotAllowedFunctionError' => $vendorDir . '/twig/twig/src/Sandbox/SecurityNotAllowedFunctionError.php', + 'Twig\\Sandbox\\SecurityNotAllowedMethodError' => $vendorDir . '/twig/twig/src/Sandbox/SecurityNotAllowedMethodError.php', + 'Twig\\Sandbox\\SecurityNotAllowedPropertyError' => $vendorDir . '/twig/twig/src/Sandbox/SecurityNotAllowedPropertyError.php', + 'Twig\\Sandbox\\SecurityNotAllowedTagError' => $vendorDir . '/twig/twig/src/Sandbox/SecurityNotAllowedTagError.php', + 'Twig\\Sandbox\\SecurityPolicy' => $vendorDir . '/twig/twig/src/Sandbox/SecurityPolicy.php', + 'Twig\\Sandbox\\SecurityPolicyInterface' => $vendorDir . '/twig/twig/src/Sandbox/SecurityPolicyInterface.php', + 'Twig\\Source' => $vendorDir . '/twig/twig/src/Source.php', + 'Twig\\Template' => $vendorDir . '/twig/twig/src/Template.php', + 'Twig\\TemplateWrapper' => $vendorDir . '/twig/twig/src/TemplateWrapper.php', + 'Twig\\Test\\IntegrationTestCase' => $vendorDir . '/twig/twig/src/Test/IntegrationTestCase.php', + 'Twig\\Test\\NodeTestCase' => $vendorDir . '/twig/twig/src/Test/NodeTestCase.php', + 'Twig\\Token' => $vendorDir . '/twig/twig/src/Token.php', + 'Twig\\TokenParser\\AbstractTokenParser' => $vendorDir . '/twig/twig/src/TokenParser/AbstractTokenParser.php', + 'Twig\\TokenParser\\ApplyTokenParser' => $vendorDir . '/twig/twig/src/TokenParser/ApplyTokenParser.php', + 'Twig\\TokenParser\\AutoEscapeTokenParser' => $vendorDir . '/twig/twig/src/TokenParser/AutoEscapeTokenParser.php', + 'Twig\\TokenParser\\BlockTokenParser' => $vendorDir . '/twig/twig/src/TokenParser/BlockTokenParser.php', + 'Twig\\TokenParser\\DeprecatedTokenParser' => $vendorDir . '/twig/twig/src/TokenParser/DeprecatedTokenParser.php', + 'Twig\\TokenParser\\DoTokenParser' => $vendorDir . '/twig/twig/src/TokenParser/DoTokenParser.php', + 'Twig\\TokenParser\\EmbedTokenParser' => $vendorDir . '/twig/twig/src/TokenParser/EmbedTokenParser.php', + 'Twig\\TokenParser\\ExtendsTokenParser' => $vendorDir . '/twig/twig/src/TokenParser/ExtendsTokenParser.php', + 'Twig\\TokenParser\\FilterTokenParser' => $vendorDir . '/twig/twig/src/TokenParser/FilterTokenParser.php', + 'Twig\\TokenParser\\FlushTokenParser' => $vendorDir . '/twig/twig/src/TokenParser/FlushTokenParser.php', + 'Twig\\TokenParser\\ForTokenParser' => $vendorDir . '/twig/twig/src/TokenParser/ForTokenParser.php', + 'Twig\\TokenParser\\FromTokenParser' => $vendorDir . '/twig/twig/src/TokenParser/FromTokenParser.php', + 'Twig\\TokenParser\\IfTokenParser' => $vendorDir . '/twig/twig/src/TokenParser/IfTokenParser.php', + 'Twig\\TokenParser\\ImportTokenParser' => $vendorDir . '/twig/twig/src/TokenParser/ImportTokenParser.php', + 'Twig\\TokenParser\\IncludeTokenParser' => $vendorDir . '/twig/twig/src/TokenParser/IncludeTokenParser.php', + 'Twig\\TokenParser\\MacroTokenParser' => $vendorDir . '/twig/twig/src/TokenParser/MacroTokenParser.php', + 'Twig\\TokenParser\\SandboxTokenParser' => $vendorDir . '/twig/twig/src/TokenParser/SandboxTokenParser.php', + 'Twig\\TokenParser\\SetTokenParser' => $vendorDir . '/twig/twig/src/TokenParser/SetTokenParser.php', + 'Twig\\TokenParser\\SpacelessTokenParser' => $vendorDir . '/twig/twig/src/TokenParser/SpacelessTokenParser.php', + 'Twig\\TokenParser\\TokenParserInterface' => $vendorDir . '/twig/twig/src/TokenParser/TokenParserInterface.php', + 'Twig\\TokenParser\\UseTokenParser' => $vendorDir . '/twig/twig/src/TokenParser/UseTokenParser.php', + 'Twig\\TokenParser\\WithTokenParser' => $vendorDir . '/twig/twig/src/TokenParser/WithTokenParser.php', + 'Twig\\TokenStream' => $vendorDir . '/twig/twig/src/TokenStream.php', + 'Twig\\TwigFilter' => $vendorDir . '/twig/twig/src/TwigFilter.php', + 'Twig\\TwigFunction' => $vendorDir . '/twig/twig/src/TwigFunction.php', + 'Twig\\TwigTest' => $vendorDir . '/twig/twig/src/TwigTest.php', + 'Twig\\Util\\DeprecationCollector' => $vendorDir . '/twig/twig/src/Util/DeprecationCollector.php', + 'Twig\\Util\\TemplateDirIterator' => $vendorDir . '/twig/twig/src/Util/TemplateDirIterator.php', + 'Twig_Autoloader' => $vendorDir . '/twig/twig/lib/Twig/Autoloader.php', + 'Twig_BaseNodeVisitor' => $vendorDir . '/twig/twig/lib/Twig/BaseNodeVisitor.php', + 'Twig_CacheInterface' => $vendorDir . '/twig/twig/lib/Twig/CacheInterface.php', + 'Twig_Cache_Filesystem' => $vendorDir . '/twig/twig/lib/Twig/Cache/Filesystem.php', + 'Twig_Cache_Null' => $vendorDir . '/twig/twig/lib/Twig/Cache/Null.php', + 'Twig_Compiler' => $vendorDir . '/twig/twig/lib/Twig/Compiler.php', + 'Twig_CompilerInterface' => $vendorDir . '/twig/twig/lib/Twig/CompilerInterface.php', + 'Twig_ContainerRuntimeLoader' => $vendorDir . '/twig/twig/lib/Twig/ContainerRuntimeLoader.php', + 'Twig_Environment' => $vendorDir . '/twig/twig/lib/Twig/Environment.php', + 'Twig_Error' => $vendorDir . '/twig/twig/lib/Twig/Error.php', + 'Twig_Error_Loader' => $vendorDir . '/twig/twig/lib/Twig/Error/Loader.php', + 'Twig_Error_Runtime' => $vendorDir . '/twig/twig/lib/Twig/Error/Runtime.php', + 'Twig_Error_Syntax' => $vendorDir . '/twig/twig/lib/Twig/Error/Syntax.php', + 'Twig_ExistsLoaderInterface' => $vendorDir . '/twig/twig/lib/Twig/ExistsLoaderInterface.php', + 'Twig_ExpressionParser' => $vendorDir . '/twig/twig/lib/Twig/ExpressionParser.php', + 'Twig_Extension' => $vendorDir . '/twig/twig/lib/Twig/Extension.php', + 'Twig_ExtensionInterface' => $vendorDir . '/twig/twig/lib/Twig/ExtensionInterface.php', + 'Twig_Extension_Core' => $vendorDir . '/twig/twig/lib/Twig/Extension/Core.php', + 'Twig_Extension_Debug' => $vendorDir . '/twig/twig/lib/Twig/Extension/Debug.php', + 'Twig_Extension_Escaper' => $vendorDir . '/twig/twig/lib/Twig/Extension/Escaper.php', + 'Twig_Extension_GlobalsInterface' => $vendorDir . '/twig/twig/lib/Twig/Extension/GlobalsInterface.php', + 'Twig_Extension_InitRuntimeInterface' => $vendorDir . '/twig/twig/lib/Twig/Extension/InitRuntimeInterface.php', + 'Twig_Extension_Optimizer' => $vendorDir . '/twig/twig/lib/Twig/Extension/Optimizer.php', + 'Twig_Extension_Profiler' => $vendorDir . '/twig/twig/lib/Twig/Extension/Profiler.php', + 'Twig_Extension_Sandbox' => $vendorDir . '/twig/twig/lib/Twig/Extension/Sandbox.php', + 'Twig_Extension_Staging' => $vendorDir . '/twig/twig/lib/Twig/Extension/Staging.php', + 'Twig_Extension_StringLoader' => $vendorDir . '/twig/twig/lib/Twig/Extension/StringLoader.php', + 'Twig_FactoryRuntimeLoader' => $vendorDir . '/twig/twig/lib/Twig/FactoryRuntimeLoader.php', + 'Twig_FileExtensionEscapingStrategy' => $vendorDir . '/twig/twig/lib/Twig/FileExtensionEscapingStrategy.php', + 'Twig_Filter' => $vendorDir . '/twig/twig/lib/Twig/Filter.php', + 'Twig_FilterCallableInterface' => $vendorDir . '/twig/twig/lib/Twig/FilterCallableInterface.php', + 'Twig_FilterInterface' => $vendorDir . '/twig/twig/lib/Twig/FilterInterface.php', + 'Twig_Filter_Function' => $vendorDir . '/twig/twig/lib/Twig/Filter/Function.php', + 'Twig_Filter_Method' => $vendorDir . '/twig/twig/lib/Twig/Filter/Method.php', + 'Twig_Filter_Node' => $vendorDir . '/twig/twig/lib/Twig/Filter/Node.php', + 'Twig_Function' => $vendorDir . '/twig/twig/lib/Twig/Function.php', + 'Twig_FunctionCallableInterface' => $vendorDir . '/twig/twig/lib/Twig/FunctionCallableInterface.php', + 'Twig_FunctionInterface' => $vendorDir . '/twig/twig/lib/Twig/FunctionInterface.php', + 'Twig_Function_Function' => $vendorDir . '/twig/twig/lib/Twig/Function/Function.php', + 'Twig_Function_Method' => $vendorDir . '/twig/twig/lib/Twig/Function/Method.php', + 'Twig_Function_Node' => $vendorDir . '/twig/twig/lib/Twig/Function/Node.php', + 'Twig_Lexer' => $vendorDir . '/twig/twig/lib/Twig/Lexer.php', + 'Twig_LexerInterface' => $vendorDir . '/twig/twig/lib/Twig/LexerInterface.php', + 'Twig_LoaderInterface' => $vendorDir . '/twig/twig/lib/Twig/LoaderInterface.php', + 'Twig_Loader_Array' => $vendorDir . '/twig/twig/lib/Twig/Loader/Array.php', + 'Twig_Loader_Chain' => $vendorDir . '/twig/twig/lib/Twig/Loader/Chain.php', + 'Twig_Loader_Filesystem' => $vendorDir . '/twig/twig/lib/Twig/Loader/Filesystem.php', + 'Twig_Loader_String' => $vendorDir . '/twig/twig/lib/Twig/Loader/String.php', + 'Twig_Markup' => $vendorDir . '/twig/twig/lib/Twig/Markup.php', + 'Twig_Node' => $vendorDir . '/twig/twig/lib/Twig/Node.php', + 'Twig_NodeCaptureInterface' => $vendorDir . '/twig/twig/lib/Twig/NodeCaptureInterface.php', + 'Twig_NodeInterface' => $vendorDir . '/twig/twig/lib/Twig/NodeInterface.php', + 'Twig_NodeOutputInterface' => $vendorDir . '/twig/twig/lib/Twig/NodeOutputInterface.php', + 'Twig_NodeTraverser' => $vendorDir . '/twig/twig/lib/Twig/NodeTraverser.php', + 'Twig_NodeVisitorInterface' => $vendorDir . '/twig/twig/lib/Twig/NodeVisitorInterface.php', + 'Twig_NodeVisitor_Escaper' => $vendorDir . '/twig/twig/lib/Twig/NodeVisitor/Escaper.php', + 'Twig_NodeVisitor_Optimizer' => $vendorDir . '/twig/twig/lib/Twig/NodeVisitor/Optimizer.php', + 'Twig_NodeVisitor_SafeAnalysis' => $vendorDir . '/twig/twig/lib/Twig/NodeVisitor/SafeAnalysis.php', + 'Twig_NodeVisitor_Sandbox' => $vendorDir . '/twig/twig/lib/Twig/NodeVisitor/Sandbox.php', + 'Twig_Node_AutoEscape' => $vendorDir . '/twig/twig/lib/Twig/Node/AutoEscape.php', + 'Twig_Node_Block' => $vendorDir . '/twig/twig/lib/Twig/Node/Block.php', + 'Twig_Node_BlockReference' => $vendorDir . '/twig/twig/lib/Twig/Node/BlockReference.php', + 'Twig_Node_Body' => $vendorDir . '/twig/twig/lib/Twig/Node/Body.php', + 'Twig_Node_CheckSecurity' => $vendorDir . '/twig/twig/lib/Twig/Node/CheckSecurity.php', + 'Twig_Node_Deprecated' => $vendorDir . '/twig/twig/lib/Twig/Node/Deprecated.php', + 'Twig_Node_Do' => $vendorDir . '/twig/twig/lib/Twig/Node/Do.php', + 'Twig_Node_Embed' => $vendorDir . '/twig/twig/lib/Twig/Node/Embed.php', + 'Twig_Node_Expression' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression.php', + 'Twig_Node_Expression_Array' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Array.php', + 'Twig_Node_Expression_AssignName' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/AssignName.php', + 'Twig_Node_Expression_Binary' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary.php', + 'Twig_Node_Expression_Binary_Add' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/Add.php', + 'Twig_Node_Expression_Binary_And' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/And.php', + 'Twig_Node_Expression_Binary_BitwiseAnd' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/BitwiseAnd.php', + 'Twig_Node_Expression_Binary_BitwiseOr' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/BitwiseOr.php', + 'Twig_Node_Expression_Binary_BitwiseXor' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/BitwiseXor.php', + 'Twig_Node_Expression_Binary_Concat' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/Concat.php', + 'Twig_Node_Expression_Binary_Div' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/Div.php', + 'Twig_Node_Expression_Binary_EndsWith' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/EndsWith.php', + 'Twig_Node_Expression_Binary_Equal' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/Equal.php', + 'Twig_Node_Expression_Binary_FloorDiv' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/FloorDiv.php', + 'Twig_Node_Expression_Binary_Greater' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/Greater.php', + 'Twig_Node_Expression_Binary_GreaterEqual' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/GreaterEqual.php', + 'Twig_Node_Expression_Binary_In' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/In.php', + 'Twig_Node_Expression_Binary_Less' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/Less.php', + 'Twig_Node_Expression_Binary_LessEqual' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/LessEqual.php', + 'Twig_Node_Expression_Binary_Matches' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/Matches.php', + 'Twig_Node_Expression_Binary_Mod' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/Mod.php', + 'Twig_Node_Expression_Binary_Mul' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/Mul.php', + 'Twig_Node_Expression_Binary_NotEqual' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/NotEqual.php', + 'Twig_Node_Expression_Binary_NotIn' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/NotIn.php', + 'Twig_Node_Expression_Binary_Or' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/Or.php', + 'Twig_Node_Expression_Binary_Power' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/Power.php', + 'Twig_Node_Expression_Binary_Range' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/Range.php', + 'Twig_Node_Expression_Binary_StartsWith' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/StartsWith.php', + 'Twig_Node_Expression_Binary_Sub' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/Sub.php', + 'Twig_Node_Expression_BlockReference' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/BlockReference.php', + 'Twig_Node_Expression_Call' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Call.php', + 'Twig_Node_Expression_Conditional' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Conditional.php', + 'Twig_Node_Expression_Constant' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Constant.php', + 'Twig_Node_Expression_ExtensionReference' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/ExtensionReference.php', + 'Twig_Node_Expression_Filter' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Filter.php', + 'Twig_Node_Expression_Filter_Default' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Filter/Default.php', + 'Twig_Node_Expression_Function' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Function.php', + 'Twig_Node_Expression_GetAttr' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/GetAttr.php', + 'Twig_Node_Expression_MethodCall' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/MethodCall.php', + 'Twig_Node_Expression_Name' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Name.php', + 'Twig_Node_Expression_NullCoalesce' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/NullCoalesce.php', + 'Twig_Node_Expression_Parent' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Parent.php', + 'Twig_Node_Expression_TempName' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/TempName.php', + 'Twig_Node_Expression_Test' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Test.php', + 'Twig_Node_Expression_Test_Constant' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Test/Constant.php', + 'Twig_Node_Expression_Test_Defined' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Test/Defined.php', + 'Twig_Node_Expression_Test_Divisibleby' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Test/Divisibleby.php', + 'Twig_Node_Expression_Test_Even' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Test/Even.php', + 'Twig_Node_Expression_Test_Null' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Test/Null.php', + 'Twig_Node_Expression_Test_Odd' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Test/Odd.php', + 'Twig_Node_Expression_Test_Sameas' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Test/Sameas.php', + 'Twig_Node_Expression_Unary' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Unary.php', + 'Twig_Node_Expression_Unary_Neg' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Unary/Neg.php', + 'Twig_Node_Expression_Unary_Not' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Unary/Not.php', + 'Twig_Node_Expression_Unary_Pos' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Unary/Pos.php', + 'Twig_Node_Flush' => $vendorDir . '/twig/twig/lib/Twig/Node/Flush.php', + 'Twig_Node_For' => $vendorDir . '/twig/twig/lib/Twig/Node/For.php', + 'Twig_Node_ForLoop' => $vendorDir . '/twig/twig/lib/Twig/Node/ForLoop.php', + 'Twig_Node_If' => $vendorDir . '/twig/twig/lib/Twig/Node/If.php', + 'Twig_Node_Import' => $vendorDir . '/twig/twig/lib/Twig/Node/Import.php', + 'Twig_Node_Include' => $vendorDir . '/twig/twig/lib/Twig/Node/Include.php', + 'Twig_Node_Macro' => $vendorDir . '/twig/twig/lib/Twig/Node/Macro.php', + 'Twig_Node_Module' => $vendorDir . '/twig/twig/lib/Twig/Node/Module.php', + 'Twig_Node_Print' => $vendorDir . '/twig/twig/lib/Twig/Node/Print.php', + 'Twig_Node_Sandbox' => $vendorDir . '/twig/twig/lib/Twig/Node/Sandbox.php', + 'Twig_Node_SandboxedPrint' => $vendorDir . '/twig/twig/lib/Twig/Node/SandboxedPrint.php', + 'Twig_Node_Set' => $vendorDir . '/twig/twig/lib/Twig/Node/Set.php', + 'Twig_Node_SetTemp' => $vendorDir . '/twig/twig/lib/Twig/Node/SetTemp.php', + 'Twig_Node_Spaceless' => $vendorDir . '/twig/twig/lib/Twig/Node/Spaceless.php', + 'Twig_Node_Text' => $vendorDir . '/twig/twig/lib/Twig/Node/Text.php', + 'Twig_Node_With' => $vendorDir . '/twig/twig/lib/Twig/Node/With.php', + 'Twig_Parser' => $vendorDir . '/twig/twig/lib/Twig/Parser.php', + 'Twig_ParserInterface' => $vendorDir . '/twig/twig/lib/Twig/ParserInterface.php', + 'Twig_Profiler_Dumper_Base' => $vendorDir . '/twig/twig/lib/Twig/Profiler/Dumper/Base.php', + 'Twig_Profiler_Dumper_Blackfire' => $vendorDir . '/twig/twig/lib/Twig/Profiler/Dumper/Blackfire.php', + 'Twig_Profiler_Dumper_Html' => $vendorDir . '/twig/twig/lib/Twig/Profiler/Dumper/Html.php', + 'Twig_Profiler_Dumper_Text' => $vendorDir . '/twig/twig/lib/Twig/Profiler/Dumper/Text.php', + 'Twig_Profiler_NodeVisitor_Profiler' => $vendorDir . '/twig/twig/lib/Twig/Profiler/NodeVisitor/Profiler.php', + 'Twig_Profiler_Node_EnterProfile' => $vendorDir . '/twig/twig/lib/Twig/Profiler/Node/EnterProfile.php', + 'Twig_Profiler_Node_LeaveProfile' => $vendorDir . '/twig/twig/lib/Twig/Profiler/Node/LeaveProfile.php', + 'Twig_Profiler_Profile' => $vendorDir . '/twig/twig/lib/Twig/Profiler/Profile.php', + 'Twig_RuntimeLoaderInterface' => $vendorDir . '/twig/twig/lib/Twig/RuntimeLoaderInterface.php', + 'Twig_Sandbox_SecurityError' => $vendorDir . '/twig/twig/lib/Twig/Sandbox/SecurityError.php', + 'Twig_Sandbox_SecurityNotAllowedFilterError' => $vendorDir . '/twig/twig/lib/Twig/Sandbox/SecurityNotAllowedFilterError.php', + 'Twig_Sandbox_SecurityNotAllowedFunctionError' => $vendorDir . '/twig/twig/lib/Twig/Sandbox/SecurityNotAllowedFunctionError.php', + 'Twig_Sandbox_SecurityNotAllowedMethodError' => $vendorDir . '/twig/twig/lib/Twig/Sandbox/SecurityNotAllowedMethodError.php', + 'Twig_Sandbox_SecurityNotAllowedPropertyError' => $vendorDir . '/twig/twig/lib/Twig/Sandbox/SecurityNotAllowedPropertyError.php', + 'Twig_Sandbox_SecurityNotAllowedTagError' => $vendorDir . '/twig/twig/lib/Twig/Sandbox/SecurityNotAllowedTagError.php', + 'Twig_Sandbox_SecurityPolicy' => $vendorDir . '/twig/twig/lib/Twig/Sandbox/SecurityPolicy.php', + 'Twig_Sandbox_SecurityPolicyInterface' => $vendorDir . '/twig/twig/lib/Twig/Sandbox/SecurityPolicyInterface.php', + 'Twig_SimpleFilter' => $vendorDir . '/twig/twig/lib/Twig/SimpleFilter.php', + 'Twig_SimpleFunction' => $vendorDir . '/twig/twig/lib/Twig/SimpleFunction.php', + 'Twig_SimpleTest' => $vendorDir . '/twig/twig/lib/Twig/SimpleTest.php', + 'Twig_Source' => $vendorDir . '/twig/twig/lib/Twig/Source.php', + 'Twig_SourceContextLoaderInterface' => $vendorDir . '/twig/twig/lib/Twig/SourceContextLoaderInterface.php', + 'Twig_Template' => $vendorDir . '/twig/twig/lib/Twig/Template.php', + 'Twig_TemplateInterface' => $vendorDir . '/twig/twig/lib/Twig/TemplateInterface.php', + 'Twig_TemplateWrapper' => $vendorDir . '/twig/twig/lib/Twig/TemplateWrapper.php', + 'Twig_Test' => $vendorDir . '/twig/twig/lib/Twig/Test.php', + 'Twig_TestCallableInterface' => $vendorDir . '/twig/twig/lib/Twig/TestCallableInterface.php', + 'Twig_TestInterface' => $vendorDir . '/twig/twig/lib/Twig/TestInterface.php', + 'Twig_Test_Function' => $vendorDir . '/twig/twig/lib/Twig/Test/Function.php', + 'Twig_Test_IntegrationTestCase' => $vendorDir . '/twig/twig/lib/Twig/Test/IntegrationTestCase.php', + 'Twig_Test_Method' => $vendorDir . '/twig/twig/lib/Twig/Test/Method.php', + 'Twig_Test_Node' => $vendorDir . '/twig/twig/lib/Twig/Test/Node.php', + 'Twig_Test_NodeTestCase' => $vendorDir . '/twig/twig/lib/Twig/Test/NodeTestCase.php', + 'Twig_Token' => $vendorDir . '/twig/twig/lib/Twig/Token.php', + 'Twig_TokenParser' => $vendorDir . '/twig/twig/lib/Twig/TokenParser.php', + 'Twig_TokenParserBroker' => $vendorDir . '/twig/twig/lib/Twig/TokenParserBroker.php', + 'Twig_TokenParserBrokerInterface' => $vendorDir . '/twig/twig/lib/Twig/TokenParserBrokerInterface.php', + 'Twig_TokenParserInterface' => $vendorDir . '/twig/twig/lib/Twig/TokenParserInterface.php', + 'Twig_TokenParser_AutoEscape' => $vendorDir . '/twig/twig/lib/Twig/TokenParser/AutoEscape.php', + 'Twig_TokenParser_Block' => $vendorDir . '/twig/twig/lib/Twig/TokenParser/Block.php', + 'Twig_TokenParser_Deprecated' => $vendorDir . '/twig/twig/lib/Twig/TokenParser/Deprecated.php', + 'Twig_TokenParser_Do' => $vendorDir . '/twig/twig/lib/Twig/TokenParser/Do.php', + 'Twig_TokenParser_Embed' => $vendorDir . '/twig/twig/lib/Twig/TokenParser/Embed.php', + 'Twig_TokenParser_Extends' => $vendorDir . '/twig/twig/lib/Twig/TokenParser/Extends.php', + 'Twig_TokenParser_Filter' => $vendorDir . '/twig/twig/lib/Twig/TokenParser/Filter.php', + 'Twig_TokenParser_Flush' => $vendorDir . '/twig/twig/lib/Twig/TokenParser/Flush.php', + 'Twig_TokenParser_For' => $vendorDir . '/twig/twig/lib/Twig/TokenParser/For.php', + 'Twig_TokenParser_From' => $vendorDir . '/twig/twig/lib/Twig/TokenParser/From.php', + 'Twig_TokenParser_If' => $vendorDir . '/twig/twig/lib/Twig/TokenParser/If.php', + 'Twig_TokenParser_Import' => $vendorDir . '/twig/twig/lib/Twig/TokenParser/Import.php', + 'Twig_TokenParser_Include' => $vendorDir . '/twig/twig/lib/Twig/TokenParser/Include.php', + 'Twig_TokenParser_Macro' => $vendorDir . '/twig/twig/lib/Twig/TokenParser/Macro.php', + 'Twig_TokenParser_Sandbox' => $vendorDir . '/twig/twig/lib/Twig/TokenParser/Sandbox.php', + 'Twig_TokenParser_Set' => $vendorDir . '/twig/twig/lib/Twig/TokenParser/Set.php', + 'Twig_TokenParser_Spaceless' => $vendorDir . '/twig/twig/lib/Twig/TokenParser/Spaceless.php', + 'Twig_TokenParser_Use' => $vendorDir . '/twig/twig/lib/Twig/TokenParser/Use.php', + 'Twig_TokenParser_With' => $vendorDir . '/twig/twig/lib/Twig/TokenParser/With.php', + 'Twig_TokenStream' => $vendorDir . '/twig/twig/lib/Twig/TokenStream.php', + 'Twig_Util_DeprecationCollector' => $vendorDir . '/twig/twig/lib/Twig/Util/DeprecationCollector.php', + 'Twig_Util_TemplateDirIterator' => $vendorDir . '/twig/twig/lib/Twig/Util/TemplateDirIterator.php', + 'TypeError' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/TypeError.php', + 'UIExtKeyWidget' => $baseDir . '/application/ui.extkeywidget.class.inc.php', + 'UIHTMLEditorWidget' => $baseDir . '/application/ui.htmleditorwidget.class.inc.php', + 'UILinksWidget' => $baseDir . '/application/ui.linkswidget.class.inc.php', + 'UILinksWidgetDirect' => $baseDir . '/application/ui.linksdirectwidget.class.inc.php', + 'UIPasswordWidget' => $baseDir . '/application/ui.passwordwidget.class.inc.php', + 'UISearchFormForeignKeys' => $baseDir . '/application/ui.searchformforeignkeys.class.inc.php', + 'UIWizard' => $baseDir . '/application/uiwizard.class.inc.php', + 'URLButtonItem' => $baseDir . '/application/applicationextension.inc.php', + 'URLPopupMenuItem' => $baseDir . '/application/applicationextension.inc.php', + 'UnaryExpression' => $baseDir . '/core/oql/expression.class.inc.php', + 'UnknownClassOqlException' => $baseDir . '/core/oql/oqlinterpreter.class.inc.php', + 'User' => $baseDir . '/core/userrights.class.inc.php', + 'UserDashboard' => $baseDir . '/application/user.dashboard.class.inc.php', + 'UserInternal' => $baseDir . '/core/userrights.class.inc.php', + 'UserRightException' => $baseDir . '/core/userrights.class.inc.php', + 'UserRights' => $baseDir . '/core/userrights.class.inc.php', + 'UserRightsAddOnAPI' => $baseDir . '/core/userrights.class.inc.php', + 'ValueSetDefinition' => $baseDir . '/core/valuesetdef.class.inc.php', + 'ValueSetEnum' => $baseDir . '/core/valuesetdef.class.inc.php', + 'ValueSetEnumClasses' => $baseDir . '/core/valuesetdef.class.inc.php', + 'ValueSetObjects' => $baseDir . '/core/valuesetdef.class.inc.php', + 'ValueSetRange' => $baseDir . '/core/valuesetdef.class.inc.php', + 'VariableExpression' => $baseDir . '/core/oql/expression.class.inc.php', + 'VariableOqlExpression' => $baseDir . '/core/oql/oqlquery.class.inc.php', + 'WebPage' => $baseDir . '/application/webpage.class.inc.php', + 'WebPageMenuNode' => $baseDir . '/application/menunode.class.inc.php', + 'WizardHelper' => $baseDir . '/application/wizardhelper.class.inc.php', + 'XLSXWriter' => $baseDir . '/application/xlsxwriter.class.php', + 'XMLBulkExport' => $baseDir . '/core/xmlbulkexport.class.inc.php', + 'XMLPage' => $baseDir . '/application/xmlpage.class.inc.php', + 'ajax_page' => $baseDir . '/application/ajaxwebpage.class.inc.php', + 'apcFile' => $baseDir . '/core/apc-emulation.php', + 'appUserPreferences' => $baseDir . '/application/user.preferences.class.inc.php', + 'cmdbAbstractObject' => $baseDir . '/application/cmdbabstract.class.inc.php', + 'cmdbDataGenerator' => $baseDir . '/core/data.generator.class.inc.php', + 'iApplicationObjectExtension' => $baseDir . '/application/applicationextension.inc.php', + 'iApplicationUIExtension' => $baseDir . '/application/applicationextension.inc.php', + 'iBackgroundProcess' => $baseDir . '/core/backgroundprocess.inc.php', + 'iDBObjectSetIterator' => $baseDir . '/core/dbobjectiterator.php', + 'iDBObjectURLMaker' => $baseDir . '/application/applicationcontext.class.inc.php', + 'iDisplay' => $baseDir . '/core/dbobject.class.php', + 'iMetricComputer' => $baseDir . '/core/computing.inc.php', + 'iNewsroomProvider' => $baseDir . '/application/newsroomprovider.class.inc.php', + 'iOnClassInitialization' => $baseDir . '/core/metamodelmodifier.inc.php', + 'iPageUIExtension' => $baseDir . '/application/applicationextension.inc.php', + 'iPopupMenuExtension' => $baseDir . '/application/applicationextension.inc.php', + 'iPortalUIExtension' => $baseDir . '/application/applicationextension.inc.php', + 'iProcess' => $baseDir . '/core/backgroundprocess.inc.php', + 'iQueryModifier' => $baseDir . '/core/querymodifier.class.inc.php', + 'iRestServiceProvider' => $baseDir . '/application/applicationextension.inc.php', + 'iScheduledProcess' => $baseDir . '/core/backgroundprocess.inc.php', + 'iSelfRegister' => $baseDir . '/core/userrights.class.inc.php', + 'iTabbedPage' => $baseDir . '/application/webpage.class.inc.php', + 'iTopMutex' => $baseDir . '/core/mutex.class.inc.php', + 'iTopOwnershipLock' => $baseDir . '/core/ownershiplock.class.inc.php', + 'iTopOwnershipToken' => $baseDir . '/core/ownershiplock.class.inc.php', + 'iTopPDF' => $baseDir . '/application/pdfpage.class.inc.php', + 'iTopStandardURLMaker' => $baseDir . '/application/applicationcontext.class.inc.php', + 'iTopWebPage' => $baseDir . '/application/itopwebpage.class.inc.php', + 'iTopWizardWebPage' => $baseDir . '/application/itopwizardwebpage.class.inc.php', + 'iWorkingTimeComputer' => $baseDir . '/core/computing.inc.php', + 'lnkTriggerAction' => $baseDir . '/core/trigger.class.inc.php', + 'ormCaseLog' => $baseDir . '/core/ormcaselog.class.inc.php', + 'ormCustomFieldsValue' => $baseDir . '/core/ormcustomfieldsvalue.class.inc.php', + 'ormDocument' => $baseDir . '/core/ormdocument.class.inc.php', + 'ormLinkSet' => $baseDir . '/core/ormlinkset.class.inc.php', + 'ormPassword' => $baseDir . '/core/ormpassword.class.inc.php', + 'ormSet' => $baseDir . '/core/ormset.class.inc.php', + 'ormStopWatch' => $baseDir . '/core/ormstopwatch.class.inc.php', + 'ormTagSet' => $baseDir . '/core/ormtagset.class.inc.php', + 'privUITransaction' => $baseDir . '/application/transaction.class.inc.php', + 'privUITransactionFile' => $baseDir . '/application/transaction.class.inc.php', + 'privUITransactionSession' => $baseDir . '/application/transaction.class.inc.php', + 'utils' => $baseDir . '/application/utils.inc.php', +); diff --git a/lib/composer/autoload_files.php b/lib/composer/autoload_files.php new file mode 100644 index 000000000..dfd395581 --- /dev/null +++ b/lib/composer/autoload_files.php @@ -0,0 +1,15 @@ + $vendorDir . '/symfony/polyfill-ctype/bootstrap.php', + '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php', + '5255c38a0faeba867671b61dfda6d864' => $vendorDir . '/paragonie/random_compat/lib/random.php', + '023d27dca8066ef29e6739335ea73bad' => $vendorDir . '/symfony/polyfill-php70/bootstrap.php', + '32dcc8afd4335739640db7d200c1971d' => $vendorDir . '/symfony/polyfill-apcu/bootstrap.php', + '667aeda72477189d0494fecd327c3641' => $vendorDir . '/symfony/var-dumper/Resources/functions/dump.php', +); diff --git a/lib/composer/autoload_namespaces.php b/lib/composer/autoload_namespaces.php new file mode 100644 index 000000000..a7edd32d5 --- /dev/null +++ b/lib/composer/autoload_namespaces.php @@ -0,0 +1,10 @@ + array($vendorDir . '/twig/twig/lib'), +); diff --git a/lib/composer/autoload_psr4.php b/lib/composer/autoload_psr4.php new file mode 100644 index 000000000..f37c0b0d8 --- /dev/null +++ b/lib/composer/autoload_psr4.php @@ -0,0 +1,38 @@ + array($vendorDir . '/twig/twig/src'), + 'Symfony\\Polyfill\\Php70\\' => array($vendorDir . '/symfony/polyfill-php70'), + 'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'), + 'Symfony\\Polyfill\\Ctype\\' => array($vendorDir . '/symfony/polyfill-ctype'), + 'Symfony\\Polyfill\\Apcu\\' => array($vendorDir . '/symfony/polyfill-apcu'), + 'Symfony\\Component\\Yaml\\' => array($vendorDir . '/symfony/yaml'), + 'Symfony\\Component\\VarDumper\\' => array($vendorDir . '/symfony/var-dumper'), + 'Symfony\\Component\\Stopwatch\\' => array($vendorDir . '/symfony/stopwatch'), + 'Symfony\\Component\\Routing\\' => array($vendorDir . '/symfony/routing'), + 'Symfony\\Component\\HttpKernel\\' => array($vendorDir . '/symfony/http-kernel'), + 'Symfony\\Component\\HttpFoundation\\' => array($vendorDir . '/symfony/http-foundation'), + 'Symfony\\Component\\Finder\\' => array($vendorDir . '/symfony/finder'), + 'Symfony\\Component\\Filesystem\\' => array($vendorDir . '/symfony/filesystem'), + 'Symfony\\Component\\EventDispatcher\\' => array($vendorDir . '/symfony/event-dispatcher'), + 'Symfony\\Component\\Dotenv\\' => array($vendorDir . '/symfony/dotenv'), + 'Symfony\\Component\\DependencyInjection\\' => array($vendorDir . '/symfony/dependency-injection'), + 'Symfony\\Component\\Debug\\' => array($vendorDir . '/symfony/debug'), + 'Symfony\\Component\\Console\\' => array($vendorDir . '/symfony/console'), + 'Symfony\\Component\\Config\\' => array($vendorDir . '/symfony/config'), + 'Symfony\\Component\\ClassLoader\\' => array($vendorDir . '/symfony/class-loader'), + 'Symfony\\Component\\Cache\\' => array($vendorDir . '/symfony/cache'), + 'Symfony\\Bundle\\WebProfilerBundle\\' => array($vendorDir . '/symfony/web-profiler-bundle'), + 'Symfony\\Bundle\\TwigBundle\\' => array($vendorDir . '/symfony/twig-bundle'), + 'Symfony\\Bundle\\FrameworkBundle\\' => array($vendorDir . '/symfony/framework-bundle'), + 'Symfony\\Bridge\\Twig\\' => array($vendorDir . '/symfony/twig-bridge'), + 'Psr\\SimpleCache\\' => array($vendorDir . '/psr/simple-cache/src'), + 'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'), + 'Psr\\Container\\' => array($vendorDir . '/psr/container/src'), + 'Psr\\Cache\\' => array($vendorDir . '/psr/cache/src'), +); diff --git a/lib/composer/autoload_real.php b/lib/composer/autoload_real.php new file mode 100644 index 000000000..fb1ee8250 --- /dev/null +++ b/lib/composer/autoload_real.php @@ -0,0 +1,61 @@ += 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\ComposerStaticInit0018331147de7601e7552f7da8e3bb8b::getInitializer($loader)); + } else { + $classMap = require __DIR__ . '/autoload_classmap.php'; + if ($classMap) { + $loader->addClassMap($classMap); + } + } + + $loader->setClassMapAuthoritative(true); + $loader->register(true); + + if ($useStaticLoader) { + $includeFiles = Composer\Autoload\ComposerStaticInit0018331147de7601e7552f7da8e3bb8b::$files; + } else { + $includeFiles = require __DIR__ . '/autoload_files.php'; + } + foreach ($includeFiles as $fileIdentifier => $file) { + composerRequire0018331147de7601e7552f7da8e3bb8b($fileIdentifier, $file); + } + + return $loader; + } +} + +function composerRequire0018331147de7601e7552f7da8e3bb8b($fileIdentifier, $file) +{ + if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { + require $file; + + $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true; + } +} diff --git a/lib/composer/autoload_static.php b/lib/composer/autoload_static.php new file mode 100644 index 000000000..d4a7cdeed --- /dev/null +++ b/lib/composer/autoload_static.php @@ -0,0 +1,2008 @@ + __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php', + '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php', + '5255c38a0faeba867671b61dfda6d864' => __DIR__ . '/..' . '/paragonie/random_compat/lib/random.php', + '023d27dca8066ef29e6739335ea73bad' => __DIR__ . '/..' . '/symfony/polyfill-php70/bootstrap.php', + '32dcc8afd4335739640db7d200c1971d' => __DIR__ . '/..' . '/symfony/polyfill-apcu/bootstrap.php', + '667aeda72477189d0494fecd327c3641' => __DIR__ . '/..' . '/symfony/var-dumper/Resources/functions/dump.php', + ); + + public static $prefixLengthsPsr4 = array ( + 'T' => + array ( + 'Twig\\' => 5, + ), + 'S' => + array ( + 'Symfony\\Polyfill\\Php70\\' => 23, + 'Symfony\\Polyfill\\Mbstring\\' => 26, + 'Symfony\\Polyfill\\Ctype\\' => 23, + 'Symfony\\Polyfill\\Apcu\\' => 22, + 'Symfony\\Component\\Yaml\\' => 23, + 'Symfony\\Component\\VarDumper\\' => 28, + 'Symfony\\Component\\Stopwatch\\' => 28, + 'Symfony\\Component\\Routing\\' => 26, + 'Symfony\\Component\\HttpKernel\\' => 29, + 'Symfony\\Component\\HttpFoundation\\' => 33, + 'Symfony\\Component\\Finder\\' => 25, + 'Symfony\\Component\\Filesystem\\' => 29, + 'Symfony\\Component\\EventDispatcher\\' => 34, + 'Symfony\\Component\\Dotenv\\' => 25, + 'Symfony\\Component\\DependencyInjection\\' => 38, + 'Symfony\\Component\\Debug\\' => 24, + 'Symfony\\Component\\Console\\' => 26, + 'Symfony\\Component\\Config\\' => 25, + 'Symfony\\Component\\ClassLoader\\' => 30, + 'Symfony\\Component\\Cache\\' => 24, + 'Symfony\\Bundle\\WebProfilerBundle\\' => 33, + 'Symfony\\Bundle\\TwigBundle\\' => 26, + 'Symfony\\Bundle\\FrameworkBundle\\' => 31, + 'Symfony\\Bridge\\Twig\\' => 20, + ), + 'P' => + array ( + 'Psr\\SimpleCache\\' => 16, + 'Psr\\Log\\' => 8, + 'Psr\\Container\\' => 14, + 'Psr\\Cache\\' => 10, + ), + ); + + public static $prefixDirsPsr4 = array ( + 'Twig\\' => + array ( + 0 => __DIR__ . '/..' . '/twig/twig/src', + ), + 'Symfony\\Polyfill\\Php70\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/polyfill-php70', + ), + 'Symfony\\Polyfill\\Mbstring\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/polyfill-mbstring', + ), + 'Symfony\\Polyfill\\Ctype\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/polyfill-ctype', + ), + 'Symfony\\Polyfill\\Apcu\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/polyfill-apcu', + ), + 'Symfony\\Component\\Yaml\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/yaml', + ), + 'Symfony\\Component\\VarDumper\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/var-dumper', + ), + 'Symfony\\Component\\Stopwatch\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/stopwatch', + ), + 'Symfony\\Component\\Routing\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/routing', + ), + 'Symfony\\Component\\HttpKernel\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/http-kernel', + ), + 'Symfony\\Component\\HttpFoundation\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/http-foundation', + ), + 'Symfony\\Component\\Finder\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/finder', + ), + 'Symfony\\Component\\Filesystem\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/filesystem', + ), + 'Symfony\\Component\\EventDispatcher\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/event-dispatcher', + ), + 'Symfony\\Component\\Dotenv\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/dotenv', + ), + 'Symfony\\Component\\DependencyInjection\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/dependency-injection', + ), + 'Symfony\\Component\\Debug\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/debug', + ), + 'Symfony\\Component\\Console\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/console', + ), + 'Symfony\\Component\\Config\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/config', + ), + 'Symfony\\Component\\ClassLoader\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/class-loader', + ), + 'Symfony\\Component\\Cache\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/cache', + ), + 'Symfony\\Bundle\\WebProfilerBundle\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/web-profiler-bundle', + ), + 'Symfony\\Bundle\\TwigBundle\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/twig-bundle', + ), + 'Symfony\\Bundle\\FrameworkBundle\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/framework-bundle', + ), + 'Symfony\\Bridge\\Twig\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/twig-bridge', + ), + 'Psr\\SimpleCache\\' => + array ( + 0 => __DIR__ . '/..' . '/psr/simple-cache/src', + ), + 'Psr\\Log\\' => + array ( + 0 => __DIR__ . '/..' . '/psr/log/Psr/Log', + ), + 'Psr\\Container\\' => + array ( + 0 => __DIR__ . '/..' . '/psr/container/src', + ), + 'Psr\\Cache\\' => + array ( + 0 => __DIR__ . '/..' . '/psr/cache/src', + ), + ); + + public static $prefixesPsr0 = array ( + 'T' => + array ( + 'Twig_' => + array ( + 0 => __DIR__ . '/..' . '/twig/twig/lib', + ), + ), + ); + + public static $classMap = array ( + 'AbstractPortalUIExtension' => __DIR__ . '/../..' . '/application/applicationextension.inc.php', + 'Action' => __DIR__ . '/../..' . '/core/action.class.inc.php', + 'ActionChecker' => __DIR__ . '/../..' . '/core/userrights.class.inc.php', + 'ActionEmail' => __DIR__ . '/../..' . '/core/action.class.inc.php', + 'ActionNotification' => __DIR__ . '/../..' . '/core/action.class.inc.php', + 'ApplicationContext' => __DIR__ . '/../..' . '/application/applicationcontext.class.inc.php', + 'ApplicationException' => __DIR__ . '/../..' . '/application/application.inc.php', + 'ApplicationMenu' => __DIR__ . '/../..' . '/application/menunode.class.inc.php', + 'ApplicationPopupMenuItem' => __DIR__ . '/../..' . '/application/applicationextension.inc.php', + 'ArchivedObjectException' => __DIR__ . '/../..' . '/core/coreexception.class.inc.php', + 'ArithmeticError' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/ArithmeticError.php', + 'AssertionError' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/AssertionError.php', + 'AsyncSendEmail' => __DIR__ . '/../..' . '/core/asynctask.class.inc.php', + 'AsyncTask' => __DIR__ . '/../..' . '/core/asynctask.class.inc.php', + 'AttributeApplicationLanguage' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php', + 'AttributeArchiveDate' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php', + 'AttributeArchiveFlag' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php', + 'AttributeBlob' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php', + 'AttributeBoolean' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php', + 'AttributeCaseLog' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php', + 'AttributeClass' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php', + 'AttributeClassAttCodeSet' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php', + 'AttributeClassState' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php', + 'AttributeCustomFields' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php', + 'AttributeDBField' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php', + 'AttributeDBFieldVoid' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php', + 'AttributeDashboard' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php', + 'AttributeDate' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php', + 'AttributeDateTime' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php', + 'AttributeDeadline' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php', + 'AttributeDecimal' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php', + 'AttributeDefinition' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php', + 'AttributeDuration' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php', + 'AttributeEmailAddress' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php', + 'AttributeEncryptedString' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php', + 'AttributeEnum' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php', + 'AttributeExternalField' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php', + 'AttributeExternalKey' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php', + 'AttributeFinalClass' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php', + 'AttributeFriendlyName' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php', + 'AttributeHTML' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php', + 'AttributeHierarchicalKey' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php', + 'AttributeIPAddress' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php', + 'AttributeImage' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php', + 'AttributeInteger' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php', + 'AttributeLinkedSet' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php', + 'AttributeLinkedSetIndirect' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php', + 'AttributeLongText' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php', + 'AttributeMetaEnum' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php', + 'AttributeOQL' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php', + 'AttributeObjectKey' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php', + 'AttributeObsolescenceDate' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php', + 'AttributeObsolescenceFlag' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php', + 'AttributeOneWayPassword' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php', + 'AttributePassword' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php', + 'AttributePercentage' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php', + 'AttributePhoneNumber' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php', + 'AttributePropertySet' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php', + 'AttributeQueryAttCodeSet' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php', + 'AttributeRedundancySettings' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php', + 'AttributeSet' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php', + 'AttributeStopWatch' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php', + 'AttributeString' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php', + 'AttributeSubItem' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php', + 'AttributeTable' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php', + 'AttributeTagSet' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php', + 'AttributeTemplateHTML' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php', + 'AttributeTemplateString' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php', + 'AttributeTemplateText' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php', + 'AttributeText' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php', + 'AttributeURL' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php', + 'AuditCategory' => __DIR__ . '/../..' . '/application/audit.category.class.inc.php', + 'AuditRule' => __DIR__ . '/../..' . '/application/audit.rule.class.inc.php', + 'BackgroundTask' => __DIR__ . '/../..' . '/core/backgroundtask.class.inc.php', + 'BinaryExpression' => __DIR__ . '/../..' . '/core/oql/expression.class.inc.php', + 'BinaryOqlExpression' => __DIR__ . '/../..' . '/core/oql/oqlquery.class.inc.php', + 'BulkChange' => __DIR__ . '/../..' . '/core/bulkchange.class.inc.php', + 'BulkChangeException' => __DIR__ . '/../..' . '/core/bulkchange.class.inc.php', + 'BulkExport' => __DIR__ . '/../..' . '/core/bulkexport.class.inc.php', + 'BulkExportException' => __DIR__ . '/../..' . '/core/bulkexport.class.inc.php', + 'BulkExportMissingParameterException' => __DIR__ . '/../..' . '/core/bulkexport.class.inc.php', + 'BulkExportResult' => __DIR__ . '/../..' . '/core/bulkexport.class.inc.php', + 'BulkExportResultGC' => __DIR__ . '/../..' . '/core/bulkexport.class.inc.php', + 'CAS_SelfRegister' => __DIR__ . '/../..' . '/core/userrights.class.inc.php', + 'CLIPage' => __DIR__ . '/../..' . '/application/clipage.class.inc.php', + 'CMDBChange' => __DIR__ . '/../..' . '/core/cmdbchange.class.inc.php', + 'CMDBChangeOp' => __DIR__ . '/../..' . '/core/cmdbchangeop.class.inc.php', + 'CMDBChangeOpCreate' => __DIR__ . '/../..' . '/core/cmdbchangeop.class.inc.php', + 'CMDBChangeOpDelete' => __DIR__ . '/../..' . '/core/cmdbchangeop.class.inc.php', + 'CMDBChangeOpPlugin' => __DIR__ . '/../..' . '/core/cmdbchangeop.class.inc.php', + 'CMDBChangeOpSetAttribute' => __DIR__ . '/../..' . '/core/cmdbchangeop.class.inc.php', + 'CMDBChangeOpSetAttributeBlob' => __DIR__ . '/../..' . '/core/cmdbchangeop.class.inc.php', + 'CMDBChangeOpSetAttributeCaseLog' => __DIR__ . '/../..' . '/core/cmdbchangeop.class.inc.php', + 'CMDBChangeOpSetAttributeCustomFields' => __DIR__ . '/../..' . '/core/cmdbchangeop.class.inc.php', + 'CMDBChangeOpSetAttributeEncrypted' => __DIR__ . '/../..' . '/core/cmdbchangeop.class.inc.php', + 'CMDBChangeOpSetAttributeHTML' => __DIR__ . '/../..' . '/core/cmdbchangeop.class.inc.php', + 'CMDBChangeOpSetAttributeLinks' => __DIR__ . '/../..' . '/core/cmdbchangeop.class.inc.php', + 'CMDBChangeOpSetAttributeLinksAddRemove' => __DIR__ . '/../..' . '/core/cmdbchangeop.class.inc.php', + 'CMDBChangeOpSetAttributeLinksTune' => __DIR__ . '/../..' . '/core/cmdbchangeop.class.inc.php', + 'CMDBChangeOpSetAttributeLongText' => __DIR__ . '/../..' . '/core/cmdbchangeop.class.inc.php', + 'CMDBChangeOpSetAttributeOneWayPassword' => __DIR__ . '/../..' . '/core/cmdbchangeop.class.inc.php', + 'CMDBChangeOpSetAttributeScalar' => __DIR__ . '/../..' . '/core/cmdbchangeop.class.inc.php', + 'CMDBChangeOpSetAttributeTagSet' => __DIR__ . '/../..' . '/core/cmdbchangeop.class.inc.php', + 'CMDBChangeOpSetAttributeText' => __DIR__ . '/../..' . '/core/cmdbchangeop.class.inc.php', + 'CMDBChangeOpSetAttributeURL' => __DIR__ . '/../..' . '/core/cmdbchangeop.class.inc.php', + 'CMDBObject' => __DIR__ . '/../..' . '/core/cmdbobject.class.inc.php', + 'CMDBObjectSet' => __DIR__ . '/../..' . '/core/cmdbobject.class.inc.php', + 'CMDBSource' => __DIR__ . '/../..' . '/core/cmdbsource.class.inc.php', + 'CSVBulkExport' => __DIR__ . '/../..' . '/core/csvbulkexport.class.inc.php', + 'CSVPage' => __DIR__ . '/../..' . '/application/csvpage.class.inc.php', + 'CSVParser' => __DIR__ . '/../..' . '/core/csvparser.class.inc.php', + 'CSVParserException' => __DIR__ . '/../..' . '/core/csvparser.class.inc.php', + 'CaptureWebPage' => __DIR__ . '/../..' . '/application/capturewebpage.class.inc.php', + 'CellChangeSpec' => __DIR__ . '/../..' . '/core/bulkchange.class.inc.php', + 'CellStatus_Ambiguous' => __DIR__ . '/../..' . '/core/bulkchange.class.inc.php', + 'CellStatus_Issue' => __DIR__ . '/../..' . '/core/bulkchange.class.inc.php', + 'CellStatus_Modify' => __DIR__ . '/../..' . '/core/bulkchange.class.inc.php', + 'CellStatus_NullIssue' => __DIR__ . '/../..' . '/core/bulkchange.class.inc.php', + 'CellStatus_SearchIssue' => __DIR__ . '/../..' . '/core/bulkchange.class.inc.php', + 'CellStatus_Void' => __DIR__ . '/../..' . '/core/bulkchange.class.inc.php', + 'CharConcatExpression' => __DIR__ . '/../..' . '/core/oql/expression.class.inc.php', + 'CharConcatWSExpression' => __DIR__ . '/../..' . '/core/oql/expression.class.inc.php', + 'CheckStopWatchThresholds' => __DIR__ . '/../..' . '/core/ormstopwatch.class.inc.php', + 'CheckableExpression' => __DIR__ . '/../..' . '/core/oql/oqlquery.class.inc.php', + 'Combodo\\iTop\\DesignDocument' => __DIR__ . '/../..' . '/core/designdocument.class.inc.php', + 'Combodo\\iTop\\DesignElement' => __DIR__ . '/../..' . '/core/designdocument.class.inc.php', + 'Config' => __DIR__ . '/../..' . '/core/config.class.inc.php', + 'ConfigException' => __DIR__ . '/../..' . '/core/config.class.inc.php', + 'ContextTag' => __DIR__ . '/../..' . '/core/contexttag.class.inc.php', + 'CoreCannotSaveObjectException' => __DIR__ . '/../..' . '/core/coreexception.class.inc.php', + 'CoreException' => __DIR__ . '/../..' . '/core/coreexception.class.inc.php', + 'CoreServices' => __DIR__ . '/../..' . '/core/restservices.class.inc.php', + 'CoreUnexpectedValue' => __DIR__ . '/../..' . '/core/coreexception.class.inc.php', + 'CoreWarning' => __DIR__ . '/../..' . '/core/coreexception.class.inc.php', + 'CryptEngine' => __DIR__ . '/../..' . '/core/simplecrypt.class.inc.php', + 'CustomFieldsHandler' => __DIR__ . '/../..' . '/core/customfieldshandler.class.inc.php', + 'DBObject' => __DIR__ . '/../..' . '/core/dbobject.class.php', + 'DBObjectSearch' => __DIR__ . '/../..' . '/core/dbobjectsearch.class.php', + 'DBObjectSet' => __DIR__ . '/../..' . '/core/dbobjectset.class.php', + 'DBObjectSetComparator' => __DIR__ . '/../..' . '/core/dbobjectset.class.php', + 'DBProperty' => __DIR__ . '/../..' . '/core/dbproperty.class.inc.php', + 'DBSearch' => __DIR__ . '/../..' . '/core/dbsearch.class.php', + 'DBUnionSearch' => __DIR__ . '/../..' . '/core/dbunionsearch.class.php', + 'Dashboard' => __DIR__ . '/../..' . '/application/dashboard.class.inc.php', + 'DashboardLayout' => __DIR__ . '/../..' . '/application/dashboardlayout.class.inc.php', + 'DashboardLayoutMultiCol' => __DIR__ . '/../..' . '/application/dashboardlayout.class.inc.php', + 'DashboardLayoutOneCol' => __DIR__ . '/../..' . '/application/dashboardlayout.class.inc.php', + 'DashboardLayoutThreeCols' => __DIR__ . '/../..' . '/application/dashboardlayout.class.inc.php', + 'DashboardLayoutTwoCols' => __DIR__ . '/../..' . '/application/dashboardlayout.class.inc.php', + 'DashboardMenuNode' => __DIR__ . '/../..' . '/application/menunode.class.inc.php', + 'Dashlet' => __DIR__ . '/../..' . '/application/dashlet.class.inc.php', + 'DashletBadge' => __DIR__ . '/../..' . '/application/dashlet.class.inc.php', + 'DashletEmptyCell' => __DIR__ . '/../..' . '/application/dashlet.class.inc.php', + 'DashletGroupBy' => __DIR__ . '/../..' . '/application/dashlet.class.inc.php', + 'DashletGroupByBars' => __DIR__ . '/../..' . '/application/dashlet.class.inc.php', + 'DashletGroupByPie' => __DIR__ . '/../..' . '/application/dashlet.class.inc.php', + 'DashletGroupByTable' => __DIR__ . '/../..' . '/application/dashlet.class.inc.php', + 'DashletHeaderDynamic' => __DIR__ . '/../..' . '/application/dashlet.class.inc.php', + 'DashletHeaderStatic' => __DIR__ . '/../..' . '/application/dashlet.class.inc.php', + 'DashletObjectList' => __DIR__ . '/../..' . '/application/dashlet.class.inc.php', + 'DashletPlainText' => __DIR__ . '/../..' . '/application/dashlet.class.inc.php', + 'DashletProxy' => __DIR__ . '/../..' . '/application/dashlet.class.inc.php', + 'DashletUnknown' => __DIR__ . '/../..' . '/application/dashlet.class.inc.php', + 'DataTable' => __DIR__ . '/../..' . '/application/datatable.class.inc.php', + 'DataTableSettings' => __DIR__ . '/../..' . '/application/datatable.class.inc.php', + 'DateTimeFormat' => __DIR__ . '/../..' . '/core/datetimeformat.class.inc.php', + 'DefaultMetricComputer' => __DIR__ . '/../..' . '/core/computing.inc.php', + 'DefaultWorkingTimeComputer' => __DIR__ . '/../..' . '/core/computing.inc.php', + 'DeleteException' => __DIR__ . '/../..' . '/core/deletionplan.class.inc.php', + 'DeletionPlan' => __DIR__ . '/../..' . '/core/deletionplan.class.inc.php', + 'DesignerBooleanField' => __DIR__ . '/../..' . '/application/forms.class.inc.php', + 'DesignerComboField' => __DIR__ . '/../..' . '/application/forms.class.inc.php', + 'DesignerForm' => __DIR__ . '/../..' . '/application/forms.class.inc.php', + 'DesignerFormField' => __DIR__ . '/../..' . '/application/forms.class.inc.php', + 'DesignerFormSelectorField' => __DIR__ . '/../..' . '/application/forms.class.inc.php', + 'DesignerHiddenField' => __DIR__ . '/../..' . '/application/forms.class.inc.php', + 'DesignerIconSelectionField' => __DIR__ . '/../..' . '/application/forms.class.inc.php', + 'DesignerIntegerField' => __DIR__ . '/../..' . '/application/forms.class.inc.php', + 'DesignerLabelField' => __DIR__ . '/../..' . '/application/forms.class.inc.php', + 'DesignerLongTextField' => __DIR__ . '/../..' . '/application/forms.class.inc.php', + 'DesignerSortableField' => __DIR__ . '/../..' . '/application/forms.class.inc.php', + 'DesignerStaticTextField' => __DIR__ . '/../..' . '/application/forms.class.inc.php', + 'DesignerSubFormField' => __DIR__ . '/../..' . '/application/forms.class.inc.php', + 'DesignerTabularForm' => __DIR__ . '/../..' . '/application/forms.class.inc.php', + 'DesignerTextField' => __DIR__ . '/../..' . '/application/forms.class.inc.php', + 'Dict' => __DIR__ . '/../..' . '/core/dict.class.inc.php', + 'DictException' => __DIR__ . '/../..' . '/core/dict.class.inc.php', + 'DictExceptionMissingString' => __DIR__ . '/../..' . '/core/dict.class.inc.php', + 'DictExceptionUnknownLanguage' => __DIR__ . '/../..' . '/core/dict.class.inc.php', + 'DisplayBlock' => __DIR__ . '/../..' . '/application/displayblock.class.inc.php', + 'DisplayTemplate' => __DIR__ . '/../..' . '/application/template.class.inc.php', + 'DisplayableEdge' => __DIR__ . '/../..' . '/core/displayablegraph.class.inc.php', + 'DisplayableGraph' => __DIR__ . '/../..' . '/core/displayablegraph.class.inc.php', + 'DisplayableGroupNode' => __DIR__ . '/../..' . '/core/displayablegraph.class.inc.php', + 'DisplayableNode' => __DIR__ . '/../..' . '/core/displayablegraph.class.inc.php', + 'DisplayableRedundancyNode' => __DIR__ . '/../..' . '/core/displayablegraph.class.inc.php', + 'DivisionByZeroError' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/DivisionByZeroError.php', + 'EMail' => __DIR__ . '/../..' . '/core/email.class.inc.php', + 'Error' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/Error.php', + 'Event' => __DIR__ . '/../..' . '/core/event.class.inc.php', + 'EventIssue' => __DIR__ . '/../..' . '/core/event.class.inc.php', + 'EventLoginUsage' => __DIR__ . '/../..' . '/core/event.class.inc.php', + 'EventNotification' => __DIR__ . '/../..' . '/core/event.class.inc.php', + 'EventNotificationEmail' => __DIR__ . '/../..' . '/core/event.class.inc.php', + 'EventOnObject' => __DIR__ . '/../..' . '/core/event.class.inc.php', + 'EventRestService' => __DIR__ . '/../..' . '/core/event.class.inc.php', + 'EventWebService' => __DIR__ . '/../..' . '/core/event.class.inc.php', + 'ExcelBulkExport' => __DIR__ . '/../..' . '/core/excelbulkexport.class.inc.php', + 'ExcelExporter' => __DIR__ . '/../..' . '/application/excelexporter.class.inc.php', + 'ExecAsyncTask' => __DIR__ . '/../..' . '/core/asynctask.class.inc.php', + 'ExecutionKPI' => __DIR__ . '/../..' . '/core/kpi.class.inc.php', + 'Expression' => __DIR__ . '/../..' . '/core/oql/expression.class.inc.php', + 'ExpressionCache' => __DIR__ . '/../..' . '/core/expressioncache.class.inc.php', + 'FalseExpression' => __DIR__ . '/../..' . '/core/oql/expression.class.inc.php', + 'FieldExpression' => __DIR__ . '/../..' . '/core/oql/expression.class.inc.php', + 'FieldExpressionResolved' => __DIR__ . '/../..' . '/core/oql/expression.class.inc.php', + 'FieldOqlExpression' => __DIR__ . '/../..' . '/core/oql/oqlquery.class.inc.php', + 'FileLog' => __DIR__ . '/../..' . '/core/log.class.inc.php', + 'FileUploadException' => __DIR__ . '/../..' . '/application/utils.inc.php', + 'FilterDefinition' => __DIR__ . '/../..' . '/core/filterdef.class.inc.php', + 'FilterFromAttribute' => __DIR__ . '/../..' . '/core/filterdef.class.inc.php', + 'FilterPrivateKey' => __DIR__ . '/../..' . '/core/filterdef.class.inc.php', + 'FunctionExpression' => __DIR__ . '/../..' . '/core/oql/expression.class.inc.php', + 'FunctionOqlExpression' => __DIR__ . '/../..' . '/core/oql/oqlquery.class.inc.php', + 'GraphEdge' => __DIR__ . '/../..' . '/core/simplegraph.class.inc.php', + 'GraphElement' => __DIR__ . '/../..' . '/core/simplegraph.class.inc.php', + 'GraphNode' => __DIR__ . '/../..' . '/core/simplegraph.class.inc.php', + 'HTMLBulkExport' => __DIR__ . '/../..' . '/core/htmlbulkexport.class.inc.php', + 'HTMLDOMSanitizer' => __DIR__ . '/../..' . '/core/htmlsanitizer.class.inc.php', + 'HTMLNullSanitizer' => __DIR__ . '/../..' . '/core/htmlsanitizer.class.inc.php', + 'HTMLSanitizer' => __DIR__ . '/../..' . '/core/htmlsanitizer.class.inc.php', + 'HistoryBlock' => __DIR__ . '/../..' . '/application/displayblock.class.inc.php', + 'Html2Text\\Html2Text' => __DIR__ . '/../..' . '/application/Html2Text.php', + 'Html2Text\\Html2TextException' => __DIR__ . '/../..' . '/application/Html2TextException.php', + 'ITopArchiveTar' => __DIR__ . '/../..' . '/core/tar-itop.class.inc.php', + 'InlineImage' => __DIR__ . '/../..' . '/core/inlineimage.class.inc.php', + 'InlineImageGC' => __DIR__ . '/../..' . '/core/inlineimage.class.inc.php', + 'InputOutputTask' => __DIR__ . '/../..' . '/application/iotask.class.inc.php', + 'IntervalExpression' => __DIR__ . '/../..' . '/core/oql/expression.class.inc.php', + 'IntervalOqlExpression' => __DIR__ . '/../..' . '/core/oql/oqlquery.class.inc.php', + 'Introspection' => __DIR__ . '/../..' . '/core/introspection.class.inc.php', + 'InvalidConfigParamException' => __DIR__ . '/../..' . '/core/coreexception.class.inc.php', + 'IssueLog' => __DIR__ . '/../..' . '/core/log.class.inc.php', + 'JSButtonItem' => __DIR__ . '/../..' . '/application/applicationextension.inc.php', + 'JSPopupMenuItem' => __DIR__ . '/../..' . '/application/applicationextension.inc.php', + 'ListExpression' => __DIR__ . '/../..' . '/core/oql/expression.class.inc.php', + 'ListOqlExpression' => __DIR__ . '/../..' . '/core/oql/oqlquery.class.inc.php', + 'LogAPI' => __DIR__ . '/../..' . '/core/log.class.inc.php', + 'LoginWebPage' => __DIR__ . '/../..' . '/application/loginwebpage.class.inc.php', + 'MatchExpression' => __DIR__ . '/../..' . '/core/oql/expression.class.inc.php', + 'MatchOqlExpression' => __DIR__ . '/../..' . '/core/oql/oqlquery.class.inc.php', + 'MenuBlock' => __DIR__ . '/../..' . '/application/displayblock.class.inc.php', + 'MenuGroup' => __DIR__ . '/../..' . '/application/menunode.class.inc.php', + 'MenuNode' => __DIR__ . '/../..' . '/application/menunode.class.inc.php', + 'MetaModel' => __DIR__ . '/../..' . '/core/metamodel.class.php', + 'MissingColumnException' => __DIR__ . '/../..' . '/core/attributedef.class.inc.php', + 'MissingQueryArgument' => __DIR__ . '/../..' . '/core/oql/expression.class.inc.php', + 'ModelReflection' => __DIR__ . '/../..' . '/core/modelreflection.class.inc.php', + 'ModelReflectionRuntime' => __DIR__ . '/../..' . '/core/modelreflection.class.inc.php', + 'ModuleDesign' => __DIR__ . '/../..' . '/core/moduledesign.class.inc.php', + 'ModuleHandlerAPI' => __DIR__ . '/../..' . '/core/modulehandler.class.inc.php', + 'ModuleHandlerApiInterface' => __DIR__ . '/../..' . '/core/modulehandler.class.inc.php', + 'MyHelpers' => __DIR__ . '/../..' . '/core/MyHelpers.class.inc.php', + 'MySQLException' => __DIR__ . '/../..' . '/core/cmdbsource.class.inc.php', + 'MySQLHasGoneAwayException' => __DIR__ . '/../..' . '/core/cmdbsource.class.inc.php', + 'MySQLQueryHasNoResultException' => __DIR__ . '/../..' . '/core/cmdbsource.class.inc.php', + 'NewObjectMenuNode' => __DIR__ . '/../..' . '/application/menunode.class.inc.php', + 'NewsroomProviderBase' => __DIR__ . '/../..' . '/application/newsroomprovider.class.inc.php', + 'NiceWebPage' => __DIR__ . '/../..' . '/application/nicewebpage.class.inc.php', + 'OQLException' => __DIR__ . '/../..' . '/core/oql/oqlexception.class.inc.php', + 'OQLLexer' => __DIR__ . '/../..' . '/core/oql/oql-lexer.php', + 'OQLLexerException' => __DIR__ . '/../..' . '/core/oql/oql-lexer.php', + 'OQLLexerRaw' => __DIR__ . '/../..' . '/core/oql/oql-lexer.php', + 'OQLMenuNode' => __DIR__ . '/../..' . '/application/menunode.class.inc.php', + 'OQLParser' => __DIR__ . '/../..' . '/core/oql/oql-parser.php', + 'OQLParserException' => __DIR__ . '/../..' . '/core/oql/oql-parser.php', + 'OQLParserRaw' => __DIR__ . '/../..' . '/core/oql/oql-parser.php', + 'OQLParser_yyStackEntry' => __DIR__ . '/../..' . '/core/oql/oql-parser.php', + 'OQLParser_yyToken' => __DIR__ . '/../..' . '/core/oql/oql-parser.php', + 'ObjectDetailsTemplate' => __DIR__ . '/../..' . '/application/template.class.inc.php', + 'ObjectResult' => __DIR__ . '/../..' . '/core/restservices.class.inc.php', + 'ObjectStimulus' => __DIR__ . '/../..' . '/core/stimulus.class.inc.php', + 'ObsolescenceDateUpdater' => __DIR__ . '/../..' . '/core/background.inc.php', + 'OqlHexValue' => __DIR__ . '/../..' . '/core/oql/oqlquery.class.inc.php', + 'OqlInterpreter' => __DIR__ . '/../..' . '/core/oql/oqlinterpreter.class.inc.php', + 'OqlInterpreterException' => __DIR__ . '/../..' . '/core/oql/oqlinterpreter.class.inc.php', + 'OqlJoinSpec' => __DIR__ . '/../..' . '/core/oql/oqlquery.class.inc.php', + 'OqlName' => __DIR__ . '/../..' . '/core/oql/oqlquery.class.inc.php', + 'OqlNormalizeException' => __DIR__ . '/../..' . '/core/oql/oqlinterpreter.class.inc.php', + 'OqlObjectQuery' => __DIR__ . '/../..' . '/core/oql/oqlquery.class.inc.php', + 'OqlQuery' => __DIR__ . '/../..' . '/core/oql/oqlquery.class.inc.php', + 'OqlUnionQuery' => __DIR__ . '/../..' . '/core/oql/oqlquery.class.inc.php', + 'PDFBulkExport' => __DIR__ . '/../..' . '/core/pdfbulkexport.class.inc.php', + 'PDFPage' => __DIR__ . '/../..' . '/application/pdfpage.class.inc.php', + 'PHP_LexerGenerator' => __DIR__ . '/../..' . '/core/oql/build/PHP/LexerGenerator.php', + 'PHP_LexerGenerator_Exception' => __DIR__ . '/../..' . '/core/oql/build/PHP/LexerGenerator/Exception.php', + 'PHP_LexerGenerator_Lexer' => __DIR__ . '/../..' . '/core/oql/build/PHP/LexerGenerator/Lexer.php', + 'PHP_LexerGenerator_Parser' => __DIR__ . '/../..' . '/core/oql/build/PHP/LexerGenerator/Parser.php', + 'PHP_LexerGenerator_ParseryyStackEntry' => __DIR__ . '/../..' . '/core/oql/build/PHP/LexerGenerator/Parser.php', + 'PHP_LexerGenerator_ParseryyToken' => __DIR__ . '/../..' . '/core/oql/build/PHP/LexerGenerator/Parser.php', + 'PHP_LexerGenerator_Regex_Lexer' => __DIR__ . '/../..' . '/core/oql/build/PHP/LexerGenerator/Regex/Lexer.php', + 'PHP_LexerGenerator_Regex_Parser' => __DIR__ . '/../..' . '/core/oql/build/PHP/LexerGenerator/Regex/Parser.php', + 'PHP_LexerGenerator_Regex_yyStackEntry' => __DIR__ . '/../..' . '/core/oql/build/PHP/LexerGenerator/Regex/Parser.php', + 'PHP_LexerGenerator_Regex_yyToken' => __DIR__ . '/../..' . '/core/oql/build/PHP/LexerGenerator/Regex/Parser.php', + 'PHP_ParserGenerator' => __DIR__ . '/../..' . '/core/oql/build/PHP/ParserGenerator.php', + 'PHP_ParserGenerator_Action' => __DIR__ . '/../..' . '/core/oql/build/PHP/ParserGenerator/Action.php', + 'PHP_ParserGenerator_ActionTable' => __DIR__ . '/../..' . '/core/oql/build/PHP/ParserGenerator/ActionTable.php', + 'PHP_ParserGenerator_Config' => __DIR__ . '/../..' . '/core/oql/build/PHP/ParserGenerator/Config.php', + 'PHP_ParserGenerator_Data' => __DIR__ . '/../..' . '/core/oql/build/PHP/ParserGenerator/Data.php', + 'PHP_ParserGenerator_Parser' => __DIR__ . '/../..' . '/core/oql/build/PHP/ParserGenerator/Parser.php', + 'PHP_ParserGenerator_PropagationLink' => __DIR__ . '/../..' . '/core/oql/build/PHP/ParserGenerator/PropagationLink.php', + 'PHP_ParserGenerator_Rule' => __DIR__ . '/../..' . '/core/oql/build/PHP/ParserGenerator/Rule.php', + 'PHP_ParserGenerator_State' => __DIR__ . '/../..' . '/core/oql/build/PHP/ParserGenerator/State.php', + 'PHP_ParserGenerator_StateNode' => __DIR__ . '/../..' . '/core/oql/build/PHP/ParserGenerator/State.php', + 'PHP_ParserGenerator_Symbol' => __DIR__ . '/../..' . '/core/oql/build/PHP/ParserGenerator/Symbol.php', + 'Page' => __DIR__ . '/../..' . '/application/webpage.class.inc.php', + 'ParseError' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/ParseError.php', + 'ParseyyStackEntry' => __DIR__ . '/../..' . '/core/oql/build/PHP/Lempar.php', + 'ParseyyToken' => __DIR__ . '/../..' . '/core/oql/build/PHP/Lempar.php', + 'PortalDispatcher' => __DIR__ . '/../..' . '/application/portaldispatcher.class.inc.php', + 'PortalURLMaker' => __DIR__ . '/../..' . '/application/applicationcontext.class.inc.php', + 'PrintableDataTable' => __DIR__ . '/../..' . '/application/datatable.class.inc.php', + 'ProcessException' => __DIR__ . '/../..' . '/core/backgroundprocess.inc.php', + 'ProcessFatalException' => __DIR__ . '/../..' . '/core/backgroundprocess.inc.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\\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\\Log\\Test\\TestLogger' => __DIR__ . '/..' . '/psr/log/Psr/Log/Test/TestLogger.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', + 'Query' => __DIR__ . '/../..' . '/application/query.class.inc.php', + 'QueryBuilderContext' => __DIR__ . '/../..' . '/core/querybuildercontext.class.inc.php', + 'QueryBuilderExpressions' => __DIR__ . '/../..' . '/core/oql/expression.class.inc.php', + 'QueryOQL' => __DIR__ . '/../..' . '/application/query.class.inc.php', + 'QueryReflection' => __DIR__ . '/../..' . '/core/modelreflection.class.inc.php', + 'QueryReflectionRuntime' => __DIR__ . '/../..' . '/core/modelreflection.class.inc.php', + 'RelationEdge' => __DIR__ . '/../..' . '/core/relationgraph.class.inc.php', + 'RelationGraph' => __DIR__ . '/../..' . '/core/relationgraph.class.inc.php', + 'RelationObjectNode' => __DIR__ . '/../..' . '/core/relationgraph.class.inc.php', + 'RelationRedundancyNode' => __DIR__ . '/../..' . '/core/relationgraph.class.inc.php', + 'RelationTypeIterator' => __DIR__ . '/../..' . '/core/simplegraph.class.inc.php', + 'RestDelete' => __DIR__ . '/../..' . '/core/restservices.class.inc.php', + 'RestResult' => __DIR__ . '/../..' . '/application/applicationextension.inc.php', + 'RestResultWithObjects' => __DIR__ . '/../..' . '/core/restservices.class.inc.php', + 'RestResultWithRelations' => __DIR__ . '/../..' . '/core/restservices.class.inc.php', + 'RestUtils' => __DIR__ . '/../..' . '/application/applicationextension.inc.php', + 'RowStatus' => __DIR__ . '/../..' . '/core/bulkchange.class.inc.php', + 'RowStatus_Disappeared' => __DIR__ . '/../..' . '/core/bulkchange.class.inc.php', + 'RowStatus_Issue' => __DIR__ . '/../..' . '/core/bulkchange.class.inc.php', + 'RowStatus_Modify' => __DIR__ . '/../..' . '/core/bulkchange.class.inc.php', + 'RowStatus_NewObj' => __DIR__ . '/../..' . '/core/bulkchange.class.inc.php', + 'RowStatus_NoChange' => __DIR__ . '/../..' . '/core/bulkchange.class.inc.php', + 'RunTimeIconSelectionField' => __DIR__ . '/../..' . '/application/forms.class.inc.php', + 'RuntimeDashboard' => __DIR__ . '/../..' . '/application/dashboard.class.inc.php', + 'SQLExpression' => __DIR__ . '/../..' . '/core/oql/expression.class.inc.php', + 'SQLObjectQuery' => __DIR__ . '/../..' . '/core/sqlobjectquery.class.inc.php', + 'SQLQuery' => __DIR__ . '/../..' . '/core/sqlquery.class.inc.php', + 'SQLUnionQuery' => __DIR__ . '/../..' . '/core/sqlunionquery.class.inc.php', + 'ScalarExpression' => __DIR__ . '/../..' . '/core/oql/expression.class.inc.php', + 'ScalarOqlExpression' => __DIR__ . '/../..' . '/core/oql/oqlquery.class.inc.php', + 'SearchMenuNode' => __DIR__ . '/../..' . '/application/menunode.class.inc.php', + 'SecurityException' => __DIR__ . '/../..' . '/core/coreexception.class.inc.php', + 'SeparatorPopupMenuItem' => __DIR__ . '/../..' . '/application/applicationextension.inc.php', + 'SessionUpdateTimestampHandlerInterface' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/SessionUpdateTimestampHandlerInterface.php', + 'SetupLog' => __DIR__ . '/../..' . '/core/log.class.inc.php', + 'Shortcut' => __DIR__ . '/../..' . '/application/shortcut.class.inc.php', + 'ShortcutContainerMenuNode' => __DIR__ . '/../..' . '/application/menunode.class.inc.php', + 'ShortcutMenuNode' => __DIR__ . '/../..' . '/application/menunode.class.inc.php', + 'ShortcutOQL' => __DIR__ . '/../..' . '/application/shortcut.class.inc.php', + 'SimpleCrypt' => __DIR__ . '/../..' . '/core/simplecrypt.class.inc.php', + 'SimpleCryptMcryptEngine' => __DIR__ . '/../..' . '/core/simplecrypt.class.inc.php', + 'SimpleCryptOpenSSLEngine' => __DIR__ . '/../..' . '/core/simplecrypt.class.inc.php', + 'SimpleCryptOpenSSLMcryptCompatibilityEngine' => __DIR__ . '/../..' . '/core/simplecrypt.class.inc.php', + 'SimpleCryptSimpleEngine' => __DIR__ . '/../..' . '/core/simplecrypt.class.inc.php', + 'SimpleCryptSodiumEngine' => __DIR__ . '/../..' . '/core/simplecrypt.class.inc.php', + 'SimpleGraph' => __DIR__ . '/../..' . '/core/simplegraph.class.inc.php', + 'SimpleGraphException' => __DIR__ . '/../..' . '/core/simplegraph.class.inc.php', + 'SpreadsheetBulkExport' => __DIR__ . '/../..' . '/core/spreadsheetbulkexport.class.inc.php', + 'StimulusChecker' => __DIR__ . '/../..' . '/core/userrights.class.inc.php', + 'StimulusInternal' => __DIR__ . '/../..' . '/core/stimulus.class.inc.php', + 'StimulusUserAction' => __DIR__ . '/../..' . '/core/stimulus.class.inc.php', + 'Str' => __DIR__ . '/../..' . '/core/MyHelpers.class.inc.php', + 'Swift_LogFileTransport' => __DIR__ . '/../..' . '/core/email.class.inc.php', + 'Swift_Transport_LogFileTransport' => __DIR__ . '/../..' . '/core/email.class.inc.php', + 'Symfony\\Bridge\\Twig\\AppVariable' => __DIR__ . '/..' . '/symfony/twig-bridge/AppVariable.php', + 'Symfony\\Bridge\\Twig\\Command\\DebugCommand' => __DIR__ . '/..' . '/symfony/twig-bridge/Command/DebugCommand.php', + 'Symfony\\Bridge\\Twig\\Command\\LintCommand' => __DIR__ . '/..' . '/symfony/twig-bridge/Command/LintCommand.php', + 'Symfony\\Bridge\\Twig\\DataCollector\\TwigDataCollector' => __DIR__ . '/..' . '/symfony/twig-bridge/DataCollector/TwigDataCollector.php', + 'Symfony\\Bridge\\Twig\\Extension\\AssetExtension' => __DIR__ . '/..' . '/symfony/twig-bridge/Extension/AssetExtension.php', + 'Symfony\\Bridge\\Twig\\Extension\\CodeExtension' => __DIR__ . '/..' . '/symfony/twig-bridge/Extension/CodeExtension.php', + 'Symfony\\Bridge\\Twig\\Extension\\DumpExtension' => __DIR__ . '/..' . '/symfony/twig-bridge/Extension/DumpExtension.php', + 'Symfony\\Bridge\\Twig\\Extension\\ExpressionExtension' => __DIR__ . '/..' . '/symfony/twig-bridge/Extension/ExpressionExtension.php', + 'Symfony\\Bridge\\Twig\\Extension\\FormExtension' => __DIR__ . '/..' . '/symfony/twig-bridge/Extension/FormExtension.php', + 'Symfony\\Bridge\\Twig\\Extension\\HttpFoundationExtension' => __DIR__ . '/..' . '/symfony/twig-bridge/Extension/HttpFoundationExtension.php', + 'Symfony\\Bridge\\Twig\\Extension\\HttpKernelExtension' => __DIR__ . '/..' . '/symfony/twig-bridge/Extension/HttpKernelExtension.php', + 'Symfony\\Bridge\\Twig\\Extension\\HttpKernelRuntime' => __DIR__ . '/..' . '/symfony/twig-bridge/Extension/HttpKernelRuntime.php', + 'Symfony\\Bridge\\Twig\\Extension\\InitRuntimeInterface' => __DIR__ . '/..' . '/symfony/twig-bridge/Extension/InitRuntimeInterface.php', + 'Symfony\\Bridge\\Twig\\Extension\\LogoutUrlExtension' => __DIR__ . '/..' . '/symfony/twig-bridge/Extension/LogoutUrlExtension.php', + 'Symfony\\Bridge\\Twig\\Extension\\ProfilerExtension' => __DIR__ . '/..' . '/symfony/twig-bridge/Extension/ProfilerExtension.php', + 'Symfony\\Bridge\\Twig\\Extension\\RoutingExtension' => __DIR__ . '/..' . '/symfony/twig-bridge/Extension/RoutingExtension.php', + 'Symfony\\Bridge\\Twig\\Extension\\SecurityExtension' => __DIR__ . '/..' . '/symfony/twig-bridge/Extension/SecurityExtension.php', + 'Symfony\\Bridge\\Twig\\Extension\\StopwatchExtension' => __DIR__ . '/..' . '/symfony/twig-bridge/Extension/StopwatchExtension.php', + 'Symfony\\Bridge\\Twig\\Extension\\TranslationExtension' => __DIR__ . '/..' . '/symfony/twig-bridge/Extension/TranslationExtension.php', + 'Symfony\\Bridge\\Twig\\Extension\\WebLinkExtension' => __DIR__ . '/..' . '/symfony/twig-bridge/Extension/WebLinkExtension.php', + 'Symfony\\Bridge\\Twig\\Extension\\WorkflowExtension' => __DIR__ . '/..' . '/symfony/twig-bridge/Extension/WorkflowExtension.php', + 'Symfony\\Bridge\\Twig\\Extension\\YamlExtension' => __DIR__ . '/..' . '/symfony/twig-bridge/Extension/YamlExtension.php', + 'Symfony\\Bridge\\Twig\\Form\\TwigRenderer' => __DIR__ . '/..' . '/symfony/twig-bridge/Form/TwigRenderer.php', + 'Symfony\\Bridge\\Twig\\Form\\TwigRendererEngine' => __DIR__ . '/..' . '/symfony/twig-bridge/Form/TwigRendererEngine.php', + 'Symfony\\Bridge\\Twig\\Form\\TwigRendererEngineInterface' => __DIR__ . '/..' . '/symfony/twig-bridge/Form/TwigRendererEngineInterface.php', + 'Symfony\\Bridge\\Twig\\Form\\TwigRendererInterface' => __DIR__ . '/..' . '/symfony/twig-bridge/Form/TwigRendererInterface.php', + 'Symfony\\Bridge\\Twig\\NodeVisitor\\Scope' => __DIR__ . '/..' . '/symfony/twig-bridge/NodeVisitor/Scope.php', + 'Symfony\\Bridge\\Twig\\NodeVisitor\\TranslationDefaultDomainNodeVisitor' => __DIR__ . '/..' . '/symfony/twig-bridge/NodeVisitor/TranslationDefaultDomainNodeVisitor.php', + 'Symfony\\Bridge\\Twig\\NodeVisitor\\TranslationNodeVisitor' => __DIR__ . '/..' . '/symfony/twig-bridge/NodeVisitor/TranslationNodeVisitor.php', + 'Symfony\\Bridge\\Twig\\Node\\DumpNode' => __DIR__ . '/..' . '/symfony/twig-bridge/Node/DumpNode.php', + 'Symfony\\Bridge\\Twig\\Node\\FormThemeNode' => __DIR__ . '/..' . '/symfony/twig-bridge/Node/FormThemeNode.php', + 'Symfony\\Bridge\\Twig\\Node\\RenderBlockNode' => __DIR__ . '/..' . '/symfony/twig-bridge/Node/RenderBlockNode.php', + 'Symfony\\Bridge\\Twig\\Node\\SearchAndRenderBlockNode' => __DIR__ . '/..' . '/symfony/twig-bridge/Node/SearchAndRenderBlockNode.php', + 'Symfony\\Bridge\\Twig\\Node\\StopwatchNode' => __DIR__ . '/..' . '/symfony/twig-bridge/Node/StopwatchNode.php', + 'Symfony\\Bridge\\Twig\\Node\\TransDefaultDomainNode' => __DIR__ . '/..' . '/symfony/twig-bridge/Node/TransDefaultDomainNode.php', + 'Symfony\\Bridge\\Twig\\Node\\TransNode' => __DIR__ . '/..' . '/symfony/twig-bridge/Node/TransNode.php', + 'Symfony\\Bridge\\Twig\\TokenParser\\DumpTokenParser' => __DIR__ . '/..' . '/symfony/twig-bridge/TokenParser/DumpTokenParser.php', + 'Symfony\\Bridge\\Twig\\TokenParser\\FormThemeTokenParser' => __DIR__ . '/..' . '/symfony/twig-bridge/TokenParser/FormThemeTokenParser.php', + 'Symfony\\Bridge\\Twig\\TokenParser\\StopwatchTokenParser' => __DIR__ . '/..' . '/symfony/twig-bridge/TokenParser/StopwatchTokenParser.php', + 'Symfony\\Bridge\\Twig\\TokenParser\\TransChoiceTokenParser' => __DIR__ . '/..' . '/symfony/twig-bridge/TokenParser/TransChoiceTokenParser.php', + 'Symfony\\Bridge\\Twig\\TokenParser\\TransDefaultDomainTokenParser' => __DIR__ . '/..' . '/symfony/twig-bridge/TokenParser/TransDefaultDomainTokenParser.php', + 'Symfony\\Bridge\\Twig\\TokenParser\\TransTokenParser' => __DIR__ . '/..' . '/symfony/twig-bridge/TokenParser/TransTokenParser.php', + 'Symfony\\Bridge\\Twig\\Translation\\TwigExtractor' => __DIR__ . '/..' . '/symfony/twig-bridge/Translation/TwigExtractor.php', + 'Symfony\\Bridge\\Twig\\TwigEngine' => __DIR__ . '/..' . '/symfony/twig-bridge/TwigEngine.php', + 'Symfony\\Bridge\\Twig\\UndefinedCallableHandler' => __DIR__ . '/..' . '/symfony/twig-bridge/UndefinedCallableHandler.php', + 'Symfony\\Bundle\\FrameworkBundle\\CacheWarmer\\AbstractPhpFileCacheWarmer' => __DIR__ . '/..' . '/symfony/framework-bundle/CacheWarmer/AbstractPhpFileCacheWarmer.php', + 'Symfony\\Bundle\\FrameworkBundle\\CacheWarmer\\AnnotationsCacheWarmer' => __DIR__ . '/..' . '/symfony/framework-bundle/CacheWarmer/AnnotationsCacheWarmer.php', + 'Symfony\\Bundle\\FrameworkBundle\\CacheWarmer\\ClassCacheCacheWarmer' => __DIR__ . '/..' . '/symfony/framework-bundle/CacheWarmer/ClassCacheCacheWarmer.php', + 'Symfony\\Bundle\\FrameworkBundle\\CacheWarmer\\RouterCacheWarmer' => __DIR__ . '/..' . '/symfony/framework-bundle/CacheWarmer/RouterCacheWarmer.php', + 'Symfony\\Bundle\\FrameworkBundle\\CacheWarmer\\SerializerCacheWarmer' => __DIR__ . '/..' . '/symfony/framework-bundle/CacheWarmer/SerializerCacheWarmer.php', + 'Symfony\\Bundle\\FrameworkBundle\\CacheWarmer\\TemplateFinder' => __DIR__ . '/..' . '/symfony/framework-bundle/CacheWarmer/TemplateFinder.php', + 'Symfony\\Bundle\\FrameworkBundle\\CacheWarmer\\TemplateFinderInterface' => __DIR__ . '/..' . '/symfony/framework-bundle/CacheWarmer/TemplateFinderInterface.php', + 'Symfony\\Bundle\\FrameworkBundle\\CacheWarmer\\TemplatePathsCacheWarmer' => __DIR__ . '/..' . '/symfony/framework-bundle/CacheWarmer/TemplatePathsCacheWarmer.php', + 'Symfony\\Bundle\\FrameworkBundle\\CacheWarmer\\TranslationsCacheWarmer' => __DIR__ . '/..' . '/symfony/framework-bundle/CacheWarmer/TranslationsCacheWarmer.php', + 'Symfony\\Bundle\\FrameworkBundle\\CacheWarmer\\ValidatorCacheWarmer' => __DIR__ . '/..' . '/symfony/framework-bundle/CacheWarmer/ValidatorCacheWarmer.php', + 'Symfony\\Bundle\\FrameworkBundle\\Client' => __DIR__ . '/..' . '/symfony/framework-bundle/Client.php', + 'Symfony\\Bundle\\FrameworkBundle\\Command\\AboutCommand' => __DIR__ . '/..' . '/symfony/framework-bundle/Command/AboutCommand.php', + 'Symfony\\Bundle\\FrameworkBundle\\Command\\AbstractConfigCommand' => __DIR__ . '/..' . '/symfony/framework-bundle/Command/AbstractConfigCommand.php', + 'Symfony\\Bundle\\FrameworkBundle\\Command\\AssetsInstallCommand' => __DIR__ . '/..' . '/symfony/framework-bundle/Command/AssetsInstallCommand.php', + 'Symfony\\Bundle\\FrameworkBundle\\Command\\CacheClearCommand' => __DIR__ . '/..' . '/symfony/framework-bundle/Command/CacheClearCommand.php', + 'Symfony\\Bundle\\FrameworkBundle\\Command\\CachePoolClearCommand' => __DIR__ . '/..' . '/symfony/framework-bundle/Command/CachePoolClearCommand.php', + 'Symfony\\Bundle\\FrameworkBundle\\Command\\CachePoolPruneCommand' => __DIR__ . '/..' . '/symfony/framework-bundle/Command/CachePoolPruneCommand.php', + 'Symfony\\Bundle\\FrameworkBundle\\Command\\CacheWarmupCommand' => __DIR__ . '/..' . '/symfony/framework-bundle/Command/CacheWarmupCommand.php', + 'Symfony\\Bundle\\FrameworkBundle\\Command\\ConfigDebugCommand' => __DIR__ . '/..' . '/symfony/framework-bundle/Command/ConfigDebugCommand.php', + 'Symfony\\Bundle\\FrameworkBundle\\Command\\ConfigDumpReferenceCommand' => __DIR__ . '/..' . '/symfony/framework-bundle/Command/ConfigDumpReferenceCommand.php', + 'Symfony\\Bundle\\FrameworkBundle\\Command\\ContainerAwareCommand' => __DIR__ . '/..' . '/symfony/framework-bundle/Command/ContainerAwareCommand.php', + 'Symfony\\Bundle\\FrameworkBundle\\Command\\ContainerDebugCommand' => __DIR__ . '/..' . '/symfony/framework-bundle/Command/ContainerDebugCommand.php', + 'Symfony\\Bundle\\FrameworkBundle\\Command\\DebugAutowiringCommand' => __DIR__ . '/..' . '/symfony/framework-bundle/Command/DebugAutowiringCommand.php', + 'Symfony\\Bundle\\FrameworkBundle\\Command\\EventDispatcherDebugCommand' => __DIR__ . '/..' . '/symfony/framework-bundle/Command/EventDispatcherDebugCommand.php', + 'Symfony\\Bundle\\FrameworkBundle\\Command\\RouterDebugCommand' => __DIR__ . '/..' . '/symfony/framework-bundle/Command/RouterDebugCommand.php', + 'Symfony\\Bundle\\FrameworkBundle\\Command\\RouterMatchCommand' => __DIR__ . '/..' . '/symfony/framework-bundle/Command/RouterMatchCommand.php', + 'Symfony\\Bundle\\FrameworkBundle\\Command\\TranslationDebugCommand' => __DIR__ . '/..' . '/symfony/framework-bundle/Command/TranslationDebugCommand.php', + 'Symfony\\Bundle\\FrameworkBundle\\Command\\TranslationUpdateCommand' => __DIR__ . '/..' . '/symfony/framework-bundle/Command/TranslationUpdateCommand.php', + 'Symfony\\Bundle\\FrameworkBundle\\Command\\WorkflowDumpCommand' => __DIR__ . '/..' . '/symfony/framework-bundle/Command/WorkflowDumpCommand.php', + 'Symfony\\Bundle\\FrameworkBundle\\Command\\XliffLintCommand' => __DIR__ . '/..' . '/symfony/framework-bundle/Command/XliffLintCommand.php', + 'Symfony\\Bundle\\FrameworkBundle\\Command\\YamlLintCommand' => __DIR__ . '/..' . '/symfony/framework-bundle/Command/YamlLintCommand.php', + 'Symfony\\Bundle\\FrameworkBundle\\Console\\Application' => __DIR__ . '/..' . '/symfony/framework-bundle/Console/Application.php', + 'Symfony\\Bundle\\FrameworkBundle\\Console\\Descriptor\\Descriptor' => __DIR__ . '/..' . '/symfony/framework-bundle/Console/Descriptor/Descriptor.php', + 'Symfony\\Bundle\\FrameworkBundle\\Console\\Descriptor\\JsonDescriptor' => __DIR__ . '/..' . '/symfony/framework-bundle/Console/Descriptor/JsonDescriptor.php', + 'Symfony\\Bundle\\FrameworkBundle\\Console\\Descriptor\\MarkdownDescriptor' => __DIR__ . '/..' . '/symfony/framework-bundle/Console/Descriptor/MarkdownDescriptor.php', + 'Symfony\\Bundle\\FrameworkBundle\\Console\\Descriptor\\TextDescriptor' => __DIR__ . '/..' . '/symfony/framework-bundle/Console/Descriptor/TextDescriptor.php', + 'Symfony\\Bundle\\FrameworkBundle\\Console\\Descriptor\\XmlDescriptor' => __DIR__ . '/..' . '/symfony/framework-bundle/Console/Descriptor/XmlDescriptor.php', + 'Symfony\\Bundle\\FrameworkBundle\\Console\\Helper\\DescriptorHelper' => __DIR__ . '/..' . '/symfony/framework-bundle/Console/Helper/DescriptorHelper.php', + 'Symfony\\Bundle\\FrameworkBundle\\Controller\\AbstractController' => __DIR__ . '/..' . '/symfony/framework-bundle/Controller/AbstractController.php', + 'Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller' => __DIR__ . '/..' . '/symfony/framework-bundle/Controller/Controller.php', + 'Symfony\\Bundle\\FrameworkBundle\\Controller\\ControllerNameParser' => __DIR__ . '/..' . '/symfony/framework-bundle/Controller/ControllerNameParser.php', + 'Symfony\\Bundle\\FrameworkBundle\\Controller\\ControllerResolver' => __DIR__ . '/..' . '/symfony/framework-bundle/Controller/ControllerResolver.php', + 'Symfony\\Bundle\\FrameworkBundle\\Controller\\ControllerTrait' => __DIR__ . '/..' . '/symfony/framework-bundle/Controller/ControllerTrait.php', + 'Symfony\\Bundle\\FrameworkBundle\\Controller\\RedirectController' => __DIR__ . '/..' . '/symfony/framework-bundle/Controller/RedirectController.php', + 'Symfony\\Bundle\\FrameworkBundle\\Controller\\TemplateController' => __DIR__ . '/..' . '/symfony/framework-bundle/Controller/TemplateController.php', + 'Symfony\\Bundle\\FrameworkBundle\\DataCollector\\RequestDataCollector' => __DIR__ . '/..' . '/symfony/framework-bundle/DataCollector/RequestDataCollector.php', + 'Symfony\\Bundle\\FrameworkBundle\\DataCollector\\RouterDataCollector' => __DIR__ . '/..' . '/symfony/framework-bundle/DataCollector/RouterDataCollector.php', + 'Symfony\\Bundle\\FrameworkBundle\\DependencyInjection\\Compiler\\AddAnnotationsCachedReaderPass' => __DIR__ . '/..' . '/symfony/framework-bundle/DependencyInjection/Compiler/AddAnnotationsCachedReaderPass.php', + 'Symfony\\Bundle\\FrameworkBundle\\DependencyInjection\\Compiler\\AddCacheClearerPass' => __DIR__ . '/..' . '/symfony/framework-bundle/DependencyInjection/Compiler/AddCacheClearerPass.php', + 'Symfony\\Bundle\\FrameworkBundle\\DependencyInjection\\Compiler\\AddCacheWarmerPass' => __DIR__ . '/..' . '/symfony/framework-bundle/DependencyInjection/Compiler/AddCacheWarmerPass.php', + 'Symfony\\Bundle\\FrameworkBundle\\DependencyInjection\\Compiler\\AddConsoleCommandPass' => __DIR__ . '/..' . '/symfony/framework-bundle/DependencyInjection/Compiler/AddConsoleCommandPass.php', + 'Symfony\\Bundle\\FrameworkBundle\\DependencyInjection\\Compiler\\AddConstraintValidatorsPass' => __DIR__ . '/..' . '/symfony/framework-bundle/DependencyInjection/Compiler/AddConstraintValidatorsPass.php', + 'Symfony\\Bundle\\FrameworkBundle\\DependencyInjection\\Compiler\\AddDebugLogProcessorPass' => __DIR__ . '/..' . '/symfony/framework-bundle/DependencyInjection/Compiler/AddDebugLogProcessorPass.php', + 'Symfony\\Bundle\\FrameworkBundle\\DependencyInjection\\Compiler\\AddExpressionLanguageProvidersPass' => __DIR__ . '/..' . '/symfony/framework-bundle/DependencyInjection/Compiler/AddExpressionLanguageProvidersPass.php', + 'Symfony\\Bundle\\FrameworkBundle\\DependencyInjection\\Compiler\\AddValidatorInitializersPass' => __DIR__ . '/..' . '/symfony/framework-bundle/DependencyInjection/Compiler/AddValidatorInitializersPass.php', + 'Symfony\\Bundle\\FrameworkBundle\\DependencyInjection\\Compiler\\CacheCollectorPass' => __DIR__ . '/..' . '/symfony/framework-bundle/DependencyInjection/Compiler/CacheCollectorPass.php', + 'Symfony\\Bundle\\FrameworkBundle\\DependencyInjection\\Compiler\\CachePoolClearerPass' => __DIR__ . '/..' . '/symfony/framework-bundle/DependencyInjection/Compiler/CachePoolClearerPass.php', + 'Symfony\\Bundle\\FrameworkBundle\\DependencyInjection\\Compiler\\CachePoolPass' => __DIR__ . '/..' . '/symfony/framework-bundle/DependencyInjection/Compiler/CachePoolPass.php', + 'Symfony\\Bundle\\FrameworkBundle\\DependencyInjection\\Compiler\\CachePoolPrunerPass' => __DIR__ . '/..' . '/symfony/framework-bundle/DependencyInjection/Compiler/CachePoolPrunerPass.php', + 'Symfony\\Bundle\\FrameworkBundle\\DependencyInjection\\Compiler\\CompilerDebugDumpPass' => __DIR__ . '/..' . '/symfony/framework-bundle/DependencyInjection/Compiler/CompilerDebugDumpPass.php', + 'Symfony\\Bundle\\FrameworkBundle\\DependencyInjection\\Compiler\\ConfigCachePass' => __DIR__ . '/..' . '/symfony/framework-bundle/DependencyInjection/Compiler/ConfigCachePass.php', + 'Symfony\\Bundle\\FrameworkBundle\\DependencyInjection\\Compiler\\ContainerBuilderDebugDumpPass' => __DIR__ . '/..' . '/symfony/framework-bundle/DependencyInjection/Compiler/ContainerBuilderDebugDumpPass.php', + 'Symfony\\Bundle\\FrameworkBundle\\DependencyInjection\\Compiler\\ControllerArgumentValueResolverPass' => __DIR__ . '/..' . '/symfony/framework-bundle/DependencyInjection/Compiler/ControllerArgumentValueResolverPass.php', + 'Symfony\\Bundle\\FrameworkBundle\\DependencyInjection\\Compiler\\DataCollectorTranslatorPass' => __DIR__ . '/..' . '/symfony/framework-bundle/DependencyInjection/Compiler/DataCollectorTranslatorPass.php', + 'Symfony\\Bundle\\FrameworkBundle\\DependencyInjection\\Compiler\\FormPass' => __DIR__ . '/..' . '/symfony/framework-bundle/DependencyInjection/Compiler/FormPass.php', + 'Symfony\\Bundle\\FrameworkBundle\\DependencyInjection\\Compiler\\LoggingTranslatorPass' => __DIR__ . '/..' . '/symfony/framework-bundle/DependencyInjection/Compiler/LoggingTranslatorPass.php', + 'Symfony\\Bundle\\FrameworkBundle\\DependencyInjection\\Compiler\\ProfilerPass' => __DIR__ . '/..' . '/symfony/framework-bundle/DependencyInjection/Compiler/ProfilerPass.php', + 'Symfony\\Bundle\\FrameworkBundle\\DependencyInjection\\Compiler\\PropertyInfoPass' => __DIR__ . '/..' . '/symfony/framework-bundle/DependencyInjection/Compiler/PropertyInfoPass.php', + 'Symfony\\Bundle\\FrameworkBundle\\DependencyInjection\\Compiler\\RoutingResolverPass' => __DIR__ . '/..' . '/symfony/framework-bundle/DependencyInjection/Compiler/RoutingResolverPass.php', + 'Symfony\\Bundle\\FrameworkBundle\\DependencyInjection\\Compiler\\SerializerPass' => __DIR__ . '/..' . '/symfony/framework-bundle/DependencyInjection/Compiler/SerializerPass.php', + 'Symfony\\Bundle\\FrameworkBundle\\DependencyInjection\\Compiler\\TemplatingPass' => __DIR__ . '/..' . '/symfony/framework-bundle/DependencyInjection/Compiler/TemplatingPass.php', + 'Symfony\\Bundle\\FrameworkBundle\\DependencyInjection\\Compiler\\TranslationDumperPass' => __DIR__ . '/..' . '/symfony/framework-bundle/DependencyInjection/Compiler/TranslationDumperPass.php', + 'Symfony\\Bundle\\FrameworkBundle\\DependencyInjection\\Compiler\\TranslationExtractorPass' => __DIR__ . '/..' . '/symfony/framework-bundle/DependencyInjection/Compiler/TranslationExtractorPass.php', + 'Symfony\\Bundle\\FrameworkBundle\\DependencyInjection\\Compiler\\TranslatorPass' => __DIR__ . '/..' . '/symfony/framework-bundle/DependencyInjection/Compiler/TranslatorPass.php', + 'Symfony\\Bundle\\FrameworkBundle\\DependencyInjection\\Compiler\\UnusedTagsPass' => __DIR__ . '/..' . '/symfony/framework-bundle/DependencyInjection/Compiler/UnusedTagsPass.php', + 'Symfony\\Bundle\\FrameworkBundle\\DependencyInjection\\Compiler\\ValidateWorkflowsPass' => __DIR__ . '/..' . '/symfony/framework-bundle/DependencyInjection/Compiler/ValidateWorkflowsPass.php', + 'Symfony\\Bundle\\FrameworkBundle\\DependencyInjection\\Compiler\\WorkflowGuardListenerPass' => __DIR__ . '/..' . '/symfony/framework-bundle/DependencyInjection/Compiler/WorkflowGuardListenerPass.php', + 'Symfony\\Bundle\\FrameworkBundle\\DependencyInjection\\Configuration' => __DIR__ . '/..' . '/symfony/framework-bundle/DependencyInjection/Configuration.php', + 'Symfony\\Bundle\\FrameworkBundle\\DependencyInjection\\FrameworkExtension' => __DIR__ . '/..' . '/symfony/framework-bundle/DependencyInjection/FrameworkExtension.php', + 'Symfony\\Bundle\\FrameworkBundle\\EventListener\\ResolveControllerNameSubscriber' => __DIR__ . '/..' . '/symfony/framework-bundle/EventListener/ResolveControllerNameSubscriber.php', + 'Symfony\\Bundle\\FrameworkBundle\\EventListener\\SessionListener' => __DIR__ . '/..' . '/symfony/framework-bundle/EventListener/SessionListener.php', + 'Symfony\\Bundle\\FrameworkBundle\\EventListener\\TestSessionListener' => __DIR__ . '/..' . '/symfony/framework-bundle/EventListener/TestSessionListener.php', + 'Symfony\\Bundle\\FrameworkBundle\\FrameworkBundle' => __DIR__ . '/..' . '/symfony/framework-bundle/FrameworkBundle.php', + 'Symfony\\Bundle\\FrameworkBundle\\HttpCache\\HttpCache' => __DIR__ . '/..' . '/symfony/framework-bundle/HttpCache/HttpCache.php', + 'Symfony\\Bundle\\FrameworkBundle\\Kernel\\MicroKernelTrait' => __DIR__ . '/..' . '/symfony/framework-bundle/Kernel/MicroKernelTrait.php', + 'Symfony\\Bundle\\FrameworkBundle\\Routing\\AnnotatedRouteControllerLoader' => __DIR__ . '/..' . '/symfony/framework-bundle/Routing/AnnotatedRouteControllerLoader.php', + 'Symfony\\Bundle\\FrameworkBundle\\Routing\\DelegatingLoader' => __DIR__ . '/..' . '/symfony/framework-bundle/Routing/DelegatingLoader.php', + 'Symfony\\Bundle\\FrameworkBundle\\Routing\\RedirectableUrlMatcher' => __DIR__ . '/..' . '/symfony/framework-bundle/Routing/RedirectableUrlMatcher.php', + 'Symfony\\Bundle\\FrameworkBundle\\Routing\\Router' => __DIR__ . '/..' . '/symfony/framework-bundle/Routing/Router.php', + 'Symfony\\Bundle\\FrameworkBundle\\Templating\\DelegatingEngine' => __DIR__ . '/..' . '/symfony/framework-bundle/Templating/DelegatingEngine.php', + 'Symfony\\Bundle\\FrameworkBundle\\Templating\\EngineInterface' => __DIR__ . '/..' . '/symfony/framework-bundle/Templating/EngineInterface.php', + 'Symfony\\Bundle\\FrameworkBundle\\Templating\\GlobalVariables' => __DIR__ . '/..' . '/symfony/framework-bundle/Templating/GlobalVariables.php', + 'Symfony\\Bundle\\FrameworkBundle\\Templating\\Helper\\ActionsHelper' => __DIR__ . '/..' . '/symfony/framework-bundle/Templating/Helper/ActionsHelper.php', + 'Symfony\\Bundle\\FrameworkBundle\\Templating\\Helper\\AssetsHelper' => __DIR__ . '/..' . '/symfony/framework-bundle/Templating/Helper/AssetsHelper.php', + 'Symfony\\Bundle\\FrameworkBundle\\Templating\\Helper\\CodeHelper' => __DIR__ . '/..' . '/symfony/framework-bundle/Templating/Helper/CodeHelper.php', + 'Symfony\\Bundle\\FrameworkBundle\\Templating\\Helper\\FormHelper' => __DIR__ . '/..' . '/symfony/framework-bundle/Templating/Helper/FormHelper.php', + 'Symfony\\Bundle\\FrameworkBundle\\Templating\\Helper\\RequestHelper' => __DIR__ . '/..' . '/symfony/framework-bundle/Templating/Helper/RequestHelper.php', + 'Symfony\\Bundle\\FrameworkBundle\\Templating\\Helper\\RouterHelper' => __DIR__ . '/..' . '/symfony/framework-bundle/Templating/Helper/RouterHelper.php', + 'Symfony\\Bundle\\FrameworkBundle\\Templating\\Helper\\SessionHelper' => __DIR__ . '/..' . '/symfony/framework-bundle/Templating/Helper/SessionHelper.php', + 'Symfony\\Bundle\\FrameworkBundle\\Templating\\Helper\\StopwatchHelper' => __DIR__ . '/..' . '/symfony/framework-bundle/Templating/Helper/StopwatchHelper.php', + 'Symfony\\Bundle\\FrameworkBundle\\Templating\\Helper\\TranslatorHelper' => __DIR__ . '/..' . '/symfony/framework-bundle/Templating/Helper/TranslatorHelper.php', + 'Symfony\\Bundle\\FrameworkBundle\\Templating\\Loader\\FilesystemLoader' => __DIR__ . '/..' . '/symfony/framework-bundle/Templating/Loader/FilesystemLoader.php', + 'Symfony\\Bundle\\FrameworkBundle\\Templating\\Loader\\TemplateLocator' => __DIR__ . '/..' . '/symfony/framework-bundle/Templating/Loader/TemplateLocator.php', + 'Symfony\\Bundle\\FrameworkBundle\\Templating\\PhpEngine' => __DIR__ . '/..' . '/symfony/framework-bundle/Templating/PhpEngine.php', + 'Symfony\\Bundle\\FrameworkBundle\\Templating\\TemplateFilenameParser' => __DIR__ . '/..' . '/symfony/framework-bundle/Templating/TemplateFilenameParser.php', + 'Symfony\\Bundle\\FrameworkBundle\\Templating\\TemplateNameParser' => __DIR__ . '/..' . '/symfony/framework-bundle/Templating/TemplateNameParser.php', + 'Symfony\\Bundle\\FrameworkBundle\\Templating\\TemplateReference' => __DIR__ . '/..' . '/symfony/framework-bundle/Templating/TemplateReference.php', + 'Symfony\\Bundle\\FrameworkBundle\\Templating\\TimedPhpEngine' => __DIR__ . '/..' . '/symfony/framework-bundle/Templating/TimedPhpEngine.php', + 'Symfony\\Bundle\\FrameworkBundle\\Test\\KernelShutdownOnTearDownTrait' => __DIR__ . '/..' . '/symfony/framework-bundle/Test/KernelShutdownOnTearDownTrait.php', + 'Symfony\\Bundle\\FrameworkBundle\\Test\\KernelTestCase' => __DIR__ . '/..' . '/symfony/framework-bundle/Test/KernelTestCase.php', + 'Symfony\\Bundle\\FrameworkBundle\\Test\\WebTestCase' => __DIR__ . '/..' . '/symfony/framework-bundle/Test/WebTestCase.php', + 'Symfony\\Bundle\\FrameworkBundle\\Translation\\PhpExtractor' => __DIR__ . '/..' . '/symfony/framework-bundle/Translation/PhpExtractor.php', + 'Symfony\\Bundle\\FrameworkBundle\\Translation\\PhpStringTokenParser' => __DIR__ . '/..' . '/symfony/framework-bundle/Translation/PhpStringTokenParser.php', + 'Symfony\\Bundle\\FrameworkBundle\\Translation\\TranslationLoader' => __DIR__ . '/..' . '/symfony/framework-bundle/Translation/TranslationLoader.php', + 'Symfony\\Bundle\\FrameworkBundle\\Translation\\Translator' => __DIR__ . '/..' . '/symfony/framework-bundle/Translation/Translator.php', + 'Symfony\\Bundle\\FrameworkBundle\\Validator\\ConstraintValidatorFactory' => __DIR__ . '/..' . '/symfony/framework-bundle/Validator/ConstraintValidatorFactory.php', + 'Symfony\\Bundle\\TwigBundle\\CacheWarmer\\TemplateCacheCacheWarmer' => __DIR__ . '/..' . '/symfony/twig-bundle/CacheWarmer/TemplateCacheCacheWarmer.php', + 'Symfony\\Bundle\\TwigBundle\\CacheWarmer\\TemplateCacheWarmer' => __DIR__ . '/..' . '/symfony/twig-bundle/CacheWarmer/TemplateCacheWarmer.php', + 'Symfony\\Bundle\\TwigBundle\\Command\\DebugCommand' => __DIR__ . '/..' . '/symfony/twig-bundle/Command/DebugCommand.php', + 'Symfony\\Bundle\\TwigBundle\\Command\\LintCommand' => __DIR__ . '/..' . '/symfony/twig-bundle/Command/LintCommand.php', + 'Symfony\\Bundle\\TwigBundle\\ContainerAwareRuntimeLoader' => __DIR__ . '/..' . '/symfony/twig-bundle/ContainerAwareRuntimeLoader.php', + 'Symfony\\Bundle\\TwigBundle\\Controller\\ExceptionController' => __DIR__ . '/..' . '/symfony/twig-bundle/Controller/ExceptionController.php', + 'Symfony\\Bundle\\TwigBundle\\Controller\\PreviewErrorController' => __DIR__ . '/..' . '/symfony/twig-bundle/Controller/PreviewErrorController.php', + 'Symfony\\Bundle\\TwigBundle\\DependencyInjection\\Compiler\\ExceptionListenerPass' => __DIR__ . '/..' . '/symfony/twig-bundle/DependencyInjection/Compiler/ExceptionListenerPass.php', + 'Symfony\\Bundle\\TwigBundle\\DependencyInjection\\Compiler\\ExtensionPass' => __DIR__ . '/..' . '/symfony/twig-bundle/DependencyInjection/Compiler/ExtensionPass.php', + 'Symfony\\Bundle\\TwigBundle\\DependencyInjection\\Compiler\\RuntimeLoaderPass' => __DIR__ . '/..' . '/symfony/twig-bundle/DependencyInjection/Compiler/RuntimeLoaderPass.php', + 'Symfony\\Bundle\\TwigBundle\\DependencyInjection\\Compiler\\TwigEnvironmentPass' => __DIR__ . '/..' . '/symfony/twig-bundle/DependencyInjection/Compiler/TwigEnvironmentPass.php', + 'Symfony\\Bundle\\TwigBundle\\DependencyInjection\\Compiler\\TwigLoaderPass' => __DIR__ . '/..' . '/symfony/twig-bundle/DependencyInjection/Compiler/TwigLoaderPass.php', + 'Symfony\\Bundle\\TwigBundle\\DependencyInjection\\Configuration' => __DIR__ . '/..' . '/symfony/twig-bundle/DependencyInjection/Configuration.php', + 'Symfony\\Bundle\\TwigBundle\\DependencyInjection\\Configurator\\EnvironmentConfigurator' => __DIR__ . '/..' . '/symfony/twig-bundle/DependencyInjection/Configurator/EnvironmentConfigurator.php', + 'Symfony\\Bundle\\TwigBundle\\DependencyInjection\\TwigExtension' => __DIR__ . '/..' . '/symfony/twig-bundle/DependencyInjection/TwigExtension.php', + 'Symfony\\Bundle\\TwigBundle\\Loader\\FilesystemLoader' => __DIR__ . '/..' . '/symfony/twig-bundle/Loader/FilesystemLoader.php', + 'Symfony\\Bundle\\TwigBundle\\TemplateIterator' => __DIR__ . '/..' . '/symfony/twig-bundle/TemplateIterator.php', + 'Symfony\\Bundle\\TwigBundle\\TwigBundle' => __DIR__ . '/..' . '/symfony/twig-bundle/TwigBundle.php', + 'Symfony\\Bundle\\TwigBundle\\TwigEngine' => __DIR__ . '/..' . '/symfony/twig-bundle/TwigEngine.php', + 'Symfony\\Bundle\\WebProfilerBundle\\Controller\\ExceptionController' => __DIR__ . '/..' . '/symfony/web-profiler-bundle/Controller/ExceptionController.php', + 'Symfony\\Bundle\\WebProfilerBundle\\Controller\\ProfilerController' => __DIR__ . '/..' . '/symfony/web-profiler-bundle/Controller/ProfilerController.php', + 'Symfony\\Bundle\\WebProfilerBundle\\Controller\\RouterController' => __DIR__ . '/..' . '/symfony/web-profiler-bundle/Controller/RouterController.php', + 'Symfony\\Bundle\\WebProfilerBundle\\Csp\\ContentSecurityPolicyHandler' => __DIR__ . '/..' . '/symfony/web-profiler-bundle/Csp/ContentSecurityPolicyHandler.php', + 'Symfony\\Bundle\\WebProfilerBundle\\Csp\\NonceGenerator' => __DIR__ . '/..' . '/symfony/web-profiler-bundle/Csp/NonceGenerator.php', + 'Symfony\\Bundle\\WebProfilerBundle\\DependencyInjection\\Configuration' => __DIR__ . '/..' . '/symfony/web-profiler-bundle/DependencyInjection/Configuration.php', + 'Symfony\\Bundle\\WebProfilerBundle\\DependencyInjection\\WebProfilerExtension' => __DIR__ . '/..' . '/symfony/web-profiler-bundle/DependencyInjection/WebProfilerExtension.php', + 'Symfony\\Bundle\\WebProfilerBundle\\EventListener\\WebDebugToolbarListener' => __DIR__ . '/..' . '/symfony/web-profiler-bundle/EventListener/WebDebugToolbarListener.php', + 'Symfony\\Bundle\\WebProfilerBundle\\Profiler\\TemplateManager' => __DIR__ . '/..' . '/symfony/web-profiler-bundle/Profiler/TemplateManager.php', + 'Symfony\\Bundle\\WebProfilerBundle\\Twig\\WebProfilerExtension' => __DIR__ . '/..' . '/symfony/web-profiler-bundle/Twig/WebProfilerExtension.php', + 'Symfony\\Bundle\\WebProfilerBundle\\WebProfilerBundle' => __DIR__ . '/..' . '/symfony/web-profiler-bundle/WebProfilerBundle.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\\ClassLoader\\ApcClassLoader' => __DIR__ . '/..' . '/symfony/class-loader/ApcClassLoader.php', + 'Symfony\\Component\\ClassLoader\\ClassCollectionLoader' => __DIR__ . '/..' . '/symfony/class-loader/ClassCollectionLoader.php', + 'Symfony\\Component\\ClassLoader\\ClassLoader' => __DIR__ . '/..' . '/symfony/class-loader/ClassLoader.php', + 'Symfony\\Component\\ClassLoader\\ClassMapGenerator' => __DIR__ . '/..' . '/symfony/class-loader/ClassMapGenerator.php', + 'Symfony\\Component\\ClassLoader\\MapClassLoader' => __DIR__ . '/..' . '/symfony/class-loader/MapClassLoader.php', + 'Symfony\\Component\\ClassLoader\\Psr4ClassLoader' => __DIR__ . '/..' . '/symfony/class-loader/Psr4ClassLoader.php', + 'Symfony\\Component\\ClassLoader\\WinCacheClassLoader' => __DIR__ . '/..' . '/symfony/class-loader/WinCacheClassLoader.php', + 'Symfony\\Component\\ClassLoader\\XcacheClassLoader' => __DIR__ . '/..' . '/symfony/class-loader/XcacheClassLoader.php', + 'Symfony\\Component\\Config\\ConfigCache' => __DIR__ . '/..' . '/symfony/config/ConfigCache.php', + 'Symfony\\Component\\Config\\ConfigCacheFactory' => __DIR__ . '/..' . '/symfony/config/ConfigCacheFactory.php', + 'Symfony\\Component\\Config\\ConfigCacheFactoryInterface' => __DIR__ . '/..' . '/symfony/config/ConfigCacheFactoryInterface.php', + 'Symfony\\Component\\Config\\ConfigCacheInterface' => __DIR__ . '/..' . '/symfony/config/ConfigCacheInterface.php', + 'Symfony\\Component\\Config\\Definition\\ArrayNode' => __DIR__ . '/..' . '/symfony/config/Definition/ArrayNode.php', + 'Symfony\\Component\\Config\\Definition\\BaseNode' => __DIR__ . '/..' . '/symfony/config/Definition/BaseNode.php', + 'Symfony\\Component\\Config\\Definition\\BooleanNode' => __DIR__ . '/..' . '/symfony/config/Definition/BooleanNode.php', + 'Symfony\\Component\\Config\\Definition\\Builder\\ArrayNodeDefinition' => __DIR__ . '/..' . '/symfony/config/Definition/Builder/ArrayNodeDefinition.php', + 'Symfony\\Component\\Config\\Definition\\Builder\\BooleanNodeDefinition' => __DIR__ . '/..' . '/symfony/config/Definition/Builder/BooleanNodeDefinition.php', + 'Symfony\\Component\\Config\\Definition\\Builder\\EnumNodeDefinition' => __DIR__ . '/..' . '/symfony/config/Definition/Builder/EnumNodeDefinition.php', + 'Symfony\\Component\\Config\\Definition\\Builder\\ExprBuilder' => __DIR__ . '/..' . '/symfony/config/Definition/Builder/ExprBuilder.php', + 'Symfony\\Component\\Config\\Definition\\Builder\\FloatNodeDefinition' => __DIR__ . '/..' . '/symfony/config/Definition/Builder/FloatNodeDefinition.php', + 'Symfony\\Component\\Config\\Definition\\Builder\\IntegerNodeDefinition' => __DIR__ . '/..' . '/symfony/config/Definition/Builder/IntegerNodeDefinition.php', + 'Symfony\\Component\\Config\\Definition\\Builder\\MergeBuilder' => __DIR__ . '/..' . '/symfony/config/Definition/Builder/MergeBuilder.php', + 'Symfony\\Component\\Config\\Definition\\Builder\\NodeBuilder' => __DIR__ . '/..' . '/symfony/config/Definition/Builder/NodeBuilder.php', + 'Symfony\\Component\\Config\\Definition\\Builder\\NodeDefinition' => __DIR__ . '/..' . '/symfony/config/Definition/Builder/NodeDefinition.php', + 'Symfony\\Component\\Config\\Definition\\Builder\\NodeParentInterface' => __DIR__ . '/..' . '/symfony/config/Definition/Builder/NodeParentInterface.php', + 'Symfony\\Component\\Config\\Definition\\Builder\\NormalizationBuilder' => __DIR__ . '/..' . '/symfony/config/Definition/Builder/NormalizationBuilder.php', + 'Symfony\\Component\\Config\\Definition\\Builder\\NumericNodeDefinition' => __DIR__ . '/..' . '/symfony/config/Definition/Builder/NumericNodeDefinition.php', + 'Symfony\\Component\\Config\\Definition\\Builder\\ParentNodeDefinitionInterface' => __DIR__ . '/..' . '/symfony/config/Definition/Builder/ParentNodeDefinitionInterface.php', + 'Symfony\\Component\\Config\\Definition\\Builder\\ScalarNodeDefinition' => __DIR__ . '/..' . '/symfony/config/Definition/Builder/ScalarNodeDefinition.php', + 'Symfony\\Component\\Config\\Definition\\Builder\\TreeBuilder' => __DIR__ . '/..' . '/symfony/config/Definition/Builder/TreeBuilder.php', + 'Symfony\\Component\\Config\\Definition\\Builder\\ValidationBuilder' => __DIR__ . '/..' . '/symfony/config/Definition/Builder/ValidationBuilder.php', + 'Symfony\\Component\\Config\\Definition\\Builder\\VariableNodeDefinition' => __DIR__ . '/..' . '/symfony/config/Definition/Builder/VariableNodeDefinition.php', + 'Symfony\\Component\\Config\\Definition\\ConfigurationInterface' => __DIR__ . '/..' . '/symfony/config/Definition/ConfigurationInterface.php', + 'Symfony\\Component\\Config\\Definition\\Dumper\\XmlReferenceDumper' => __DIR__ . '/..' . '/symfony/config/Definition/Dumper/XmlReferenceDumper.php', + 'Symfony\\Component\\Config\\Definition\\Dumper\\YamlReferenceDumper' => __DIR__ . '/..' . '/symfony/config/Definition/Dumper/YamlReferenceDumper.php', + 'Symfony\\Component\\Config\\Definition\\EnumNode' => __DIR__ . '/..' . '/symfony/config/Definition/EnumNode.php', + 'Symfony\\Component\\Config\\Definition\\Exception\\DuplicateKeyException' => __DIR__ . '/..' . '/symfony/config/Definition/Exception/DuplicateKeyException.php', + 'Symfony\\Component\\Config\\Definition\\Exception\\Exception' => __DIR__ . '/..' . '/symfony/config/Definition/Exception/Exception.php', + 'Symfony\\Component\\Config\\Definition\\Exception\\ForbiddenOverwriteException' => __DIR__ . '/..' . '/symfony/config/Definition/Exception/ForbiddenOverwriteException.php', + 'Symfony\\Component\\Config\\Definition\\Exception\\InvalidConfigurationException' => __DIR__ . '/..' . '/symfony/config/Definition/Exception/InvalidConfigurationException.php', + 'Symfony\\Component\\Config\\Definition\\Exception\\InvalidDefinitionException' => __DIR__ . '/..' . '/symfony/config/Definition/Exception/InvalidDefinitionException.php', + 'Symfony\\Component\\Config\\Definition\\Exception\\InvalidTypeException' => __DIR__ . '/..' . '/symfony/config/Definition/Exception/InvalidTypeException.php', + 'Symfony\\Component\\Config\\Definition\\Exception\\UnsetKeyException' => __DIR__ . '/..' . '/symfony/config/Definition/Exception/UnsetKeyException.php', + 'Symfony\\Component\\Config\\Definition\\FloatNode' => __DIR__ . '/..' . '/symfony/config/Definition/FloatNode.php', + 'Symfony\\Component\\Config\\Definition\\IntegerNode' => __DIR__ . '/..' . '/symfony/config/Definition/IntegerNode.php', + 'Symfony\\Component\\Config\\Definition\\NodeInterface' => __DIR__ . '/..' . '/symfony/config/Definition/NodeInterface.php', + 'Symfony\\Component\\Config\\Definition\\NumericNode' => __DIR__ . '/..' . '/symfony/config/Definition/NumericNode.php', + 'Symfony\\Component\\Config\\Definition\\Processor' => __DIR__ . '/..' . '/symfony/config/Definition/Processor.php', + 'Symfony\\Component\\Config\\Definition\\PrototypeNodeInterface' => __DIR__ . '/..' . '/symfony/config/Definition/PrototypeNodeInterface.php', + 'Symfony\\Component\\Config\\Definition\\PrototypedArrayNode' => __DIR__ . '/..' . '/symfony/config/Definition/PrototypedArrayNode.php', + 'Symfony\\Component\\Config\\Definition\\ScalarNode' => __DIR__ . '/..' . '/symfony/config/Definition/ScalarNode.php', + 'Symfony\\Component\\Config\\Definition\\VariableNode' => __DIR__ . '/..' . '/symfony/config/Definition/VariableNode.php', + 'Symfony\\Component\\Config\\DependencyInjection\\ConfigCachePass' => __DIR__ . '/..' . '/symfony/config/DependencyInjection/ConfigCachePass.php', + 'Symfony\\Component\\Config\\Exception\\FileLoaderImportCircularReferenceException' => __DIR__ . '/..' . '/symfony/config/Exception/FileLoaderImportCircularReferenceException.php', + 'Symfony\\Component\\Config\\Exception\\FileLoaderLoadException' => __DIR__ . '/..' . '/symfony/config/Exception/FileLoaderLoadException.php', + 'Symfony\\Component\\Config\\Exception\\FileLocatorFileNotFoundException' => __DIR__ . '/..' . '/symfony/config/Exception/FileLocatorFileNotFoundException.php', + 'Symfony\\Component\\Config\\FileLocator' => __DIR__ . '/..' . '/symfony/config/FileLocator.php', + 'Symfony\\Component\\Config\\FileLocatorInterface' => __DIR__ . '/..' . '/symfony/config/FileLocatorInterface.php', + 'Symfony\\Component\\Config\\Loader\\DelegatingLoader' => __DIR__ . '/..' . '/symfony/config/Loader/DelegatingLoader.php', + 'Symfony\\Component\\Config\\Loader\\FileLoader' => __DIR__ . '/..' . '/symfony/config/Loader/FileLoader.php', + 'Symfony\\Component\\Config\\Loader\\GlobFileLoader' => __DIR__ . '/..' . '/symfony/config/Loader/GlobFileLoader.php', + 'Symfony\\Component\\Config\\Loader\\Loader' => __DIR__ . '/..' . '/symfony/config/Loader/Loader.php', + 'Symfony\\Component\\Config\\Loader\\LoaderInterface' => __DIR__ . '/..' . '/symfony/config/Loader/LoaderInterface.php', + 'Symfony\\Component\\Config\\Loader\\LoaderResolver' => __DIR__ . '/..' . '/symfony/config/Loader/LoaderResolver.php', + 'Symfony\\Component\\Config\\Loader\\LoaderResolverInterface' => __DIR__ . '/..' . '/symfony/config/Loader/LoaderResolverInterface.php', + 'Symfony\\Component\\Config\\ResourceCheckerConfigCache' => __DIR__ . '/..' . '/symfony/config/ResourceCheckerConfigCache.php', + 'Symfony\\Component\\Config\\ResourceCheckerConfigCacheFactory' => __DIR__ . '/..' . '/symfony/config/ResourceCheckerConfigCacheFactory.php', + 'Symfony\\Component\\Config\\ResourceCheckerInterface' => __DIR__ . '/..' . '/symfony/config/ResourceCheckerInterface.php', + 'Symfony\\Component\\Config\\Resource\\ClassExistenceResource' => __DIR__ . '/..' . '/symfony/config/Resource/ClassExistenceResource.php', + 'Symfony\\Component\\Config\\Resource\\ComposerResource' => __DIR__ . '/..' . '/symfony/config/Resource/ComposerResource.php', + 'Symfony\\Component\\Config\\Resource\\DirectoryResource' => __DIR__ . '/..' . '/symfony/config/Resource/DirectoryResource.php', + 'Symfony\\Component\\Config\\Resource\\FileExistenceResource' => __DIR__ . '/..' . '/symfony/config/Resource/FileExistenceResource.php', + 'Symfony\\Component\\Config\\Resource\\FileResource' => __DIR__ . '/..' . '/symfony/config/Resource/FileResource.php', + 'Symfony\\Component\\Config\\Resource\\GlobResource' => __DIR__ . '/..' . '/symfony/config/Resource/GlobResource.php', + 'Symfony\\Component\\Config\\Resource\\ReflectionClassResource' => __DIR__ . '/..' . '/symfony/config/Resource/ReflectionClassResource.php', + 'Symfony\\Component\\Config\\Resource\\ReflectionMethodHhvmWrapper' => __DIR__ . '/..' . '/symfony/config/Resource/ReflectionClassResource.php', + 'Symfony\\Component\\Config\\Resource\\ReflectionParameterHhvmWrapper' => __DIR__ . '/..' . '/symfony/config/Resource/ReflectionClassResource.php', + 'Symfony\\Component\\Config\\Resource\\ResourceInterface' => __DIR__ . '/..' . '/symfony/config/Resource/ResourceInterface.php', + 'Symfony\\Component\\Config\\Resource\\SelfCheckingResourceChecker' => __DIR__ . '/..' . '/symfony/config/Resource/SelfCheckingResourceChecker.php', + 'Symfony\\Component\\Config\\Resource\\SelfCheckingResourceInterface' => __DIR__ . '/..' . '/symfony/config/Resource/SelfCheckingResourceInterface.php', + 'Symfony\\Component\\Config\\Util\\Exception\\InvalidXmlException' => __DIR__ . '/..' . '/symfony/config/Util/Exception/InvalidXmlException.php', + 'Symfony\\Component\\Config\\Util\\Exception\\XmlParsingException' => __DIR__ . '/..' . '/symfony/config/Util/Exception/XmlParsingException.php', + 'Symfony\\Component\\Config\\Util\\XmlUtils' => __DIR__ . '/..' . '/symfony/config/Util/XmlUtils.php', + 'Symfony\\Component\\Console\\Application' => __DIR__ . '/..' . '/symfony/console/Application.php', + 'Symfony\\Component\\Console\\CommandLoader\\CommandLoaderInterface' => __DIR__ . '/..' . '/symfony/console/CommandLoader/CommandLoaderInterface.php', + 'Symfony\\Component\\Console\\CommandLoader\\ContainerCommandLoader' => __DIR__ . '/..' . '/symfony/console/CommandLoader/ContainerCommandLoader.php', + 'Symfony\\Component\\Console\\CommandLoader\\FactoryCommandLoader' => __DIR__ . '/..' . '/symfony/console/CommandLoader/FactoryCommandLoader.php', + 'Symfony\\Component\\Console\\Command\\Command' => __DIR__ . '/..' . '/symfony/console/Command/Command.php', + 'Symfony\\Component\\Console\\Command\\HelpCommand' => __DIR__ . '/..' . '/symfony/console/Command/HelpCommand.php', + 'Symfony\\Component\\Console\\Command\\ListCommand' => __DIR__ . '/..' . '/symfony/console/Command/ListCommand.php', + 'Symfony\\Component\\Console\\Command\\LockableTrait' => __DIR__ . '/..' . '/symfony/console/Command/LockableTrait.php', + 'Symfony\\Component\\Console\\ConsoleEvents' => __DIR__ . '/..' . '/symfony/console/ConsoleEvents.php', + 'Symfony\\Component\\Console\\DependencyInjection\\AddConsoleCommandPass' => __DIR__ . '/..' . '/symfony/console/DependencyInjection/AddConsoleCommandPass.php', + 'Symfony\\Component\\Console\\Descriptor\\ApplicationDescription' => __DIR__ . '/..' . '/symfony/console/Descriptor/ApplicationDescription.php', + 'Symfony\\Component\\Console\\Descriptor\\Descriptor' => __DIR__ . '/..' . '/symfony/console/Descriptor/Descriptor.php', + 'Symfony\\Component\\Console\\Descriptor\\DescriptorInterface' => __DIR__ . '/..' . '/symfony/console/Descriptor/DescriptorInterface.php', + 'Symfony\\Component\\Console\\Descriptor\\JsonDescriptor' => __DIR__ . '/..' . '/symfony/console/Descriptor/JsonDescriptor.php', + 'Symfony\\Component\\Console\\Descriptor\\MarkdownDescriptor' => __DIR__ . '/..' . '/symfony/console/Descriptor/MarkdownDescriptor.php', + 'Symfony\\Component\\Console\\Descriptor\\TextDescriptor' => __DIR__ . '/..' . '/symfony/console/Descriptor/TextDescriptor.php', + 'Symfony\\Component\\Console\\Descriptor\\XmlDescriptor' => __DIR__ . '/..' . '/symfony/console/Descriptor/XmlDescriptor.php', + 'Symfony\\Component\\Console\\EventListener\\ErrorListener' => __DIR__ . '/..' . '/symfony/console/EventListener/ErrorListener.php', + 'Symfony\\Component\\Console\\Event\\ConsoleCommandEvent' => __DIR__ . '/..' . '/symfony/console/Event/ConsoleCommandEvent.php', + 'Symfony\\Component\\Console\\Event\\ConsoleErrorEvent' => __DIR__ . '/..' . '/symfony/console/Event/ConsoleErrorEvent.php', + 'Symfony\\Component\\Console\\Event\\ConsoleEvent' => __DIR__ . '/..' . '/symfony/console/Event/ConsoleEvent.php', + 'Symfony\\Component\\Console\\Event\\ConsoleExceptionEvent' => __DIR__ . '/..' . '/symfony/console/Event/ConsoleExceptionEvent.php', + 'Symfony\\Component\\Console\\Event\\ConsoleTerminateEvent' => __DIR__ . '/..' . '/symfony/console/Event/ConsoleTerminateEvent.php', + 'Symfony\\Component\\Console\\Exception\\CommandNotFoundException' => __DIR__ . '/..' . '/symfony/console/Exception/CommandNotFoundException.php', + 'Symfony\\Component\\Console\\Exception\\ExceptionInterface' => __DIR__ . '/..' . '/symfony/console/Exception/ExceptionInterface.php', + 'Symfony\\Component\\Console\\Exception\\InvalidArgumentException' => __DIR__ . '/..' . '/symfony/console/Exception/InvalidArgumentException.php', + 'Symfony\\Component\\Console\\Exception\\InvalidOptionException' => __DIR__ . '/..' . '/symfony/console/Exception/InvalidOptionException.php', + 'Symfony\\Component\\Console\\Exception\\LogicException' => __DIR__ . '/..' . '/symfony/console/Exception/LogicException.php', + 'Symfony\\Component\\Console\\Exception\\RuntimeException' => __DIR__ . '/..' . '/symfony/console/Exception/RuntimeException.php', + 'Symfony\\Component\\Console\\Formatter\\OutputFormatter' => __DIR__ . '/..' . '/symfony/console/Formatter/OutputFormatter.php', + 'Symfony\\Component\\Console\\Formatter\\OutputFormatterInterface' => __DIR__ . '/..' . '/symfony/console/Formatter/OutputFormatterInterface.php', + 'Symfony\\Component\\Console\\Formatter\\OutputFormatterStyle' => __DIR__ . '/..' . '/symfony/console/Formatter/OutputFormatterStyle.php', + 'Symfony\\Component\\Console\\Formatter\\OutputFormatterStyleInterface' => __DIR__ . '/..' . '/symfony/console/Formatter/OutputFormatterStyleInterface.php', + 'Symfony\\Component\\Console\\Formatter\\OutputFormatterStyleStack' => __DIR__ . '/..' . '/symfony/console/Formatter/OutputFormatterStyleStack.php', + 'Symfony\\Component\\Console\\Helper\\DebugFormatterHelper' => __DIR__ . '/..' . '/symfony/console/Helper/DebugFormatterHelper.php', + 'Symfony\\Component\\Console\\Helper\\DescriptorHelper' => __DIR__ . '/..' . '/symfony/console/Helper/DescriptorHelper.php', + 'Symfony\\Component\\Console\\Helper\\FormatterHelper' => __DIR__ . '/..' . '/symfony/console/Helper/FormatterHelper.php', + 'Symfony\\Component\\Console\\Helper\\Helper' => __DIR__ . '/..' . '/symfony/console/Helper/Helper.php', + 'Symfony\\Component\\Console\\Helper\\HelperInterface' => __DIR__ . '/..' . '/symfony/console/Helper/HelperInterface.php', + 'Symfony\\Component\\Console\\Helper\\HelperSet' => __DIR__ . '/..' . '/symfony/console/Helper/HelperSet.php', + 'Symfony\\Component\\Console\\Helper\\InputAwareHelper' => __DIR__ . '/..' . '/symfony/console/Helper/InputAwareHelper.php', + 'Symfony\\Component\\Console\\Helper\\ProcessHelper' => __DIR__ . '/..' . '/symfony/console/Helper/ProcessHelper.php', + 'Symfony\\Component\\Console\\Helper\\ProgressBar' => __DIR__ . '/..' . '/symfony/console/Helper/ProgressBar.php', + 'Symfony\\Component\\Console\\Helper\\ProgressIndicator' => __DIR__ . '/..' . '/symfony/console/Helper/ProgressIndicator.php', + 'Symfony\\Component\\Console\\Helper\\QuestionHelper' => __DIR__ . '/..' . '/symfony/console/Helper/QuestionHelper.php', + 'Symfony\\Component\\Console\\Helper\\SymfonyQuestionHelper' => __DIR__ . '/..' . '/symfony/console/Helper/SymfonyQuestionHelper.php', + 'Symfony\\Component\\Console\\Helper\\Table' => __DIR__ . '/..' . '/symfony/console/Helper/Table.php', + 'Symfony\\Component\\Console\\Helper\\TableCell' => __DIR__ . '/..' . '/symfony/console/Helper/TableCell.php', + 'Symfony\\Component\\Console\\Helper\\TableSeparator' => __DIR__ . '/..' . '/symfony/console/Helper/TableSeparator.php', + 'Symfony\\Component\\Console\\Helper\\TableStyle' => __DIR__ . '/..' . '/symfony/console/Helper/TableStyle.php', + 'Symfony\\Component\\Console\\Input\\ArgvInput' => __DIR__ . '/..' . '/symfony/console/Input/ArgvInput.php', + 'Symfony\\Component\\Console\\Input\\ArrayInput' => __DIR__ . '/..' . '/symfony/console/Input/ArrayInput.php', + 'Symfony\\Component\\Console\\Input\\Input' => __DIR__ . '/..' . '/symfony/console/Input/Input.php', + 'Symfony\\Component\\Console\\Input\\InputArgument' => __DIR__ . '/..' . '/symfony/console/Input/InputArgument.php', + 'Symfony\\Component\\Console\\Input\\InputAwareInterface' => __DIR__ . '/..' . '/symfony/console/Input/InputAwareInterface.php', + 'Symfony\\Component\\Console\\Input\\InputDefinition' => __DIR__ . '/..' . '/symfony/console/Input/InputDefinition.php', + 'Symfony\\Component\\Console\\Input\\InputInterface' => __DIR__ . '/..' . '/symfony/console/Input/InputInterface.php', + 'Symfony\\Component\\Console\\Input\\InputOption' => __DIR__ . '/..' . '/symfony/console/Input/InputOption.php', + 'Symfony\\Component\\Console\\Input\\StreamableInputInterface' => __DIR__ . '/..' . '/symfony/console/Input/StreamableInputInterface.php', + 'Symfony\\Component\\Console\\Input\\StringInput' => __DIR__ . '/..' . '/symfony/console/Input/StringInput.php', + 'Symfony\\Component\\Console\\Logger\\ConsoleLogger' => __DIR__ . '/..' . '/symfony/console/Logger/ConsoleLogger.php', + 'Symfony\\Component\\Console\\Output\\BufferedOutput' => __DIR__ . '/..' . '/symfony/console/Output/BufferedOutput.php', + 'Symfony\\Component\\Console\\Output\\ConsoleOutput' => __DIR__ . '/..' . '/symfony/console/Output/ConsoleOutput.php', + 'Symfony\\Component\\Console\\Output\\ConsoleOutputInterface' => __DIR__ . '/..' . '/symfony/console/Output/ConsoleOutputInterface.php', + 'Symfony\\Component\\Console\\Output\\NullOutput' => __DIR__ . '/..' . '/symfony/console/Output/NullOutput.php', + 'Symfony\\Component\\Console\\Output\\Output' => __DIR__ . '/..' . '/symfony/console/Output/Output.php', + 'Symfony\\Component\\Console\\Output\\OutputInterface' => __DIR__ . '/..' . '/symfony/console/Output/OutputInterface.php', + 'Symfony\\Component\\Console\\Output\\StreamOutput' => __DIR__ . '/..' . '/symfony/console/Output/StreamOutput.php', + 'Symfony\\Component\\Console\\Question\\ChoiceQuestion' => __DIR__ . '/..' . '/symfony/console/Question/ChoiceQuestion.php', + 'Symfony\\Component\\Console\\Question\\ConfirmationQuestion' => __DIR__ . '/..' . '/symfony/console/Question/ConfirmationQuestion.php', + 'Symfony\\Component\\Console\\Question\\Question' => __DIR__ . '/..' . '/symfony/console/Question/Question.php', + 'Symfony\\Component\\Console\\Style\\OutputStyle' => __DIR__ . '/..' . '/symfony/console/Style/OutputStyle.php', + 'Symfony\\Component\\Console\\Style\\StyleInterface' => __DIR__ . '/..' . '/symfony/console/Style/StyleInterface.php', + 'Symfony\\Component\\Console\\Style\\SymfonyStyle' => __DIR__ . '/..' . '/symfony/console/Style/SymfonyStyle.php', + 'Symfony\\Component\\Console\\Terminal' => __DIR__ . '/..' . '/symfony/console/Terminal.php', + 'Symfony\\Component\\Console\\Tester\\ApplicationTester' => __DIR__ . '/..' . '/symfony/console/Tester/ApplicationTester.php', + 'Symfony\\Component\\Console\\Tester\\CommandTester' => __DIR__ . '/..' . '/symfony/console/Tester/CommandTester.php', + 'Symfony\\Component\\Debug\\BufferingLogger' => __DIR__ . '/..' . '/symfony/debug/BufferingLogger.php', + 'Symfony\\Component\\Debug\\Debug' => __DIR__ . '/..' . '/symfony/debug/Debug.php', + 'Symfony\\Component\\Debug\\DebugClassLoader' => __DIR__ . '/..' . '/symfony/debug/DebugClassLoader.php', + 'Symfony\\Component\\Debug\\ErrorHandler' => __DIR__ . '/..' . '/symfony/debug/ErrorHandler.php', + 'Symfony\\Component\\Debug\\ExceptionHandler' => __DIR__ . '/..' . '/symfony/debug/ExceptionHandler.php', + 'Symfony\\Component\\Debug\\Exception\\ClassNotFoundException' => __DIR__ . '/..' . '/symfony/debug/Exception/ClassNotFoundException.php', + 'Symfony\\Component\\Debug\\Exception\\ContextErrorException' => __DIR__ . '/..' . '/symfony/debug/Exception/ContextErrorException.php', + 'Symfony\\Component\\Debug\\Exception\\FatalErrorException' => __DIR__ . '/..' . '/symfony/debug/Exception/FatalErrorException.php', + 'Symfony\\Component\\Debug\\Exception\\FatalThrowableError' => __DIR__ . '/..' . '/symfony/debug/Exception/FatalThrowableError.php', + 'Symfony\\Component\\Debug\\Exception\\FlattenException' => __DIR__ . '/..' . '/symfony/debug/Exception/FlattenException.php', + 'Symfony\\Component\\Debug\\Exception\\OutOfMemoryException' => __DIR__ . '/..' . '/symfony/debug/Exception/OutOfMemoryException.php', + 'Symfony\\Component\\Debug\\Exception\\SilencedErrorContext' => __DIR__ . '/..' . '/symfony/debug/Exception/SilencedErrorContext.php', + 'Symfony\\Component\\Debug\\Exception\\UndefinedFunctionException' => __DIR__ . '/..' . '/symfony/debug/Exception/UndefinedFunctionException.php', + 'Symfony\\Component\\Debug\\Exception\\UndefinedMethodException' => __DIR__ . '/..' . '/symfony/debug/Exception/UndefinedMethodException.php', + 'Symfony\\Component\\Debug\\FatalErrorHandler\\ClassNotFoundFatalErrorHandler' => __DIR__ . '/..' . '/symfony/debug/FatalErrorHandler/ClassNotFoundFatalErrorHandler.php', + 'Symfony\\Component\\Debug\\FatalErrorHandler\\FatalErrorHandlerInterface' => __DIR__ . '/..' . '/symfony/debug/FatalErrorHandler/FatalErrorHandlerInterface.php', + 'Symfony\\Component\\Debug\\FatalErrorHandler\\UndefinedFunctionFatalErrorHandler' => __DIR__ . '/..' . '/symfony/debug/FatalErrorHandler/UndefinedFunctionFatalErrorHandler.php', + 'Symfony\\Component\\Debug\\FatalErrorHandler\\UndefinedMethodFatalErrorHandler' => __DIR__ . '/..' . '/symfony/debug/FatalErrorHandler/UndefinedMethodFatalErrorHandler.php', + 'Symfony\\Component\\DependencyInjection\\Alias' => __DIR__ . '/..' . '/symfony/dependency-injection/Alias.php', + 'Symfony\\Component\\DependencyInjection\\Argument\\ArgumentInterface' => __DIR__ . '/..' . '/symfony/dependency-injection/Argument/ArgumentInterface.php', + 'Symfony\\Component\\DependencyInjection\\Argument\\BoundArgument' => __DIR__ . '/..' . '/symfony/dependency-injection/Argument/BoundArgument.php', + 'Symfony\\Component\\DependencyInjection\\Argument\\IteratorArgument' => __DIR__ . '/..' . '/symfony/dependency-injection/Argument/IteratorArgument.php', + 'Symfony\\Component\\DependencyInjection\\Argument\\RewindableGenerator' => __DIR__ . '/..' . '/symfony/dependency-injection/Argument/RewindableGenerator.php', + 'Symfony\\Component\\DependencyInjection\\Argument\\ServiceClosureArgument' => __DIR__ . '/..' . '/symfony/dependency-injection/Argument/ServiceClosureArgument.php', + 'Symfony\\Component\\DependencyInjection\\Argument\\TaggedIteratorArgument' => __DIR__ . '/..' . '/symfony/dependency-injection/Argument/TaggedIteratorArgument.php', + 'Symfony\\Component\\DependencyInjection\\ChildDefinition' => __DIR__ . '/..' . '/symfony/dependency-injection/ChildDefinition.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\AbstractRecursivePass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/AbstractRecursivePass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\AnalyzeServiceReferencesPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/AnalyzeServiceReferencesPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\AutoAliasServicePass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/AutoAliasServicePass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\AutowireExceptionPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/AutowireExceptionPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\AutowirePass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/AutowirePass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\AutowireRequiredMethodsPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/AutowireRequiredMethodsPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\CheckArgumentsValidityPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/CheckArgumentsValidityPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\CheckCircularReferencesPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/CheckCircularReferencesPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\CheckDefinitionValidityPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/CheckDefinitionValidityPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\CheckExceptionOnInvalidReferenceBehaviorPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/CheckExceptionOnInvalidReferenceBehaviorPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\CheckReferenceValidityPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/CheckReferenceValidityPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\Compiler' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/Compiler.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\CompilerPassInterface' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/CompilerPassInterface.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\DecoratorServicePass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/DecoratorServicePass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\DefinitionErrorExceptionPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/DefinitionErrorExceptionPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\ExtensionCompilerPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/ExtensionCompilerPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\FactoryReturnTypePass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/FactoryReturnTypePass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\InlineServiceDefinitionsPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/InlineServiceDefinitionsPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\LoggingFormatter' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/LoggingFormatter.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\MergeExtensionConfigurationContainerBuilder' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/MergeExtensionConfigurationPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\MergeExtensionConfigurationParameterBag' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/MergeExtensionConfigurationPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\MergeExtensionConfigurationPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/MergeExtensionConfigurationPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\PassConfig' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/PassConfig.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\PriorityTaggedServiceTrait' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/PriorityTaggedServiceTrait.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\RegisterEnvVarProcessorsPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/RegisterEnvVarProcessorsPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\RegisterServiceSubscribersPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/RegisterServiceSubscribersPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\RemoveAbstractDefinitionsPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/RemoveAbstractDefinitionsPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\RemovePrivateAliasesPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/RemovePrivateAliasesPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\RemoveUnusedDefinitionsPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/RemoveUnusedDefinitionsPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\RepeatablePassInterface' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/RepeatablePassInterface.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\RepeatedPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/RepeatedPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\ReplaceAliasByActualDefinitionPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/ReplaceAliasByActualDefinitionPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\ResolveBindingsPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/ResolveBindingsPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\ResolveChildDefinitionsPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/ResolveChildDefinitionsPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\ResolveClassPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/ResolveClassPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\ResolveDefinitionTemplatesPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/ResolveDefinitionTemplatesPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\ResolveEnvPlaceholdersPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/ResolveEnvPlaceholdersPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\ResolveFactoryClassPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/ResolveFactoryClassPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\ResolveHotPathPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/ResolveHotPathPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\ResolveInstanceofConditionalsPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/ResolveInstanceofConditionalsPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\ResolveInvalidReferencesPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/ResolveInvalidReferencesPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\ResolveNamedArgumentsPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/ResolveNamedArgumentsPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\ResolveParameterPlaceHoldersPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/ResolveParameterPlaceHoldersPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\ResolvePrivatesPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/ResolvePrivatesPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\ResolveReferencesToAliasesPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/ResolveReferencesToAliasesPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\ResolveServiceSubscribersPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/ResolveServiceSubscribersPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\ResolveTaggedIteratorArgumentPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/ResolveTaggedIteratorArgumentPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\ServiceLocatorTagPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/ServiceLocatorTagPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\ServiceReferenceGraph' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/ServiceReferenceGraph.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\ServiceReferenceGraphEdge' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/ServiceReferenceGraphEdge.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\ServiceReferenceGraphNode' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/ServiceReferenceGraphNode.php', + 'Symfony\\Component\\DependencyInjection\\Config\\AutowireServiceResource' => __DIR__ . '/..' . '/symfony/dependency-injection/Config/AutowireServiceResource.php', + 'Symfony\\Component\\DependencyInjection\\Config\\ContainerParametersResource' => __DIR__ . '/..' . '/symfony/dependency-injection/Config/ContainerParametersResource.php', + 'Symfony\\Component\\DependencyInjection\\Config\\ContainerParametersResourceChecker' => __DIR__ . '/..' . '/symfony/dependency-injection/Config/ContainerParametersResourceChecker.php', + 'Symfony\\Component\\DependencyInjection\\Container' => __DIR__ . '/..' . '/symfony/dependency-injection/Container.php', + 'Symfony\\Component\\DependencyInjection\\ContainerAwareInterface' => __DIR__ . '/..' . '/symfony/dependency-injection/ContainerAwareInterface.php', + 'Symfony\\Component\\DependencyInjection\\ContainerAwareTrait' => __DIR__ . '/..' . '/symfony/dependency-injection/ContainerAwareTrait.php', + 'Symfony\\Component\\DependencyInjection\\ContainerBuilder' => __DIR__ . '/..' . '/symfony/dependency-injection/ContainerBuilder.php', + 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => __DIR__ . '/..' . '/symfony/dependency-injection/ContainerInterface.php', + 'Symfony\\Component\\DependencyInjection\\Definition' => __DIR__ . '/..' . '/symfony/dependency-injection/Definition.php', + 'Symfony\\Component\\DependencyInjection\\DefinitionDecorator' => __DIR__ . '/..' . '/symfony/dependency-injection/DefinitionDecorator.php', + 'Symfony\\Component\\DependencyInjection\\Dumper\\Dumper' => __DIR__ . '/..' . '/symfony/dependency-injection/Dumper/Dumper.php', + 'Symfony\\Component\\DependencyInjection\\Dumper\\DumperInterface' => __DIR__ . '/..' . '/symfony/dependency-injection/Dumper/DumperInterface.php', + 'Symfony\\Component\\DependencyInjection\\Dumper\\GraphvizDumper' => __DIR__ . '/..' . '/symfony/dependency-injection/Dumper/GraphvizDumper.php', + 'Symfony\\Component\\DependencyInjection\\Dumper\\PhpDumper' => __DIR__ . '/..' . '/symfony/dependency-injection/Dumper/PhpDumper.php', + 'Symfony\\Component\\DependencyInjection\\Dumper\\XmlDumper' => __DIR__ . '/..' . '/symfony/dependency-injection/Dumper/XmlDumper.php', + 'Symfony\\Component\\DependencyInjection\\Dumper\\YamlDumper' => __DIR__ . '/..' . '/symfony/dependency-injection/Dumper/YamlDumper.php', + 'Symfony\\Component\\DependencyInjection\\EnvVarProcessor' => __DIR__ . '/..' . '/symfony/dependency-injection/EnvVarProcessor.php', + 'Symfony\\Component\\DependencyInjection\\EnvVarProcessorInterface' => __DIR__ . '/..' . '/symfony/dependency-injection/EnvVarProcessorInterface.php', + 'Symfony\\Component\\DependencyInjection\\Exception\\AutowiringFailedException' => __DIR__ . '/..' . '/symfony/dependency-injection/Exception/AutowiringFailedException.php', + 'Symfony\\Component\\DependencyInjection\\Exception\\BadMethodCallException' => __DIR__ . '/..' . '/symfony/dependency-injection/Exception/BadMethodCallException.php', + 'Symfony\\Component\\DependencyInjection\\Exception\\EnvNotFoundException' => __DIR__ . '/..' . '/symfony/dependency-injection/Exception/EnvNotFoundException.php', + 'Symfony\\Component\\DependencyInjection\\Exception\\EnvParameterException' => __DIR__ . '/..' . '/symfony/dependency-injection/Exception/EnvParameterException.php', + 'Symfony\\Component\\DependencyInjection\\Exception\\ExceptionInterface' => __DIR__ . '/..' . '/symfony/dependency-injection/Exception/ExceptionInterface.php', + 'Symfony\\Component\\DependencyInjection\\Exception\\InvalidArgumentException' => __DIR__ . '/..' . '/symfony/dependency-injection/Exception/InvalidArgumentException.php', + 'Symfony\\Component\\DependencyInjection\\Exception\\LogicException' => __DIR__ . '/..' . '/symfony/dependency-injection/Exception/LogicException.php', + 'Symfony\\Component\\DependencyInjection\\Exception\\OutOfBoundsException' => __DIR__ . '/..' . '/symfony/dependency-injection/Exception/OutOfBoundsException.php', + 'Symfony\\Component\\DependencyInjection\\Exception\\ParameterCircularReferenceException' => __DIR__ . '/..' . '/symfony/dependency-injection/Exception/ParameterCircularReferenceException.php', + 'Symfony\\Component\\DependencyInjection\\Exception\\ParameterNotFoundException' => __DIR__ . '/..' . '/symfony/dependency-injection/Exception/ParameterNotFoundException.php', + 'Symfony\\Component\\DependencyInjection\\Exception\\RuntimeException' => __DIR__ . '/..' . '/symfony/dependency-injection/Exception/RuntimeException.php', + 'Symfony\\Component\\DependencyInjection\\Exception\\ServiceCircularReferenceException' => __DIR__ . '/..' . '/symfony/dependency-injection/Exception/ServiceCircularReferenceException.php', + 'Symfony\\Component\\DependencyInjection\\Exception\\ServiceNotFoundException' => __DIR__ . '/..' . '/symfony/dependency-injection/Exception/ServiceNotFoundException.php', + 'Symfony\\Component\\DependencyInjection\\ExpressionLanguage' => __DIR__ . '/..' . '/symfony/dependency-injection/ExpressionLanguage.php', + 'Symfony\\Component\\DependencyInjection\\ExpressionLanguageProvider' => __DIR__ . '/..' . '/symfony/dependency-injection/ExpressionLanguageProvider.php', + 'Symfony\\Component\\DependencyInjection\\Extension\\ConfigurationExtensionInterface' => __DIR__ . '/..' . '/symfony/dependency-injection/Extension/ConfigurationExtensionInterface.php', + 'Symfony\\Component\\DependencyInjection\\Extension\\Extension' => __DIR__ . '/..' . '/symfony/dependency-injection/Extension/Extension.php', + 'Symfony\\Component\\DependencyInjection\\Extension\\ExtensionInterface' => __DIR__ . '/..' . '/symfony/dependency-injection/Extension/ExtensionInterface.php', + 'Symfony\\Component\\DependencyInjection\\Extension\\PrependExtensionInterface' => __DIR__ . '/..' . '/symfony/dependency-injection/Extension/PrependExtensionInterface.php', + 'Symfony\\Component\\DependencyInjection\\LazyProxy\\Instantiator\\InstantiatorInterface' => __DIR__ . '/..' . '/symfony/dependency-injection/LazyProxy/Instantiator/InstantiatorInterface.php', + 'Symfony\\Component\\DependencyInjection\\LazyProxy\\Instantiator\\RealServiceInstantiator' => __DIR__ . '/..' . '/symfony/dependency-injection/LazyProxy/Instantiator/RealServiceInstantiator.php', + 'Symfony\\Component\\DependencyInjection\\LazyProxy\\PhpDumper\\DumperInterface' => __DIR__ . '/..' . '/symfony/dependency-injection/LazyProxy/PhpDumper/DumperInterface.php', + 'Symfony\\Component\\DependencyInjection\\LazyProxy\\PhpDumper\\NullDumper' => __DIR__ . '/..' . '/symfony/dependency-injection/LazyProxy/PhpDumper/NullDumper.php', + 'Symfony\\Component\\DependencyInjection\\LazyProxy\\ProxyHelper' => __DIR__ . '/..' . '/symfony/dependency-injection/LazyProxy/ProxyHelper.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\ClosureLoader' => __DIR__ . '/..' . '/symfony/dependency-injection/Loader/ClosureLoader.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\AbstractConfigurator' => __DIR__ . '/..' . '/symfony/dependency-injection/Loader/Configurator/AbstractConfigurator.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\AbstractServiceConfigurator' => __DIR__ . '/..' . '/symfony/dependency-injection/Loader/Configurator/AbstractServiceConfigurator.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\AliasConfigurator' => __DIR__ . '/..' . '/symfony/dependency-injection/Loader/Configurator/AliasConfigurator.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\ContainerConfigurator' => __DIR__ . '/..' . '/symfony/dependency-injection/Loader/Configurator/ContainerConfigurator.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\DefaultsConfigurator' => __DIR__ . '/..' . '/symfony/dependency-injection/Loader/Configurator/DefaultsConfigurator.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\InlineServiceConfigurator' => __DIR__ . '/..' . '/symfony/dependency-injection/Loader/Configurator/InlineServiceConfigurator.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\InstanceofConfigurator' => __DIR__ . '/..' . '/symfony/dependency-injection/Loader/Configurator/InstanceofConfigurator.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\ParametersConfigurator' => __DIR__ . '/..' . '/symfony/dependency-injection/Loader/Configurator/ParametersConfigurator.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\PrototypeConfigurator' => __DIR__ . '/..' . '/symfony/dependency-injection/Loader/Configurator/PrototypeConfigurator.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\ReferenceConfigurator' => __DIR__ . '/..' . '/symfony/dependency-injection/Loader/Configurator/ReferenceConfigurator.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\ServiceConfigurator' => __DIR__ . '/..' . '/symfony/dependency-injection/Loader/Configurator/ServiceConfigurator.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\ServicesConfigurator' => __DIR__ . '/..' . '/symfony/dependency-injection/Loader/Configurator/ServicesConfigurator.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\Traits\\AbstractTrait' => __DIR__ . '/..' . '/symfony/dependency-injection/Loader/Configurator/Traits/AbstractTrait.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\Traits\\ArgumentTrait' => __DIR__ . '/..' . '/symfony/dependency-injection/Loader/Configurator/Traits/ArgumentTrait.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\Traits\\AutoconfigureTrait' => __DIR__ . '/..' . '/symfony/dependency-injection/Loader/Configurator/Traits/AutoconfigureTrait.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\Traits\\AutowireTrait' => __DIR__ . '/..' . '/symfony/dependency-injection/Loader/Configurator/Traits/AutowireTrait.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\Traits\\BindTrait' => __DIR__ . '/..' . '/symfony/dependency-injection/Loader/Configurator/Traits/BindTrait.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\Traits\\CallTrait' => __DIR__ . '/..' . '/symfony/dependency-injection/Loader/Configurator/Traits/CallTrait.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\Traits\\ClassTrait' => __DIR__ . '/..' . '/symfony/dependency-injection/Loader/Configurator/Traits/ClassTrait.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\Traits\\ConfiguratorTrait' => __DIR__ . '/..' . '/symfony/dependency-injection/Loader/Configurator/Traits/ConfiguratorTrait.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\Traits\\DecorateTrait' => __DIR__ . '/..' . '/symfony/dependency-injection/Loader/Configurator/Traits/DecorateTrait.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\Traits\\DeprecateTrait' => __DIR__ . '/..' . '/symfony/dependency-injection/Loader/Configurator/Traits/DeprecateTrait.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\Traits\\FactoryTrait' => __DIR__ . '/..' . '/symfony/dependency-injection/Loader/Configurator/Traits/FactoryTrait.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\Traits\\FileTrait' => __DIR__ . '/..' . '/symfony/dependency-injection/Loader/Configurator/Traits/FileTrait.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\Traits\\LazyTrait' => __DIR__ . '/..' . '/symfony/dependency-injection/Loader/Configurator/Traits/LazyTrait.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\Traits\\ParentTrait' => __DIR__ . '/..' . '/symfony/dependency-injection/Loader/Configurator/Traits/ParentTrait.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\Traits\\PropertyTrait' => __DIR__ . '/..' . '/symfony/dependency-injection/Loader/Configurator/Traits/PropertyTrait.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\Traits\\PublicTrait' => __DIR__ . '/..' . '/symfony/dependency-injection/Loader/Configurator/Traits/PublicTrait.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\Traits\\ShareTrait' => __DIR__ . '/..' . '/symfony/dependency-injection/Loader/Configurator/Traits/ShareTrait.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\Traits\\SyntheticTrait' => __DIR__ . '/..' . '/symfony/dependency-injection/Loader/Configurator/Traits/SyntheticTrait.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\Traits\\TagTrait' => __DIR__ . '/..' . '/symfony/dependency-injection/Loader/Configurator/Traits/TagTrait.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\DirectoryLoader' => __DIR__ . '/..' . '/symfony/dependency-injection/Loader/DirectoryLoader.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\FileLoader' => __DIR__ . '/..' . '/symfony/dependency-injection/Loader/FileLoader.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\GlobFileLoader' => __DIR__ . '/..' . '/symfony/dependency-injection/Loader/GlobFileLoader.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\IniFileLoader' => __DIR__ . '/..' . '/symfony/dependency-injection/Loader/IniFileLoader.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\PhpFileLoader' => __DIR__ . '/..' . '/symfony/dependency-injection/Loader/PhpFileLoader.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\ProtectedPhpFileLoader' => __DIR__ . '/..' . '/symfony/dependency-injection/Loader/PhpFileLoader.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\XmlFileLoader' => __DIR__ . '/..' . '/symfony/dependency-injection/Loader/XmlFileLoader.php', + 'Symfony\\Component\\DependencyInjection\\Loader\\YamlFileLoader' => __DIR__ . '/..' . '/symfony/dependency-injection/Loader/YamlFileLoader.php', + 'Symfony\\Component\\DependencyInjection\\Parameter' => __DIR__ . '/..' . '/symfony/dependency-injection/Parameter.php', + 'Symfony\\Component\\DependencyInjection\\ParameterBag\\EnvPlaceholderParameterBag' => __DIR__ . '/..' . '/symfony/dependency-injection/ParameterBag/EnvPlaceholderParameterBag.php', + 'Symfony\\Component\\DependencyInjection\\ParameterBag\\FrozenParameterBag' => __DIR__ . '/..' . '/symfony/dependency-injection/ParameterBag/FrozenParameterBag.php', + 'Symfony\\Component\\DependencyInjection\\ParameterBag\\ParameterBag' => __DIR__ . '/..' . '/symfony/dependency-injection/ParameterBag/ParameterBag.php', + 'Symfony\\Component\\DependencyInjection\\ParameterBag\\ParameterBagInterface' => __DIR__ . '/..' . '/symfony/dependency-injection/ParameterBag/ParameterBagInterface.php', + 'Symfony\\Component\\DependencyInjection\\Reference' => __DIR__ . '/..' . '/symfony/dependency-injection/Reference.php', + 'Symfony\\Component\\DependencyInjection\\ResettableContainerInterface' => __DIR__ . '/..' . '/symfony/dependency-injection/ResettableContainerInterface.php', + 'Symfony\\Component\\DependencyInjection\\ServiceLocator' => __DIR__ . '/..' . '/symfony/dependency-injection/ServiceLocator.php', + 'Symfony\\Component\\DependencyInjection\\ServiceSubscriberInterface' => __DIR__ . '/..' . '/symfony/dependency-injection/ServiceSubscriberInterface.php', + 'Symfony\\Component\\DependencyInjection\\TaggedContainerInterface' => __DIR__ . '/..' . '/symfony/dependency-injection/TaggedContainerInterface.php', + 'Symfony\\Component\\DependencyInjection\\TypedReference' => __DIR__ . '/..' . '/symfony/dependency-injection/TypedReference.php', + 'Symfony\\Component\\DependencyInjection\\Variable' => __DIR__ . '/..' . '/symfony/dependency-injection/Variable.php', + 'Symfony\\Component\\Dotenv\\Dotenv' => __DIR__ . '/..' . '/symfony/dotenv/Dotenv.php', + 'Symfony\\Component\\Dotenv\\Exception\\ExceptionInterface' => __DIR__ . '/..' . '/symfony/dotenv/Exception/ExceptionInterface.php', + 'Symfony\\Component\\Dotenv\\Exception\\FormatException' => __DIR__ . '/..' . '/symfony/dotenv/Exception/FormatException.php', + 'Symfony\\Component\\Dotenv\\Exception\\FormatExceptionContext' => __DIR__ . '/..' . '/symfony/dotenv/Exception/FormatExceptionContext.php', + 'Symfony\\Component\\Dotenv\\Exception\\PathException' => __DIR__ . '/..' . '/symfony/dotenv/Exception/PathException.php', + 'Symfony\\Component\\EventDispatcher\\ContainerAwareEventDispatcher' => __DIR__ . '/..' . '/symfony/event-dispatcher/ContainerAwareEventDispatcher.php', + 'Symfony\\Component\\EventDispatcher\\Debug\\TraceableEventDispatcher' => __DIR__ . '/..' . '/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php', + 'Symfony\\Component\\EventDispatcher\\Debug\\TraceableEventDispatcherInterface' => __DIR__ . '/..' . '/symfony/event-dispatcher/Debug/TraceableEventDispatcherInterface.php', + 'Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener' => __DIR__ . '/..' . '/symfony/event-dispatcher/Debug/WrappedListener.php', + 'Symfony\\Component\\EventDispatcher\\DependencyInjection\\ExtractingEventDispatcher' => __DIR__ . '/..' . '/symfony/event-dispatcher/DependencyInjection/RegisterListenersPass.php', + 'Symfony\\Component\\EventDispatcher\\DependencyInjection\\RegisterListenersPass' => __DIR__ . '/..' . '/symfony/event-dispatcher/DependencyInjection/RegisterListenersPass.php', + 'Symfony\\Component\\EventDispatcher\\Event' => __DIR__ . '/..' . '/symfony/event-dispatcher/Event.php', + 'Symfony\\Component\\EventDispatcher\\EventDispatcher' => __DIR__ . '/..' . '/symfony/event-dispatcher/EventDispatcher.php', + 'Symfony\\Component\\EventDispatcher\\EventDispatcherInterface' => __DIR__ . '/..' . '/symfony/event-dispatcher/EventDispatcherInterface.php', + 'Symfony\\Component\\EventDispatcher\\EventSubscriberInterface' => __DIR__ . '/..' . '/symfony/event-dispatcher/EventSubscriberInterface.php', + 'Symfony\\Component\\EventDispatcher\\GenericEvent' => __DIR__ . '/..' . '/symfony/event-dispatcher/GenericEvent.php', + 'Symfony\\Component\\EventDispatcher\\ImmutableEventDispatcher' => __DIR__ . '/..' . '/symfony/event-dispatcher/ImmutableEventDispatcher.php', + 'Symfony\\Component\\Filesystem\\Exception\\ExceptionInterface' => __DIR__ . '/..' . '/symfony/filesystem/Exception/ExceptionInterface.php', + 'Symfony\\Component\\Filesystem\\Exception\\FileNotFoundException' => __DIR__ . '/..' . '/symfony/filesystem/Exception/FileNotFoundException.php', + 'Symfony\\Component\\Filesystem\\Exception\\IOException' => __DIR__ . '/..' . '/symfony/filesystem/Exception/IOException.php', + 'Symfony\\Component\\Filesystem\\Exception\\IOExceptionInterface' => __DIR__ . '/..' . '/symfony/filesystem/Exception/IOExceptionInterface.php', + 'Symfony\\Component\\Filesystem\\Filesystem' => __DIR__ . '/..' . '/symfony/filesystem/Filesystem.php', + 'Symfony\\Component\\Filesystem\\LockHandler' => __DIR__ . '/..' . '/symfony/filesystem/LockHandler.php', + 'Symfony\\Component\\Finder\\Comparator\\Comparator' => __DIR__ . '/..' . '/symfony/finder/Comparator/Comparator.php', + 'Symfony\\Component\\Finder\\Comparator\\DateComparator' => __DIR__ . '/..' . '/symfony/finder/Comparator/DateComparator.php', + 'Symfony\\Component\\Finder\\Comparator\\NumberComparator' => __DIR__ . '/..' . '/symfony/finder/Comparator/NumberComparator.php', + 'Symfony\\Component\\Finder\\Exception\\AccessDeniedException' => __DIR__ . '/..' . '/symfony/finder/Exception/AccessDeniedException.php', + 'Symfony\\Component\\Finder\\Exception\\ExceptionInterface' => __DIR__ . '/..' . '/symfony/finder/Exception/ExceptionInterface.php', + 'Symfony\\Component\\Finder\\Finder' => __DIR__ . '/..' . '/symfony/finder/Finder.php', + 'Symfony\\Component\\Finder\\Glob' => __DIR__ . '/..' . '/symfony/finder/Glob.php', + 'Symfony\\Component\\Finder\\Iterator\\CustomFilterIterator' => __DIR__ . '/..' . '/symfony/finder/Iterator/CustomFilterIterator.php', + 'Symfony\\Component\\Finder\\Iterator\\DateRangeFilterIterator' => __DIR__ . '/..' . '/symfony/finder/Iterator/DateRangeFilterIterator.php', + 'Symfony\\Component\\Finder\\Iterator\\DepthRangeFilterIterator' => __DIR__ . '/..' . '/symfony/finder/Iterator/DepthRangeFilterIterator.php', + 'Symfony\\Component\\Finder\\Iterator\\ExcludeDirectoryFilterIterator' => __DIR__ . '/..' . '/symfony/finder/Iterator/ExcludeDirectoryFilterIterator.php', + 'Symfony\\Component\\Finder\\Iterator\\FileTypeFilterIterator' => __DIR__ . '/..' . '/symfony/finder/Iterator/FileTypeFilterIterator.php', + 'Symfony\\Component\\Finder\\Iterator\\FilecontentFilterIterator' => __DIR__ . '/..' . '/symfony/finder/Iterator/FilecontentFilterIterator.php', + 'Symfony\\Component\\Finder\\Iterator\\FilenameFilterIterator' => __DIR__ . '/..' . '/symfony/finder/Iterator/FilenameFilterIterator.php', + 'Symfony\\Component\\Finder\\Iterator\\FilterIterator' => __DIR__ . '/..' . '/symfony/finder/Iterator/FilterIterator.php', + 'Symfony\\Component\\Finder\\Iterator\\MultiplePcreFilterIterator' => __DIR__ . '/..' . '/symfony/finder/Iterator/MultiplePcreFilterIterator.php', + 'Symfony\\Component\\Finder\\Iterator\\PathFilterIterator' => __DIR__ . '/..' . '/symfony/finder/Iterator/PathFilterIterator.php', + 'Symfony\\Component\\Finder\\Iterator\\RecursiveDirectoryIterator' => __DIR__ . '/..' . '/symfony/finder/Iterator/RecursiveDirectoryIterator.php', + 'Symfony\\Component\\Finder\\Iterator\\SizeRangeFilterIterator' => __DIR__ . '/..' . '/symfony/finder/Iterator/SizeRangeFilterIterator.php', + 'Symfony\\Component\\Finder\\Iterator\\SortableIterator' => __DIR__ . '/..' . '/symfony/finder/Iterator/SortableIterator.php', + 'Symfony\\Component\\Finder\\SplFileInfo' => __DIR__ . '/..' . '/symfony/finder/SplFileInfo.php', + 'Symfony\\Component\\HttpFoundation\\AcceptHeader' => __DIR__ . '/..' . '/symfony/http-foundation/AcceptHeader.php', + 'Symfony\\Component\\HttpFoundation\\AcceptHeaderItem' => __DIR__ . '/..' . '/symfony/http-foundation/AcceptHeaderItem.php', + 'Symfony\\Component\\HttpFoundation\\ApacheRequest' => __DIR__ . '/..' . '/symfony/http-foundation/ApacheRequest.php', + 'Symfony\\Component\\HttpFoundation\\BinaryFileResponse' => __DIR__ . '/..' . '/symfony/http-foundation/BinaryFileResponse.php', + 'Symfony\\Component\\HttpFoundation\\Cookie' => __DIR__ . '/..' . '/symfony/http-foundation/Cookie.php', + 'Symfony\\Component\\HttpFoundation\\Exception\\ConflictingHeadersException' => __DIR__ . '/..' . '/symfony/http-foundation/Exception/ConflictingHeadersException.php', + 'Symfony\\Component\\HttpFoundation\\Exception\\RequestExceptionInterface' => __DIR__ . '/..' . '/symfony/http-foundation/Exception/RequestExceptionInterface.php', + 'Symfony\\Component\\HttpFoundation\\Exception\\SuspiciousOperationException' => __DIR__ . '/..' . '/symfony/http-foundation/Exception/SuspiciousOperationException.php', + 'Symfony\\Component\\HttpFoundation\\ExpressionRequestMatcher' => __DIR__ . '/..' . '/symfony/http-foundation/ExpressionRequestMatcher.php', + 'Symfony\\Component\\HttpFoundation\\FileBag' => __DIR__ . '/..' . '/symfony/http-foundation/FileBag.php', + 'Symfony\\Component\\HttpFoundation\\File\\Exception\\AccessDeniedException' => __DIR__ . '/..' . '/symfony/http-foundation/File/Exception/AccessDeniedException.php', + 'Symfony\\Component\\HttpFoundation\\File\\Exception\\FileException' => __DIR__ . '/..' . '/symfony/http-foundation/File/Exception/FileException.php', + 'Symfony\\Component\\HttpFoundation\\File\\Exception\\FileNotFoundException' => __DIR__ . '/..' . '/symfony/http-foundation/File/Exception/FileNotFoundException.php', + 'Symfony\\Component\\HttpFoundation\\File\\Exception\\UnexpectedTypeException' => __DIR__ . '/..' . '/symfony/http-foundation/File/Exception/UnexpectedTypeException.php', + 'Symfony\\Component\\HttpFoundation\\File\\Exception\\UploadException' => __DIR__ . '/..' . '/symfony/http-foundation/File/Exception/UploadException.php', + 'Symfony\\Component\\HttpFoundation\\File\\File' => __DIR__ . '/..' . '/symfony/http-foundation/File/File.php', + 'Symfony\\Component\\HttpFoundation\\File\\MimeType\\ExtensionGuesser' => __DIR__ . '/..' . '/symfony/http-foundation/File/MimeType/ExtensionGuesser.php', + 'Symfony\\Component\\HttpFoundation\\File\\MimeType\\ExtensionGuesserInterface' => __DIR__ . '/..' . '/symfony/http-foundation/File/MimeType/ExtensionGuesserInterface.php', + 'Symfony\\Component\\HttpFoundation\\File\\MimeType\\FileBinaryMimeTypeGuesser' => __DIR__ . '/..' . '/symfony/http-foundation/File/MimeType/FileBinaryMimeTypeGuesser.php', + 'Symfony\\Component\\HttpFoundation\\File\\MimeType\\FileinfoMimeTypeGuesser' => __DIR__ . '/..' . '/symfony/http-foundation/File/MimeType/FileinfoMimeTypeGuesser.php', + 'Symfony\\Component\\HttpFoundation\\File\\MimeType\\MimeTypeExtensionGuesser' => __DIR__ . '/..' . '/symfony/http-foundation/File/MimeType/MimeTypeExtensionGuesser.php', + 'Symfony\\Component\\HttpFoundation\\File\\MimeType\\MimeTypeGuesser' => __DIR__ . '/..' . '/symfony/http-foundation/File/MimeType/MimeTypeGuesser.php', + 'Symfony\\Component\\HttpFoundation\\File\\MimeType\\MimeTypeGuesserInterface' => __DIR__ . '/..' . '/symfony/http-foundation/File/MimeType/MimeTypeGuesserInterface.php', + 'Symfony\\Component\\HttpFoundation\\File\\Stream' => __DIR__ . '/..' . '/symfony/http-foundation/File/Stream.php', + 'Symfony\\Component\\HttpFoundation\\File\\UploadedFile' => __DIR__ . '/..' . '/symfony/http-foundation/File/UploadedFile.php', + 'Symfony\\Component\\HttpFoundation\\HeaderBag' => __DIR__ . '/..' . '/symfony/http-foundation/HeaderBag.php', + 'Symfony\\Component\\HttpFoundation\\IpUtils' => __DIR__ . '/..' . '/symfony/http-foundation/IpUtils.php', + 'Symfony\\Component\\HttpFoundation\\JsonResponse' => __DIR__ . '/..' . '/symfony/http-foundation/JsonResponse.php', + 'Symfony\\Component\\HttpFoundation\\ParameterBag' => __DIR__ . '/..' . '/symfony/http-foundation/ParameterBag.php', + 'Symfony\\Component\\HttpFoundation\\RedirectResponse' => __DIR__ . '/..' . '/symfony/http-foundation/RedirectResponse.php', + 'Symfony\\Component\\HttpFoundation\\Request' => __DIR__ . '/..' . '/symfony/http-foundation/Request.php', + 'Symfony\\Component\\HttpFoundation\\RequestMatcher' => __DIR__ . '/..' . '/symfony/http-foundation/RequestMatcher.php', + 'Symfony\\Component\\HttpFoundation\\RequestMatcherInterface' => __DIR__ . '/..' . '/symfony/http-foundation/RequestMatcherInterface.php', + 'Symfony\\Component\\HttpFoundation\\RequestStack' => __DIR__ . '/..' . '/symfony/http-foundation/RequestStack.php', + 'Symfony\\Component\\HttpFoundation\\Response' => __DIR__ . '/..' . '/symfony/http-foundation/Response.php', + 'Symfony\\Component\\HttpFoundation\\ResponseHeaderBag' => __DIR__ . '/..' . '/symfony/http-foundation/ResponseHeaderBag.php', + 'Symfony\\Component\\HttpFoundation\\ServerBag' => __DIR__ . '/..' . '/symfony/http-foundation/ServerBag.php', + 'Symfony\\Component\\HttpFoundation\\Session\\Attribute\\AttributeBag' => __DIR__ . '/..' . '/symfony/http-foundation/Session/Attribute/AttributeBag.php', + 'Symfony\\Component\\HttpFoundation\\Session\\Attribute\\AttributeBagInterface' => __DIR__ . '/..' . '/symfony/http-foundation/Session/Attribute/AttributeBagInterface.php', + 'Symfony\\Component\\HttpFoundation\\Session\\Attribute\\NamespacedAttributeBag' => __DIR__ . '/..' . '/symfony/http-foundation/Session/Attribute/NamespacedAttributeBag.php', + 'Symfony\\Component\\HttpFoundation\\Session\\Flash\\AutoExpireFlashBag' => __DIR__ . '/..' . '/symfony/http-foundation/Session/Flash/AutoExpireFlashBag.php', + 'Symfony\\Component\\HttpFoundation\\Session\\Flash\\FlashBag' => __DIR__ . '/..' . '/symfony/http-foundation/Session/Flash/FlashBag.php', + 'Symfony\\Component\\HttpFoundation\\Session\\Flash\\FlashBagInterface' => __DIR__ . '/..' . '/symfony/http-foundation/Session/Flash/FlashBagInterface.php', + 'Symfony\\Component\\HttpFoundation\\Session\\Session' => __DIR__ . '/..' . '/symfony/http-foundation/Session/Session.php', + 'Symfony\\Component\\HttpFoundation\\Session\\SessionBagInterface' => __DIR__ . '/..' . '/symfony/http-foundation/Session/SessionBagInterface.php', + 'Symfony\\Component\\HttpFoundation\\Session\\SessionBagProxy' => __DIR__ . '/..' . '/symfony/http-foundation/Session/SessionBagProxy.php', + 'Symfony\\Component\\HttpFoundation\\Session\\SessionInterface' => __DIR__ . '/..' . '/symfony/http-foundation/Session/SessionInterface.php', + 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\AbstractSessionHandler' => __DIR__ . '/..' . '/symfony/http-foundation/Session/Storage/Handler/AbstractSessionHandler.php', + 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\MemcacheSessionHandler' => __DIR__ . '/..' . '/symfony/http-foundation/Session/Storage/Handler/MemcacheSessionHandler.php', + 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\MemcachedSessionHandler' => __DIR__ . '/..' . '/symfony/http-foundation/Session/Storage/Handler/MemcachedSessionHandler.php', + 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\MongoDbSessionHandler' => __DIR__ . '/..' . '/symfony/http-foundation/Session/Storage/Handler/MongoDbSessionHandler.php', + 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\NativeFileSessionHandler' => __DIR__ . '/..' . '/symfony/http-foundation/Session/Storage/Handler/NativeFileSessionHandler.php', + 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\NativeSessionHandler' => __DIR__ . '/..' . '/symfony/http-foundation/Session/Storage/Handler/NativeSessionHandler.php', + 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\NullSessionHandler' => __DIR__ . '/..' . '/symfony/http-foundation/Session/Storage/Handler/NullSessionHandler.php', + 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\PdoSessionHandler' => __DIR__ . '/..' . '/symfony/http-foundation/Session/Storage/Handler/PdoSessionHandler.php', + 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\StrictSessionHandler' => __DIR__ . '/..' . '/symfony/http-foundation/Session/Storage/Handler/StrictSessionHandler.php', + 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\WriteCheckSessionHandler' => __DIR__ . '/..' . '/symfony/http-foundation/Session/Storage/Handler/WriteCheckSessionHandler.php', + 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\MetadataBag' => __DIR__ . '/..' . '/symfony/http-foundation/Session/Storage/MetadataBag.php', + 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\MockArraySessionStorage' => __DIR__ . '/..' . '/symfony/http-foundation/Session/Storage/MockArraySessionStorage.php', + 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\MockFileSessionStorage' => __DIR__ . '/..' . '/symfony/http-foundation/Session/Storage/MockFileSessionStorage.php', + 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\NativeSessionStorage' => __DIR__ . '/..' . '/symfony/http-foundation/Session/Storage/NativeSessionStorage.php', + 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\PhpBridgeSessionStorage' => __DIR__ . '/..' . '/symfony/http-foundation/Session/Storage/PhpBridgeSessionStorage.php', + 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Proxy\\AbstractProxy' => __DIR__ . '/..' . '/symfony/http-foundation/Session/Storage/Proxy/AbstractProxy.php', + 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Proxy\\NativeProxy' => __DIR__ . '/..' . '/symfony/http-foundation/Session/Storage/Proxy/NativeProxy.php', + 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Proxy\\SessionHandlerProxy' => __DIR__ . '/..' . '/symfony/http-foundation/Session/Storage/Proxy/SessionHandlerProxy.php', + 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\SessionStorageInterface' => __DIR__ . '/..' . '/symfony/http-foundation/Session/Storage/SessionStorageInterface.php', + 'Symfony\\Component\\HttpFoundation\\StreamedResponse' => __DIR__ . '/..' . '/symfony/http-foundation/StreamedResponse.php', + 'Symfony\\Component\\HttpKernel\\Bundle\\Bundle' => __DIR__ . '/..' . '/symfony/http-kernel/Bundle/Bundle.php', + 'Symfony\\Component\\HttpKernel\\Bundle\\BundleInterface' => __DIR__ . '/..' . '/symfony/http-kernel/Bundle/BundleInterface.php', + 'Symfony\\Component\\HttpKernel\\CacheClearer\\CacheClearerInterface' => __DIR__ . '/..' . '/symfony/http-kernel/CacheClearer/CacheClearerInterface.php', + 'Symfony\\Component\\HttpKernel\\CacheClearer\\ChainCacheClearer' => __DIR__ . '/..' . '/symfony/http-kernel/CacheClearer/ChainCacheClearer.php', + 'Symfony\\Component\\HttpKernel\\CacheClearer\\Psr6CacheClearer' => __DIR__ . '/..' . '/symfony/http-kernel/CacheClearer/Psr6CacheClearer.php', + 'Symfony\\Component\\HttpKernel\\CacheWarmer\\CacheWarmer' => __DIR__ . '/..' . '/symfony/http-kernel/CacheWarmer/CacheWarmer.php', + 'Symfony\\Component\\HttpKernel\\CacheWarmer\\CacheWarmerAggregate' => __DIR__ . '/..' . '/symfony/http-kernel/CacheWarmer/CacheWarmerAggregate.php', + 'Symfony\\Component\\HttpKernel\\CacheWarmer\\CacheWarmerInterface' => __DIR__ . '/..' . '/symfony/http-kernel/CacheWarmer/CacheWarmerInterface.php', + 'Symfony\\Component\\HttpKernel\\CacheWarmer\\WarmableInterface' => __DIR__ . '/..' . '/symfony/http-kernel/CacheWarmer/WarmableInterface.php', + 'Symfony\\Component\\HttpKernel\\Client' => __DIR__ . '/..' . '/symfony/http-kernel/Client.php', + 'Symfony\\Component\\HttpKernel\\Config\\EnvParametersResource' => __DIR__ . '/..' . '/symfony/http-kernel/Config/EnvParametersResource.php', + 'Symfony\\Component\\HttpKernel\\Config\\FileLocator' => __DIR__ . '/..' . '/symfony/http-kernel/Config/FileLocator.php', + 'Symfony\\Component\\HttpKernel\\ControllerMetadata\\ArgumentMetadata' => __DIR__ . '/..' . '/symfony/http-kernel/ControllerMetadata/ArgumentMetadata.php', + 'Symfony\\Component\\HttpKernel\\ControllerMetadata\\ArgumentMetadataFactory' => __DIR__ . '/..' . '/symfony/http-kernel/ControllerMetadata/ArgumentMetadataFactory.php', + 'Symfony\\Component\\HttpKernel\\ControllerMetadata\\ArgumentMetadataFactoryInterface' => __DIR__ . '/..' . '/symfony/http-kernel/ControllerMetadata/ArgumentMetadataFactoryInterface.php', + 'Symfony\\Component\\HttpKernel\\Controller\\ArgumentResolver' => __DIR__ . '/..' . '/symfony/http-kernel/Controller/ArgumentResolver.php', + 'Symfony\\Component\\HttpKernel\\Controller\\ArgumentResolverInterface' => __DIR__ . '/..' . '/symfony/http-kernel/Controller/ArgumentResolverInterface.php', + 'Symfony\\Component\\HttpKernel\\Controller\\ArgumentResolver\\DefaultValueResolver' => __DIR__ . '/..' . '/symfony/http-kernel/Controller/ArgumentResolver/DefaultValueResolver.php', + 'Symfony\\Component\\HttpKernel\\Controller\\ArgumentResolver\\RequestAttributeValueResolver' => __DIR__ . '/..' . '/symfony/http-kernel/Controller/ArgumentResolver/RequestAttributeValueResolver.php', + 'Symfony\\Component\\HttpKernel\\Controller\\ArgumentResolver\\RequestValueResolver' => __DIR__ . '/..' . '/symfony/http-kernel/Controller/ArgumentResolver/RequestValueResolver.php', + 'Symfony\\Component\\HttpKernel\\Controller\\ArgumentResolver\\ServiceValueResolver' => __DIR__ . '/..' . '/symfony/http-kernel/Controller/ArgumentResolver/ServiceValueResolver.php', + 'Symfony\\Component\\HttpKernel\\Controller\\ArgumentResolver\\SessionValueResolver' => __DIR__ . '/..' . '/symfony/http-kernel/Controller/ArgumentResolver/SessionValueResolver.php', + 'Symfony\\Component\\HttpKernel\\Controller\\ArgumentResolver\\VariadicValueResolver' => __DIR__ . '/..' . '/symfony/http-kernel/Controller/ArgumentResolver/VariadicValueResolver.php', + 'Symfony\\Component\\HttpKernel\\Controller\\ArgumentValueResolverInterface' => __DIR__ . '/..' . '/symfony/http-kernel/Controller/ArgumentValueResolverInterface.php', + 'Symfony\\Component\\HttpKernel\\Controller\\ContainerControllerResolver' => __DIR__ . '/..' . '/symfony/http-kernel/Controller/ContainerControllerResolver.php', + 'Symfony\\Component\\HttpKernel\\Controller\\ControllerReference' => __DIR__ . '/..' . '/symfony/http-kernel/Controller/ControllerReference.php', + 'Symfony\\Component\\HttpKernel\\Controller\\ControllerResolver' => __DIR__ . '/..' . '/symfony/http-kernel/Controller/ControllerResolver.php', + 'Symfony\\Component\\HttpKernel\\Controller\\ControllerResolverInterface' => __DIR__ . '/..' . '/symfony/http-kernel/Controller/ControllerResolverInterface.php', + 'Symfony\\Component\\HttpKernel\\Controller\\TraceableArgumentResolver' => __DIR__ . '/..' . '/symfony/http-kernel/Controller/TraceableArgumentResolver.php', + 'Symfony\\Component\\HttpKernel\\Controller\\TraceableControllerResolver' => __DIR__ . '/..' . '/symfony/http-kernel/Controller/TraceableControllerResolver.php', + 'Symfony\\Component\\HttpKernel\\DataCollector\\AjaxDataCollector' => __DIR__ . '/..' . '/symfony/http-kernel/DataCollector/AjaxDataCollector.php', + 'Symfony\\Component\\HttpKernel\\DataCollector\\ConfigDataCollector' => __DIR__ . '/..' . '/symfony/http-kernel/DataCollector/ConfigDataCollector.php', + 'Symfony\\Component\\HttpKernel\\DataCollector\\DataCollector' => __DIR__ . '/..' . '/symfony/http-kernel/DataCollector/DataCollector.php', + 'Symfony\\Component\\HttpKernel\\DataCollector\\DataCollectorInterface' => __DIR__ . '/..' . '/symfony/http-kernel/DataCollector/DataCollectorInterface.php', + 'Symfony\\Component\\HttpKernel\\DataCollector\\DumpDataCollector' => __DIR__ . '/..' . '/symfony/http-kernel/DataCollector/DumpDataCollector.php', + 'Symfony\\Component\\HttpKernel\\DataCollector\\EventDataCollector' => __DIR__ . '/..' . '/symfony/http-kernel/DataCollector/EventDataCollector.php', + 'Symfony\\Component\\HttpKernel\\DataCollector\\ExceptionDataCollector' => __DIR__ . '/..' . '/symfony/http-kernel/DataCollector/ExceptionDataCollector.php', + 'Symfony\\Component\\HttpKernel\\DataCollector\\LateDataCollectorInterface' => __DIR__ . '/..' . '/symfony/http-kernel/DataCollector/LateDataCollectorInterface.php', + 'Symfony\\Component\\HttpKernel\\DataCollector\\LoggerDataCollector' => __DIR__ . '/..' . '/symfony/http-kernel/DataCollector/LoggerDataCollector.php', + 'Symfony\\Component\\HttpKernel\\DataCollector\\MemoryDataCollector' => __DIR__ . '/..' . '/symfony/http-kernel/DataCollector/MemoryDataCollector.php', + 'Symfony\\Component\\HttpKernel\\DataCollector\\RequestDataCollector' => __DIR__ . '/..' . '/symfony/http-kernel/DataCollector/RequestDataCollector.php', + 'Symfony\\Component\\HttpKernel\\DataCollector\\RouterDataCollector' => __DIR__ . '/..' . '/symfony/http-kernel/DataCollector/RouterDataCollector.php', + 'Symfony\\Component\\HttpKernel\\DataCollector\\TimeDataCollector' => __DIR__ . '/..' . '/symfony/http-kernel/DataCollector/TimeDataCollector.php', + 'Symfony\\Component\\HttpKernel\\DataCollector\\Util\\ValueExporter' => __DIR__ . '/..' . '/symfony/http-kernel/DataCollector/Util/ValueExporter.php', + 'Symfony\\Component\\HttpKernel\\Debug\\FileLinkFormatter' => __DIR__ . '/..' . '/symfony/http-kernel/Debug/FileLinkFormatter.php', + 'Symfony\\Component\\HttpKernel\\Debug\\TraceableEventDispatcher' => __DIR__ . '/..' . '/symfony/http-kernel/Debug/TraceableEventDispatcher.php', + 'Symfony\\Component\\HttpKernel\\DependencyInjection\\AddAnnotatedClassesToCachePass' => __DIR__ . '/..' . '/symfony/http-kernel/DependencyInjection/AddAnnotatedClassesToCachePass.php', + 'Symfony\\Component\\HttpKernel\\DependencyInjection\\AddClassesToCachePass' => __DIR__ . '/..' . '/symfony/http-kernel/DependencyInjection/AddClassesToCachePass.php', + 'Symfony\\Component\\HttpKernel\\DependencyInjection\\ConfigurableExtension' => __DIR__ . '/..' . '/symfony/http-kernel/DependencyInjection/ConfigurableExtension.php', + 'Symfony\\Component\\HttpKernel\\DependencyInjection\\ControllerArgumentValueResolverPass' => __DIR__ . '/..' . '/symfony/http-kernel/DependencyInjection/ControllerArgumentValueResolverPass.php', + 'Symfony\\Component\\HttpKernel\\DependencyInjection\\Extension' => __DIR__ . '/..' . '/symfony/http-kernel/DependencyInjection/Extension.php', + 'Symfony\\Component\\HttpKernel\\DependencyInjection\\FragmentRendererPass' => __DIR__ . '/..' . '/symfony/http-kernel/DependencyInjection/FragmentRendererPass.php', + 'Symfony\\Component\\HttpKernel\\DependencyInjection\\LazyLoadingFragmentHandler' => __DIR__ . '/..' . '/symfony/http-kernel/DependencyInjection/LazyLoadingFragmentHandler.php', + 'Symfony\\Component\\HttpKernel\\DependencyInjection\\LoggerPass' => __DIR__ . '/..' . '/symfony/http-kernel/DependencyInjection/LoggerPass.php', + 'Symfony\\Component\\HttpKernel\\DependencyInjection\\MergeExtensionConfigurationPass' => __DIR__ . '/..' . '/symfony/http-kernel/DependencyInjection/MergeExtensionConfigurationPass.php', + 'Symfony\\Component\\HttpKernel\\DependencyInjection\\RegisterControllerArgumentLocatorsPass' => __DIR__ . '/..' . '/symfony/http-kernel/DependencyInjection/RegisterControllerArgumentLocatorsPass.php', + 'Symfony\\Component\\HttpKernel\\DependencyInjection\\RemoveEmptyControllerArgumentLocatorsPass' => __DIR__ . '/..' . '/symfony/http-kernel/DependencyInjection/RemoveEmptyControllerArgumentLocatorsPass.php', + 'Symfony\\Component\\HttpKernel\\DependencyInjection\\ResettableServicePass' => __DIR__ . '/..' . '/symfony/http-kernel/DependencyInjection/ResettableServicePass.php', + 'Symfony\\Component\\HttpKernel\\DependencyInjection\\ServicesResetter' => __DIR__ . '/..' . '/symfony/http-kernel/DependencyInjection/ServicesResetter.php', + 'Symfony\\Component\\HttpKernel\\EventListener\\AbstractSessionListener' => __DIR__ . '/..' . '/symfony/http-kernel/EventListener/AbstractSessionListener.php', + 'Symfony\\Component\\HttpKernel\\EventListener\\AbstractTestSessionListener' => __DIR__ . '/..' . '/symfony/http-kernel/EventListener/AbstractTestSessionListener.php', + 'Symfony\\Component\\HttpKernel\\EventListener\\AddRequestFormatsListener' => __DIR__ . '/..' . '/symfony/http-kernel/EventListener/AddRequestFormatsListener.php', + 'Symfony\\Component\\HttpKernel\\EventListener\\DebugHandlersListener' => __DIR__ . '/..' . '/symfony/http-kernel/EventListener/DebugHandlersListener.php', + 'Symfony\\Component\\HttpKernel\\EventListener\\DumpListener' => __DIR__ . '/..' . '/symfony/http-kernel/EventListener/DumpListener.php', + 'Symfony\\Component\\HttpKernel\\EventListener\\ExceptionListener' => __DIR__ . '/..' . '/symfony/http-kernel/EventListener/ExceptionListener.php', + 'Symfony\\Component\\HttpKernel\\EventListener\\FragmentListener' => __DIR__ . '/..' . '/symfony/http-kernel/EventListener/FragmentListener.php', + 'Symfony\\Component\\HttpKernel\\EventListener\\LocaleListener' => __DIR__ . '/..' . '/symfony/http-kernel/EventListener/LocaleListener.php', + 'Symfony\\Component\\HttpKernel\\EventListener\\ProfilerListener' => __DIR__ . '/..' . '/symfony/http-kernel/EventListener/ProfilerListener.php', + 'Symfony\\Component\\HttpKernel\\EventListener\\ResponseListener' => __DIR__ . '/..' . '/symfony/http-kernel/EventListener/ResponseListener.php', + 'Symfony\\Component\\HttpKernel\\EventListener\\RouterListener' => __DIR__ . '/..' . '/symfony/http-kernel/EventListener/RouterListener.php', + 'Symfony\\Component\\HttpKernel\\EventListener\\SaveSessionListener' => __DIR__ . '/..' . '/symfony/http-kernel/EventListener/SaveSessionListener.php', + 'Symfony\\Component\\HttpKernel\\EventListener\\SessionListener' => __DIR__ . '/..' . '/symfony/http-kernel/EventListener/SessionListener.php', + 'Symfony\\Component\\HttpKernel\\EventListener\\StreamedResponseListener' => __DIR__ . '/..' . '/symfony/http-kernel/EventListener/StreamedResponseListener.php', + 'Symfony\\Component\\HttpKernel\\EventListener\\SurrogateListener' => __DIR__ . '/..' . '/symfony/http-kernel/EventListener/SurrogateListener.php', + 'Symfony\\Component\\HttpKernel\\EventListener\\TestSessionListener' => __DIR__ . '/..' . '/symfony/http-kernel/EventListener/TestSessionListener.php', + 'Symfony\\Component\\HttpKernel\\EventListener\\TranslatorListener' => __DIR__ . '/..' . '/symfony/http-kernel/EventListener/TranslatorListener.php', + 'Symfony\\Component\\HttpKernel\\EventListener\\ValidateRequestListener' => __DIR__ . '/..' . '/symfony/http-kernel/EventListener/ValidateRequestListener.php', + 'Symfony\\Component\\HttpKernel\\Event\\FilterControllerArgumentsEvent' => __DIR__ . '/..' . '/symfony/http-kernel/Event/FilterControllerArgumentsEvent.php', + 'Symfony\\Component\\HttpKernel\\Event\\FilterControllerEvent' => __DIR__ . '/..' . '/symfony/http-kernel/Event/FilterControllerEvent.php', + 'Symfony\\Component\\HttpKernel\\Event\\FilterResponseEvent' => __DIR__ . '/..' . '/symfony/http-kernel/Event/FilterResponseEvent.php', + 'Symfony\\Component\\HttpKernel\\Event\\FinishRequestEvent' => __DIR__ . '/..' . '/symfony/http-kernel/Event/FinishRequestEvent.php', + 'Symfony\\Component\\HttpKernel\\Event\\GetResponseEvent' => __DIR__ . '/..' . '/symfony/http-kernel/Event/GetResponseEvent.php', + 'Symfony\\Component\\HttpKernel\\Event\\GetResponseForControllerResultEvent' => __DIR__ . '/..' . '/symfony/http-kernel/Event/GetResponseForControllerResultEvent.php', + 'Symfony\\Component\\HttpKernel\\Event\\GetResponseForExceptionEvent' => __DIR__ . '/..' . '/symfony/http-kernel/Event/GetResponseForExceptionEvent.php', + 'Symfony\\Component\\HttpKernel\\Event\\KernelEvent' => __DIR__ . '/..' . '/symfony/http-kernel/Event/KernelEvent.php', + 'Symfony\\Component\\HttpKernel\\Event\\PostResponseEvent' => __DIR__ . '/..' . '/symfony/http-kernel/Event/PostResponseEvent.php', + 'Symfony\\Component\\HttpKernel\\Exception\\AccessDeniedHttpException' => __DIR__ . '/..' . '/symfony/http-kernel/Exception/AccessDeniedHttpException.php', + 'Symfony\\Component\\HttpKernel\\Exception\\BadRequestHttpException' => __DIR__ . '/..' . '/symfony/http-kernel/Exception/BadRequestHttpException.php', + 'Symfony\\Component\\HttpKernel\\Exception\\ConflictHttpException' => __DIR__ . '/..' . '/symfony/http-kernel/Exception/ConflictHttpException.php', + 'Symfony\\Component\\HttpKernel\\Exception\\GoneHttpException' => __DIR__ . '/..' . '/symfony/http-kernel/Exception/GoneHttpException.php', + 'Symfony\\Component\\HttpKernel\\Exception\\HttpException' => __DIR__ . '/..' . '/symfony/http-kernel/Exception/HttpException.php', + 'Symfony\\Component\\HttpKernel\\Exception\\HttpExceptionInterface' => __DIR__ . '/..' . '/symfony/http-kernel/Exception/HttpExceptionInterface.php', + 'Symfony\\Component\\HttpKernel\\Exception\\LengthRequiredHttpException' => __DIR__ . '/..' . '/symfony/http-kernel/Exception/LengthRequiredHttpException.php', + 'Symfony\\Component\\HttpKernel\\Exception\\MethodNotAllowedHttpException' => __DIR__ . '/..' . '/symfony/http-kernel/Exception/MethodNotAllowedHttpException.php', + 'Symfony\\Component\\HttpKernel\\Exception\\NotAcceptableHttpException' => __DIR__ . '/..' . '/symfony/http-kernel/Exception/NotAcceptableHttpException.php', + 'Symfony\\Component\\HttpKernel\\Exception\\NotFoundHttpException' => __DIR__ . '/..' . '/symfony/http-kernel/Exception/NotFoundHttpException.php', + 'Symfony\\Component\\HttpKernel\\Exception\\PreconditionFailedHttpException' => __DIR__ . '/..' . '/symfony/http-kernel/Exception/PreconditionFailedHttpException.php', + 'Symfony\\Component\\HttpKernel\\Exception\\PreconditionRequiredHttpException' => __DIR__ . '/..' . '/symfony/http-kernel/Exception/PreconditionRequiredHttpException.php', + 'Symfony\\Component\\HttpKernel\\Exception\\ServiceUnavailableHttpException' => __DIR__ . '/..' . '/symfony/http-kernel/Exception/ServiceUnavailableHttpException.php', + 'Symfony\\Component\\HttpKernel\\Exception\\TooManyRequestsHttpException' => __DIR__ . '/..' . '/symfony/http-kernel/Exception/TooManyRequestsHttpException.php', + 'Symfony\\Component\\HttpKernel\\Exception\\UnauthorizedHttpException' => __DIR__ . '/..' . '/symfony/http-kernel/Exception/UnauthorizedHttpException.php', + 'Symfony\\Component\\HttpKernel\\Exception\\UnprocessableEntityHttpException' => __DIR__ . '/..' . '/symfony/http-kernel/Exception/UnprocessableEntityHttpException.php', + 'Symfony\\Component\\HttpKernel\\Exception\\UnsupportedMediaTypeHttpException' => __DIR__ . '/..' . '/symfony/http-kernel/Exception/UnsupportedMediaTypeHttpException.php', + 'Symfony\\Component\\HttpKernel\\Fragment\\AbstractSurrogateFragmentRenderer' => __DIR__ . '/..' . '/symfony/http-kernel/Fragment/AbstractSurrogateFragmentRenderer.php', + 'Symfony\\Component\\HttpKernel\\Fragment\\EsiFragmentRenderer' => __DIR__ . '/..' . '/symfony/http-kernel/Fragment/EsiFragmentRenderer.php', + 'Symfony\\Component\\HttpKernel\\Fragment\\FragmentHandler' => __DIR__ . '/..' . '/symfony/http-kernel/Fragment/FragmentHandler.php', + 'Symfony\\Component\\HttpKernel\\Fragment\\FragmentRendererInterface' => __DIR__ . '/..' . '/symfony/http-kernel/Fragment/FragmentRendererInterface.php', + 'Symfony\\Component\\HttpKernel\\Fragment\\HIncludeFragmentRenderer' => __DIR__ . '/..' . '/symfony/http-kernel/Fragment/HIncludeFragmentRenderer.php', + 'Symfony\\Component\\HttpKernel\\Fragment\\InlineFragmentRenderer' => __DIR__ . '/..' . '/symfony/http-kernel/Fragment/InlineFragmentRenderer.php', + 'Symfony\\Component\\HttpKernel\\Fragment\\RoutableFragmentRenderer' => __DIR__ . '/..' . '/symfony/http-kernel/Fragment/RoutableFragmentRenderer.php', + 'Symfony\\Component\\HttpKernel\\Fragment\\SsiFragmentRenderer' => __DIR__ . '/..' . '/symfony/http-kernel/Fragment/SsiFragmentRenderer.php', + 'Symfony\\Component\\HttpKernel\\HttpCache\\AbstractSurrogate' => __DIR__ . '/..' . '/symfony/http-kernel/HttpCache/AbstractSurrogate.php', + 'Symfony\\Component\\HttpKernel\\HttpCache\\Esi' => __DIR__ . '/..' . '/symfony/http-kernel/HttpCache/Esi.php', + 'Symfony\\Component\\HttpKernel\\HttpCache\\HttpCache' => __DIR__ . '/..' . '/symfony/http-kernel/HttpCache/HttpCache.php', + 'Symfony\\Component\\HttpKernel\\HttpCache\\ResponseCacheStrategy' => __DIR__ . '/..' . '/symfony/http-kernel/HttpCache/ResponseCacheStrategy.php', + 'Symfony\\Component\\HttpKernel\\HttpCache\\ResponseCacheStrategyInterface' => __DIR__ . '/..' . '/symfony/http-kernel/HttpCache/ResponseCacheStrategyInterface.php', + 'Symfony\\Component\\HttpKernel\\HttpCache\\Ssi' => __DIR__ . '/..' . '/symfony/http-kernel/HttpCache/Ssi.php', + 'Symfony\\Component\\HttpKernel\\HttpCache\\Store' => __DIR__ . '/..' . '/symfony/http-kernel/HttpCache/Store.php', + 'Symfony\\Component\\HttpKernel\\HttpCache\\StoreInterface' => __DIR__ . '/..' . '/symfony/http-kernel/HttpCache/StoreInterface.php', + 'Symfony\\Component\\HttpKernel\\HttpCache\\SubRequestHandler' => __DIR__ . '/..' . '/symfony/http-kernel/HttpCache/SubRequestHandler.php', + 'Symfony\\Component\\HttpKernel\\HttpCache\\SurrogateInterface' => __DIR__ . '/..' . '/symfony/http-kernel/HttpCache/SurrogateInterface.php', + 'Symfony\\Component\\HttpKernel\\HttpKernel' => __DIR__ . '/..' . '/symfony/http-kernel/HttpKernel.php', + 'Symfony\\Component\\HttpKernel\\HttpKernelInterface' => __DIR__ . '/..' . '/symfony/http-kernel/HttpKernelInterface.php', + 'Symfony\\Component\\HttpKernel\\Kernel' => __DIR__ . '/..' . '/symfony/http-kernel/Kernel.php', + 'Symfony\\Component\\HttpKernel\\KernelEvents' => __DIR__ . '/..' . '/symfony/http-kernel/KernelEvents.php', + 'Symfony\\Component\\HttpKernel\\KernelInterface' => __DIR__ . '/..' . '/symfony/http-kernel/KernelInterface.php', + 'Symfony\\Component\\HttpKernel\\Log\\DebugLoggerInterface' => __DIR__ . '/..' . '/symfony/http-kernel/Log/DebugLoggerInterface.php', + 'Symfony\\Component\\HttpKernel\\Log\\Logger' => __DIR__ . '/..' . '/symfony/http-kernel/Log/Logger.php', + 'Symfony\\Component\\HttpKernel\\Profiler\\FileProfilerStorage' => __DIR__ . '/..' . '/symfony/http-kernel/Profiler/FileProfilerStorage.php', + 'Symfony\\Component\\HttpKernel\\Profiler\\Profile' => __DIR__ . '/..' . '/symfony/http-kernel/Profiler/Profile.php', + 'Symfony\\Component\\HttpKernel\\Profiler\\Profiler' => __DIR__ . '/..' . '/symfony/http-kernel/Profiler/Profiler.php', + 'Symfony\\Component\\HttpKernel\\Profiler\\ProfilerStorageInterface' => __DIR__ . '/..' . '/symfony/http-kernel/Profiler/ProfilerStorageInterface.php', + 'Symfony\\Component\\HttpKernel\\RebootableInterface' => __DIR__ . '/..' . '/symfony/http-kernel/RebootableInterface.php', + 'Symfony\\Component\\HttpKernel\\TerminableInterface' => __DIR__ . '/..' . '/symfony/http-kernel/TerminableInterface.php', + 'Symfony\\Component\\HttpKernel\\UriSigner' => __DIR__ . '/..' . '/symfony/http-kernel/UriSigner.php', + 'Symfony\\Component\\Routing\\Annotation\\Route' => __DIR__ . '/..' . '/symfony/routing/Annotation/Route.php', + 'Symfony\\Component\\Routing\\CompiledRoute' => __DIR__ . '/..' . '/symfony/routing/CompiledRoute.php', + 'Symfony\\Component\\Routing\\DependencyInjection\\RoutingResolverPass' => __DIR__ . '/..' . '/symfony/routing/DependencyInjection/RoutingResolverPass.php', + 'Symfony\\Component\\Routing\\Exception\\ExceptionInterface' => __DIR__ . '/..' . '/symfony/routing/Exception/ExceptionInterface.php', + 'Symfony\\Component\\Routing\\Exception\\InvalidParameterException' => __DIR__ . '/..' . '/symfony/routing/Exception/InvalidParameterException.php', + 'Symfony\\Component\\Routing\\Exception\\MethodNotAllowedException' => __DIR__ . '/..' . '/symfony/routing/Exception/MethodNotAllowedException.php', + 'Symfony\\Component\\Routing\\Exception\\MissingMandatoryParametersException' => __DIR__ . '/..' . '/symfony/routing/Exception/MissingMandatoryParametersException.php', + 'Symfony\\Component\\Routing\\Exception\\NoConfigurationException' => __DIR__ . '/..' . '/symfony/routing/Exception/NoConfigurationException.php', + 'Symfony\\Component\\Routing\\Exception\\ResourceNotFoundException' => __DIR__ . '/..' . '/symfony/routing/Exception/ResourceNotFoundException.php', + 'Symfony\\Component\\Routing\\Exception\\RouteNotFoundException' => __DIR__ . '/..' . '/symfony/routing/Exception/RouteNotFoundException.php', + 'Symfony\\Component\\Routing\\Generator\\ConfigurableRequirementsInterface' => __DIR__ . '/..' . '/symfony/routing/Generator/ConfigurableRequirementsInterface.php', + 'Symfony\\Component\\Routing\\Generator\\Dumper\\GeneratorDumper' => __DIR__ . '/..' . '/symfony/routing/Generator/Dumper/GeneratorDumper.php', + 'Symfony\\Component\\Routing\\Generator\\Dumper\\GeneratorDumperInterface' => __DIR__ . '/..' . '/symfony/routing/Generator/Dumper/GeneratorDumperInterface.php', + 'Symfony\\Component\\Routing\\Generator\\Dumper\\PhpGeneratorDumper' => __DIR__ . '/..' . '/symfony/routing/Generator/Dumper/PhpGeneratorDumper.php', + 'Symfony\\Component\\Routing\\Generator\\UrlGenerator' => __DIR__ . '/..' . '/symfony/routing/Generator/UrlGenerator.php', + 'Symfony\\Component\\Routing\\Generator\\UrlGeneratorInterface' => __DIR__ . '/..' . '/symfony/routing/Generator/UrlGeneratorInterface.php', + 'Symfony\\Component\\Routing\\Loader\\AnnotationClassLoader' => __DIR__ . '/..' . '/symfony/routing/Loader/AnnotationClassLoader.php', + 'Symfony\\Component\\Routing\\Loader\\AnnotationDirectoryLoader' => __DIR__ . '/..' . '/symfony/routing/Loader/AnnotationDirectoryLoader.php', + 'Symfony\\Component\\Routing\\Loader\\AnnotationFileLoader' => __DIR__ . '/..' . '/symfony/routing/Loader/AnnotationFileLoader.php', + 'Symfony\\Component\\Routing\\Loader\\ClosureLoader' => __DIR__ . '/..' . '/symfony/routing/Loader/ClosureLoader.php', + 'Symfony\\Component\\Routing\\Loader\\Configurator\\CollectionConfigurator' => __DIR__ . '/..' . '/symfony/routing/Loader/Configurator/CollectionConfigurator.php', + 'Symfony\\Component\\Routing\\Loader\\Configurator\\ImportConfigurator' => __DIR__ . '/..' . '/symfony/routing/Loader/Configurator/ImportConfigurator.php', + 'Symfony\\Component\\Routing\\Loader\\Configurator\\RouteConfigurator' => __DIR__ . '/..' . '/symfony/routing/Loader/Configurator/RouteConfigurator.php', + 'Symfony\\Component\\Routing\\Loader\\Configurator\\RoutingConfigurator' => __DIR__ . '/..' . '/symfony/routing/Loader/Configurator/RoutingConfigurator.php', + 'Symfony\\Component\\Routing\\Loader\\Configurator\\Traits\\AddTrait' => __DIR__ . '/..' . '/symfony/routing/Loader/Configurator/Traits/AddTrait.php', + 'Symfony\\Component\\Routing\\Loader\\Configurator\\Traits\\RouteTrait' => __DIR__ . '/..' . '/symfony/routing/Loader/Configurator/Traits/RouteTrait.php', + 'Symfony\\Component\\Routing\\Loader\\DependencyInjection\\ServiceRouterLoader' => __DIR__ . '/..' . '/symfony/routing/Loader/DependencyInjection/ServiceRouterLoader.php', + 'Symfony\\Component\\Routing\\Loader\\DirectoryLoader' => __DIR__ . '/..' . '/symfony/routing/Loader/DirectoryLoader.php', + 'Symfony\\Component\\Routing\\Loader\\GlobFileLoader' => __DIR__ . '/..' . '/symfony/routing/Loader/GlobFileLoader.php', + 'Symfony\\Component\\Routing\\Loader\\ObjectRouteLoader' => __DIR__ . '/..' . '/symfony/routing/Loader/ObjectRouteLoader.php', + 'Symfony\\Component\\Routing\\Loader\\PhpFileLoader' => __DIR__ . '/..' . '/symfony/routing/Loader/PhpFileLoader.php', + 'Symfony\\Component\\Routing\\Loader\\ProtectedPhpFileLoader' => __DIR__ . '/..' . '/symfony/routing/Loader/PhpFileLoader.php', + 'Symfony\\Component\\Routing\\Loader\\XmlFileLoader' => __DIR__ . '/..' . '/symfony/routing/Loader/XmlFileLoader.php', + 'Symfony\\Component\\Routing\\Loader\\YamlFileLoader' => __DIR__ . '/..' . '/symfony/routing/Loader/YamlFileLoader.php', + 'Symfony\\Component\\Routing\\Matcher\\Dumper\\DumperCollection' => __DIR__ . '/..' . '/symfony/routing/Matcher/Dumper/DumperCollection.php', + 'Symfony\\Component\\Routing\\Matcher\\Dumper\\DumperRoute' => __DIR__ . '/..' . '/symfony/routing/Matcher/Dumper/DumperRoute.php', + 'Symfony\\Component\\Routing\\Matcher\\Dumper\\MatcherDumper' => __DIR__ . '/..' . '/symfony/routing/Matcher/Dumper/MatcherDumper.php', + 'Symfony\\Component\\Routing\\Matcher\\Dumper\\MatcherDumperInterface' => __DIR__ . '/..' . '/symfony/routing/Matcher/Dumper/MatcherDumperInterface.php', + 'Symfony\\Component\\Routing\\Matcher\\Dumper\\PhpMatcherDumper' => __DIR__ . '/..' . '/symfony/routing/Matcher/Dumper/PhpMatcherDumper.php', + 'Symfony\\Component\\Routing\\Matcher\\Dumper\\StaticPrefixCollection' => __DIR__ . '/..' . '/symfony/routing/Matcher/Dumper/StaticPrefixCollection.php', + 'Symfony\\Component\\Routing\\Matcher\\RedirectableUrlMatcher' => __DIR__ . '/..' . '/symfony/routing/Matcher/RedirectableUrlMatcher.php', + 'Symfony\\Component\\Routing\\Matcher\\RedirectableUrlMatcherInterface' => __DIR__ . '/..' . '/symfony/routing/Matcher/RedirectableUrlMatcherInterface.php', + 'Symfony\\Component\\Routing\\Matcher\\RequestMatcherInterface' => __DIR__ . '/..' . '/symfony/routing/Matcher/RequestMatcherInterface.php', + 'Symfony\\Component\\Routing\\Matcher\\TraceableUrlMatcher' => __DIR__ . '/..' . '/symfony/routing/Matcher/TraceableUrlMatcher.php', + 'Symfony\\Component\\Routing\\Matcher\\UrlMatcher' => __DIR__ . '/..' . '/symfony/routing/Matcher/UrlMatcher.php', + 'Symfony\\Component\\Routing\\Matcher\\UrlMatcherInterface' => __DIR__ . '/..' . '/symfony/routing/Matcher/UrlMatcherInterface.php', + 'Symfony\\Component\\Routing\\RequestContext' => __DIR__ . '/..' . '/symfony/routing/RequestContext.php', + 'Symfony\\Component\\Routing\\RequestContextAwareInterface' => __DIR__ . '/..' . '/symfony/routing/RequestContextAwareInterface.php', + 'Symfony\\Component\\Routing\\Route' => __DIR__ . '/..' . '/symfony/routing/Route.php', + 'Symfony\\Component\\Routing\\RouteCollection' => __DIR__ . '/..' . '/symfony/routing/RouteCollection.php', + 'Symfony\\Component\\Routing\\RouteCollectionBuilder' => __DIR__ . '/..' . '/symfony/routing/RouteCollectionBuilder.php', + 'Symfony\\Component\\Routing\\RouteCompiler' => __DIR__ . '/..' . '/symfony/routing/RouteCompiler.php', + 'Symfony\\Component\\Routing\\RouteCompilerInterface' => __DIR__ . '/..' . '/symfony/routing/RouteCompilerInterface.php', + 'Symfony\\Component\\Routing\\Router' => __DIR__ . '/..' . '/symfony/routing/Router.php', + 'Symfony\\Component\\Routing\\RouterInterface' => __DIR__ . '/..' . '/symfony/routing/RouterInterface.php', + 'Symfony\\Component\\Stopwatch\\Section' => __DIR__ . '/..' . '/symfony/stopwatch/Section.php', + 'Symfony\\Component\\Stopwatch\\Stopwatch' => __DIR__ . '/..' . '/symfony/stopwatch/Stopwatch.php', + 'Symfony\\Component\\Stopwatch\\StopwatchEvent' => __DIR__ . '/..' . '/symfony/stopwatch/StopwatchEvent.php', + 'Symfony\\Component\\Stopwatch\\StopwatchPeriod' => __DIR__ . '/..' . '/symfony/stopwatch/StopwatchPeriod.php', + 'Symfony\\Component\\VarDumper\\Caster\\AmqpCaster' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/AmqpCaster.php', + 'Symfony\\Component\\VarDumper\\Caster\\ArgsStub' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/ArgsStub.php', + 'Symfony\\Component\\VarDumper\\Caster\\Caster' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/Caster.php', + 'Symfony\\Component\\VarDumper\\Caster\\ClassStub' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/ClassStub.php', + 'Symfony\\Component\\VarDumper\\Caster\\ConstStub' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/ConstStub.php', + 'Symfony\\Component\\VarDumper\\Caster\\CutArrayStub' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/CutArrayStub.php', + 'Symfony\\Component\\VarDumper\\Caster\\CutStub' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/CutStub.php', + 'Symfony\\Component\\VarDumper\\Caster\\DOMCaster' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/DOMCaster.php', + 'Symfony\\Component\\VarDumper\\Caster\\DateCaster' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/DateCaster.php', + 'Symfony\\Component\\VarDumper\\Caster\\DoctrineCaster' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/DoctrineCaster.php', + 'Symfony\\Component\\VarDumper\\Caster\\EnumStub' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/EnumStub.php', + 'Symfony\\Component\\VarDumper\\Caster\\ExceptionCaster' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/ExceptionCaster.php', + 'Symfony\\Component\\VarDumper\\Caster\\FrameStub' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/FrameStub.php', + 'Symfony\\Component\\VarDumper\\Caster\\LinkStub' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/LinkStub.php', + 'Symfony\\Component\\VarDumper\\Caster\\MongoCaster' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/MongoCaster.php', + 'Symfony\\Component\\VarDumper\\Caster\\PdoCaster' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/PdoCaster.php', + 'Symfony\\Component\\VarDumper\\Caster\\PgSqlCaster' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/PgSqlCaster.php', + 'Symfony\\Component\\VarDumper\\Caster\\RedisCaster' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/RedisCaster.php', + 'Symfony\\Component\\VarDumper\\Caster\\ReflectionCaster' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/ReflectionCaster.php', + 'Symfony\\Component\\VarDumper\\Caster\\ResourceCaster' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/ResourceCaster.php', + 'Symfony\\Component\\VarDumper\\Caster\\SplCaster' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/SplCaster.php', + 'Symfony\\Component\\VarDumper\\Caster\\StubCaster' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/StubCaster.php', + 'Symfony\\Component\\VarDumper\\Caster\\SymfonyCaster' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/SymfonyCaster.php', + 'Symfony\\Component\\VarDumper\\Caster\\TraceStub' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/TraceStub.php', + 'Symfony\\Component\\VarDumper\\Caster\\XmlReaderCaster' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/XmlReaderCaster.php', + 'Symfony\\Component\\VarDumper\\Caster\\XmlResourceCaster' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/XmlResourceCaster.php', + 'Symfony\\Component\\VarDumper\\Cloner\\AbstractCloner' => __DIR__ . '/..' . '/symfony/var-dumper/Cloner/AbstractCloner.php', + 'Symfony\\Component\\VarDumper\\Cloner\\ClonerInterface' => __DIR__ . '/..' . '/symfony/var-dumper/Cloner/ClonerInterface.php', + 'Symfony\\Component\\VarDumper\\Cloner\\Cursor' => __DIR__ . '/..' . '/symfony/var-dumper/Cloner/Cursor.php', + 'Symfony\\Component\\VarDumper\\Cloner\\Data' => __DIR__ . '/..' . '/symfony/var-dumper/Cloner/Data.php', + 'Symfony\\Component\\VarDumper\\Cloner\\DumperInterface' => __DIR__ . '/..' . '/symfony/var-dumper/Cloner/DumperInterface.php', + 'Symfony\\Component\\VarDumper\\Cloner\\Stub' => __DIR__ . '/..' . '/symfony/var-dumper/Cloner/Stub.php', + 'Symfony\\Component\\VarDumper\\Cloner\\VarCloner' => __DIR__ . '/..' . '/symfony/var-dumper/Cloner/VarCloner.php', + 'Symfony\\Component\\VarDumper\\Dumper\\AbstractDumper' => __DIR__ . '/..' . '/symfony/var-dumper/Dumper/AbstractDumper.php', + 'Symfony\\Component\\VarDumper\\Dumper\\CliDumper' => __DIR__ . '/..' . '/symfony/var-dumper/Dumper/CliDumper.php', + 'Symfony\\Component\\VarDumper\\Dumper\\DataDumperInterface' => __DIR__ . '/..' . '/symfony/var-dumper/Dumper/DataDumperInterface.php', + 'Symfony\\Component\\VarDumper\\Dumper\\HtmlDumper' => __DIR__ . '/..' . '/symfony/var-dumper/Dumper/HtmlDumper.php', + 'Symfony\\Component\\VarDumper\\Exception\\ThrowingCasterException' => __DIR__ . '/..' . '/symfony/var-dumper/Exception/ThrowingCasterException.php', + 'Symfony\\Component\\VarDumper\\Test\\VarDumperTestTrait' => __DIR__ . '/..' . '/symfony/var-dumper/Test/VarDumperTestTrait.php', + 'Symfony\\Component\\VarDumper\\VarDumper' => __DIR__ . '/..' . '/symfony/var-dumper/VarDumper.php', + 'Symfony\\Component\\Yaml\\Command\\LintCommand' => __DIR__ . '/..' . '/symfony/yaml/Command/LintCommand.php', + 'Symfony\\Component\\Yaml\\Dumper' => __DIR__ . '/..' . '/symfony/yaml/Dumper.php', + 'Symfony\\Component\\Yaml\\Escaper' => __DIR__ . '/..' . '/symfony/yaml/Escaper.php', + 'Symfony\\Component\\Yaml\\Exception\\DumpException' => __DIR__ . '/..' . '/symfony/yaml/Exception/DumpException.php', + 'Symfony\\Component\\Yaml\\Exception\\ExceptionInterface' => __DIR__ . '/..' . '/symfony/yaml/Exception/ExceptionInterface.php', + 'Symfony\\Component\\Yaml\\Exception\\ParseException' => __DIR__ . '/..' . '/symfony/yaml/Exception/ParseException.php', + 'Symfony\\Component\\Yaml\\Exception\\RuntimeException' => __DIR__ . '/..' . '/symfony/yaml/Exception/RuntimeException.php', + 'Symfony\\Component\\Yaml\\Inline' => __DIR__ . '/..' . '/symfony/yaml/Inline.php', + 'Symfony\\Component\\Yaml\\Parser' => __DIR__ . '/..' . '/symfony/yaml/Parser.php', + 'Symfony\\Component\\Yaml\\Tag\\TaggedValue' => __DIR__ . '/..' . '/symfony/yaml/Tag/TaggedValue.php', + 'Symfony\\Component\\Yaml\\Unescaper' => __DIR__ . '/..' . '/symfony/yaml/Unescaper.php', + 'Symfony\\Component\\Yaml\\Yaml' => __DIR__ . '/..' . '/symfony/yaml/Yaml.php', + 'Symfony\\Polyfill\\Apcu\\Apcu' => __DIR__ . '/..' . '/symfony/polyfill-apcu/Apcu.php', + 'Symfony\\Polyfill\\Ctype\\Ctype' => __DIR__ . '/..' . '/symfony/polyfill-ctype/Ctype.php', + 'Symfony\\Polyfill\\Mbstring\\Mbstring' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/Mbstring.php', + 'Symfony\\Polyfill\\Php70\\Php70' => __DIR__ . '/..' . '/symfony/polyfill-php70/Php70.php', + 'TabManager' => __DIR__ . '/../..' . '/application/webpage.class.inc.php', + 'TabularBulkExport' => __DIR__ . '/../..' . '/core/tabularbulkexport.class.inc.php', + 'TagSetFieldData' => __DIR__ . '/../..' . '/core/tagsetfield.class.inc.php', + 'TemplateMenuNode' => __DIR__ . '/../..' . '/application/menunode.class.inc.php', + 'TemplateString' => __DIR__ . '/../..' . '/core/templatestring.class.inc.php', + 'TemplateStringPlaceholder' => __DIR__ . '/../..' . '/core/templatestring.class.inc.php', + 'ToolsLog' => __DIR__ . '/../..' . '/core/log.class.inc.php', + 'Trigger' => __DIR__ . '/../..' . '/core/trigger.class.inc.php', + 'TriggerOnObject' => __DIR__ . '/../..' . '/core/trigger.class.inc.php', + 'TriggerOnObjectCreate' => __DIR__ . '/../..' . '/core/trigger.class.inc.php', + 'TriggerOnObjectDelete' => __DIR__ . '/../..' . '/core/trigger.class.inc.php', + 'TriggerOnObjectUpdate' => __DIR__ . '/../..' . '/core/trigger.class.inc.php', + 'TriggerOnPortalUpdate' => __DIR__ . '/../..' . '/core/trigger.class.inc.php', + 'TriggerOnStateChange' => __DIR__ . '/../..' . '/core/trigger.class.inc.php', + 'TriggerOnStateEnter' => __DIR__ . '/../..' . '/core/trigger.class.inc.php', + 'TriggerOnStateLeave' => __DIR__ . '/../..' . '/core/trigger.class.inc.php', + 'TriggerOnThresholdReached' => __DIR__ . '/../..' . '/core/trigger.class.inc.php', + 'TrueExpression' => __DIR__ . '/../..' . '/core/oql/expression.class.inc.php', + 'Twig\\Cache\\CacheInterface' => __DIR__ . '/..' . '/twig/twig/src/Cache/CacheInterface.php', + 'Twig\\Cache\\FilesystemCache' => __DIR__ . '/..' . '/twig/twig/src/Cache/FilesystemCache.php', + 'Twig\\Cache\\NullCache' => __DIR__ . '/..' . '/twig/twig/src/Cache/NullCache.php', + 'Twig\\Compiler' => __DIR__ . '/..' . '/twig/twig/src/Compiler.php', + 'Twig\\Environment' => __DIR__ . '/..' . '/twig/twig/src/Environment.php', + 'Twig\\Error\\Error' => __DIR__ . '/..' . '/twig/twig/src/Error/Error.php', + 'Twig\\Error\\LoaderError' => __DIR__ . '/..' . '/twig/twig/src/Error/LoaderError.php', + 'Twig\\Error\\RuntimeError' => __DIR__ . '/..' . '/twig/twig/src/Error/RuntimeError.php', + 'Twig\\Error\\SyntaxError' => __DIR__ . '/..' . '/twig/twig/src/Error/SyntaxError.php', + 'Twig\\ExpressionParser' => __DIR__ . '/..' . '/twig/twig/src/ExpressionParser.php', + 'Twig\\Extension\\AbstractExtension' => __DIR__ . '/..' . '/twig/twig/src/Extension/AbstractExtension.php', + 'Twig\\Extension\\CoreExtension' => __DIR__ . '/..' . '/twig/twig/src/Extension/CoreExtension.php', + 'Twig\\Extension\\DebugExtension' => __DIR__ . '/..' . '/twig/twig/src/Extension/DebugExtension.php', + 'Twig\\Extension\\EscaperExtension' => __DIR__ . '/..' . '/twig/twig/src/Extension/EscaperExtension.php', + 'Twig\\Extension\\ExtensionInterface' => __DIR__ . '/..' . '/twig/twig/src/Extension/ExtensionInterface.php', + 'Twig\\Extension\\GlobalsInterface' => __DIR__ . '/..' . '/twig/twig/src/Extension/GlobalsInterface.php', + 'Twig\\Extension\\InitRuntimeInterface' => __DIR__ . '/..' . '/twig/twig/src/Extension/InitRuntimeInterface.php', + 'Twig\\Extension\\OptimizerExtension' => __DIR__ . '/..' . '/twig/twig/src/Extension/OptimizerExtension.php', + 'Twig\\Extension\\ProfilerExtension' => __DIR__ . '/..' . '/twig/twig/src/Extension/ProfilerExtension.php', + 'Twig\\Extension\\RuntimeExtensionInterface' => __DIR__ . '/..' . '/twig/twig/src/Extension/RuntimeExtensionInterface.php', + 'Twig\\Extension\\SandboxExtension' => __DIR__ . '/..' . '/twig/twig/src/Extension/SandboxExtension.php', + 'Twig\\Extension\\StagingExtension' => __DIR__ . '/..' . '/twig/twig/src/Extension/StagingExtension.php', + 'Twig\\Extension\\StringLoaderExtension' => __DIR__ . '/..' . '/twig/twig/src/Extension/StringLoaderExtension.php', + 'Twig\\FileExtensionEscapingStrategy' => __DIR__ . '/..' . '/twig/twig/src/FileExtensionEscapingStrategy.php', + 'Twig\\Lexer' => __DIR__ . '/..' . '/twig/twig/src/Lexer.php', + 'Twig\\Loader\\ArrayLoader' => __DIR__ . '/..' . '/twig/twig/src/Loader/ArrayLoader.php', + 'Twig\\Loader\\ChainLoader' => __DIR__ . '/..' . '/twig/twig/src/Loader/ChainLoader.php', + 'Twig\\Loader\\ExistsLoaderInterface' => __DIR__ . '/..' . '/twig/twig/src/Loader/ExistsLoaderInterface.php', + 'Twig\\Loader\\FilesystemLoader' => __DIR__ . '/..' . '/twig/twig/src/Loader/FilesystemLoader.php', + 'Twig\\Loader\\LoaderInterface' => __DIR__ . '/..' . '/twig/twig/src/Loader/LoaderInterface.php', + 'Twig\\Loader\\SourceContextLoaderInterface' => __DIR__ . '/..' . '/twig/twig/src/Loader/SourceContextLoaderInterface.php', + 'Twig\\Markup' => __DIR__ . '/..' . '/twig/twig/src/Markup.php', + 'Twig\\NodeTraverser' => __DIR__ . '/..' . '/twig/twig/src/NodeTraverser.php', + 'Twig\\NodeVisitor\\AbstractNodeVisitor' => __DIR__ . '/..' . '/twig/twig/src/NodeVisitor/AbstractNodeVisitor.php', + 'Twig\\NodeVisitor\\EscaperNodeVisitor' => __DIR__ . '/..' . '/twig/twig/src/NodeVisitor/EscaperNodeVisitor.php', + 'Twig\\NodeVisitor\\NodeVisitorInterface' => __DIR__ . '/..' . '/twig/twig/src/NodeVisitor/NodeVisitorInterface.php', + 'Twig\\NodeVisitor\\OptimizerNodeVisitor' => __DIR__ . '/..' . '/twig/twig/src/NodeVisitor/OptimizerNodeVisitor.php', + 'Twig\\NodeVisitor\\SafeAnalysisNodeVisitor' => __DIR__ . '/..' . '/twig/twig/src/NodeVisitor/SafeAnalysisNodeVisitor.php', + 'Twig\\NodeVisitor\\SandboxNodeVisitor' => __DIR__ . '/..' . '/twig/twig/src/NodeVisitor/SandboxNodeVisitor.php', + 'Twig\\Node\\AutoEscapeNode' => __DIR__ . '/..' . '/twig/twig/src/Node/AutoEscapeNode.php', + 'Twig\\Node\\BlockNode' => __DIR__ . '/..' . '/twig/twig/src/Node/BlockNode.php', + 'Twig\\Node\\BlockReferenceNode' => __DIR__ . '/..' . '/twig/twig/src/Node/BlockReferenceNode.php', + 'Twig\\Node\\BodyNode' => __DIR__ . '/..' . '/twig/twig/src/Node/BodyNode.php', + 'Twig\\Node\\CheckSecurityNode' => __DIR__ . '/..' . '/twig/twig/src/Node/CheckSecurityNode.php', + 'Twig\\Node\\CheckToStringNode' => __DIR__ . '/..' . '/twig/twig/src/Node/CheckToStringNode.php', + 'Twig\\Node\\DeprecatedNode' => __DIR__ . '/..' . '/twig/twig/src/Node/DeprecatedNode.php', + 'Twig\\Node\\DoNode' => __DIR__ . '/..' . '/twig/twig/src/Node/DoNode.php', + 'Twig\\Node\\EmbedNode' => __DIR__ . '/..' . '/twig/twig/src/Node/EmbedNode.php', + 'Twig\\Node\\Expression\\AbstractExpression' => __DIR__ . '/..' . '/twig/twig/src/Node/Expression/AbstractExpression.php', + 'Twig\\Node\\Expression\\ArrayExpression' => __DIR__ . '/..' . '/twig/twig/src/Node/Expression/ArrayExpression.php', + 'Twig\\Node\\Expression\\ArrowFunctionExpression' => __DIR__ . '/..' . '/twig/twig/src/Node/Expression/ArrowFunctionExpression.php', + 'Twig\\Node\\Expression\\AssignNameExpression' => __DIR__ . '/..' . '/twig/twig/src/Node/Expression/AssignNameExpression.php', + 'Twig\\Node\\Expression\\Binary\\AbstractBinary' => __DIR__ . '/..' . '/twig/twig/src/Node/Expression/Binary/AbstractBinary.php', + 'Twig\\Node\\Expression\\Binary\\AddBinary' => __DIR__ . '/..' . '/twig/twig/src/Node/Expression/Binary/AddBinary.php', + 'Twig\\Node\\Expression\\Binary\\AndBinary' => __DIR__ . '/..' . '/twig/twig/src/Node/Expression/Binary/AndBinary.php', + 'Twig\\Node\\Expression\\Binary\\BitwiseAndBinary' => __DIR__ . '/..' . '/twig/twig/src/Node/Expression/Binary/BitwiseAndBinary.php', + 'Twig\\Node\\Expression\\Binary\\BitwiseOrBinary' => __DIR__ . '/..' . '/twig/twig/src/Node/Expression/Binary/BitwiseOrBinary.php', + 'Twig\\Node\\Expression\\Binary\\BitwiseXorBinary' => __DIR__ . '/..' . '/twig/twig/src/Node/Expression/Binary/BitwiseXorBinary.php', + 'Twig\\Node\\Expression\\Binary\\ConcatBinary' => __DIR__ . '/..' . '/twig/twig/src/Node/Expression/Binary/ConcatBinary.php', + 'Twig\\Node\\Expression\\Binary\\DivBinary' => __DIR__ . '/..' . '/twig/twig/src/Node/Expression/Binary/DivBinary.php', + 'Twig\\Node\\Expression\\Binary\\EndsWithBinary' => __DIR__ . '/..' . '/twig/twig/src/Node/Expression/Binary/EndsWithBinary.php', + 'Twig\\Node\\Expression\\Binary\\EqualBinary' => __DIR__ . '/..' . '/twig/twig/src/Node/Expression/Binary/EqualBinary.php', + 'Twig\\Node\\Expression\\Binary\\FloorDivBinary' => __DIR__ . '/..' . '/twig/twig/src/Node/Expression/Binary/FloorDivBinary.php', + 'Twig\\Node\\Expression\\Binary\\GreaterBinary' => __DIR__ . '/..' . '/twig/twig/src/Node/Expression/Binary/GreaterBinary.php', + 'Twig\\Node\\Expression\\Binary\\GreaterEqualBinary' => __DIR__ . '/..' . '/twig/twig/src/Node/Expression/Binary/GreaterEqualBinary.php', + 'Twig\\Node\\Expression\\Binary\\InBinary' => __DIR__ . '/..' . '/twig/twig/src/Node/Expression/Binary/InBinary.php', + 'Twig\\Node\\Expression\\Binary\\LessBinary' => __DIR__ . '/..' . '/twig/twig/src/Node/Expression/Binary/LessBinary.php', + 'Twig\\Node\\Expression\\Binary\\LessEqualBinary' => __DIR__ . '/..' . '/twig/twig/src/Node/Expression/Binary/LessEqualBinary.php', + 'Twig\\Node\\Expression\\Binary\\MatchesBinary' => __DIR__ . '/..' . '/twig/twig/src/Node/Expression/Binary/MatchesBinary.php', + 'Twig\\Node\\Expression\\Binary\\ModBinary' => __DIR__ . '/..' . '/twig/twig/src/Node/Expression/Binary/ModBinary.php', + 'Twig\\Node\\Expression\\Binary\\MulBinary' => __DIR__ . '/..' . '/twig/twig/src/Node/Expression/Binary/MulBinary.php', + 'Twig\\Node\\Expression\\Binary\\NotEqualBinary' => __DIR__ . '/..' . '/twig/twig/src/Node/Expression/Binary/NotEqualBinary.php', + 'Twig\\Node\\Expression\\Binary\\NotInBinary' => __DIR__ . '/..' . '/twig/twig/src/Node/Expression/Binary/NotInBinary.php', + 'Twig\\Node\\Expression\\Binary\\OrBinary' => __DIR__ . '/..' . '/twig/twig/src/Node/Expression/Binary/OrBinary.php', + 'Twig\\Node\\Expression\\Binary\\PowerBinary' => __DIR__ . '/..' . '/twig/twig/src/Node/Expression/Binary/PowerBinary.php', + 'Twig\\Node\\Expression\\Binary\\RangeBinary' => __DIR__ . '/..' . '/twig/twig/src/Node/Expression/Binary/RangeBinary.php', + 'Twig\\Node\\Expression\\Binary\\StartsWithBinary' => __DIR__ . '/..' . '/twig/twig/src/Node/Expression/Binary/StartsWithBinary.php', + 'Twig\\Node\\Expression\\Binary\\SubBinary' => __DIR__ . '/..' . '/twig/twig/src/Node/Expression/Binary/SubBinary.php', + 'Twig\\Node\\Expression\\BlockReferenceExpression' => __DIR__ . '/..' . '/twig/twig/src/Node/Expression/BlockReferenceExpression.php', + 'Twig\\Node\\Expression\\CallExpression' => __DIR__ . '/..' . '/twig/twig/src/Node/Expression/CallExpression.php', + 'Twig\\Node\\Expression\\ConditionalExpression' => __DIR__ . '/..' . '/twig/twig/src/Node/Expression/ConditionalExpression.php', + 'Twig\\Node\\Expression\\ConstantExpression' => __DIR__ . '/..' . '/twig/twig/src/Node/Expression/ConstantExpression.php', + 'Twig\\Node\\Expression\\FilterExpression' => __DIR__ . '/..' . '/twig/twig/src/Node/Expression/FilterExpression.php', + 'Twig\\Node\\Expression\\Filter\\DefaultFilter' => __DIR__ . '/..' . '/twig/twig/src/Node/Expression/Filter/DefaultFilter.php', + 'Twig\\Node\\Expression\\FunctionExpression' => __DIR__ . '/..' . '/twig/twig/src/Node/Expression/FunctionExpression.php', + 'Twig\\Node\\Expression\\GetAttrExpression' => __DIR__ . '/..' . '/twig/twig/src/Node/Expression/GetAttrExpression.php', + 'Twig\\Node\\Expression\\InlinePrint' => __DIR__ . '/..' . '/twig/twig/src/Node/Expression/InlinePrint.php', + 'Twig\\Node\\Expression\\MethodCallExpression' => __DIR__ . '/..' . '/twig/twig/src/Node/Expression/MethodCallExpression.php', + 'Twig\\Node\\Expression\\NameExpression' => __DIR__ . '/..' . '/twig/twig/src/Node/Expression/NameExpression.php', + 'Twig\\Node\\Expression\\NullCoalesceExpression' => __DIR__ . '/..' . '/twig/twig/src/Node/Expression/NullCoalesceExpression.php', + 'Twig\\Node\\Expression\\ParentExpression' => __DIR__ . '/..' . '/twig/twig/src/Node/Expression/ParentExpression.php', + 'Twig\\Node\\Expression\\TempNameExpression' => __DIR__ . '/..' . '/twig/twig/src/Node/Expression/TempNameExpression.php', + 'Twig\\Node\\Expression\\TestExpression' => __DIR__ . '/..' . '/twig/twig/src/Node/Expression/TestExpression.php', + 'Twig\\Node\\Expression\\Test\\ConstantTest' => __DIR__ . '/..' . '/twig/twig/src/Node/Expression/Test/ConstantTest.php', + 'Twig\\Node\\Expression\\Test\\DefinedTest' => __DIR__ . '/..' . '/twig/twig/src/Node/Expression/Test/DefinedTest.php', + 'Twig\\Node\\Expression\\Test\\DivisiblebyTest' => __DIR__ . '/..' . '/twig/twig/src/Node/Expression/Test/DivisiblebyTest.php', + 'Twig\\Node\\Expression\\Test\\EvenTest' => __DIR__ . '/..' . '/twig/twig/src/Node/Expression/Test/EvenTest.php', + 'Twig\\Node\\Expression\\Test\\NullTest' => __DIR__ . '/..' . '/twig/twig/src/Node/Expression/Test/NullTest.php', + 'Twig\\Node\\Expression\\Test\\OddTest' => __DIR__ . '/..' . '/twig/twig/src/Node/Expression/Test/OddTest.php', + 'Twig\\Node\\Expression\\Test\\SameasTest' => __DIR__ . '/..' . '/twig/twig/src/Node/Expression/Test/SameasTest.php', + 'Twig\\Node\\Expression\\Unary\\AbstractUnary' => __DIR__ . '/..' . '/twig/twig/src/Node/Expression/Unary/AbstractUnary.php', + 'Twig\\Node\\Expression\\Unary\\NegUnary' => __DIR__ . '/..' . '/twig/twig/src/Node/Expression/Unary/NegUnary.php', + 'Twig\\Node\\Expression\\Unary\\NotUnary' => __DIR__ . '/..' . '/twig/twig/src/Node/Expression/Unary/NotUnary.php', + 'Twig\\Node\\Expression\\Unary\\PosUnary' => __DIR__ . '/..' . '/twig/twig/src/Node/Expression/Unary/PosUnary.php', + 'Twig\\Node\\FlushNode' => __DIR__ . '/..' . '/twig/twig/src/Node/FlushNode.php', + 'Twig\\Node\\ForLoopNode' => __DIR__ . '/..' . '/twig/twig/src/Node/ForLoopNode.php', + 'Twig\\Node\\ForNode' => __DIR__ . '/..' . '/twig/twig/src/Node/ForNode.php', + 'Twig\\Node\\IfNode' => __DIR__ . '/..' . '/twig/twig/src/Node/IfNode.php', + 'Twig\\Node\\ImportNode' => __DIR__ . '/..' . '/twig/twig/src/Node/ImportNode.php', + 'Twig\\Node\\IncludeNode' => __DIR__ . '/..' . '/twig/twig/src/Node/IncludeNode.php', + 'Twig\\Node\\MacroNode' => __DIR__ . '/..' . '/twig/twig/src/Node/MacroNode.php', + 'Twig\\Node\\ModuleNode' => __DIR__ . '/..' . '/twig/twig/src/Node/ModuleNode.php', + 'Twig\\Node\\Node' => __DIR__ . '/..' . '/twig/twig/src/Node/Node.php', + 'Twig\\Node\\NodeCaptureInterface' => __DIR__ . '/..' . '/twig/twig/src/Node/NodeCaptureInterface.php', + 'Twig\\Node\\NodeOutputInterface' => __DIR__ . '/..' . '/twig/twig/src/Node/NodeOutputInterface.php', + 'Twig\\Node\\PrintNode' => __DIR__ . '/..' . '/twig/twig/src/Node/PrintNode.php', + 'Twig\\Node\\SandboxNode' => __DIR__ . '/..' . '/twig/twig/src/Node/SandboxNode.php', + 'Twig\\Node\\SandboxedPrintNode' => __DIR__ . '/..' . '/twig/twig/src/Node/SandboxedPrintNode.php', + 'Twig\\Node\\SetNode' => __DIR__ . '/..' . '/twig/twig/src/Node/SetNode.php', + 'Twig\\Node\\SetTempNode' => __DIR__ . '/..' . '/twig/twig/src/Node/SetTempNode.php', + 'Twig\\Node\\SpacelessNode' => __DIR__ . '/..' . '/twig/twig/src/Node/SpacelessNode.php', + 'Twig\\Node\\TextNode' => __DIR__ . '/..' . '/twig/twig/src/Node/TextNode.php', + 'Twig\\Node\\WithNode' => __DIR__ . '/..' . '/twig/twig/src/Node/WithNode.php', + 'Twig\\Parser' => __DIR__ . '/..' . '/twig/twig/src/Parser.php', + 'Twig\\Profiler\\Dumper\\BaseDumper' => __DIR__ . '/..' . '/twig/twig/src/Profiler/Dumper/BaseDumper.php', + 'Twig\\Profiler\\Dumper\\BlackfireDumper' => __DIR__ . '/..' . '/twig/twig/src/Profiler/Dumper/BlackfireDumper.php', + 'Twig\\Profiler\\Dumper\\HtmlDumper' => __DIR__ . '/..' . '/twig/twig/src/Profiler/Dumper/HtmlDumper.php', + 'Twig\\Profiler\\Dumper\\TextDumper' => __DIR__ . '/..' . '/twig/twig/src/Profiler/Dumper/TextDumper.php', + 'Twig\\Profiler\\NodeVisitor\\ProfilerNodeVisitor' => __DIR__ . '/..' . '/twig/twig/src/Profiler/NodeVisitor/ProfilerNodeVisitor.php', + 'Twig\\Profiler\\Node\\EnterProfileNode' => __DIR__ . '/..' . '/twig/twig/src/Profiler/Node/EnterProfileNode.php', + 'Twig\\Profiler\\Node\\LeaveProfileNode' => __DIR__ . '/..' . '/twig/twig/src/Profiler/Node/LeaveProfileNode.php', + 'Twig\\Profiler\\Profile' => __DIR__ . '/..' . '/twig/twig/src/Profiler/Profile.php', + 'Twig\\RuntimeLoader\\ContainerRuntimeLoader' => __DIR__ . '/..' . '/twig/twig/src/RuntimeLoader/ContainerRuntimeLoader.php', + 'Twig\\RuntimeLoader\\FactoryRuntimeLoader' => __DIR__ . '/..' . '/twig/twig/src/RuntimeLoader/FactoryRuntimeLoader.php', + 'Twig\\RuntimeLoader\\RuntimeLoaderInterface' => __DIR__ . '/..' . '/twig/twig/src/RuntimeLoader/RuntimeLoaderInterface.php', + 'Twig\\Sandbox\\SecurityError' => __DIR__ . '/..' . '/twig/twig/src/Sandbox/SecurityError.php', + 'Twig\\Sandbox\\SecurityNotAllowedFilterError' => __DIR__ . '/..' . '/twig/twig/src/Sandbox/SecurityNotAllowedFilterError.php', + 'Twig\\Sandbox\\SecurityNotAllowedFunctionError' => __DIR__ . '/..' . '/twig/twig/src/Sandbox/SecurityNotAllowedFunctionError.php', + 'Twig\\Sandbox\\SecurityNotAllowedMethodError' => __DIR__ . '/..' . '/twig/twig/src/Sandbox/SecurityNotAllowedMethodError.php', + 'Twig\\Sandbox\\SecurityNotAllowedPropertyError' => __DIR__ . '/..' . '/twig/twig/src/Sandbox/SecurityNotAllowedPropertyError.php', + 'Twig\\Sandbox\\SecurityNotAllowedTagError' => __DIR__ . '/..' . '/twig/twig/src/Sandbox/SecurityNotAllowedTagError.php', + 'Twig\\Sandbox\\SecurityPolicy' => __DIR__ . '/..' . '/twig/twig/src/Sandbox/SecurityPolicy.php', + 'Twig\\Sandbox\\SecurityPolicyInterface' => __DIR__ . '/..' . '/twig/twig/src/Sandbox/SecurityPolicyInterface.php', + 'Twig\\Source' => __DIR__ . '/..' . '/twig/twig/src/Source.php', + 'Twig\\Template' => __DIR__ . '/..' . '/twig/twig/src/Template.php', + 'Twig\\TemplateWrapper' => __DIR__ . '/..' . '/twig/twig/src/TemplateWrapper.php', + 'Twig\\Test\\IntegrationTestCase' => __DIR__ . '/..' . '/twig/twig/src/Test/IntegrationTestCase.php', + 'Twig\\Test\\NodeTestCase' => __DIR__ . '/..' . '/twig/twig/src/Test/NodeTestCase.php', + 'Twig\\Token' => __DIR__ . '/..' . '/twig/twig/src/Token.php', + 'Twig\\TokenParser\\AbstractTokenParser' => __DIR__ . '/..' . '/twig/twig/src/TokenParser/AbstractTokenParser.php', + 'Twig\\TokenParser\\ApplyTokenParser' => __DIR__ . '/..' . '/twig/twig/src/TokenParser/ApplyTokenParser.php', + 'Twig\\TokenParser\\AutoEscapeTokenParser' => __DIR__ . '/..' . '/twig/twig/src/TokenParser/AutoEscapeTokenParser.php', + 'Twig\\TokenParser\\BlockTokenParser' => __DIR__ . '/..' . '/twig/twig/src/TokenParser/BlockTokenParser.php', + 'Twig\\TokenParser\\DeprecatedTokenParser' => __DIR__ . '/..' . '/twig/twig/src/TokenParser/DeprecatedTokenParser.php', + 'Twig\\TokenParser\\DoTokenParser' => __DIR__ . '/..' . '/twig/twig/src/TokenParser/DoTokenParser.php', + 'Twig\\TokenParser\\EmbedTokenParser' => __DIR__ . '/..' . '/twig/twig/src/TokenParser/EmbedTokenParser.php', + 'Twig\\TokenParser\\ExtendsTokenParser' => __DIR__ . '/..' . '/twig/twig/src/TokenParser/ExtendsTokenParser.php', + 'Twig\\TokenParser\\FilterTokenParser' => __DIR__ . '/..' . '/twig/twig/src/TokenParser/FilterTokenParser.php', + 'Twig\\TokenParser\\FlushTokenParser' => __DIR__ . '/..' . '/twig/twig/src/TokenParser/FlushTokenParser.php', + 'Twig\\TokenParser\\ForTokenParser' => __DIR__ . '/..' . '/twig/twig/src/TokenParser/ForTokenParser.php', + 'Twig\\TokenParser\\FromTokenParser' => __DIR__ . '/..' . '/twig/twig/src/TokenParser/FromTokenParser.php', + 'Twig\\TokenParser\\IfTokenParser' => __DIR__ . '/..' . '/twig/twig/src/TokenParser/IfTokenParser.php', + 'Twig\\TokenParser\\ImportTokenParser' => __DIR__ . '/..' . '/twig/twig/src/TokenParser/ImportTokenParser.php', + 'Twig\\TokenParser\\IncludeTokenParser' => __DIR__ . '/..' . '/twig/twig/src/TokenParser/IncludeTokenParser.php', + 'Twig\\TokenParser\\MacroTokenParser' => __DIR__ . '/..' . '/twig/twig/src/TokenParser/MacroTokenParser.php', + 'Twig\\TokenParser\\SandboxTokenParser' => __DIR__ . '/..' . '/twig/twig/src/TokenParser/SandboxTokenParser.php', + 'Twig\\TokenParser\\SetTokenParser' => __DIR__ . '/..' . '/twig/twig/src/TokenParser/SetTokenParser.php', + 'Twig\\TokenParser\\SpacelessTokenParser' => __DIR__ . '/..' . '/twig/twig/src/TokenParser/SpacelessTokenParser.php', + 'Twig\\TokenParser\\TokenParserInterface' => __DIR__ . '/..' . '/twig/twig/src/TokenParser/TokenParserInterface.php', + 'Twig\\TokenParser\\UseTokenParser' => __DIR__ . '/..' . '/twig/twig/src/TokenParser/UseTokenParser.php', + 'Twig\\TokenParser\\WithTokenParser' => __DIR__ . '/..' . '/twig/twig/src/TokenParser/WithTokenParser.php', + 'Twig\\TokenStream' => __DIR__ . '/..' . '/twig/twig/src/TokenStream.php', + 'Twig\\TwigFilter' => __DIR__ . '/..' . '/twig/twig/src/TwigFilter.php', + 'Twig\\TwigFunction' => __DIR__ . '/..' . '/twig/twig/src/TwigFunction.php', + 'Twig\\TwigTest' => __DIR__ . '/..' . '/twig/twig/src/TwigTest.php', + 'Twig\\Util\\DeprecationCollector' => __DIR__ . '/..' . '/twig/twig/src/Util/DeprecationCollector.php', + 'Twig\\Util\\TemplateDirIterator' => __DIR__ . '/..' . '/twig/twig/src/Util/TemplateDirIterator.php', + 'Twig_Autoloader' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Autoloader.php', + 'Twig_BaseNodeVisitor' => __DIR__ . '/..' . '/twig/twig/lib/Twig/BaseNodeVisitor.php', + 'Twig_CacheInterface' => __DIR__ . '/..' . '/twig/twig/lib/Twig/CacheInterface.php', + 'Twig_Cache_Filesystem' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Cache/Filesystem.php', + 'Twig_Cache_Null' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Cache/Null.php', + 'Twig_Compiler' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Compiler.php', + 'Twig_CompilerInterface' => __DIR__ . '/..' . '/twig/twig/lib/Twig/CompilerInterface.php', + 'Twig_ContainerRuntimeLoader' => __DIR__ . '/..' . '/twig/twig/lib/Twig/ContainerRuntimeLoader.php', + 'Twig_Environment' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Environment.php', + 'Twig_Error' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Error.php', + 'Twig_Error_Loader' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Error/Loader.php', + 'Twig_Error_Runtime' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Error/Runtime.php', + 'Twig_Error_Syntax' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Error/Syntax.php', + 'Twig_ExistsLoaderInterface' => __DIR__ . '/..' . '/twig/twig/lib/Twig/ExistsLoaderInterface.php', + 'Twig_ExpressionParser' => __DIR__ . '/..' . '/twig/twig/lib/Twig/ExpressionParser.php', + 'Twig_Extension' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Extension.php', + 'Twig_ExtensionInterface' => __DIR__ . '/..' . '/twig/twig/lib/Twig/ExtensionInterface.php', + 'Twig_Extension_Core' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Extension/Core.php', + 'Twig_Extension_Debug' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Extension/Debug.php', + 'Twig_Extension_Escaper' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Extension/Escaper.php', + 'Twig_Extension_GlobalsInterface' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Extension/GlobalsInterface.php', + 'Twig_Extension_InitRuntimeInterface' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Extension/InitRuntimeInterface.php', + 'Twig_Extension_Optimizer' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Extension/Optimizer.php', + 'Twig_Extension_Profiler' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Extension/Profiler.php', + 'Twig_Extension_Sandbox' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Extension/Sandbox.php', + 'Twig_Extension_Staging' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Extension/Staging.php', + 'Twig_Extension_StringLoader' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Extension/StringLoader.php', + 'Twig_FactoryRuntimeLoader' => __DIR__ . '/..' . '/twig/twig/lib/Twig/FactoryRuntimeLoader.php', + 'Twig_FileExtensionEscapingStrategy' => __DIR__ . '/..' . '/twig/twig/lib/Twig/FileExtensionEscapingStrategy.php', + 'Twig_Filter' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Filter.php', + 'Twig_FilterCallableInterface' => __DIR__ . '/..' . '/twig/twig/lib/Twig/FilterCallableInterface.php', + 'Twig_FilterInterface' => __DIR__ . '/..' . '/twig/twig/lib/Twig/FilterInterface.php', + 'Twig_Filter_Function' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Filter/Function.php', + 'Twig_Filter_Method' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Filter/Method.php', + 'Twig_Filter_Node' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Filter/Node.php', + 'Twig_Function' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Function.php', + 'Twig_FunctionCallableInterface' => __DIR__ . '/..' . '/twig/twig/lib/Twig/FunctionCallableInterface.php', + 'Twig_FunctionInterface' => __DIR__ . '/..' . '/twig/twig/lib/Twig/FunctionInterface.php', + 'Twig_Function_Function' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Function/Function.php', + 'Twig_Function_Method' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Function/Method.php', + 'Twig_Function_Node' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Function/Node.php', + 'Twig_Lexer' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Lexer.php', + 'Twig_LexerInterface' => __DIR__ . '/..' . '/twig/twig/lib/Twig/LexerInterface.php', + 'Twig_LoaderInterface' => __DIR__ . '/..' . '/twig/twig/lib/Twig/LoaderInterface.php', + 'Twig_Loader_Array' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Loader/Array.php', + 'Twig_Loader_Chain' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Loader/Chain.php', + 'Twig_Loader_Filesystem' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Loader/Filesystem.php', + 'Twig_Loader_String' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Loader/String.php', + 'Twig_Markup' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Markup.php', + 'Twig_Node' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node.php', + 'Twig_NodeCaptureInterface' => __DIR__ . '/..' . '/twig/twig/lib/Twig/NodeCaptureInterface.php', + 'Twig_NodeInterface' => __DIR__ . '/..' . '/twig/twig/lib/Twig/NodeInterface.php', + 'Twig_NodeOutputInterface' => __DIR__ . '/..' . '/twig/twig/lib/Twig/NodeOutputInterface.php', + 'Twig_NodeTraverser' => __DIR__ . '/..' . '/twig/twig/lib/Twig/NodeTraverser.php', + 'Twig_NodeVisitorInterface' => __DIR__ . '/..' . '/twig/twig/lib/Twig/NodeVisitorInterface.php', + 'Twig_NodeVisitor_Escaper' => __DIR__ . '/..' . '/twig/twig/lib/Twig/NodeVisitor/Escaper.php', + 'Twig_NodeVisitor_Optimizer' => __DIR__ . '/..' . '/twig/twig/lib/Twig/NodeVisitor/Optimizer.php', + 'Twig_NodeVisitor_SafeAnalysis' => __DIR__ . '/..' . '/twig/twig/lib/Twig/NodeVisitor/SafeAnalysis.php', + 'Twig_NodeVisitor_Sandbox' => __DIR__ . '/..' . '/twig/twig/lib/Twig/NodeVisitor/Sandbox.php', + 'Twig_Node_AutoEscape' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/AutoEscape.php', + 'Twig_Node_Block' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/Block.php', + 'Twig_Node_BlockReference' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/BlockReference.php', + 'Twig_Node_Body' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/Body.php', + 'Twig_Node_CheckSecurity' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/CheckSecurity.php', + 'Twig_Node_Deprecated' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/Deprecated.php', + 'Twig_Node_Do' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/Do.php', + 'Twig_Node_Embed' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/Embed.php', + 'Twig_Node_Expression' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/Expression.php', + 'Twig_Node_Expression_Array' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/Expression/Array.php', + 'Twig_Node_Expression_AssignName' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/Expression/AssignName.php', + 'Twig_Node_Expression_Binary' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/Expression/Binary.php', + 'Twig_Node_Expression_Binary_Add' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/Expression/Binary/Add.php', + 'Twig_Node_Expression_Binary_And' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/Expression/Binary/And.php', + 'Twig_Node_Expression_Binary_BitwiseAnd' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/Expression/Binary/BitwiseAnd.php', + 'Twig_Node_Expression_Binary_BitwiseOr' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/Expression/Binary/BitwiseOr.php', + 'Twig_Node_Expression_Binary_BitwiseXor' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/Expression/Binary/BitwiseXor.php', + 'Twig_Node_Expression_Binary_Concat' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/Expression/Binary/Concat.php', + 'Twig_Node_Expression_Binary_Div' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/Expression/Binary/Div.php', + 'Twig_Node_Expression_Binary_EndsWith' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/Expression/Binary/EndsWith.php', + 'Twig_Node_Expression_Binary_Equal' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/Expression/Binary/Equal.php', + 'Twig_Node_Expression_Binary_FloorDiv' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/Expression/Binary/FloorDiv.php', + 'Twig_Node_Expression_Binary_Greater' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/Expression/Binary/Greater.php', + 'Twig_Node_Expression_Binary_GreaterEqual' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/Expression/Binary/GreaterEqual.php', + 'Twig_Node_Expression_Binary_In' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/Expression/Binary/In.php', + 'Twig_Node_Expression_Binary_Less' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/Expression/Binary/Less.php', + 'Twig_Node_Expression_Binary_LessEqual' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/Expression/Binary/LessEqual.php', + 'Twig_Node_Expression_Binary_Matches' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/Expression/Binary/Matches.php', + 'Twig_Node_Expression_Binary_Mod' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/Expression/Binary/Mod.php', + 'Twig_Node_Expression_Binary_Mul' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/Expression/Binary/Mul.php', + 'Twig_Node_Expression_Binary_NotEqual' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/Expression/Binary/NotEqual.php', + 'Twig_Node_Expression_Binary_NotIn' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/Expression/Binary/NotIn.php', + 'Twig_Node_Expression_Binary_Or' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/Expression/Binary/Or.php', + 'Twig_Node_Expression_Binary_Power' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/Expression/Binary/Power.php', + 'Twig_Node_Expression_Binary_Range' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/Expression/Binary/Range.php', + 'Twig_Node_Expression_Binary_StartsWith' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/Expression/Binary/StartsWith.php', + 'Twig_Node_Expression_Binary_Sub' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/Expression/Binary/Sub.php', + 'Twig_Node_Expression_BlockReference' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/Expression/BlockReference.php', + 'Twig_Node_Expression_Call' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/Expression/Call.php', + 'Twig_Node_Expression_Conditional' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/Expression/Conditional.php', + 'Twig_Node_Expression_Constant' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/Expression/Constant.php', + 'Twig_Node_Expression_ExtensionReference' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/Expression/ExtensionReference.php', + 'Twig_Node_Expression_Filter' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/Expression/Filter.php', + 'Twig_Node_Expression_Filter_Default' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/Expression/Filter/Default.php', + 'Twig_Node_Expression_Function' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/Expression/Function.php', + 'Twig_Node_Expression_GetAttr' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/Expression/GetAttr.php', + 'Twig_Node_Expression_MethodCall' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/Expression/MethodCall.php', + 'Twig_Node_Expression_Name' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/Expression/Name.php', + 'Twig_Node_Expression_NullCoalesce' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/Expression/NullCoalesce.php', + 'Twig_Node_Expression_Parent' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/Expression/Parent.php', + 'Twig_Node_Expression_TempName' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/Expression/TempName.php', + 'Twig_Node_Expression_Test' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/Expression/Test.php', + 'Twig_Node_Expression_Test_Constant' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/Expression/Test/Constant.php', + 'Twig_Node_Expression_Test_Defined' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/Expression/Test/Defined.php', + 'Twig_Node_Expression_Test_Divisibleby' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/Expression/Test/Divisibleby.php', + 'Twig_Node_Expression_Test_Even' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/Expression/Test/Even.php', + 'Twig_Node_Expression_Test_Null' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/Expression/Test/Null.php', + 'Twig_Node_Expression_Test_Odd' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/Expression/Test/Odd.php', + 'Twig_Node_Expression_Test_Sameas' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/Expression/Test/Sameas.php', + 'Twig_Node_Expression_Unary' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/Expression/Unary.php', + 'Twig_Node_Expression_Unary_Neg' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/Expression/Unary/Neg.php', + 'Twig_Node_Expression_Unary_Not' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/Expression/Unary/Not.php', + 'Twig_Node_Expression_Unary_Pos' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/Expression/Unary/Pos.php', + 'Twig_Node_Flush' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/Flush.php', + 'Twig_Node_For' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/For.php', + 'Twig_Node_ForLoop' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/ForLoop.php', + 'Twig_Node_If' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/If.php', + 'Twig_Node_Import' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/Import.php', + 'Twig_Node_Include' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/Include.php', + 'Twig_Node_Macro' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/Macro.php', + 'Twig_Node_Module' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/Module.php', + 'Twig_Node_Print' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/Print.php', + 'Twig_Node_Sandbox' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/Sandbox.php', + 'Twig_Node_SandboxedPrint' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/SandboxedPrint.php', + 'Twig_Node_Set' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/Set.php', + 'Twig_Node_SetTemp' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/SetTemp.php', + 'Twig_Node_Spaceless' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/Spaceless.php', + 'Twig_Node_Text' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/Text.php', + 'Twig_Node_With' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Node/With.php', + 'Twig_Parser' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Parser.php', + 'Twig_ParserInterface' => __DIR__ . '/..' . '/twig/twig/lib/Twig/ParserInterface.php', + 'Twig_Profiler_Dumper_Base' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Profiler/Dumper/Base.php', + 'Twig_Profiler_Dumper_Blackfire' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Profiler/Dumper/Blackfire.php', + 'Twig_Profiler_Dumper_Html' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Profiler/Dumper/Html.php', + 'Twig_Profiler_Dumper_Text' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Profiler/Dumper/Text.php', + 'Twig_Profiler_NodeVisitor_Profiler' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Profiler/NodeVisitor/Profiler.php', + 'Twig_Profiler_Node_EnterProfile' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Profiler/Node/EnterProfile.php', + 'Twig_Profiler_Node_LeaveProfile' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Profiler/Node/LeaveProfile.php', + 'Twig_Profiler_Profile' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Profiler/Profile.php', + 'Twig_RuntimeLoaderInterface' => __DIR__ . '/..' . '/twig/twig/lib/Twig/RuntimeLoaderInterface.php', + 'Twig_Sandbox_SecurityError' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Sandbox/SecurityError.php', + 'Twig_Sandbox_SecurityNotAllowedFilterError' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Sandbox/SecurityNotAllowedFilterError.php', + 'Twig_Sandbox_SecurityNotAllowedFunctionError' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Sandbox/SecurityNotAllowedFunctionError.php', + 'Twig_Sandbox_SecurityNotAllowedMethodError' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Sandbox/SecurityNotAllowedMethodError.php', + 'Twig_Sandbox_SecurityNotAllowedPropertyError' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Sandbox/SecurityNotAllowedPropertyError.php', + 'Twig_Sandbox_SecurityNotAllowedTagError' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Sandbox/SecurityNotAllowedTagError.php', + 'Twig_Sandbox_SecurityPolicy' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Sandbox/SecurityPolicy.php', + 'Twig_Sandbox_SecurityPolicyInterface' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Sandbox/SecurityPolicyInterface.php', + 'Twig_SimpleFilter' => __DIR__ . '/..' . '/twig/twig/lib/Twig/SimpleFilter.php', + 'Twig_SimpleFunction' => __DIR__ . '/..' . '/twig/twig/lib/Twig/SimpleFunction.php', + 'Twig_SimpleTest' => __DIR__ . '/..' . '/twig/twig/lib/Twig/SimpleTest.php', + 'Twig_Source' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Source.php', + 'Twig_SourceContextLoaderInterface' => __DIR__ . '/..' . '/twig/twig/lib/Twig/SourceContextLoaderInterface.php', + 'Twig_Template' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Template.php', + 'Twig_TemplateInterface' => __DIR__ . '/..' . '/twig/twig/lib/Twig/TemplateInterface.php', + 'Twig_TemplateWrapper' => __DIR__ . '/..' . '/twig/twig/lib/Twig/TemplateWrapper.php', + 'Twig_Test' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Test.php', + 'Twig_TestCallableInterface' => __DIR__ . '/..' . '/twig/twig/lib/Twig/TestCallableInterface.php', + 'Twig_TestInterface' => __DIR__ . '/..' . '/twig/twig/lib/Twig/TestInterface.php', + 'Twig_Test_Function' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Test/Function.php', + 'Twig_Test_IntegrationTestCase' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Test/IntegrationTestCase.php', + 'Twig_Test_Method' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Test/Method.php', + 'Twig_Test_Node' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Test/Node.php', + 'Twig_Test_NodeTestCase' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Test/NodeTestCase.php', + 'Twig_Token' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Token.php', + 'Twig_TokenParser' => __DIR__ . '/..' . '/twig/twig/lib/Twig/TokenParser.php', + 'Twig_TokenParserBroker' => __DIR__ . '/..' . '/twig/twig/lib/Twig/TokenParserBroker.php', + 'Twig_TokenParserBrokerInterface' => __DIR__ . '/..' . '/twig/twig/lib/Twig/TokenParserBrokerInterface.php', + 'Twig_TokenParserInterface' => __DIR__ . '/..' . '/twig/twig/lib/Twig/TokenParserInterface.php', + 'Twig_TokenParser_AutoEscape' => __DIR__ . '/..' . '/twig/twig/lib/Twig/TokenParser/AutoEscape.php', + 'Twig_TokenParser_Block' => __DIR__ . '/..' . '/twig/twig/lib/Twig/TokenParser/Block.php', + 'Twig_TokenParser_Deprecated' => __DIR__ . '/..' . '/twig/twig/lib/Twig/TokenParser/Deprecated.php', + 'Twig_TokenParser_Do' => __DIR__ . '/..' . '/twig/twig/lib/Twig/TokenParser/Do.php', + 'Twig_TokenParser_Embed' => __DIR__ . '/..' . '/twig/twig/lib/Twig/TokenParser/Embed.php', + 'Twig_TokenParser_Extends' => __DIR__ . '/..' . '/twig/twig/lib/Twig/TokenParser/Extends.php', + 'Twig_TokenParser_Filter' => __DIR__ . '/..' . '/twig/twig/lib/Twig/TokenParser/Filter.php', + 'Twig_TokenParser_Flush' => __DIR__ . '/..' . '/twig/twig/lib/Twig/TokenParser/Flush.php', + 'Twig_TokenParser_For' => __DIR__ . '/..' . '/twig/twig/lib/Twig/TokenParser/For.php', + 'Twig_TokenParser_From' => __DIR__ . '/..' . '/twig/twig/lib/Twig/TokenParser/From.php', + 'Twig_TokenParser_If' => __DIR__ . '/..' . '/twig/twig/lib/Twig/TokenParser/If.php', + 'Twig_TokenParser_Import' => __DIR__ . '/..' . '/twig/twig/lib/Twig/TokenParser/Import.php', + 'Twig_TokenParser_Include' => __DIR__ . '/..' . '/twig/twig/lib/Twig/TokenParser/Include.php', + 'Twig_TokenParser_Macro' => __DIR__ . '/..' . '/twig/twig/lib/Twig/TokenParser/Macro.php', + 'Twig_TokenParser_Sandbox' => __DIR__ . '/..' . '/twig/twig/lib/Twig/TokenParser/Sandbox.php', + 'Twig_TokenParser_Set' => __DIR__ . '/..' . '/twig/twig/lib/Twig/TokenParser/Set.php', + 'Twig_TokenParser_Spaceless' => __DIR__ . '/..' . '/twig/twig/lib/Twig/TokenParser/Spaceless.php', + 'Twig_TokenParser_Use' => __DIR__ . '/..' . '/twig/twig/lib/Twig/TokenParser/Use.php', + 'Twig_TokenParser_With' => __DIR__ . '/..' . '/twig/twig/lib/Twig/TokenParser/With.php', + 'Twig_TokenStream' => __DIR__ . '/..' . '/twig/twig/lib/Twig/TokenStream.php', + 'Twig_Util_DeprecationCollector' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Util/DeprecationCollector.php', + 'Twig_Util_TemplateDirIterator' => __DIR__ . '/..' . '/twig/twig/lib/Twig/Util/TemplateDirIterator.php', + 'TypeError' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/TypeError.php', + 'UIExtKeyWidget' => __DIR__ . '/../..' . '/application/ui.extkeywidget.class.inc.php', + 'UIHTMLEditorWidget' => __DIR__ . '/../..' . '/application/ui.htmleditorwidget.class.inc.php', + 'UILinksWidget' => __DIR__ . '/../..' . '/application/ui.linkswidget.class.inc.php', + 'UILinksWidgetDirect' => __DIR__ . '/../..' . '/application/ui.linksdirectwidget.class.inc.php', + 'UIPasswordWidget' => __DIR__ . '/../..' . '/application/ui.passwordwidget.class.inc.php', + 'UISearchFormForeignKeys' => __DIR__ . '/../..' . '/application/ui.searchformforeignkeys.class.inc.php', + 'UIWizard' => __DIR__ . '/../..' . '/application/uiwizard.class.inc.php', + 'URLButtonItem' => __DIR__ . '/../..' . '/application/applicationextension.inc.php', + 'URLPopupMenuItem' => __DIR__ . '/../..' . '/application/applicationextension.inc.php', + 'UnaryExpression' => __DIR__ . '/../..' . '/core/oql/expression.class.inc.php', + 'UnknownClassOqlException' => __DIR__ . '/../..' . '/core/oql/oqlinterpreter.class.inc.php', + 'User' => __DIR__ . '/../..' . '/core/userrights.class.inc.php', + 'UserDashboard' => __DIR__ . '/../..' . '/application/user.dashboard.class.inc.php', + 'UserInternal' => __DIR__ . '/../..' . '/core/userrights.class.inc.php', + 'UserRightException' => __DIR__ . '/../..' . '/core/userrights.class.inc.php', + 'UserRights' => __DIR__ . '/../..' . '/core/userrights.class.inc.php', + 'UserRightsAddOnAPI' => __DIR__ . '/../..' . '/core/userrights.class.inc.php', + 'ValueSetDefinition' => __DIR__ . '/../..' . '/core/valuesetdef.class.inc.php', + 'ValueSetEnum' => __DIR__ . '/../..' . '/core/valuesetdef.class.inc.php', + 'ValueSetEnumClasses' => __DIR__ . '/../..' . '/core/valuesetdef.class.inc.php', + 'ValueSetObjects' => __DIR__ . '/../..' . '/core/valuesetdef.class.inc.php', + 'ValueSetRange' => __DIR__ . '/../..' . '/core/valuesetdef.class.inc.php', + 'VariableExpression' => __DIR__ . '/../..' . '/core/oql/expression.class.inc.php', + 'VariableOqlExpression' => __DIR__ . '/../..' . '/core/oql/oqlquery.class.inc.php', + 'WebPage' => __DIR__ . '/../..' . '/application/webpage.class.inc.php', + 'WebPageMenuNode' => __DIR__ . '/../..' . '/application/menunode.class.inc.php', + 'WizardHelper' => __DIR__ . '/../..' . '/application/wizardhelper.class.inc.php', + 'XLSXWriter' => __DIR__ . '/../..' . '/application/xlsxwriter.class.php', + 'XMLBulkExport' => __DIR__ . '/../..' . '/core/xmlbulkexport.class.inc.php', + 'XMLPage' => __DIR__ . '/../..' . '/application/xmlpage.class.inc.php', + 'ajax_page' => __DIR__ . '/../..' . '/application/ajaxwebpage.class.inc.php', + 'apcFile' => __DIR__ . '/../..' . '/core/apc-emulation.php', + 'appUserPreferences' => __DIR__ . '/../..' . '/application/user.preferences.class.inc.php', + 'cmdbAbstractObject' => __DIR__ . '/../..' . '/application/cmdbabstract.class.inc.php', + 'cmdbDataGenerator' => __DIR__ . '/../..' . '/core/data.generator.class.inc.php', + 'iApplicationObjectExtension' => __DIR__ . '/../..' . '/application/applicationextension.inc.php', + 'iApplicationUIExtension' => __DIR__ . '/../..' . '/application/applicationextension.inc.php', + 'iBackgroundProcess' => __DIR__ . '/../..' . '/core/backgroundprocess.inc.php', + 'iDBObjectSetIterator' => __DIR__ . '/../..' . '/core/dbobjectiterator.php', + 'iDBObjectURLMaker' => __DIR__ . '/../..' . '/application/applicationcontext.class.inc.php', + 'iDisplay' => __DIR__ . '/../..' . '/core/dbobject.class.php', + 'iMetricComputer' => __DIR__ . '/../..' . '/core/computing.inc.php', + 'iNewsroomProvider' => __DIR__ . '/../..' . '/application/newsroomprovider.class.inc.php', + 'iOnClassInitialization' => __DIR__ . '/../..' . '/core/metamodelmodifier.inc.php', + 'iPageUIExtension' => __DIR__ . '/../..' . '/application/applicationextension.inc.php', + 'iPopupMenuExtension' => __DIR__ . '/../..' . '/application/applicationextension.inc.php', + 'iPortalUIExtension' => __DIR__ . '/../..' . '/application/applicationextension.inc.php', + 'iProcess' => __DIR__ . '/../..' . '/core/backgroundprocess.inc.php', + 'iQueryModifier' => __DIR__ . '/../..' . '/core/querymodifier.class.inc.php', + 'iRestServiceProvider' => __DIR__ . '/../..' . '/application/applicationextension.inc.php', + 'iScheduledProcess' => __DIR__ . '/../..' . '/core/backgroundprocess.inc.php', + 'iSelfRegister' => __DIR__ . '/../..' . '/core/userrights.class.inc.php', + 'iTabbedPage' => __DIR__ . '/../..' . '/application/webpage.class.inc.php', + 'iTopMutex' => __DIR__ . '/../..' . '/core/mutex.class.inc.php', + 'iTopOwnershipLock' => __DIR__ . '/../..' . '/core/ownershiplock.class.inc.php', + 'iTopOwnershipToken' => __DIR__ . '/../..' . '/core/ownershiplock.class.inc.php', + 'iTopPDF' => __DIR__ . '/../..' . '/application/pdfpage.class.inc.php', + 'iTopStandardURLMaker' => __DIR__ . '/../..' . '/application/applicationcontext.class.inc.php', + 'iTopWebPage' => __DIR__ . '/../..' . '/application/itopwebpage.class.inc.php', + 'iTopWizardWebPage' => __DIR__ . '/../..' . '/application/itopwizardwebpage.class.inc.php', + 'iWorkingTimeComputer' => __DIR__ . '/../..' . '/core/computing.inc.php', + 'lnkTriggerAction' => __DIR__ . '/../..' . '/core/trigger.class.inc.php', + 'ormCaseLog' => __DIR__ . '/../..' . '/core/ormcaselog.class.inc.php', + 'ormCustomFieldsValue' => __DIR__ . '/../..' . '/core/ormcustomfieldsvalue.class.inc.php', + 'ormDocument' => __DIR__ . '/../..' . '/core/ormdocument.class.inc.php', + 'ormLinkSet' => __DIR__ . '/../..' . '/core/ormlinkset.class.inc.php', + 'ormPassword' => __DIR__ . '/../..' . '/core/ormpassword.class.inc.php', + 'ormSet' => __DIR__ . '/../..' . '/core/ormset.class.inc.php', + 'ormStopWatch' => __DIR__ . '/../..' . '/core/ormstopwatch.class.inc.php', + 'ormTagSet' => __DIR__ . '/../..' . '/core/ormtagset.class.inc.php', + 'privUITransaction' => __DIR__ . '/../..' . '/application/transaction.class.inc.php', + 'privUITransactionFile' => __DIR__ . '/../..' . '/application/transaction.class.inc.php', + 'privUITransactionSession' => __DIR__ . '/../..' . '/application/transaction.class.inc.php', + 'utils' => __DIR__ . '/../..' . '/application/utils.inc.php', + ); + + public static function getInitializer(ClassLoader $loader) + { + return \Closure::bind(function () use ($loader) { + $loader->prefixLengthsPsr4 = ComposerStaticInit0018331147de7601e7552f7da8e3bb8b::$prefixLengthsPsr4; + $loader->prefixDirsPsr4 = ComposerStaticInit0018331147de7601e7552f7da8e3bb8b::$prefixDirsPsr4; + $loader->prefixesPsr0 = ComposerStaticInit0018331147de7601e7552f7da8e3bb8b::$prefixesPsr0; + $loader->classMap = ComposerStaticInit0018331147de7601e7552f7da8e3bb8b::$classMap; + + }, null, ClassLoader::class); + } +} diff --git a/lib/composer/installed.json b/lib/composer/installed.json new file mode 100644 index 000000000..bd3979547 --- /dev/null +++ b/lib/composer/installed.json @@ -0,0 +1,1950 @@ +[ + { + "name": "paragonie/random_compat", + "version": "v2.0.18", + "version_normalized": "2.0.18.0", + "source": { + "type": "git", + "url": "https://github.com/paragonie/random_compat.git", + "reference": "0a58ef6e3146256cc3dc7cc393927bcc7d1b72db" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/0a58ef6e3146256cc3dc7cc393927bcc7d1b72db", + "reference": "0a58ef6e3146256cc3dc7cc393927bcc7d1b72db", + "shasum": "" + }, + "require": { + "php": ">=5.2.0" + }, + "require-dev": { + "phpunit/phpunit": "4.*|5.*" + }, + "suggest": { + "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." + }, + "time": "2019-01-03T20:59:08+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "files": [ + "lib/random.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com" + } + ], + "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", + "keywords": [ + "csprng", + "polyfill", + "pseudorandom", + "random" + ] + }, + { + "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/log", + "version": "1.1.0", + "version_normalized": "1.1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd", + "reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "time": "2018-11-20T15:27:04+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": "symfony/cache", + "version": "v3.4.30", + "version_normalized": "3.4.30.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/cache.git", + "reference": "8c6e162f0f7626771edbfa0a0e45b46623bbae1c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/cache/zipball/8c6e162f0f7626771edbfa0a0e45b46623bbae1c", + "reference": "8c6e162f0f7626771edbfa0a0e45b46623bbae1c", + "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": "2019-06-17T17:26:15+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/class-loader", + "version": "v3.4.30", + "version_normalized": "3.4.30.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/class-loader.git", + "reference": "4459eef5298dedfb69f771186a580062b8516497" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/class-loader/zipball/4459eef5298dedfb69f771186a580062b8516497", + "reference": "4459eef5298dedfb69f771186a580062b8516497", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8" + }, + "require-dev": { + "symfony/finder": "~2.8|~3.0|~4.0", + "symfony/polyfill-apcu": "~1.1" + }, + "suggest": { + "symfony/polyfill-apcu": "For using ApcClassLoader on HHVM" + }, + "time": "2019-01-16T09:39:14+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Component\\ClassLoader\\": "" + }, + "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 ClassLoader Component", + "homepage": "https://symfony.com" + }, + { + "name": "symfony/config", + "version": "v3.4.30", + "version_normalized": "3.4.30.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/config.git", + "reference": "623fd6be3e5d4112d667003488c8c3ec12b66f62" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/config/zipball/623fd6be3e5d4112d667003488c8c3ec12b66f62", + "reference": "623fd6be3e5d4112d667003488c8c3ec12b66f62", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8", + "symfony/filesystem": "~2.8|~3.0|~4.0", + "symfony/polyfill-ctype": "~1.8" + }, + "conflict": { + "symfony/dependency-injection": "<3.3", + "symfony/finder": "<3.3" + }, + "require-dev": { + "symfony/dependency-injection": "~3.3|~4.0", + "symfony/event-dispatcher": "~3.3|~4.0", + "symfony/finder": "~3.3|~4.0", + "symfony/yaml": "~3.0|~4.0" + }, + "suggest": { + "symfony/yaml": "To use the yaml reference dumper" + }, + "time": "2019-07-17T15:23:18+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Component\\Config\\": "" + }, + "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 Config Component", + "homepage": "https://symfony.com" + }, + { + "name": "symfony/console", + "version": "v3.4.30", + "version_normalized": "3.4.30.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "12940f20a816c978860fa4925b3f1bbb27e9ac46" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/12940f20a816c978860fa4925b3f1bbb27e9ac46", + "reference": "12940f20a816c978860fa4925b3f1bbb27e9ac46", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8", + "symfony/debug": "~2.8|~3.0|~4.0", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/dependency-injection": "<3.4", + "symfony/process": "<3.3" + }, + "provide": { + "psr/log-implementation": "1.0" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/config": "~3.3|~4.0", + "symfony/dependency-injection": "~3.4|~4.0", + "symfony/event-dispatcher": "~2.8|~3.0|~4.0", + "symfony/lock": "~3.4|~4.0", + "symfony/process": "~3.3|~4.0" + }, + "suggest": { + "psr/log": "For using the console logger", + "symfony/event-dispatcher": "", + "symfony/lock": "", + "symfony/process": "" + }, + "time": "2019-07-24T14:46:41+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "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 Console Component", + "homepage": "https://symfony.com" + }, + { + "name": "symfony/debug", + "version": "v3.4.30", + "version_normalized": "3.4.30.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/debug.git", + "reference": "bc977cb2681d75988ab2d53d14c4245c6c04f82f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/debug/zipball/bc977cb2681d75988ab2d53d14c4245c6c04f82f", + "reference": "bc977cb2681d75988ab2d53d14c4245c6c04f82f", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8", + "psr/log": "~1.0" + }, + "conflict": { + "symfony/http-kernel": ">=2.3,<2.3.24|~2.4.0|>=2.5,<2.5.9|>=2.6,<2.6.2" + }, + "require-dev": { + "symfony/http-kernel": "~2.8|~3.0|~4.0" + }, + "time": "2019-07-23T08:39:19+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Component\\Debug\\": "" + }, + "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 Debug Component", + "homepage": "https://symfony.com" + }, + { + "name": "symfony/dependency-injection", + "version": "v3.4.30", + "version_normalized": "3.4.30.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/dependency-injection.git", + "reference": "ade939fe83d5ec5fcaa98628dc42d83232c8eb41" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/ade939fe83d5ec5fcaa98628dc42d83232c8eb41", + "reference": "ade939fe83d5ec5fcaa98628dc42d83232c8eb41", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8", + "psr/container": "^1.0" + }, + "conflict": { + "symfony/config": "<3.3.7", + "symfony/finder": "<3.3", + "symfony/proxy-manager-bridge": "<3.4", + "symfony/yaml": "<3.4" + }, + "provide": { + "psr/container-implementation": "1.0" + }, + "require-dev": { + "symfony/config": "~3.3|~4.0", + "symfony/expression-language": "~2.8|~3.0|~4.0", + "symfony/yaml": "~3.4|~4.0" + }, + "suggest": { + "symfony/config": "", + "symfony/expression-language": "For using expressions in service container configuration", + "symfony/finder": "For using double-star glob patterns or when GLOB_BRACE portability is required", + "symfony/proxy-manager-bridge": "Generate service proxies to lazy load them", + "symfony/yaml": "" + }, + "time": "2019-07-19T11:52:08+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Component\\DependencyInjection\\": "" + }, + "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 DependencyInjection Component", + "homepage": "https://symfony.com" + }, + { + "name": "symfony/dotenv", + "version": "v3.4.30", + "version_normalized": "3.4.30.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/dotenv.git", + "reference": "3f4fdfb551bf36f2017d75cd2e6490fbe67f9d2d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/dotenv/zipball/3f4fdfb551bf36f2017d75cd2e6490fbe67f9d2d", + "reference": "3f4fdfb551bf36f2017d75cd2e6490fbe67f9d2d", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8" + }, + "require-dev": { + "symfony/process": "~3.2|~4.0" + }, + "time": "2019-06-23T08:10:04+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Component\\Dotenv\\": "" + }, + "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": "Registers environment variables from a .env file", + "homepage": "https://symfony.com", + "keywords": [ + "dotenv", + "env", + "environment" + ] + }, + { + "name": "symfony/event-dispatcher", + "version": "v3.4.30", + "version_normalized": "3.4.30.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "f18fdd6cc7006441865e698420cee26bac94741f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/f18fdd6cc7006441865e698420cee26bac94741f", + "reference": "f18fdd6cc7006441865e698420cee26bac94741f", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8" + }, + "conflict": { + "symfony/dependency-injection": "<3.3" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/config": "~2.8|~3.0|~4.0", + "symfony/dependency-injection": "~3.3|~4.0", + "symfony/expression-language": "~2.8|~3.0|~4.0", + "symfony/stopwatch": "~2.8|~3.0|~4.0" + }, + "suggest": { + "symfony/dependency-injection": "", + "symfony/http-kernel": "" + }, + "time": "2019-06-25T07:45:31+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Component\\EventDispatcher\\": "" + }, + "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 EventDispatcher Component", + "homepage": "https://symfony.com" + }, + { + "name": "symfony/filesystem", + "version": "v3.4.30", + "version_normalized": "3.4.30.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "70adda061ef83bb7def63a17953dc41f203308a7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/70adda061ef83bb7def63a17953dc41f203308a7", + "reference": "70adda061ef83bb7def63a17953dc41f203308a7", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8", + "symfony/polyfill-ctype": "~1.8" + }, + "time": "2019-06-23T09:29:17+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "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 Filesystem Component", + "homepage": "https://symfony.com" + }, + { + "name": "symfony/finder", + "version": "v3.4.30", + "version_normalized": "3.4.30.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "1e762fdf73ace6ceb42ba5a6ca280be86082364a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/1e762fdf73ace6ceb42ba5a6ca280be86082364a", + "reference": "1e762fdf73ace6ceb42ba5a6ca280be86082364a", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8" + }, + "time": "2019-06-28T08:02:59+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "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 Finder Component", + "homepage": "https://symfony.com" + }, + { + "name": "symfony/framework-bundle", + "version": "v3.4.30", + "version_normalized": "3.4.30.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/framework-bundle.git", + "reference": "0bb32eafe4adeb39faff1902dd9ec54e0ed2a265" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/0bb32eafe4adeb39faff1902dd9ec54e0ed2a265", + "reference": "0bb32eafe4adeb39faff1902dd9ec54e0ed2a265", + "shasum": "" + }, + "require": { + "ext-xml": "*", + "php": "^5.5.9|>=7.0.8", + "symfony/cache": "~3.4|~4.0", + "symfony/class-loader": "~3.2", + "symfony/config": "~3.4|~4.0", + "symfony/debug": "~2.8|~3.0|~4.0", + "symfony/dependency-injection": "^3.4.24|^4.2.5", + "symfony/event-dispatcher": "~3.4|~4.0", + "symfony/filesystem": "~2.8|~3.0|~4.0", + "symfony/finder": "~2.8|~3.0|~4.0", + "symfony/http-foundation": "^3.3.11|~4.0", + "symfony/http-kernel": "~3.4|~4.0", + "symfony/polyfill-mbstring": "~1.0", + "symfony/routing": "^3.4.5|^4.0.5" + }, + "conflict": { + "phpdocumentor/reflection-docblock": "<3.0", + "phpdocumentor/type-resolver": "<0.2.1", + "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0", + "symfony/asset": "<3.3", + "symfony/console": "<3.4", + "symfony/form": "<3.4", + "symfony/property-info": "<3.3", + "symfony/serializer": "<3.3", + "symfony/stopwatch": "<3.4", + "symfony/translation": "<3.4", + "symfony/validator": "<3.4", + "symfony/workflow": "<3.3" + }, + "require-dev": { + "doctrine/annotations": "~1.0", + "doctrine/cache": "~1.0", + "fig/link-util": "^1.0", + "phpdocumentor/reflection-docblock": "^3.0|^4.0", + "symfony/asset": "~3.3|~4.0", + "symfony/browser-kit": "~2.8|~3.0|~4.0", + "symfony/console": "~3.4|~4.0", + "symfony/css-selector": "~2.8|~3.0|~4.0", + "symfony/dom-crawler": "~2.8|~3.0|~4.0", + "symfony/expression-language": "~2.8|~3.0|~4.0", + "symfony/form": "^3.4.22|~4.1.11|^4.2.3", + "symfony/lock": "~3.4|~4.0", + "symfony/polyfill-intl-icu": "~1.0", + "symfony/process": "~2.8|~3.0|~4.0", + "symfony/property-info": "~3.3|~4.0", + "symfony/security-core": "~3.2|~4.0", + "symfony/security-csrf": "^2.8.31|^3.3.13|~4.0", + "symfony/serializer": "~3.3|~4.0", + "symfony/stopwatch": "~3.4|~4.0", + "symfony/templating": "~2.8|~3.0|~4.0", + "symfony/translation": "~3.4|~4.0", + "symfony/validator": "~3.4|~4.0", + "symfony/var-dumper": "~3.3|~4.0", + "symfony/web-link": "~3.3|~4.0", + "symfony/workflow": "~3.3|~4.0", + "symfony/yaml": "~3.2|~4.0", + "twig/twig": "~1.34|~2.4" + }, + "suggest": { + "ext-apcu": "For best performance of the system caches", + "symfony/console": "For using the console commands", + "symfony/form": "For using forms", + "symfony/property-info": "For using the property_info service", + "symfony/serializer": "For using the serializer service", + "symfony/validator": "For using validation", + "symfony/web-link": "For using web links, features such as preloading, prefetching or prerendering", + "symfony/yaml": "For using the debug:config and lint:yaml commands" + }, + "time": "2019-07-24T16:59:53+00:00", + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Bundle\\FrameworkBundle\\": "" + }, + "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 FrameworkBundle", + "homepage": "https://symfony.com" + }, + { + "name": "symfony/http-foundation", + "version": "v3.4.30", + "version_normalized": "3.4.30.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-foundation.git", + "reference": "c450706851050ade2e1f30d012d50bb9173f7f3d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/c450706851050ade2e1f30d012d50bb9173f7f3d", + "reference": "c450706851050ade2e1f30d012d50bb9173f7f3d", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8", + "symfony/polyfill-mbstring": "~1.1", + "symfony/polyfill-php70": "~1.6" + }, + "require-dev": { + "symfony/expression-language": "~2.8|~3.0|~4.0" + }, + "time": "2019-07-23T06:27:47+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpFoundation\\": "" + }, + "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 HttpFoundation Component", + "homepage": "https://symfony.com" + }, + { + "name": "symfony/http-kernel", + "version": "v3.4.30", + "version_normalized": "3.4.30.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-kernel.git", + "reference": "83a1b30c5dd02f5c3cd708a432071d0c99474eb3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/83a1b30c5dd02f5c3cd708a432071d0c99474eb3", + "reference": "83a1b30c5dd02f5c3cd708a432071d0c99474eb3", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8", + "psr/log": "~1.0", + "symfony/debug": "^3.3.3|~4.0", + "symfony/event-dispatcher": "~2.8|~3.0|~4.0", + "symfony/http-foundation": "~3.4.12|~4.0.12|^4.1.1", + "symfony/polyfill-ctype": "~1.8" + }, + "conflict": { + "symfony/config": "<2.8", + "symfony/dependency-injection": "<3.4.10|<4.0.10,>=4", + "symfony/var-dumper": "<3.3", + "twig/twig": "<1.34|<2.4,>=2" + }, + "provide": { + "psr/log-implementation": "1.0" + }, + "require-dev": { + "psr/cache": "~1.0", + "symfony/browser-kit": "~2.8|~3.0|~4.0", + "symfony/class-loader": "~2.8|~3.0", + "symfony/config": "~2.8|~3.0|~4.0", + "symfony/console": "~2.8|~3.0|~4.0", + "symfony/css-selector": "~2.8|~3.0|~4.0", + "symfony/dependency-injection": "^3.4.10|^4.0.10", + "symfony/dom-crawler": "~2.8|~3.0|~4.0", + "symfony/expression-language": "~2.8|~3.0|~4.0", + "symfony/finder": "~2.8|~3.0|~4.0", + "symfony/process": "~2.8|~3.0|~4.0", + "symfony/routing": "~3.4|~4.0", + "symfony/stopwatch": "~2.8|~3.0|~4.0", + "symfony/templating": "~2.8|~3.0|~4.0", + "symfony/translation": "~2.8|~3.0|~4.0", + "symfony/var-dumper": "~3.3|~4.0" + }, + "suggest": { + "symfony/browser-kit": "", + "symfony/config": "", + "symfony/console": "", + "symfony/dependency-injection": "", + "symfony/finder": "", + "symfony/var-dumper": "" + }, + "time": "2019-07-27T17:14:06+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpKernel\\": "" + }, + "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 HttpKernel Component", + "homepage": "https://symfony.com" + }, + { + "name": "symfony/polyfill-apcu", + "version": "v1.12.0", + "version_normalized": "1.12.0.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-apcu.git", + "reference": "71ce80635d5dcd67772b4dda00b86068595f64d5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-apcu/zipball/71ce80635d5dcd67772b4dda00b86068595f64d5", + "reference": "71ce80635d5dcd67772b4dda00b86068595f64d5", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "time": "2019-08-06T08:03:45+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.12-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" + ] + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.12.0", + "version_normalized": "1.12.0.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "550ebaac289296ce228a706d0867afc34687e3f4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/550ebaac289296ce228a706d0867afc34687e3f4", + "reference": "550ebaac289296ce228a706d0867afc34687e3f4", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "time": "2019-08-06T08:03:45+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.12-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ] + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.12.0", + "version_normalized": "1.12.0.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "b42a2f66e8f1b15ccf25652c3424265923eb4f17" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/b42a2f66e8f1b15ccf25652c3424265923eb4f17", + "reference": "b42a2f66e8f1b15ccf25652c3424265923eb4f17", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "time": "2019-08-06T08:03:45+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.12-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + }, + "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 for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ] + }, + { + "name": "symfony/polyfill-php70", + "version": "v1.12.0", + "version_normalized": "1.12.0.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php70.git", + "reference": "54b4c428a0054e254223797d2713c31e08610831" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/54b4c428a0054e254223797d2713c31e08610831", + "reference": "54b4c428a0054e254223797d2713c31e08610831", + "shasum": "" + }, + "require": { + "paragonie/random_compat": "~1.0|~2.0|~9.99", + "php": ">=5.3.3" + }, + "time": "2019-08-06T08:03:45+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.12-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php70\\": "" + }, + "files": [ + "bootstrap.php" + ], + "classmap": [ + "Resources/stubs" + ] + }, + "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 some PHP 7.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ] + }, + { + "name": "symfony/routing", + "version": "v3.4.30", + "version_normalized": "3.4.30.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/routing.git", + "reference": "8d804d8a65a26dc9de1aaf2ff3a421e581d050e6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/routing/zipball/8d804d8a65a26dc9de1aaf2ff3a421e581d050e6", + "reference": "8d804d8a65a26dc9de1aaf2ff3a421e581d050e6", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8" + }, + "conflict": { + "symfony/config": "<3.3.1", + "symfony/dependency-injection": "<3.3", + "symfony/yaml": "<3.4" + }, + "require-dev": { + "doctrine/annotations": "~1.0", + "psr/log": "~1.0", + "symfony/config": "^3.3.1|~4.0", + "symfony/dependency-injection": "~3.3|~4.0", + "symfony/expression-language": "~2.8|~3.0|~4.0", + "symfony/http-foundation": "~2.8|~3.0|~4.0", + "symfony/yaml": "~3.4|~4.0" + }, + "suggest": { + "doctrine/annotations": "For using the annotation loader", + "symfony/config": "For using the all-in-one router or any loader", + "symfony/expression-language": "For using expression matching", + "symfony/http-foundation": "For using a Symfony Request object", + "symfony/yaml": "For using the YAML loader" + }, + "time": "2019-06-26T11:14:13+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Component\\Routing\\": "" + }, + "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 Routing Component", + "homepage": "https://symfony.com", + "keywords": [ + "router", + "routing", + "uri", + "url" + ] + }, + { + "name": "symfony/stopwatch", + "version": "v3.4.30", + "version_normalized": "3.4.30.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/stopwatch.git", + "reference": "2a651c2645c10bbedd21170771f122d935e0dd58" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/2a651c2645c10bbedd21170771f122d935e0dd58", + "reference": "2a651c2645c10bbedd21170771f122d935e0dd58", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8" + }, + "time": "2019-01-16T09:39:14+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Component\\Stopwatch\\": "" + }, + "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 Stopwatch Component", + "homepage": "https://symfony.com" + }, + { + "name": "symfony/twig-bridge", + "version": "v3.4.30", + "version_normalized": "3.4.30.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/twig-bridge.git", + "reference": "bf136ba1fbd18126ab65a930ea0daebb0b0fd159" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/twig-bridge/zipball/bf136ba1fbd18126ab65a930ea0daebb0b0fd159", + "reference": "bf136ba1fbd18126ab65a930ea0daebb0b0fd159", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8", + "twig/twig": "^1.40|^2.9" + }, + "conflict": { + "symfony/console": "<3.4", + "symfony/form": "<3.4.13|>=4.0,<4.0.13|>=4.1,<4.1.2" + }, + "require-dev": { + "symfony/asset": "~2.8|~3.0|~4.0", + "symfony/console": "~3.4|~4.0", + "symfony/dependency-injection": "~2.8|~3.0|~4.0", + "symfony/expression-language": "~2.8|~3.0|~4.0", + "symfony/finder": "~2.8|~3.0|~4.0", + "symfony/form": "^3.4.23|^4.2.4", + "symfony/http-foundation": "^3.3.11|~4.0", + "symfony/http-kernel": "~3.2|~4.0", + "symfony/polyfill-intl-icu": "~1.0", + "symfony/routing": "~2.8|~3.0|~4.0", + "symfony/security": "^2.8.31|^3.3.13|~4.0", + "symfony/security-acl": "~2.8|~3.0", + "symfony/stopwatch": "~2.8|~3.0|~4.0", + "symfony/templating": "~2.8|~3.0|~4.0", + "symfony/translation": "~2.8|~3.0|~4.0", + "symfony/var-dumper": "~2.8.10|~3.1.4|~3.2|~4.0", + "symfony/web-link": "~3.3|~4.0", + "symfony/workflow": "~3.3|~4.0", + "symfony/yaml": "~2.8|~3.0|~4.0" + }, + "suggest": { + "symfony/asset": "For using the AssetExtension", + "symfony/expression-language": "For using the ExpressionExtension", + "symfony/finder": "", + "symfony/form": "For using the FormExtension", + "symfony/http-kernel": "For using the HttpKernelExtension", + "symfony/routing": "For using the RoutingExtension", + "symfony/security": "For using the SecurityExtension", + "symfony/stopwatch": "For using the StopwatchExtension", + "symfony/templating": "For using the TwigEngine", + "symfony/translation": "For using the TranslationExtension", + "symfony/var-dumper": "For using the DumpExtension", + "symfony/web-link": "For using the WebLinkExtension", + "symfony/yaml": "For using the YamlExtension" + }, + "time": "2019-07-24T19:39:07+00:00", + "type": "symfony-bridge", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Bridge\\Twig\\": "" + }, + "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 Twig Bridge", + "homepage": "https://symfony.com" + }, + { + "name": "symfony/twig-bundle", + "version": "v3.4.30", + "version_normalized": "3.4.30.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/twig-bundle.git", + "reference": "fae76fd8d8cff50295282d752e9842324bd277e4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/twig-bundle/zipball/fae76fd8d8cff50295282d752e9842324bd277e4", + "reference": "fae76fd8d8cff50295282d752e9842324bd277e4", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8", + "symfony/config": "~3.2|~4.0", + "symfony/debug": "~2.8|~3.0|~4.0", + "symfony/http-foundation": "~2.8|~3.0|~4.0", + "symfony/http-kernel": "^3.3|~4.0", + "symfony/polyfill-ctype": "~1.8", + "symfony/twig-bridge": "^3.4.3|^4.0.3", + "twig/twig": "~1.40|~2.9" + }, + "conflict": { + "symfony/dependency-injection": "<3.4", + "symfony/event-dispatcher": "<3.3.1" + }, + "require-dev": { + "doctrine/annotations": "~1.0", + "doctrine/cache": "~1.0", + "symfony/asset": "~2.8|~3.0|~4.0", + "symfony/dependency-injection": "~3.4.24|^4.2.5", + "symfony/expression-language": "~2.8|~3.0|~4.0", + "symfony/finder": "~2.8|~3.0|~4.0", + "symfony/form": "~2.8|~3.0|~4.0", + "symfony/framework-bundle": "^3.3.11|~4.0", + "symfony/routing": "~2.8|~3.0|~4.0", + "symfony/stopwatch": "~2.8|~3.0|~4.0", + "symfony/templating": "~2.8|~3.0|~4.0", + "symfony/web-link": "~3.3|~4.0", + "symfony/yaml": "~2.8|~3.0|~4.0" + }, + "time": "2019-07-19T08:16:37+00:00", + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Bundle\\TwigBundle\\": "" + }, + "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 TwigBundle", + "homepage": "https://symfony.com" + }, + { + "name": "symfony/var-dumper", + "version": "v3.4.30", + "version_normalized": "3.4.30.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/var-dumper.git", + "reference": "b6a45abfe961183a4c26fad98a6112c487e983bf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/b6a45abfe961183a4c26fad98a6112c487e983bf", + "reference": "b6a45abfe961183a4c26fad98a6112c487e983bf", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0" + }, + "require-dev": { + "ext-iconv": "*", + "twig/twig": "~1.34|~2.4" + }, + "suggest": { + "ext-iconv": "To convert non-UTF-8 strings to UTF-8 (or symfony/polyfill-iconv in case ext-iconv cannot be used).", + "ext-intl": "To show region name in time zone dump", + "ext-symfony_debug": "" + }, + "time": "2019-07-26T11:29:23+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "installation-source": "dist", + "autoload": { + "files": [ + "Resources/functions/dump.php" + ], + "psr-4": { + "Symfony\\Component\\VarDumper\\": "" + }, + "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 mechanism for exploring and dumping PHP variables", + "homepage": "https://symfony.com", + "keywords": [ + "debug", + "dump" + ] + }, + { + "name": "symfony/web-profiler-bundle", + "version": "v3.4.30", + "version_normalized": "3.4.30.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/web-profiler-bundle.git", + "reference": "0e9627b1a3864949fa1d72eec78dab02830b5206" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/web-profiler-bundle/zipball/0e9627b1a3864949fa1d72eec78dab02830b5206", + "reference": "0e9627b1a3864949fa1d72eec78dab02830b5206", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8", + "symfony/http-kernel": "~3.4.25|^4.2.6", + "symfony/polyfill-php70": "~1.0", + "symfony/routing": "~2.8|~3.0|~4.0", + "symfony/twig-bridge": "~2.8|~3.0|~4.0", + "symfony/var-dumper": "~3.3|~4.0", + "twig/twig": "~1.34|~2.4" + }, + "conflict": { + "symfony/config": "<3.4", + "symfony/dependency-injection": "<3.4", + "symfony/event-dispatcher": "<3.3.1", + "symfony/var-dumper": "<3.3" + }, + "require-dev": { + "symfony/config": "~3.4|~4.0", + "symfony/console": "~2.8|~3.0|~4.0", + "symfony/dependency-injection": "~3.4|~4.0", + "symfony/stopwatch": "~2.8|~3.0|~4.0" + }, + "time": "2019-07-24T10:33:03+00:00", + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Bundle\\WebProfilerBundle\\": "" + }, + "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 WebProfilerBundle", + "homepage": "https://symfony.com" + }, + { + "name": "symfony/yaml", + "version": "v3.4.30", + "version_normalized": "3.4.30.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "051d045c684148060ebfc9affb7e3f5e0899d40b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/051d045c684148060ebfc9affb7e3f5e0899d40b", + "reference": "051d045c684148060ebfc9affb7e3f5e0899d40b", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8", + "symfony/polyfill-ctype": "~1.8" + }, + "conflict": { + "symfony/console": "<3.4" + }, + "require-dev": { + "symfony/console": "~3.4|~4.0" + }, + "suggest": { + "symfony/console": "For validating YAML files using the lint command" + }, + "time": "2019-07-24T13:01:31+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Component\\Yaml\\": "" + }, + "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 Yaml Component", + "homepage": "https://symfony.com" + }, + { + "name": "twig/twig", + "version": "v1.42.2", + "version_normalized": "1.42.2.0", + "source": { + "type": "git", + "url": "https://github.com/twigphp/Twig.git", + "reference": "21707d6ebd05476854805e4f91b836531941bcd4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/21707d6ebd05476854805e4f91b836531941bcd4", + "reference": "21707d6ebd05476854805e4f91b836531941bcd4", + "shasum": "" + }, + "require": { + "php": ">=5.4.0", + "symfony/polyfill-ctype": "^1.8" + }, + "require-dev": { + "psr/container": "^1.0", + "symfony/debug": "^2.7", + "symfony/phpunit-bridge": "^3.4.19|^4.1.8|^5.0" + }, + "time": "2019-06-18T15:35:16+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.42-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "Twig_": "lib/" + }, + "psr-4": { + "Twig\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Fabien Potencier", + "role": "Lead Developer", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org" + }, + { + "name": "Armin Ronacher", + "role": "Project Founder", + "email": "armin.ronacher@active-4.com" + }, + { + "name": "Twig Team", + "role": "Contributors", + "homepage": "https://twig.symfony.com/contributors" + } + ], + "description": "Twig, the flexible, fast, and secure template language for PHP", + "homepage": "https://twig.symfony.com", + "keywords": [ + "templating" + ] + } +] diff --git a/lib/paragonie/random_compat/LICENSE b/lib/paragonie/random_compat/LICENSE new file mode 100644 index 000000000..45c7017df --- /dev/null +++ b/lib/paragonie/random_compat/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 Paragon Initiative Enterprises + +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. + diff --git a/lib/paragonie/random_compat/composer.json b/lib/paragonie/random_compat/composer.json new file mode 100644 index 000000000..34f1381d5 --- /dev/null +++ b/lib/paragonie/random_compat/composer.json @@ -0,0 +1,38 @@ +{ + "name": "paragonie/random_compat", + "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", + "keywords": [ + "csprng", + "random", + "polyfill", + "pseudorandom" + ], + "license": "MIT", + "type": "library", + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com" + } + ], + "support": { + "issues": "https://github.com/paragonie/random_compat/issues", + "email": "info@paragonie.com", + "source": "https://github.com/paragonie/random_compat" + }, + "require": { + "php": ">=5.2.0" + }, + "require-dev": { + "phpunit/phpunit": "4.*|5.*" + }, + "suggest": { + "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." + }, + "autoload": { + "files": [ + "lib/random.php" + ] + } +} diff --git a/lib/paragonie/random_compat/dist/random_compat.phar.pubkey b/lib/paragonie/random_compat/dist/random_compat.phar.pubkey new file mode 100644 index 000000000..eb50ebfcd --- /dev/null +++ b/lib/paragonie/random_compat/dist/random_compat.phar.pubkey @@ -0,0 +1,5 @@ +-----BEGIN PUBLIC KEY----- +MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEEd+wCqJDrx5B4OldM0dQE0ZMX+lx1ZWm +pui0SUqD4G29L3NGsz9UhJ/0HjBdbnkhIK5xviT0X5vtjacF6ajgcCArbTB+ds+p ++h7Q084NuSuIpNb6YPfoUFgC/CL9kAoc +-----END PUBLIC KEY----- diff --git a/lib/paragonie/random_compat/dist/random_compat.phar.pubkey.asc b/lib/paragonie/random_compat/dist/random_compat.phar.pubkey.asc new file mode 100644 index 000000000..6a1d7f300 --- /dev/null +++ b/lib/paragonie/random_compat/dist/random_compat.phar.pubkey.asc @@ -0,0 +1,11 @@ +-----BEGIN PGP SIGNATURE----- +Version: GnuPG v2.0.22 (MingW32) + +iQEcBAABAgAGBQJWtW1hAAoJEGuXocKCZATaJf0H+wbZGgskK1dcRTsuVJl9IWip +QwGw/qIKI280SD6/ckoUMxKDCJiFuPR14zmqnS36k7N5UNPnpdTJTS8T11jttSpg +1LCmgpbEIpgaTah+cELDqFCav99fS+bEiAL5lWDAHBTE/XPjGVCqeehyPYref4IW +NDBIEsvnHPHPLsn6X5jq4+Yj5oUixgxaMPiR+bcO4Sh+RzOVB6i2D0upWfRXBFXA +NNnsg9/zjvoC7ZW73y9uSH+dPJTt/Vgfeiv52/v41XliyzbUyLalf02GNPY+9goV +JHG1ulEEBJOCiUD9cE1PUIJwHA/HqyhHIvV350YoEFiHl8iSwm7SiZu5kPjaq74= +=B6+8 +-----END PGP SIGNATURE----- diff --git a/lib/paragonie/random_compat/lib/byte_safe_strings.php b/lib/paragonie/random_compat/lib/byte_safe_strings.php new file mode 100644 index 000000000..ef24488f9 --- /dev/null +++ b/lib/paragonie/random_compat/lib/byte_safe_strings.php @@ -0,0 +1,195 @@ + RandomCompat_strlen($binary_string)) { + return ''; + } + + return (string) mb_substr( + (string) $binary_string, + (int) $start, + (int) $length, + '8bit' + ); + } + + } else { + + /** + * substr() implementation that isn't brittle to mbstring.func_overload + * + * This version just uses the default substr() + * + * @param string $binary_string + * @param int $start + * @param int|null $length (optional) + * + * @throws TypeError + * + * @return string + */ + function RandomCompat_substr($binary_string, $start, $length = null) + { + if (!is_string($binary_string)) { + throw new TypeError( + 'RandomCompat_substr(): First argument should be a string' + ); + } + + if (!is_int($start)) { + throw new TypeError( + 'RandomCompat_substr(): Second argument should be an integer' + ); + } + + if ($length !== null) { + if (!is_int($length)) { + throw new TypeError( + 'RandomCompat_substr(): Third argument should be an integer, or omitted' + ); + } + + return (string) substr( + (string )$binary_string, + (int) $start, + (int) $length + ); + } + + return (string) substr( + (string) $binary_string, + (int) $start + ); + } + } +} diff --git a/lib/paragonie/random_compat/lib/cast_to_int.php b/lib/paragonie/random_compat/lib/cast_to_int.php new file mode 100644 index 000000000..1b1bbfe8d --- /dev/null +++ b/lib/paragonie/random_compat/lib/cast_to_int.php @@ -0,0 +1,77 @@ + operators might accidentally let a float + * through. + * + * @param int|float $number The number we want to convert to an int + * @param bool $fail_open Set to true to not throw an exception + * + * @return float|int + * @psalm-suppress InvalidReturnType + * + * @throws TypeError + */ + function RandomCompat_intval($number, $fail_open = false) + { + if (is_int($number) || is_float($number)) { + $number += 0; + } elseif (is_numeric($number)) { + /** @psalm-suppress InvalidOperand */ + $number += 0; + } + /** @var int|float $number */ + + if ( + is_float($number) + && + $number > ~PHP_INT_MAX + && + $number < PHP_INT_MAX + ) { + $number = (int) $number; + } + + if (is_int($number)) { + return (int) $number; + } elseif (!$fail_open) { + throw new TypeError( + 'Expected an integer.' + ); + } + return $number; + } +} diff --git a/lib/paragonie/random_compat/lib/error_polyfill.php b/lib/paragonie/random_compat/lib/error_polyfill.php new file mode 100644 index 000000000..c02c5c8b4 --- /dev/null +++ b/lib/paragonie/random_compat/lib/error_polyfill.php @@ -0,0 +1,49 @@ += 70000) { + return; +} + +if (!defined('RANDOM_COMPAT_READ_BUFFER')) { + define('RANDOM_COMPAT_READ_BUFFER', 8); +} + +$RandomCompatDIR = dirname(__FILE__); + +require_once $RandomCompatDIR . DIRECTORY_SEPARATOR . 'byte_safe_strings.php'; +require_once $RandomCompatDIR . DIRECTORY_SEPARATOR . 'cast_to_int.php'; +require_once $RandomCompatDIR . DIRECTORY_SEPARATOR . 'error_polyfill.php'; + +if (!is_callable('random_bytes')) { + /** + * PHP 5.2.0 - 5.6.x way to implement random_bytes() + * + * We use conditional statements here to define the function in accordance + * to the operating environment. It's a micro-optimization. + * + * In order of preference: + * 1. Use libsodium if available. + * 2. fread() /dev/urandom if available (never on Windows) + * 3. mcrypt_create_iv($bytes, MCRYPT_DEV_URANDOM) + * 4. COM('CAPICOM.Utilities.1')->GetRandom() + * + * See RATIONALE.md for our reasoning behind this particular order + */ + if (extension_loaded('libsodium')) { + // See random_bytes_libsodium.php + if (PHP_VERSION_ID >= 50300 && is_callable('\\Sodium\\randombytes_buf')) { + require_once $RandomCompatDIR . DIRECTORY_SEPARATOR . 'random_bytes_libsodium.php'; + } elseif (method_exists('Sodium', 'randombytes_buf')) { + require_once $RandomCompatDIR . DIRECTORY_SEPARATOR . 'random_bytes_libsodium_legacy.php'; + } + } + + /** + * Reading directly from /dev/urandom: + */ + if (DIRECTORY_SEPARATOR === '/') { + // DIRECTORY_SEPARATOR === '/' on Unix-like OSes -- this is a fast + // way to exclude Windows. + $RandomCompatUrandom = true; + $RandomCompat_basedir = ini_get('open_basedir'); + + if (!empty($RandomCompat_basedir)) { + $RandomCompat_open_basedir = explode( + PATH_SEPARATOR, + strtolower($RandomCompat_basedir) + ); + $RandomCompatUrandom = (array() !== array_intersect( + array('/dev', '/dev/', '/dev/urandom'), + $RandomCompat_open_basedir + )); + $RandomCompat_open_basedir = null; + } + + if ( + !is_callable('random_bytes') + && + $RandomCompatUrandom + && + @is_readable('/dev/urandom') + ) { + // Error suppression on is_readable() in case of an open_basedir + // or safe_mode failure. All we care about is whether or not we + // can read it at this point. If the PHP environment is going to + // panic over trying to see if the file can be read in the first + // place, that is not helpful to us here. + + // See random_bytes_dev_urandom.php + require_once $RandomCompatDIR . DIRECTORY_SEPARATOR . 'random_bytes_dev_urandom.php'; + } + // Unset variables after use + $RandomCompat_basedir = null; + } else { + $RandomCompatUrandom = false; + } + + /** + * mcrypt_create_iv() + * + * We only want to use mcypt_create_iv() if: + * + * - random_bytes() hasn't already been defined + * - the mcrypt extensions is loaded + * - One of these two conditions is true: + * - We're on Windows (DIRECTORY_SEPARATOR !== '/') + * - We're not on Windows and /dev/urandom is readabale + * (i.e. we're not in a chroot jail) + * - Special case: + * - If we're not on Windows, but the PHP version is between + * 5.6.10 and 5.6.12, we don't want to use mcrypt. It will + * hang indefinitely. This is bad. + * - If we're on Windows, we want to use PHP >= 5.3.7 or else + * we get insufficient entropy errors. + */ + if ( + !is_callable('random_bytes') + && + // Windows on PHP < 5.3.7 is broken, but non-Windows is not known to be. + (DIRECTORY_SEPARATOR === '/' || PHP_VERSION_ID >= 50307) + && + // Prevent this code from hanging indefinitely on non-Windows; + // see https://bugs.php.net/bug.php?id=69833 + ( + DIRECTORY_SEPARATOR !== '/' || + (PHP_VERSION_ID <= 50609 || PHP_VERSION_ID >= 50613) + ) + && + extension_loaded('mcrypt') + ) { + // See random_bytes_mcrypt.php + require_once $RandomCompatDIR . DIRECTORY_SEPARATOR . 'random_bytes_mcrypt.php'; + } + $RandomCompatUrandom = null; + + /** + * This is a Windows-specific fallback, for when the mcrypt extension + * isn't loaded. + */ + if ( + !is_callable('random_bytes') + && + extension_loaded('com_dotnet') + && + class_exists('COM') + ) { + $RandomCompat_disabled_classes = preg_split( + '#\s*,\s*#', + strtolower(ini_get('disable_classes')) + ); + + if (!in_array('com', $RandomCompat_disabled_classes)) { + try { + $RandomCompatCOMtest = new COM('CAPICOM.Utilities.1'); + if (method_exists($RandomCompatCOMtest, 'GetRandom')) { + // See random_bytes_com_dotnet.php + require_once $RandomCompatDIR . DIRECTORY_SEPARATOR . 'random_bytes_com_dotnet.php'; + } + } catch (com_exception $e) { + // Don't try to use it. + } + } + $RandomCompat_disabled_classes = null; + $RandomCompatCOMtest = null; + } + + /** + * throw new Exception + */ + if (!is_callable('random_bytes')) { + /** + * We don't have any more options, so let's throw an exception right now + * and hope the developer won't let it fail silently. + * + * @param mixed $length + * @psalm-suppress InvalidReturnType + * @throws Exception + * @return string + */ + function random_bytes($length) + { + unset($length); // Suppress "variable not used" warnings. + throw new Exception( + 'There is no suitable CSPRNG installed on your system' + ); + return ''; + } + } +} + +if (!is_callable('random_int')) { + require_once $RandomCompatDIR . DIRECTORY_SEPARATOR . 'random_int.php'; +} + +$RandomCompatDIR = null; diff --git a/lib/paragonie/random_compat/lib/random_bytes_com_dotnet.php b/lib/paragonie/random_compat/lib/random_bytes_com_dotnet.php new file mode 100644 index 000000000..537d02b27 --- /dev/null +++ b/lib/paragonie/random_compat/lib/random_bytes_com_dotnet.php @@ -0,0 +1,91 @@ +GetRandom($bytes, 0)); + if (RandomCompat_strlen($buf) >= $bytes) { + /** + * Return our random entropy buffer here: + */ + return (string) RandomCompat_substr($buf, 0, $bytes); + } + ++$execCount; + } while ($execCount < $bytes); + + /** + * If we reach here, PHP has failed us. + */ + throw new Exception( + 'Could not gather sufficient random data' + ); + } +} diff --git a/lib/paragonie/random_compat/lib/random_bytes_dev_urandom.php b/lib/paragonie/random_compat/lib/random_bytes_dev_urandom.php new file mode 100644 index 000000000..c4e31ccbb --- /dev/null +++ b/lib/paragonie/random_compat/lib/random_bytes_dev_urandom.php @@ -0,0 +1,190 @@ + $st */ + $st = fstat($fp); + if (($st['mode'] & 0170000) !== 020000) { + fclose($fp); + $fp = false; + } + } + } + + if (is_resource($fp)) { + /** + * stream_set_read_buffer() does not exist in HHVM + * + * If we don't set the stream's read buffer to 0, PHP will + * internally buffer 8192 bytes, which can waste entropy + * + * stream_set_read_buffer returns 0 on success + */ + if (is_callable('stream_set_read_buffer')) { + stream_set_read_buffer($fp, RANDOM_COMPAT_READ_BUFFER); + } + if (is_callable('stream_set_chunk_size')) { + stream_set_chunk_size($fp, RANDOM_COMPAT_READ_BUFFER); + } + } + } + + try { + /** @var int $bytes */ + $bytes = RandomCompat_intval($bytes); + } catch (TypeError $ex) { + throw new TypeError( + 'random_bytes(): $bytes must be an integer' + ); + } + + if ($bytes < 1) { + throw new Error( + 'Length must be greater than 0' + ); + } + + /** + * This if() block only runs if we managed to open a file handle + * + * It does not belong in an else {} block, because the above + * if (empty($fp)) line is logic that should only be run once per + * page load. + */ + if (is_resource($fp)) { + /** + * @var int + */ + $remaining = $bytes; + + /** + * @var string|bool + */ + $buf = ''; + + /** + * We use fread() in a loop to protect against partial reads + */ + do { + /** + * @var string|bool + */ + $read = fread($fp, $remaining); + if (!is_string($read)) { + /** + * We cannot safely read from the file. Exit the + * do-while loop and trigger the exception condition + * + * @var string|bool + */ + $buf = false; + break; + } + /** + * Decrease the number of bytes returned from remaining + */ + $remaining -= RandomCompat_strlen($read); + /** + * @var string $buf + */ + $buf .= $read; + } while ($remaining > 0); + + /** + * Is our result valid? + * @var string|bool $buf + */ + if (is_string($buf)) { + if (RandomCompat_strlen($buf) === $bytes) { + /** + * Return our random entropy buffer here: + */ + return $buf; + } + } + } + + /** + * If we reach here, PHP has failed us. + */ + throw new Exception( + 'Error reading from source device' + ); + } +} diff --git a/lib/paragonie/random_compat/lib/random_bytes_libsodium.php b/lib/paragonie/random_compat/lib/random_bytes_libsodium.php new file mode 100644 index 000000000..2e5629018 --- /dev/null +++ b/lib/paragonie/random_compat/lib/random_bytes_libsodium.php @@ -0,0 +1,91 @@ + 2147483647) { + $buf = ''; + for ($i = 0; $i < $bytes; $i += 1073741824) { + $n = ($bytes - $i) > 1073741824 + ? 1073741824 + : $bytes - $i; + $buf .= \Sodium\randombytes_buf($n); + } + } else { + /** @var string|bool $buf */ + $buf = \Sodium\randombytes_buf($bytes); + } + + if (is_string($buf)) { + if (RandomCompat_strlen($buf) === $bytes) { + return $buf; + } + } + + /** + * If we reach here, PHP has failed us. + */ + throw new Exception( + 'Could not gather sufficient random data' + ); + } +} diff --git a/lib/paragonie/random_compat/lib/random_bytes_libsodium_legacy.php b/lib/paragonie/random_compat/lib/random_bytes_libsodium_legacy.php new file mode 100644 index 000000000..f78b2199a --- /dev/null +++ b/lib/paragonie/random_compat/lib/random_bytes_libsodium_legacy.php @@ -0,0 +1,93 @@ + 2147483647) { + for ($i = 0; $i < $bytes; $i += 1073741824) { + $n = ($bytes - $i) > 1073741824 + ? 1073741824 + : $bytes - $i; + $buf .= Sodium::randombytes_buf((int) $n); + } + } else { + $buf .= Sodium::randombytes_buf((int) $bytes); + } + + if (is_string($buf)) { + if (RandomCompat_strlen($buf) === $bytes) { + return $buf; + } + } + + /** + * If we reach here, PHP has failed us. + */ + throw new Exception( + 'Could not gather sufficient random data' + ); + } +} diff --git a/lib/paragonie/random_compat/lib/random_bytes_mcrypt.php b/lib/paragonie/random_compat/lib/random_bytes_mcrypt.php new file mode 100644 index 000000000..0b13fa73c --- /dev/null +++ b/lib/paragonie/random_compat/lib/random_bytes_mcrypt.php @@ -0,0 +1,79 @@ + operators might accidentally let a float + * through. + */ + + try { + /** @var int $min */ + $min = RandomCompat_intval($min); + } catch (TypeError $ex) { + throw new TypeError( + 'random_int(): $min must be an integer' + ); + } + + try { + /** @var int $max */ + $max = RandomCompat_intval($max); + } catch (TypeError $ex) { + throw new TypeError( + 'random_int(): $max must be an integer' + ); + } + + /** + * Now that we've verified our weak typing system has given us an integer, + * let's validate the logic then we can move forward with generating random + * integers along a given range. + */ + if ($min > $max) { + throw new Error( + 'Minimum value must be less than or equal to the maximum value' + ); + } + + if ($max === $min) { + return (int) $min; + } + + /** + * Initialize variables to 0 + * + * We want to store: + * $bytes => the number of random bytes we need + * $mask => an integer bitmask (for use with the &) operator + * so we can minimize the number of discards + */ + $attempts = $bits = $bytes = $mask = $valueShift = 0; + /** @var int $attempts */ + /** @var int $bits */ + /** @var int $bytes */ + /** @var int $mask */ + /** @var int $valueShift */ + + /** + * At this point, $range is a positive number greater than 0. It might + * overflow, however, if $max - $min > PHP_INT_MAX. PHP will cast it to + * a float and we will lose some precision. + * + * @var int|float $range + */ + $range = $max - $min; + + /** + * Test for integer overflow: + */ + if (!is_int($range)) { + + /** + * Still safely calculate wider ranges. + * Provided by @CodesInChaos, @oittaa + * + * @ref https://gist.github.com/CodesInChaos/03f9ea0b58e8b2b8d435 + * + * We use ~0 as a mask in this case because it generates all 1s + * + * @ref https://eval.in/400356 (32-bit) + * @ref http://3v4l.org/XX9r5 (64-bit) + */ + $bytes = PHP_INT_SIZE; + /** @var int $mask */ + $mask = ~0; + + } else { + + /** + * $bits is effectively ceil(log($range, 2)) without dealing with + * type juggling + */ + while ($range > 0) { + if ($bits % 8 === 0) { + ++$bytes; + } + ++$bits; + $range >>= 1; + /** @var int $mask */ + $mask = $mask << 1 | 1; + } + $valueShift = $min; + } + + /** @var int $val */ + $val = 0; + /** + * Now that we have our parameters set up, let's begin generating + * random integers until one falls between $min and $max + */ + /** @psalm-suppress RedundantCondition */ + do { + /** + * The rejection probability is at most 0.5, so this corresponds + * to a failure probability of 2^-128 for a working RNG + */ + if ($attempts > 128) { + throw new Exception( + 'random_int: RNG is broken - too many rejections' + ); + } + + /** + * Let's grab the necessary number of random bytes + */ + $randomByteString = random_bytes($bytes); + + /** + * Let's turn $randomByteString into an integer + * + * This uses bitwise operators (<< and |) to build an integer + * out of the values extracted from ord() + * + * Example: [9F] | [6D] | [32] | [0C] => + * 159 + 27904 + 3276800 + 201326592 => + * 204631455 + */ + $val &= 0; + for ($i = 0; $i < $bytes; ++$i) { + $val |= ord($randomByteString[$i]) << ($i * 8); + } + /** @var int $val */ + + /** + * Apply mask + */ + $val &= $mask; + $val += $valueShift; + + ++$attempts; + /** + * If $val overflows to a floating point number, + * ... or is larger than $max, + * ... or smaller than $min, + * then try again. + */ + } while (!is_int($val) || $val > $max || $val < $min); + + return (int) $val; + } +} diff --git a/lib/psr/cache/CHANGELOG.md b/lib/psr/cache/CHANGELOG.md new file mode 100644 index 000000000..58ddab05a --- /dev/null +++ b/lib/psr/cache/CHANGELOG.md @@ -0,0 +1,16 @@ +# Changelog + +All notable changes to this project will be documented in this file, in reverse chronological order by release. + +## 1.0.1 - 2016-08-06 + +### Fixed + +- Make spacing consistent in phpdoc annotations php-fig/cache#9 - chalasr +- Fix grammar in phpdoc annotations php-fig/cache#10 - chalasr +- Be more specific in docblocks that `getItems()` and `deleteItems()` take an array of strings (`string[]`) compared to just `array` php-fig/cache#8 - GrahamCampbell +- For `expiresAt()` and `expiresAfter()` in CacheItemInterface fix docblock to specify null as a valid parameters as well as an implementation of DateTimeInterface php-fig/cache#7 - GrahamCampbell + +## 1.0.0 - 2015-12-11 + +Initial stable release; reflects accepted PSR-6 specification diff --git a/lib/psr/cache/LICENSE.txt b/lib/psr/cache/LICENSE.txt new file mode 100644 index 000000000..b1c2c97b9 --- /dev/null +++ b/lib/psr/cache/LICENSE.txt @@ -0,0 +1,19 @@ +Copyright (c) 2015 PHP Framework Interoperability Group + +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. diff --git a/lib/psr/cache/README.md b/lib/psr/cache/README.md new file mode 100644 index 000000000..c8706ceea --- /dev/null +++ b/lib/psr/cache/README.md @@ -0,0 +1,9 @@ +PSR Cache +========= + +This repository holds all interfaces defined by +[PSR-6](http://www.php-fig.org/psr/psr-6/). + +Note that this is not a Cache implementation of its own. It is merely an +interface that describes a Cache implementation. See the specification for more +details. diff --git a/lib/psr/cache/composer.json b/lib/psr/cache/composer.json new file mode 100644 index 000000000..e828fec94 --- /dev/null +++ b/lib/psr/cache/composer.json @@ -0,0 +1,25 @@ +{ + "name": "psr/cache", + "description": "Common interface for caching libraries", + "keywords": ["psr", "psr-6", "cache"], + "license": "MIT", + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "require": { + "php": ">=5.3.0" + }, + "autoload": { + "psr-4": { + "Psr\\Cache\\": "src/" + } + }, + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + } +} diff --git a/lib/psr/cache/src/CacheException.php b/lib/psr/cache/src/CacheException.php new file mode 100644 index 000000000..e27f22f8d --- /dev/null +++ b/lib/psr/cache/src/CacheException.php @@ -0,0 +1,10 @@ +=5.3.0" + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + } +} diff --git a/lib/psr/container/src/ContainerExceptionInterface.php b/lib/psr/container/src/ContainerExceptionInterface.php new file mode 100644 index 000000000..d35c6b4d8 --- /dev/null +++ b/lib/psr/container/src/ContainerExceptionInterface.php @@ -0,0 +1,13 @@ +log(LogLevel::EMERGENCY, $message, $context); + } + + /** + * Action must be taken immediately. + * + * Example: Entire website down, database unavailable, etc. This should + * trigger the SMS alerts and wake you up. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function alert($message, array $context = array()) + { + $this->log(LogLevel::ALERT, $message, $context); + } + + /** + * Critical conditions. + * + * Example: Application component unavailable, unexpected exception. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function critical($message, array $context = array()) + { + $this->log(LogLevel::CRITICAL, $message, $context); + } + + /** + * Runtime errors that do not require immediate action but should typically + * be logged and monitored. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function error($message, array $context = array()) + { + $this->log(LogLevel::ERROR, $message, $context); + } + + /** + * Exceptional occurrences that are not errors. + * + * Example: Use of deprecated APIs, poor use of an API, undesirable things + * that are not necessarily wrong. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function warning($message, array $context = array()) + { + $this->log(LogLevel::WARNING, $message, $context); + } + + /** + * Normal but significant events. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function notice($message, array $context = array()) + { + $this->log(LogLevel::NOTICE, $message, $context); + } + + /** + * Interesting events. + * + * Example: User logs in, SQL logs. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function info($message, array $context = array()) + { + $this->log(LogLevel::INFO, $message, $context); + } + + /** + * Detailed debug information. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function debug($message, array $context = array()) + { + $this->log(LogLevel::DEBUG, $message, $context); + } +} diff --git a/lib/psr/log/Psr/Log/InvalidArgumentException.php b/lib/psr/log/Psr/Log/InvalidArgumentException.php new file mode 100644 index 000000000..67f852d1d --- /dev/null +++ b/lib/psr/log/Psr/Log/InvalidArgumentException.php @@ -0,0 +1,7 @@ +logger = $logger; + } +} diff --git a/lib/psr/log/Psr/Log/LoggerInterface.php b/lib/psr/log/Psr/Log/LoggerInterface.php new file mode 100644 index 000000000..5ea72438b --- /dev/null +++ b/lib/psr/log/Psr/Log/LoggerInterface.php @@ -0,0 +1,123 @@ +log(LogLevel::EMERGENCY, $message, $context); + } + + /** + * Action must be taken immediately. + * + * Example: Entire website down, database unavailable, etc. This should + * trigger the SMS alerts and wake you up. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function alert($message, array $context = array()) + { + $this->log(LogLevel::ALERT, $message, $context); + } + + /** + * Critical conditions. + * + * Example: Application component unavailable, unexpected exception. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function critical($message, array $context = array()) + { + $this->log(LogLevel::CRITICAL, $message, $context); + } + + /** + * Runtime errors that do not require immediate action but should typically + * be logged and monitored. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function error($message, array $context = array()) + { + $this->log(LogLevel::ERROR, $message, $context); + } + + /** + * Exceptional occurrences that are not errors. + * + * Example: Use of deprecated APIs, poor use of an API, undesirable things + * that are not necessarily wrong. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function warning($message, array $context = array()) + { + $this->log(LogLevel::WARNING, $message, $context); + } + + /** + * Normal but significant events. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function notice($message, array $context = array()) + { + $this->log(LogLevel::NOTICE, $message, $context); + } + + /** + * Interesting events. + * + * Example: User logs in, SQL logs. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function info($message, array $context = array()) + { + $this->log(LogLevel::INFO, $message, $context); + } + + /** + * Detailed debug information. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function debug($message, array $context = array()) + { + $this->log(LogLevel::DEBUG, $message, $context); + } + + /** + * Logs with an arbitrary level. + * + * @param mixed $level + * @param string $message + * @param array $context + * + * @return void + */ + abstract public function log($level, $message, array $context = array()); +} diff --git a/lib/psr/log/Psr/Log/NullLogger.php b/lib/psr/log/Psr/Log/NullLogger.php new file mode 100644 index 000000000..d8cd682c8 --- /dev/null +++ b/lib/psr/log/Psr/Log/NullLogger.php @@ -0,0 +1,28 @@ +logger) { }` + * blocks. + */ +class NullLogger extends AbstractLogger +{ + /** + * Logs with an arbitrary level. + * + * @param mixed $level + * @param string $message + * @param array $context + * + * @return void + */ + public function log($level, $message, array $context = array()) + { + // noop + } +} diff --git a/lib/psr/log/Psr/Log/Test/LoggerInterfaceTest.php b/lib/psr/log/Psr/Log/Test/LoggerInterfaceTest.php new file mode 100644 index 000000000..4b861c3ef --- /dev/null +++ b/lib/psr/log/Psr/Log/Test/LoggerInterfaceTest.php @@ -0,0 +1,144 @@ + ". + * + * Example ->error('Foo') would yield "error Foo". + * + * @return string[] + */ + abstract public function getLogs(); + + public function testImplements() + { + $this->assertInstanceOf('Psr\Log\LoggerInterface', $this->getLogger()); + } + + /** + * @dataProvider provideLevelsAndMessages + */ + public function testLogsAtAllLevels($level, $message) + { + $logger = $this->getLogger(); + $logger->{$level}($message, array('user' => 'Bob')); + $logger->log($level, $message, array('user' => 'Bob')); + + $expected = array( + $level.' message of level '.$level.' with context: Bob', + $level.' message of level '.$level.' with context: Bob', + ); + $this->assertEquals($expected, $this->getLogs()); + } + + public function provideLevelsAndMessages() + { + return array( + LogLevel::EMERGENCY => array(LogLevel::EMERGENCY, 'message of level emergency with context: {user}'), + LogLevel::ALERT => array(LogLevel::ALERT, 'message of level alert with context: {user}'), + LogLevel::CRITICAL => array(LogLevel::CRITICAL, 'message of level critical with context: {user}'), + LogLevel::ERROR => array(LogLevel::ERROR, 'message of level error with context: {user}'), + LogLevel::WARNING => array(LogLevel::WARNING, 'message of level warning with context: {user}'), + LogLevel::NOTICE => array(LogLevel::NOTICE, 'message of level notice with context: {user}'), + LogLevel::INFO => array(LogLevel::INFO, 'message of level info with context: {user}'), + LogLevel::DEBUG => array(LogLevel::DEBUG, 'message of level debug with context: {user}'), + ); + } + + /** + * @expectedException \Psr\Log\InvalidArgumentException + */ + public function testThrowsOnInvalidLevel() + { + $logger = $this->getLogger(); + $logger->log('invalid level', 'Foo'); + } + + public function testContextReplacement() + { + $logger = $this->getLogger(); + $logger->info('{Message {nothing} {user} {foo.bar} a}', array('user' => 'Bob', 'foo.bar' => 'Bar')); + + $expected = array('info {Message {nothing} Bob Bar a}'); + $this->assertEquals($expected, $this->getLogs()); + } + + public function testObjectCastToString() + { + if (method_exists($this, 'createPartialMock')) { + $dummy = $this->createPartialMock('Psr\Log\Test\DummyTest', array('__toString')); + } else { + $dummy = $this->getMock('Psr\Log\Test\DummyTest', array('__toString')); + } + $dummy->expects($this->once()) + ->method('__toString') + ->will($this->returnValue('DUMMY')); + + $this->getLogger()->warning($dummy); + + $expected = array('warning DUMMY'); + $this->assertEquals($expected, $this->getLogs()); + } + + public function testContextCanContainAnything() + { + $closed = fopen('php://memory', 'r'); + fclose($closed); + + $context = array( + 'bool' => true, + 'null' => null, + 'string' => 'Foo', + 'int' => 0, + 'float' => 0.5, + 'nested' => array('with object' => new DummyTest), + 'object' => new \DateTime, + 'resource' => fopen('php://memory', 'r'), + 'closed' => $closed, + ); + + $this->getLogger()->warning('Crazy context data', $context); + + $expected = array('warning Crazy context data'); + $this->assertEquals($expected, $this->getLogs()); + } + + public function testContextExceptionKeyCanBeExceptionOrOtherValues() + { + $logger = $this->getLogger(); + $logger->warning('Random message', array('exception' => 'oops')); + $logger->critical('Uncaught Exception!', array('exception' => new \LogicException('Fail'))); + + $expected = array( + 'warning Random message', + 'critical Uncaught Exception!' + ); + $this->assertEquals($expected, $this->getLogs()); + } +} + +class DummyTest +{ + public function __toString() + { + } +} diff --git a/lib/psr/log/Psr/Log/Test/TestLogger.php b/lib/psr/log/Psr/Log/Test/TestLogger.php new file mode 100644 index 000000000..0cdffe4f9 --- /dev/null +++ b/lib/psr/log/Psr/Log/Test/TestLogger.php @@ -0,0 +1,146 @@ + $level, + 'message' => $message, + 'context' => $context, + ]; + + $this->recordsByLevel[$record['level']][] = $record; + $this->records[] = $record; + } + + public function hasRecords($level) + { + return isset($this->recordsByLevel[$level]); + } + + public function hasRecord($record, $level) + { + if (is_string($record)) { + $record = ['message' => $record]; + } + return $this->hasRecordThatPasses(function ($rec) use ($record) { + if ($rec['message'] !== $record['message']) { + return false; + } + if (isset($record['context']) && $rec['context'] !== $record['context']) { + return false; + } + return true; + }, $level); + } + + public function hasRecordThatContains($message, $level) + { + return $this->hasRecordThatPasses(function ($rec) use ($message) { + return strpos($rec['message'], $message) !== false; + }, $level); + } + + public function hasRecordThatMatches($regex, $level) + { + return $this->hasRecordThatPasses(function ($rec) use ($regex) { + return preg_match($regex, $rec['message']) > 0; + }, $level); + } + + public function hasRecordThatPasses(callable $predicate, $level) + { + if (!isset($this->recordsByLevel[$level])) { + return false; + } + foreach ($this->recordsByLevel[$level] as $i => $rec) { + if (call_user_func($predicate, $rec, $i)) { + return true; + } + } + return false; + } + + public function __call($method, $args) + { + if (preg_match('/(.*)(Debug|Info|Notice|Warning|Error|Critical|Alert|Emergency)(.*)/', $method, $matches) > 0) { + $genericMethod = $matches[1] . ('Records' !== $matches[3] ? 'Record' : '') . $matches[3]; + $level = strtolower($matches[2]); + if (method_exists($this, $genericMethod)) { + $args[] = $level; + return call_user_func_array([$this, $genericMethod], $args); + } + } + throw new \BadMethodCallException('Call to undefined method ' . get_class($this) . '::' . $method . '()'); + } + + public function reset() + { + $this->records = []; + } +} diff --git a/lib/psr/log/README.md b/lib/psr/log/README.md new file mode 100644 index 000000000..5571a25e8 --- /dev/null +++ b/lib/psr/log/README.md @@ -0,0 +1,52 @@ +PSR Log +======= + +This repository holds all interfaces/classes/traits related to +[PSR-3](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md). + +Note that this is not a logger of its own. It is merely an interface that +describes a logger. See the specification for more details. + +Installation +------------ + +```bash +composer require psr/log +``` + +Usage +----- + +If you need a logger, you can use the interface like this: + +```php +logger = $logger; + } + + public function doSomething() + { + if ($this->logger) { + $this->logger->info('Doing work'); + } + + // do something useful + } +} +``` + +You can then pick one of the implementations of the interface to get a logger. + +If you want to implement the interface, you can require this package and +implement `Psr\Log\LoggerInterface` in your code. Please read the +[specification text](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md) +for details. diff --git a/lib/psr/log/composer.json b/lib/psr/log/composer.json new file mode 100644 index 000000000..87934d707 --- /dev/null +++ b/lib/psr/log/composer.json @@ -0,0 +1,26 @@ +{ + "name": "psr/log", + "description": "Common interface for logging libraries", + "keywords": ["psr", "psr-3", "log"], + "homepage": "https://github.com/php-fig/log", + "license": "MIT", + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "require": { + "php": ">=5.3.0" + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "Psr/Log/" + } + }, + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + } +} diff --git a/lib/psr/simple-cache/.editorconfig b/lib/psr/simple-cache/.editorconfig new file mode 100644 index 000000000..48542cbb4 --- /dev/null +++ b/lib/psr/simple-cache/.editorconfig @@ -0,0 +1,12 @@ +; This file is for unifying the coding style for different editors and IDEs. +; More information at http://editorconfig.org + +root = true + +[*] +charset = utf-8 +indent_size = 4 +indent_style = space +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true diff --git a/lib/psr/simple-cache/LICENSE.md b/lib/psr/simple-cache/LICENSE.md new file mode 100644 index 000000000..e49a7c85a --- /dev/null +++ b/lib/psr/simple-cache/LICENSE.md @@ -0,0 +1,21 @@ +# The MIT License (MIT) + +Copyright (c) 2016 PHP Framework Interoperability Group + +> 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. diff --git a/lib/psr/simple-cache/README.md b/lib/psr/simple-cache/README.md new file mode 100644 index 000000000..43641d175 --- /dev/null +++ b/lib/psr/simple-cache/README.md @@ -0,0 +1,8 @@ +PHP FIG Simple Cache PSR +======================== + +This repository holds all interfaces related to PSR-16. + +Note that this is not a cache implementation of its own. It is merely an interface that describes a cache implementation. See [the specification](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-16-simple-cache.md) for more details. + +You can find implementations of the specification by looking for packages providing the [psr/simple-cache-implementation](https://packagist.org/providers/psr/simple-cache-implementation) virtual package. diff --git a/lib/psr/simple-cache/composer.json b/lib/psr/simple-cache/composer.json new file mode 100644 index 000000000..2978fa559 --- /dev/null +++ b/lib/psr/simple-cache/composer.json @@ -0,0 +1,25 @@ +{ + "name": "psr/simple-cache", + "description": "Common interfaces for simple caching", + "keywords": ["psr", "psr-16", "cache", "simple-cache", "caching"], + "license": "MIT", + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "require": { + "php": ">=5.3.0" + }, + "autoload": { + "psr-4": { + "Psr\\SimpleCache\\": "src/" + } + }, + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + } +} diff --git a/lib/psr/simple-cache/src/CacheException.php b/lib/psr/simple-cache/src/CacheException.php new file mode 100644 index 000000000..eba53815c --- /dev/null +++ b/lib/psr/simple-cache/src/CacheException.php @@ -0,0 +1,10 @@ + value pairs. Cache keys that do not exist or are stale will have $default as value. + * + * @throws \Psr\SimpleCache\InvalidArgumentException + * MUST be thrown if $keys is neither an array nor a Traversable, + * or if any of the $keys are not a legal value. + */ + public function getMultiple($keys, $default = null); + + /** + * Persists a set of key => value pairs in the cache, with an optional TTL. + * + * @param iterable $values A list of key => value pairs for a multiple-set operation. + * @param null|int|\DateInterval $ttl Optional. The TTL value of this item. If no value is sent and + * the driver supports TTL then the library may set a default value + * for it or let the driver take care of that. + * + * @return bool True on success and false on failure. + * + * @throws \Psr\SimpleCache\InvalidArgumentException + * MUST be thrown if $values is neither an array nor a Traversable, + * or if any of the $values are not a legal value. + */ + public function setMultiple($values, $ttl = null); + + /** + * Deletes multiple cache items in a single operation. + * + * @param iterable $keys A list of string-based keys to be deleted. + * + * @return bool True if the items were successfully removed. False if there was an error. + * + * @throws \Psr\SimpleCache\InvalidArgumentException + * MUST be thrown if $keys is neither an array nor a Traversable, + * or if any of the $keys are not a legal value. + */ + public function deleteMultiple($keys); + + /** + * Determines whether an item is present in the cache. + * + * NOTE: It is recommended that has() is only to be used for cache warming type purposes + * and not to be used within your live applications operations for get/set, as this method + * is subject to a race condition where your has() will return true and immediately after, + * another script can remove it making the state of your app out of date. + * + * @param string $key The cache item key. + * + * @return bool + * + * @throws \Psr\SimpleCache\InvalidArgumentException + * MUST be thrown if the $key string is not a legal value. + */ + public function has($key); +} diff --git a/lib/psr/simple-cache/src/InvalidArgumentException.php b/lib/psr/simple-cache/src/InvalidArgumentException.php new file mode 100644 index 000000000..6a9524a20 --- /dev/null +++ b/lib/psr/simple-cache/src/InvalidArgumentException.php @@ -0,0 +1,13 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Adapter; + +use Psr\Cache\CacheItemInterface; +use Psr\Log\LoggerAwareInterface; +use Psr\Log\LoggerInterface; +use Psr\Log\NullLogger; +use Symfony\Component\Cache\CacheItem; +use Symfony\Component\Cache\Exception\InvalidArgumentException; +use Symfony\Component\Cache\ResettableInterface; +use Symfony\Component\Cache\Traits\AbstractTrait; + +/** + * @author Nicolas Grekas + */ +abstract class AbstractAdapter implements AdapterInterface, LoggerAwareInterface, ResettableInterface +{ + /** + * @internal + */ + const NS_SEPARATOR = ':'; + + use AbstractTrait; + + private static $apcuSupported; + private static $phpFilesSupported; + + private $createCacheItem; + private $mergeByLifetime; + + /** + * @param string $namespace + * @param int $defaultLifetime + */ + protected function __construct($namespace = '', $defaultLifetime = 0) + { + $this->namespace = '' === $namespace ? '' : CacheItem::validateKey($namespace).static::NS_SEPARATOR; + if (null !== $this->maxIdLength && \strlen($namespace) > $this->maxIdLength - 24) { + throw new InvalidArgumentException(sprintf('Namespace must be %d chars max, %d given ("%s")', $this->maxIdLength - 24, \strlen($namespace), $namespace)); + } + $this->createCacheItem = \Closure::bind( + function ($key, $value, $isHit) use ($defaultLifetime) { + $item = new CacheItem(); + $item->key = $key; + $item->value = $value; + $item->isHit = $isHit; + $item->defaultLifetime = $defaultLifetime; + + return $item; + }, + null, + CacheItem::class + ); + $getId = function ($key) { return $this->getId((string) $key); }; + $this->mergeByLifetime = \Closure::bind( + function ($deferred, $namespace, &$expiredIds) use ($getId) { + $byLifetime = []; + $now = time(); + $expiredIds = []; + + foreach ($deferred as $key => $item) { + if (null === $item->expiry) { + $byLifetime[0 < $item->defaultLifetime ? $item->defaultLifetime : 0][$getId($key)] = $item->value; + } elseif ($item->expiry > $now) { + $byLifetime[$item->expiry - $now][$getId($key)] = $item->value; + } else { + $expiredIds[] = $getId($key); + } + } + + return $byLifetime; + }, + null, + CacheItem::class + ); + } + + /** + * @param string $namespace + * @param int $defaultLifetime + * @param string $version + * @param string $directory + * @param LoggerInterface|null $logger + * + * @return AdapterInterface + */ + public static function createSystemCache($namespace, $defaultLifetime, $version, $directory, LoggerInterface $logger = null) + { + if (null === self::$apcuSupported) { + self::$apcuSupported = ApcuAdapter::isSupported(); + } + + if (!self::$apcuSupported && null === self::$phpFilesSupported) { + self::$phpFilesSupported = PhpFilesAdapter::isSupported(); + } + + if (self::$phpFilesSupported) { + $opcache = new PhpFilesAdapter($namespace, $defaultLifetime, $directory); + if (null !== $logger) { + $opcache->setLogger($logger); + } + + return $opcache; + } + + $fs = new FilesystemAdapter($namespace, $defaultLifetime, $directory); + if (null !== $logger) { + $fs->setLogger($logger); + } + if (!self::$apcuSupported) { + return $fs; + } + + $apcu = new ApcuAdapter($namespace, (int) $defaultLifetime / 5, $version); + if ('cli' === \PHP_SAPI && !filter_var(ini_get('apc.enable_cli'), FILTER_VALIDATE_BOOLEAN)) { + $apcu->setLogger(new NullLogger()); + } elseif (null !== $logger) { + $apcu->setLogger($logger); + } + + return new ChainAdapter([$apcu, $fs]); + } + + public static function createConnection($dsn, array $options = []) + { + if (!\is_string($dsn)) { + throw new InvalidArgumentException(sprintf('The %s() method expect argument #1 to be string, %s given.', __METHOD__, \gettype($dsn))); + } + if (0 === strpos($dsn, 'redis://')) { + return RedisAdapter::createConnection($dsn, $options); + } + if (0 === strpos($dsn, 'memcached://')) { + return MemcachedAdapter::createConnection($dsn, $options); + } + + throw new InvalidArgumentException(sprintf('Unsupported DSN: %s.', $dsn)); + } + + /** + * {@inheritdoc} + */ + public function getItem($key) + { + if ($this->deferred) { + $this->commit(); + } + $id = $this->getId($key); + + $f = $this->createCacheItem; + $isHit = false; + $value = null; + + try { + foreach ($this->doFetch([$id]) as $value) { + $isHit = true; + } + } catch (\Exception $e) { + CacheItem::log($this->logger, 'Failed to fetch key "{key}"', ['key' => $key, 'exception' => $e]); + } + + return $f($key, $value, $isHit); + } + + /** + * {@inheritdoc} + */ + public function getItems(array $keys = []) + { + if ($this->deferred) { + $this->commit(); + } + $ids = []; + + foreach ($keys as $key) { + $ids[] = $this->getId($key); + } + try { + $items = $this->doFetch($ids); + } catch (\Exception $e) { + CacheItem::log($this->logger, 'Failed to fetch requested items', ['keys' => $keys, 'exception' => $e]); + $items = []; + } + $ids = array_combine($ids, $keys); + + return $this->generateItems($items, $ids); + } + + /** + * {@inheritdoc} + */ + public function save(CacheItemInterface $item) + { + if (!$item instanceof CacheItem) { + return false; + } + $this->deferred[$item->getKey()] = $item; + + return $this->commit(); + } + + /** + * {@inheritdoc} + */ + public function saveDeferred(CacheItemInterface $item) + { + if (!$item instanceof CacheItem) { + return false; + } + $this->deferred[$item->getKey()] = $item; + + return true; + } + + /** + * {@inheritdoc} + */ + public function commit() + { + $ok = true; + $byLifetime = $this->mergeByLifetime; + $byLifetime = $byLifetime($this->deferred, $this->namespace, $expiredIds); + $retry = $this->deferred = []; + + if ($expiredIds) { + $this->doDelete($expiredIds); + } + foreach ($byLifetime as $lifetime => $values) { + try { + $e = $this->doSave($values, $lifetime); + } catch (\Exception $e) { + } + if (true === $e || [] === $e) { + continue; + } + if (\is_array($e) || 1 === \count($values)) { + foreach (\is_array($e) ? $e : array_keys($values) as $id) { + $ok = false; + $v = $values[$id]; + $type = \is_object($v) ? \get_class($v) : \gettype($v); + CacheItem::log($this->logger, 'Failed to save key "{key}" ({type})', ['key' => substr($id, \strlen($this->namespace)), 'type' => $type, 'exception' => $e instanceof \Exception ? $e : null]); + } + } else { + foreach ($values as $id => $v) { + $retry[$lifetime][] = $id; + } + } + } + + // When bulk-save failed, retry each item individually + foreach ($retry as $lifetime => $ids) { + foreach ($ids as $id) { + try { + $v = $byLifetime[$lifetime][$id]; + $e = $this->doSave([$id => $v], $lifetime); + } catch (\Exception $e) { + } + if (true === $e || [] === $e) { + continue; + } + $ok = false; + $type = \is_object($v) ? \get_class($v) : \gettype($v); + CacheItem::log($this->logger, 'Failed to save key "{key}" ({type})', ['key' => substr($id, \strlen($this->namespace)), 'type' => $type, 'exception' => $e instanceof \Exception ? $e : null]); + } + } + + return $ok; + } + + public function __destruct() + { + if ($this->deferred) { + $this->commit(); + } + } + + private function generateItems($items, &$keys) + { + $f = $this->createCacheItem; + + try { + foreach ($items as $id => $value) { + if (!isset($keys[$id])) { + $id = key($keys); + } + $key = $keys[$id]; + unset($keys[$id]); + yield $key => $f($key, $value, true); + } + } catch (\Exception $e) { + CacheItem::log($this->logger, 'Failed to fetch requested items', ['keys' => array_values($keys), 'exception' => $e]); + } + + foreach ($keys as $key) { + yield $key => $f($key, null, false); + } + } +} diff --git a/lib/symfony/cache/Adapter/AdapterInterface.php b/lib/symfony/cache/Adapter/AdapterInterface.php new file mode 100644 index 000000000..85fe07684 --- /dev/null +++ b/lib/symfony/cache/Adapter/AdapterInterface.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Adapter; + +use Psr\Cache\CacheItemPoolInterface; +use Symfony\Component\Cache\CacheItem; + +/** + * Interface for adapters managing instances of Symfony's CacheItem. + * + * @author Kévin Dunglas + */ +interface AdapterInterface extends CacheItemPoolInterface +{ + /** + * {@inheritdoc} + * + * @return CacheItem + */ + public function getItem($key); + + /** + * {@inheritdoc} + * + * @return \Traversable|CacheItem[] + */ + public function getItems(array $keys = []); +} diff --git a/lib/symfony/cache/Adapter/ApcuAdapter.php b/lib/symfony/cache/Adapter/ApcuAdapter.php new file mode 100644 index 000000000..50554ed68 --- /dev/null +++ b/lib/symfony/cache/Adapter/ApcuAdapter.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Adapter; + +use Symfony\Component\Cache\Traits\ApcuTrait; + +class ApcuAdapter extends AbstractAdapter +{ + use ApcuTrait; + + /** + * @param string $namespace + * @param int $defaultLifetime + * @param string|null $version + * + * @throws CacheException if APCu is not enabled + */ + public function __construct($namespace = '', $defaultLifetime = 0, $version = null) + { + $this->init($namespace, $defaultLifetime, $version); + } +} diff --git a/lib/symfony/cache/Adapter/ArrayAdapter.php b/lib/symfony/cache/Adapter/ArrayAdapter.php new file mode 100644 index 000000000..858a47e9a --- /dev/null +++ b/lib/symfony/cache/Adapter/ArrayAdapter.php @@ -0,0 +1,155 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Adapter; + +use Psr\Cache\CacheItemInterface; +use Psr\Log\LoggerAwareInterface; +use Symfony\Component\Cache\CacheItem; +use Symfony\Component\Cache\ResettableInterface; +use Symfony\Component\Cache\Traits\ArrayTrait; + +/** + * @author Nicolas Grekas + */ +class ArrayAdapter implements AdapterInterface, LoggerAwareInterface, ResettableInterface +{ + use ArrayTrait; + + private $createCacheItem; + + /** + * @param int $defaultLifetime + * @param bool $storeSerialized Disabling serialization can lead to cache corruptions when storing mutable values but increases performance otherwise + */ + public function __construct($defaultLifetime = 0, $storeSerialized = true) + { + $this->storeSerialized = $storeSerialized; + $this->createCacheItem = \Closure::bind( + function ($key, $value, $isHit) use ($defaultLifetime) { + $item = new CacheItem(); + $item->key = $key; + $item->value = $value; + $item->isHit = $isHit; + $item->defaultLifetime = $defaultLifetime; + + return $item; + }, + null, + CacheItem::class + ); + } + + /** + * {@inheritdoc} + */ + public function getItem($key) + { + $isHit = $this->hasItem($key); + try { + if (!$isHit) { + $this->values[$key] = $value = null; + } elseif (!$this->storeSerialized) { + $value = $this->values[$key]; + } elseif ('b:0;' === $value = $this->values[$key]) { + $value = false; + } elseif (false === $value = unserialize($value)) { + $this->values[$key] = $value = null; + $isHit = false; + } + } catch (\Exception $e) { + CacheItem::log($this->logger, 'Failed to unserialize key "{key}"', ['key' => $key, 'exception' => $e]); + $this->values[$key] = $value = null; + $isHit = false; + } + $f = $this->createCacheItem; + + return $f($key, $value, $isHit); + } + + /** + * {@inheritdoc} + */ + public function getItems(array $keys = []) + { + foreach ($keys as $key) { + CacheItem::validateKey($key); + } + + return $this->generateItems($keys, time(), $this->createCacheItem); + } + + /** + * {@inheritdoc} + */ + public function deleteItems(array $keys) + { + foreach ($keys as $key) { + $this->deleteItem($key); + } + + return true; + } + + /** + * {@inheritdoc} + */ + public function save(CacheItemInterface $item) + { + if (!$item instanceof CacheItem) { + return false; + } + $item = (array) $item; + $key = $item["\0*\0key"]; + $value = $item["\0*\0value"]; + $expiry = $item["\0*\0expiry"]; + + if (null !== $expiry && $expiry <= time()) { + $this->deleteItem($key); + + return true; + } + if ($this->storeSerialized) { + try { + $value = serialize($value); + } catch (\Exception $e) { + $type = \is_object($value) ? \get_class($value) : \gettype($value); + CacheItem::log($this->logger, 'Failed to save key "{key}" ({type})', ['key' => $key, 'type' => $type, 'exception' => $e]); + + return false; + } + } + if (null === $expiry && 0 < $item["\0*\0defaultLifetime"]) { + $expiry = time() + $item["\0*\0defaultLifetime"]; + } + + $this->values[$key] = $value; + $this->expiries[$key] = null !== $expiry ? $expiry : PHP_INT_MAX; + + return true; + } + + /** + * {@inheritdoc} + */ + public function saveDeferred(CacheItemInterface $item) + { + return $this->save($item); + } + + /** + * {@inheritdoc} + */ + public function commit() + { + return true; + } +} diff --git a/lib/symfony/cache/Adapter/ChainAdapter.php b/lib/symfony/cache/Adapter/ChainAdapter.php new file mode 100644 index 000000000..1f4d319e4 --- /dev/null +++ b/lib/symfony/cache/Adapter/ChainAdapter.php @@ -0,0 +1,273 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Adapter; + +use Psr\Cache\CacheItemInterface; +use Psr\Cache\CacheItemPoolInterface; +use Symfony\Component\Cache\CacheItem; +use Symfony\Component\Cache\Exception\InvalidArgumentException; +use Symfony\Component\Cache\PruneableInterface; +use Symfony\Component\Cache\ResettableInterface; + +/** + * Chains several adapters together. + * + * Cached items are fetched from the first adapter having them in its data store. + * They are saved and deleted in all adapters at once. + * + * @author Kévin Dunglas + */ +class ChainAdapter implements AdapterInterface, PruneableInterface, ResettableInterface +{ + private $adapters = []; + private $adapterCount; + private $syncItem; + + /** + * @param CacheItemPoolInterface[] $adapters The ordered list of adapters used to fetch cached items + * @param int $defaultLifetime The default lifetime of items propagated from lower adapters to upper ones + */ + public function __construct(array $adapters, $defaultLifetime = 0) + { + if (!$adapters) { + throw new InvalidArgumentException('At least one adapter must be specified.'); + } + + foreach ($adapters as $adapter) { + if (!$adapter instanceof CacheItemPoolInterface) { + throw new InvalidArgumentException(sprintf('The class "%s" does not implement the "%s" interface.', \get_class($adapter), CacheItemPoolInterface::class)); + } + + if ($adapter instanceof AdapterInterface) { + $this->adapters[] = $adapter; + } else { + $this->adapters[] = new ProxyAdapter($adapter); + } + } + $this->adapterCount = \count($this->adapters); + + $this->syncItem = \Closure::bind( + function ($sourceItem, $item) use ($defaultLifetime) { + $item->value = $sourceItem->value; + $item->expiry = $sourceItem->expiry; + $item->isHit = $sourceItem->isHit; + + if (0 < $sourceItem->defaultLifetime && $sourceItem->defaultLifetime < $defaultLifetime) { + $defaultLifetime = $sourceItem->defaultLifetime; + } + if (0 < $defaultLifetime && ($item->defaultLifetime <= 0 || $defaultLifetime < $item->defaultLifetime)) { + $item->defaultLifetime = $defaultLifetime; + } + + return $item; + }, + null, + CacheItem::class + ); + } + + /** + * {@inheritdoc} + */ + public function getItem($key) + { + $syncItem = $this->syncItem; + $misses = []; + + foreach ($this->adapters as $i => $adapter) { + $item = $adapter->getItem($key); + + if ($item->isHit()) { + while (0 <= --$i) { + $this->adapters[$i]->save($syncItem($item, $misses[$i])); + } + + return $item; + } + + $misses[$i] = $item; + } + + return $item; + } + + /** + * {@inheritdoc} + */ + public function getItems(array $keys = []) + { + return $this->generateItems($this->adapters[0]->getItems($keys), 0); + } + + private function generateItems($items, $adapterIndex) + { + $missing = []; + $misses = []; + $nextAdapterIndex = $adapterIndex + 1; + $nextAdapter = isset($this->adapters[$nextAdapterIndex]) ? $this->adapters[$nextAdapterIndex] : null; + + foreach ($items as $k => $item) { + if (!$nextAdapter || $item->isHit()) { + yield $k => $item; + } else { + $missing[] = $k; + $misses[$k] = $item; + } + } + + if ($missing) { + $syncItem = $this->syncItem; + $adapter = $this->adapters[$adapterIndex]; + $items = $this->generateItems($nextAdapter->getItems($missing), $nextAdapterIndex); + + foreach ($items as $k => $item) { + if ($item->isHit()) { + $adapter->save($syncItem($item, $misses[$k])); + } + + yield $k => $item; + } + } + } + + /** + * {@inheritdoc} + */ + public function hasItem($key) + { + foreach ($this->adapters as $adapter) { + if ($adapter->hasItem($key)) { + return true; + } + } + + return false; + } + + /** + * {@inheritdoc} + */ + public function clear() + { + $cleared = true; + $i = $this->adapterCount; + + while ($i--) { + $cleared = $this->adapters[$i]->clear() && $cleared; + } + + return $cleared; + } + + /** + * {@inheritdoc} + */ + public function deleteItem($key) + { + $deleted = true; + $i = $this->adapterCount; + + while ($i--) { + $deleted = $this->adapters[$i]->deleteItem($key) && $deleted; + } + + return $deleted; + } + + /** + * {@inheritdoc} + */ + public function deleteItems(array $keys) + { + $deleted = true; + $i = $this->adapterCount; + + while ($i--) { + $deleted = $this->adapters[$i]->deleteItems($keys) && $deleted; + } + + return $deleted; + } + + /** + * {@inheritdoc} + */ + public function save(CacheItemInterface $item) + { + $saved = true; + $i = $this->adapterCount; + + while ($i--) { + $saved = $this->adapters[$i]->save($item) && $saved; + } + + return $saved; + } + + /** + * {@inheritdoc} + */ + public function saveDeferred(CacheItemInterface $item) + { + $saved = true; + $i = $this->adapterCount; + + while ($i--) { + $saved = $this->adapters[$i]->saveDeferred($item) && $saved; + } + + return $saved; + } + + /** + * {@inheritdoc} + */ + public function commit() + { + $committed = true; + $i = $this->adapterCount; + + while ($i--) { + $committed = $this->adapters[$i]->commit() && $committed; + } + + return $committed; + } + + /** + * {@inheritdoc} + */ + public function prune() + { + $pruned = true; + + foreach ($this->adapters as $adapter) { + if ($adapter instanceof PruneableInterface) { + $pruned = $adapter->prune() && $pruned; + } + } + + return $pruned; + } + + /** + * {@inheritdoc} + */ + public function reset() + { + foreach ($this->adapters as $adapter) { + if ($adapter instanceof ResettableInterface) { + $adapter->reset(); + } + } + } +} diff --git a/lib/symfony/cache/Adapter/DoctrineAdapter.php b/lib/symfony/cache/Adapter/DoctrineAdapter.php new file mode 100644 index 000000000..972d2b415 --- /dev/null +++ b/lib/symfony/cache/Adapter/DoctrineAdapter.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Adapter; + +use Doctrine\Common\Cache\CacheProvider; +use Symfony\Component\Cache\Traits\DoctrineTrait; + +class DoctrineAdapter extends AbstractAdapter +{ + use DoctrineTrait; + + /** + * @param CacheProvider $provider + * @param string $namespace + * @param int $defaultLifetime + */ + public function __construct(CacheProvider $provider, $namespace = '', $defaultLifetime = 0) + { + parent::__construct('', $defaultLifetime); + $this->provider = $provider; + $provider->setNamespace($namespace); + } +} diff --git a/lib/symfony/cache/Adapter/FilesystemAdapter.php b/lib/symfony/cache/Adapter/FilesystemAdapter.php new file mode 100644 index 000000000..d071964ec --- /dev/null +++ b/lib/symfony/cache/Adapter/FilesystemAdapter.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Adapter; + +use Symfony\Component\Cache\PruneableInterface; +use Symfony\Component\Cache\Traits\FilesystemTrait; + +class FilesystemAdapter extends AbstractAdapter implements PruneableInterface +{ + use FilesystemTrait; + + /** + * @param string $namespace + * @param int $defaultLifetime + * @param string|null $directory + */ + public function __construct($namespace = '', $defaultLifetime = 0, $directory = null) + { + parent::__construct('', $defaultLifetime); + $this->init($namespace, $directory); + } +} diff --git a/lib/symfony/cache/Adapter/MemcachedAdapter.php b/lib/symfony/cache/Adapter/MemcachedAdapter.php new file mode 100644 index 000000000..5637141a7 --- /dev/null +++ b/lib/symfony/cache/Adapter/MemcachedAdapter.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Adapter; + +use Symfony\Component\Cache\Traits\MemcachedTrait; + +class MemcachedAdapter extends AbstractAdapter +{ + use MemcachedTrait; + + protected $maxIdLength = 250; + + /** + * Using a MemcachedAdapter with a TagAwareAdapter for storing tags is discouraged. + * Using a RedisAdapter is recommended instead. If you cannot do otherwise, be aware that: + * - the Memcached::OPT_BINARY_PROTOCOL must be enabled + * (that's the default when using MemcachedAdapter::createConnection()); + * - tags eviction by Memcached's LRU algorithm will break by-tags invalidation; + * your Memcached memory should be large enough to never trigger LRU. + * + * Using a MemcachedAdapter as a pure items store is fine. + */ + public function __construct(\Memcached $client, $namespace = '', $defaultLifetime = 0) + { + $this->init($client, $namespace, $defaultLifetime); + } +} diff --git a/lib/symfony/cache/Adapter/NullAdapter.php b/lib/symfony/cache/Adapter/NullAdapter.php new file mode 100644 index 000000000..c81a1cd64 --- /dev/null +++ b/lib/symfony/cache/Adapter/NullAdapter.php @@ -0,0 +1,121 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Adapter; + +use Psr\Cache\CacheItemInterface; +use Symfony\Component\Cache\CacheItem; + +/** + * @author Titouan Galopin + */ +class NullAdapter implements AdapterInterface +{ + private $createCacheItem; + + public function __construct() + { + $this->createCacheItem = \Closure::bind( + function ($key) { + $item = new CacheItem(); + $item->key = $key; + $item->isHit = false; + + return $item; + }, + $this, + CacheItem::class + ); + } + + /** + * {@inheritdoc} + */ + public function getItem($key) + { + $f = $this->createCacheItem; + + return $f($key); + } + + /** + * {@inheritdoc} + */ + public function getItems(array $keys = []) + { + return $this->generateItems($keys); + } + + /** + * {@inheritdoc} + */ + public function hasItem($key) + { + return false; + } + + /** + * {@inheritdoc} + */ + public function clear() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function deleteItem($key) + { + return true; + } + + /** + * {@inheritdoc} + */ + public function deleteItems(array $keys) + { + return true; + } + + /** + * {@inheritdoc} + */ + public function save(CacheItemInterface $item) + { + return false; + } + + /** + * {@inheritdoc} + */ + public function saveDeferred(CacheItemInterface $item) + { + return false; + } + + /** + * {@inheritdoc} + */ + public function commit() + { + return false; + } + + private function generateItems(array $keys) + { + $f = $this->createCacheItem; + + foreach ($keys as $key) { + yield $key => $f($key); + } + } +} diff --git a/lib/symfony/cache/Adapter/PdoAdapter.php b/lib/symfony/cache/Adapter/PdoAdapter.php new file mode 100644 index 000000000..590386794 --- /dev/null +++ b/lib/symfony/cache/Adapter/PdoAdapter.php @@ -0,0 +1,53 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Adapter; + +use Doctrine\DBAL\Connection; +use Symfony\Component\Cache\Exception\InvalidArgumentException; +use Symfony\Component\Cache\PruneableInterface; +use Symfony\Component\Cache\Traits\PdoTrait; + +class PdoAdapter extends AbstractAdapter implements PruneableInterface +{ + use PdoTrait; + + protected $maxIdLength = 255; + + /** + * You can either pass an existing database connection as PDO instance or + * a Doctrine DBAL Connection or a DSN string that will be used to + * lazy-connect to the database when the cache is actually used. + * + * List of available options: + * * db_table: The name of the table [default: cache_items] + * * db_id_col: The column where to store the cache id [default: item_id] + * * db_data_col: The column where to store the cache data [default: item_data] + * * db_lifetime_col: The column where to store the lifetime [default: item_lifetime] + * * db_time_col: The column where to store the timestamp [default: item_time] + * * db_username: The username when lazy-connect [default: ''] + * * db_password: The password when lazy-connect [default: ''] + * * db_connection_options: An array of driver-specific connection options [default: []] + * + * @param \PDO|Connection|string $connOrDsn A \PDO or Connection instance or DSN string or null + * @param string $namespace + * @param int $defaultLifetime + * @param array $options An associative array of options + * + * @throws InvalidArgumentException When first argument is not PDO nor Connection nor string + * @throws InvalidArgumentException When PDO error mode is not PDO::ERRMODE_EXCEPTION + * @throws InvalidArgumentException When namespace contains invalid characters + */ + public function __construct($connOrDsn, $namespace = '', $defaultLifetime = 0, array $options = []) + { + $this->init($connOrDsn, $namespace, $defaultLifetime, $options); + } +} diff --git a/lib/symfony/cache/Adapter/PhpArrayAdapter.php b/lib/symfony/cache/Adapter/PhpArrayAdapter.php new file mode 100644 index 000000000..42a414209 --- /dev/null +++ b/lib/symfony/cache/Adapter/PhpArrayAdapter.php @@ -0,0 +1,303 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Adapter; + +use Psr\Cache\CacheItemInterface; +use Psr\Cache\CacheItemPoolInterface; +use Symfony\Component\Cache\CacheItem; +use Symfony\Component\Cache\Exception\InvalidArgumentException; +use Symfony\Component\Cache\PruneableInterface; +use Symfony\Component\Cache\ResettableInterface; +use Symfony\Component\Cache\Traits\PhpArrayTrait; + +/** + * Caches items at warm up time using a PHP array that is stored in shared memory by OPCache since PHP 7.0. + * Warmed up items are read-only and run-time discovered items are cached using a fallback adapter. + * + * @author Titouan Galopin + * @author Nicolas Grekas + */ +class PhpArrayAdapter implements AdapterInterface, PruneableInterface, ResettableInterface +{ + use PhpArrayTrait; + + private $createCacheItem; + + /** + * @param string $file The PHP file were values are cached + * @param AdapterInterface $fallbackPool A pool to fallback on when an item is not hit + */ + public function __construct($file, AdapterInterface $fallbackPool) + { + $this->file = $file; + $this->pool = $fallbackPool; + $this->zendDetectUnicode = filter_var(ini_get('zend.detect_unicode'), FILTER_VALIDATE_BOOLEAN); + $this->createCacheItem = \Closure::bind( + function ($key, $value, $isHit) { + $item = new CacheItem(); + $item->key = $key; + $item->value = $value; + $item->isHit = $isHit; + + return $item; + }, + null, + CacheItem::class + ); + } + + /** + * This adapter should only be used on PHP 7.0+ to take advantage of how PHP + * stores arrays in its latest versions. This factory method decorates the given + * fallback pool with this adapter only if the current PHP version is supported. + * + * @param string $file The PHP file were values are cached + * @param CacheItemPoolInterface $fallbackPool Fallback for old PHP versions or opcache disabled + * + * @return CacheItemPoolInterface + */ + public static function create($file, CacheItemPoolInterface $fallbackPool) + { + // Shared memory is available in PHP 7.0+ with OPCache enabled and in HHVM + if ((\PHP_VERSION_ID >= 70000 && filter_var(ini_get('opcache.enable'), FILTER_VALIDATE_BOOLEAN)) || \defined('HHVM_VERSION')) { + if (!$fallbackPool instanceof AdapterInterface) { + $fallbackPool = new ProxyAdapter($fallbackPool); + } + + return new static($file, $fallbackPool); + } + + return $fallbackPool; + } + + /** + * {@inheritdoc} + */ + public function getItem($key) + { + if (!\is_string($key)) { + throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given.', \is_object($key) ? \get_class($key) : \gettype($key))); + } + if (null === $this->values) { + $this->initialize(); + } + if (!isset($this->values[$key])) { + return $this->pool->getItem($key); + } + + $value = $this->values[$key]; + $isHit = true; + + if ('N;' === $value) { + $value = null; + } elseif (\is_string($value) && isset($value[2]) && ':' === $value[1]) { + try { + $e = null; + $value = unserialize($value); + } catch (\Error $e) { + } catch (\Exception $e) { + } + if (null !== $e) { + $value = null; + $isHit = false; + } + } + + $f = $this->createCacheItem; + + return $f($key, $value, $isHit); + } + + /** + * {@inheritdoc} + */ + public function getItems(array $keys = []) + { + foreach ($keys as $key) { + if (!\is_string($key)) { + throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given.', \is_object($key) ? \get_class($key) : \gettype($key))); + } + } + if (null === $this->values) { + $this->initialize(); + } + + return $this->generateItems($keys); + } + + /** + * {@inheritdoc} + */ + public function hasItem($key) + { + if (!\is_string($key)) { + throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given.', \is_object($key) ? \get_class($key) : \gettype($key))); + } + if (null === $this->values) { + $this->initialize(); + } + + return isset($this->values[$key]) || $this->pool->hasItem($key); + } + + /** + * {@inheritdoc} + */ + public function deleteItem($key) + { + if (!\is_string($key)) { + throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given.', \is_object($key) ? \get_class($key) : \gettype($key))); + } + if (null === $this->values) { + $this->initialize(); + } + + return !isset($this->values[$key]) && $this->pool->deleteItem($key); + } + + /** + * {@inheritdoc} + */ + public function deleteItems(array $keys) + { + $deleted = true; + $fallbackKeys = []; + + foreach ($keys as $key) { + if (!\is_string($key)) { + throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given.', \is_object($key) ? \get_class($key) : \gettype($key))); + } + + if (isset($this->values[$key])) { + $deleted = false; + } else { + $fallbackKeys[] = $key; + } + } + if (null === $this->values) { + $this->initialize(); + } + + if ($fallbackKeys) { + $deleted = $this->pool->deleteItems($fallbackKeys) && $deleted; + } + + return $deleted; + } + + /** + * {@inheritdoc} + */ + public function save(CacheItemInterface $item) + { + if (null === $this->values) { + $this->initialize(); + } + + return !isset($this->values[$item->getKey()]) && $this->pool->save($item); + } + + /** + * {@inheritdoc} + */ + public function saveDeferred(CacheItemInterface $item) + { + if (null === $this->values) { + $this->initialize(); + } + + return !isset($this->values[$item->getKey()]) && $this->pool->saveDeferred($item); + } + + /** + * {@inheritdoc} + */ + public function commit() + { + return $this->pool->commit(); + } + + /** + * @return \Generator + */ + private function generateItems(array $keys) + { + $f = $this->createCacheItem; + $fallbackKeys = []; + + foreach ($keys as $key) { + if (isset($this->values[$key])) { + $value = $this->values[$key]; + + if ('N;' === $value) { + yield $key => $f($key, null, true); + } elseif (\is_string($value) && isset($value[2]) && ':' === $value[1]) { + try { + yield $key => $f($key, unserialize($value), true); + } catch (\Error $e) { + yield $key => $f($key, null, false); + } catch (\Exception $e) { + yield $key => $f($key, null, false); + } + } else { + yield $key => $f($key, $value, true); + } + } else { + $fallbackKeys[] = $key; + } + } + + if ($fallbackKeys) { + foreach ($this->pool->getItems($fallbackKeys) as $key => $item) { + yield $key => $item; + } + } + } + + /** + * @throws \ReflectionException When $class is not found and is required + * + * @internal + */ + public static function throwOnRequiredClass($class) + { + $e = new \ReflectionException("Class $class does not exist"); + $trace = $e->getTrace(); + $autoloadFrame = [ + 'function' => 'spl_autoload_call', + 'args' => [$class], + ]; + $i = 1 + array_search($autoloadFrame, $trace, true); + + if (isset($trace[$i]['function']) && !isset($trace[$i]['class'])) { + switch ($trace[$i]['function']) { + case 'get_class_methods': + case 'get_class_vars': + case 'get_parent_class': + case 'is_a': + case 'is_subclass_of': + case 'class_exists': + case 'class_implements': + case 'class_parents': + case 'trait_exists': + case 'defined': + case 'interface_exists': + case 'method_exists': + case 'property_exists': + case 'is_callable': + return; + } + } + + throw $e; + } +} diff --git a/lib/symfony/cache/Adapter/PhpFilesAdapter.php b/lib/symfony/cache/Adapter/PhpFilesAdapter.php new file mode 100644 index 000000000..9ab870534 --- /dev/null +++ b/lib/symfony/cache/Adapter/PhpFilesAdapter.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Adapter; + +use Symfony\Component\Cache\Exception\CacheException; +use Symfony\Component\Cache\PruneableInterface; +use Symfony\Component\Cache\Traits\PhpFilesTrait; + +class PhpFilesAdapter extends AbstractAdapter implements PruneableInterface +{ + use PhpFilesTrait; + + /** + * @param string $namespace + * @param int $defaultLifetime + * @param string|null $directory + * + * @throws CacheException if OPcache is not enabled + */ + public function __construct($namespace = '', $defaultLifetime = 0, $directory = null) + { + if (!static::isSupported()) { + throw new CacheException('OPcache is not enabled'); + } + parent::__construct('', $defaultLifetime); + $this->init($namespace, $directory); + + $e = new \Exception(); + $this->includeHandler = function () use ($e) { throw $e; }; + $this->zendDetectUnicode = filter_var(ini_get('zend.detect_unicode'), FILTER_VALIDATE_BOOLEAN); + } +} diff --git a/lib/symfony/cache/Adapter/ProxyAdapter.php b/lib/symfony/cache/Adapter/ProxyAdapter.php new file mode 100644 index 000000000..0b7918287 --- /dev/null +++ b/lib/symfony/cache/Adapter/ProxyAdapter.php @@ -0,0 +1,199 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Adapter; + +use Psr\Cache\CacheItemInterface; +use Psr\Cache\CacheItemPoolInterface; +use Symfony\Component\Cache\CacheItem; +use Symfony\Component\Cache\PruneableInterface; +use Symfony\Component\Cache\ResettableInterface; +use Symfony\Component\Cache\Traits\ProxyTrait; + +/** + * @author Nicolas Grekas + */ +class ProxyAdapter implements AdapterInterface, PruneableInterface, ResettableInterface +{ + use ProxyTrait; + + private $namespace; + private $namespaceLen; + private $createCacheItem; + private $poolHash; + + /** + * @param CacheItemPoolInterface $pool + * @param string $namespace + * @param int $defaultLifetime + */ + public function __construct(CacheItemPoolInterface $pool, $namespace = '', $defaultLifetime = 0) + { + $this->pool = $pool; + $this->poolHash = $poolHash = spl_object_hash($pool); + $this->namespace = '' === $namespace ? '' : CacheItem::validateKey($namespace); + $this->namespaceLen = \strlen($namespace); + $this->createCacheItem = \Closure::bind( + function ($key, $innerItem) use ($defaultLifetime, $poolHash) { + $item = new CacheItem(); + $item->key = $key; + $item->defaultLifetime = $defaultLifetime; + $item->poolHash = $poolHash; + + if (null !== $innerItem) { + $item->value = $innerItem->get(); + $item->isHit = $innerItem->isHit(); + $item->innerItem = $innerItem; + $innerItem->set(null); + } + + return $item; + }, + null, + CacheItem::class + ); + } + + /** + * {@inheritdoc} + */ + public function getItem($key) + { + $f = $this->createCacheItem; + $item = $this->pool->getItem($this->getId($key)); + + return $f($key, $item); + } + + /** + * {@inheritdoc} + */ + public function getItems(array $keys = []) + { + if ($this->namespaceLen) { + foreach ($keys as $i => $key) { + $keys[$i] = $this->getId($key); + } + } + + return $this->generateItems($this->pool->getItems($keys)); + } + + /** + * {@inheritdoc} + */ + public function hasItem($key) + { + return $this->pool->hasItem($this->getId($key)); + } + + /** + * {@inheritdoc} + */ + public function clear() + { + return $this->pool->clear(); + } + + /** + * {@inheritdoc} + */ + public function deleteItem($key) + { + return $this->pool->deleteItem($this->getId($key)); + } + + /** + * {@inheritdoc} + */ + public function deleteItems(array $keys) + { + if ($this->namespaceLen) { + foreach ($keys as $i => $key) { + $keys[$i] = $this->getId($key); + } + } + + return $this->pool->deleteItems($keys); + } + + /** + * {@inheritdoc} + */ + public function save(CacheItemInterface $item) + { + return $this->doSave($item, __FUNCTION__); + } + + /** + * {@inheritdoc} + */ + public function saveDeferred(CacheItemInterface $item) + { + return $this->doSave($item, __FUNCTION__); + } + + /** + * {@inheritdoc} + */ + public function commit() + { + return $this->pool->commit(); + } + + private function doSave(CacheItemInterface $item, $method) + { + if (!$item instanceof CacheItem) { + return false; + } + $item = (array) $item; + $expiry = $item["\0*\0expiry"]; + if (null === $expiry && 0 < $item["\0*\0defaultLifetime"]) { + $expiry = time() + $item["\0*\0defaultLifetime"]; + } + + if ($item["\0*\0poolHash"] === $this->poolHash && $item["\0*\0innerItem"]) { + $innerItem = $item["\0*\0innerItem"]; + } elseif ($this->pool instanceof AdapterInterface) { + // this is an optimization specific for AdapterInterface implementations + // so we can save a round-trip to the backend by just creating a new item + $f = $this->createCacheItem; + $innerItem = $f($this->namespace.$item["\0*\0key"], null); + } else { + $innerItem = $this->pool->getItem($this->namespace.$item["\0*\0key"]); + } + + $innerItem->set($item["\0*\0value"]); + $innerItem->expiresAt(null !== $expiry ? \DateTime::createFromFormat('U', $expiry) : null); + + return $this->pool->$method($innerItem); + } + + private function generateItems($items) + { + $f = $this->createCacheItem; + + foreach ($items as $key => $item) { + if ($this->namespaceLen) { + $key = substr($key, $this->namespaceLen); + } + + yield $key => $f($key, $item); + } + } + + private function getId($key) + { + CacheItem::validateKey($key); + + return $this->namespace.$key; + } +} diff --git a/lib/symfony/cache/Adapter/RedisAdapter.php b/lib/symfony/cache/Adapter/RedisAdapter.php new file mode 100644 index 000000000..c1e17997f --- /dev/null +++ b/lib/symfony/cache/Adapter/RedisAdapter.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Adapter; + +use Symfony\Component\Cache\Traits\RedisTrait; + +class RedisAdapter extends AbstractAdapter +{ + use RedisTrait; + + /** + * @param \Redis|\RedisArray|\RedisCluster|\Predis\Client $redisClient The redis client + * @param string $namespace The default namespace + * @param int $defaultLifetime The default lifetime + */ + public function __construct($redisClient, $namespace = '', $defaultLifetime = 0) + { + $this->init($redisClient, $namespace, $defaultLifetime); + } +} diff --git a/lib/symfony/cache/Adapter/SimpleCacheAdapter.php b/lib/symfony/cache/Adapter/SimpleCacheAdapter.php new file mode 100644 index 000000000..d3d0ede64 --- /dev/null +++ b/lib/symfony/cache/Adapter/SimpleCacheAdapter.php @@ -0,0 +1,83 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Adapter; + +use Psr\SimpleCache\CacheInterface; +use Symfony\Component\Cache\PruneableInterface; +use Symfony\Component\Cache\Traits\ProxyTrait; + +/** + * @author Nicolas Grekas + */ +class SimpleCacheAdapter extends AbstractAdapter implements PruneableInterface +{ + /** + * @internal + */ + const NS_SEPARATOR = '_'; + + use ProxyTrait; + + private $miss; + + public function __construct(CacheInterface $pool, $namespace = '', $defaultLifetime = 0) + { + parent::__construct($namespace, $defaultLifetime); + + $this->pool = $pool; + $this->miss = new \stdClass(); + } + + /** + * {@inheritdoc} + */ + protected function doFetch(array $ids) + { + foreach ($this->pool->getMultiple($ids, $this->miss) as $key => $value) { + if ($this->miss !== $value) { + yield $key => $value; + } + } + } + + /** + * {@inheritdoc} + */ + protected function doHave($id) + { + return $this->pool->has($id); + } + + /** + * {@inheritdoc} + */ + protected function doClear($namespace) + { + return $this->pool->clear(); + } + + /** + * {@inheritdoc} + */ + protected function doDelete(array $ids) + { + return $this->pool->deleteMultiple($ids); + } + + /** + * {@inheritdoc} + */ + protected function doSave(array $values, $lifetime) + { + return $this->pool->setMultiple($values, 0 === $lifetime ? null : $lifetime); + } +} diff --git a/lib/symfony/cache/Adapter/TagAwareAdapter.php b/lib/symfony/cache/Adapter/TagAwareAdapter.php new file mode 100644 index 000000000..362aceed0 --- /dev/null +++ b/lib/symfony/cache/Adapter/TagAwareAdapter.php @@ -0,0 +1,373 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Adapter; + +use Psr\Cache\CacheItemInterface; +use Psr\Cache\InvalidArgumentException; +use Symfony\Component\Cache\CacheItem; +use Symfony\Component\Cache\PruneableInterface; +use Symfony\Component\Cache\ResettableInterface; +use Symfony\Component\Cache\Traits\ProxyTrait; + +/** + * @author Nicolas Grekas + */ +class TagAwareAdapter implements TagAwareAdapterInterface, PruneableInterface, ResettableInterface +{ + const TAGS_PREFIX = "\0tags\0"; + + use ProxyTrait; + + private $deferred = []; + private $createCacheItem; + private $setCacheItemTags; + private $getTagsByKey; + private $invalidateTags; + private $tags; + private $knownTagVersions = []; + private $knownTagVersionsTtl; + + public function __construct(AdapterInterface $itemsPool, AdapterInterface $tagsPool = null, $knownTagVersionsTtl = 0.15) + { + $this->pool = $itemsPool; + $this->tags = $tagsPool ?: $itemsPool; + $this->knownTagVersionsTtl = $knownTagVersionsTtl; + $this->createCacheItem = \Closure::bind( + function ($key, $value, CacheItem $protoItem) { + $item = new CacheItem(); + $item->key = $key; + $item->value = $value; + $item->defaultLifetime = $protoItem->defaultLifetime; + $item->expiry = $protoItem->expiry; + $item->poolHash = $protoItem->poolHash; + + return $item; + }, + null, + CacheItem::class + ); + $this->setCacheItemTags = \Closure::bind( + function (CacheItem $item, $key, array &$itemTags) { + if (!$item->isHit) { + return $item; + } + if (isset($itemTags[$key])) { + foreach ($itemTags[$key] as $tag => $version) { + $item->prevTags[$tag] = $tag; + } + unset($itemTags[$key]); + } else { + $item->value = null; + $item->isHit = false; + } + + return $item; + }, + null, + CacheItem::class + ); + $this->getTagsByKey = \Closure::bind( + function ($deferred) { + $tagsByKey = []; + foreach ($deferred as $key => $item) { + $tagsByKey[$key] = $item->tags; + } + + return $tagsByKey; + }, + null, + CacheItem::class + ); + $this->invalidateTags = \Closure::bind( + function (AdapterInterface $tagsAdapter, array $tags) { + foreach ($tags as $v) { + $v->defaultLifetime = 0; + $v->expiry = null; + $tagsAdapter->saveDeferred($v); + } + + return $tagsAdapter->commit(); + }, + null, + CacheItem::class + ); + } + + /** + * {@inheritdoc} + */ + public function invalidateTags(array $tags) + { + $ok = true; + $tagsByKey = []; + $invalidatedTags = []; + foreach ($tags as $tag) { + CacheItem::validateKey($tag); + $invalidatedTags[$tag] = 0; + } + + if ($this->deferred) { + $items = $this->deferred; + foreach ($items as $key => $item) { + if (!$this->pool->saveDeferred($item)) { + unset($this->deferred[$key]); + $ok = false; + } + } + + $f = $this->getTagsByKey; + $tagsByKey = $f($items); + $this->deferred = []; + } + + $tagVersions = $this->getTagVersions($tagsByKey, $invalidatedTags); + $f = $this->createCacheItem; + + foreach ($tagsByKey as $key => $tags) { + $this->pool->saveDeferred($f(static::TAGS_PREFIX.$key, array_intersect_key($tagVersions, $tags), $items[$key])); + } + $ok = $this->pool->commit() && $ok; + + if ($invalidatedTags) { + $f = $this->invalidateTags; + $ok = $f($this->tags, $invalidatedTags) && $ok; + } + + return $ok; + } + + /** + * {@inheritdoc} + */ + public function hasItem($key) + { + if ($this->deferred) { + $this->commit(); + } + if (!$this->pool->hasItem($key)) { + return false; + } + if (!$itemTags = $this->pool->getItem(static::TAGS_PREFIX.$key)->get()) { + return true; + } + + foreach ($this->getTagVersions([$itemTags]) as $tag => $version) { + if ($itemTags[$tag] !== $version && 1 !== $itemTags[$tag] - $version) { + return false; + } + } + + return true; + } + + /** + * {@inheritdoc} + */ + public function getItem($key) + { + foreach ($this->getItems([$key]) as $item) { + return $item; + } + } + + /** + * {@inheritdoc} + */ + public function getItems(array $keys = []) + { + if ($this->deferred) { + $this->commit(); + } + $tagKeys = []; + + foreach ($keys as $key) { + if ('' !== $key && \is_string($key)) { + $key = static::TAGS_PREFIX.$key; + $tagKeys[$key] = $key; + } + } + + try { + $items = $this->pool->getItems($tagKeys + $keys); + } catch (InvalidArgumentException $e) { + $this->pool->getItems($keys); // Should throw an exception + + throw $e; + } + + return $this->generateItems($items, $tagKeys); + } + + /** + * {@inheritdoc} + */ + public function clear() + { + $this->deferred = []; + + return $this->pool->clear(); + } + + /** + * {@inheritdoc} + */ + public function deleteItem($key) + { + return $this->deleteItems([$key]); + } + + /** + * {@inheritdoc} + */ + public function deleteItems(array $keys) + { + foreach ($keys as $key) { + if ('' !== $key && \is_string($key)) { + $keys[] = static::TAGS_PREFIX.$key; + } + } + + return $this->pool->deleteItems($keys); + } + + /** + * {@inheritdoc} + */ + public function save(CacheItemInterface $item) + { + if (!$item instanceof CacheItem) { + return false; + } + $this->deferred[$item->getKey()] = $item; + + return $this->commit(); + } + + /** + * {@inheritdoc} + */ + public function saveDeferred(CacheItemInterface $item) + { + if (!$item instanceof CacheItem) { + return false; + } + $this->deferred[$item->getKey()] = $item; + + return true; + } + + /** + * {@inheritdoc} + */ + public function commit() + { + return $this->invalidateTags([]); + } + + public function __destruct() + { + $this->commit(); + } + + private function generateItems($items, array $tagKeys) + { + $bufferedItems = $itemTags = []; + $f = $this->setCacheItemTags; + + foreach ($items as $key => $item) { + if (!$tagKeys) { + yield $key => $f($item, static::TAGS_PREFIX.$key, $itemTags); + continue; + } + if (!isset($tagKeys[$key])) { + $bufferedItems[$key] = $item; + continue; + } + + unset($tagKeys[$key]); + $itemTags[$key] = $item->get() ?: []; + + if (!$tagKeys) { + $tagVersions = $this->getTagVersions($itemTags); + + foreach ($itemTags as $key => $tags) { + foreach ($tags as $tag => $version) { + if ($tagVersions[$tag] !== $version && 1 !== $version - $tagVersions[$tag]) { + unset($itemTags[$key]); + continue 2; + } + } + } + $tagVersions = $tagKeys = null; + + foreach ($bufferedItems as $key => $item) { + yield $key => $f($item, static::TAGS_PREFIX.$key, $itemTags); + } + $bufferedItems = null; + } + } + } + + private function getTagVersions(array $tagsByKey, array &$invalidatedTags = []) + { + $tagVersions = $invalidatedTags; + + foreach ($tagsByKey as $tags) { + $tagVersions += $tags; + } + + if (!$tagVersions) { + return []; + } + + if (!$fetchTagVersions = 1 !== \func_num_args()) { + foreach ($tagsByKey as $tags) { + foreach ($tags as $tag => $version) { + if ($tagVersions[$tag] > $version) { + $tagVersions[$tag] = $version; + } + } + } + } + + $now = microtime(true); + $tags = []; + foreach ($tagVersions as $tag => $version) { + $tags[$tag.static::TAGS_PREFIX] = $tag; + if ($fetchTagVersions || !isset($this->knownTagVersions[$tag])) { + $fetchTagVersions = true; + continue; + } + $version -= $this->knownTagVersions[$tag][1]; + if ((0 !== $version && 1 !== $version) || $this->knownTagVersionsTtl > $now - $this->knownTagVersions[$tag][0]) { + // reuse previously fetched tag versions up to the ttl, unless we are storing items or a potential miss arises + $fetchTagVersions = true; + } else { + $this->knownTagVersions[$tag][1] += $version; + } + } + + if (!$fetchTagVersions) { + return $tagVersions; + } + + foreach ($this->tags->getItems(array_keys($tags)) as $tag => $version) { + $tagVersions[$tag = $tags[$tag]] = $version->get() ?: 0; + if (isset($invalidatedTags[$tag])) { + $invalidatedTags[$tag] = $version->set(++$tagVersions[$tag]); + } + $this->knownTagVersions[$tag] = [$now, $tagVersions[$tag]]; + } + + return $tagVersions; + } +} diff --git a/lib/symfony/cache/Adapter/TagAwareAdapterInterface.php b/lib/symfony/cache/Adapter/TagAwareAdapterInterface.php new file mode 100644 index 000000000..340048c10 --- /dev/null +++ b/lib/symfony/cache/Adapter/TagAwareAdapterInterface.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Adapter; + +use Psr\Cache\InvalidArgumentException; + +/** + * Interface for invalidating cached items using tags. + * + * @author Nicolas Grekas + */ +interface TagAwareAdapterInterface extends AdapterInterface +{ + /** + * Invalidates cached items using tags. + * + * @param string[] $tags An array of tags to invalidate + * + * @return bool True on success + * + * @throws InvalidArgumentException When $tags is not valid + */ + public function invalidateTags(array $tags); +} diff --git a/lib/symfony/cache/Adapter/TraceableAdapter.php b/lib/symfony/cache/Adapter/TraceableAdapter.php new file mode 100644 index 000000000..4aed2d718 --- /dev/null +++ b/lib/symfony/cache/Adapter/TraceableAdapter.php @@ -0,0 +1,233 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Adapter; + +use Psr\Cache\CacheItemInterface; +use Symfony\Component\Cache\PruneableInterface; +use Symfony\Component\Cache\ResettableInterface; + +/** + * An adapter that collects data about all cache calls. + * + * @author Aaron Scherer + * @author Tobias Nyholm + * @author Nicolas Grekas + */ +class TraceableAdapter implements AdapterInterface, PruneableInterface, ResettableInterface +{ + protected $pool; + private $calls = []; + + public function __construct(AdapterInterface $pool) + { + $this->pool = $pool; + } + + /** + * {@inheritdoc} + */ + public function getItem($key) + { + $event = $this->start(__FUNCTION__); + try { + $item = $this->pool->getItem($key); + } finally { + $event->end = microtime(true); + } + if ($event->result[$key] = $item->isHit()) { + ++$event->hits; + } else { + ++$event->misses; + } + + return $item; + } + + /** + * {@inheritdoc} + */ + public function hasItem($key) + { + $event = $this->start(__FUNCTION__); + try { + return $event->result[$key] = $this->pool->hasItem($key); + } finally { + $event->end = microtime(true); + } + } + + /** + * {@inheritdoc} + */ + public function deleteItem($key) + { + $event = $this->start(__FUNCTION__); + try { + return $event->result[$key] = $this->pool->deleteItem($key); + } finally { + $event->end = microtime(true); + } + } + + /** + * {@inheritdoc} + */ + public function save(CacheItemInterface $item) + { + $event = $this->start(__FUNCTION__); + try { + return $event->result[$item->getKey()] = $this->pool->save($item); + } finally { + $event->end = microtime(true); + } + } + + /** + * {@inheritdoc} + */ + public function saveDeferred(CacheItemInterface $item) + { + $event = $this->start(__FUNCTION__); + try { + return $event->result[$item->getKey()] = $this->pool->saveDeferred($item); + } finally { + $event->end = microtime(true); + } + } + + /** + * {@inheritdoc} + */ + public function getItems(array $keys = []) + { + $event = $this->start(__FUNCTION__); + try { + $result = $this->pool->getItems($keys); + } finally { + $event->end = microtime(true); + } + $f = function () use ($result, $event) { + $event->result = []; + foreach ($result as $key => $item) { + if ($event->result[$key] = $item->isHit()) { + ++$event->hits; + } else { + ++$event->misses; + } + yield $key => $item; + } + }; + + return $f(); + } + + /** + * {@inheritdoc} + */ + public function clear() + { + $event = $this->start(__FUNCTION__); + try { + return $event->result = $this->pool->clear(); + } finally { + $event->end = microtime(true); + } + } + + /** + * {@inheritdoc} + */ + public function deleteItems(array $keys) + { + $event = $this->start(__FUNCTION__); + $event->result['keys'] = $keys; + try { + return $event->result['result'] = $this->pool->deleteItems($keys); + } finally { + $event->end = microtime(true); + } + } + + /** + * {@inheritdoc} + */ + public function commit() + { + $event = $this->start(__FUNCTION__); + try { + return $event->result = $this->pool->commit(); + } finally { + $event->end = microtime(true); + } + } + + /** + * {@inheritdoc} + */ + public function prune() + { + if (!$this->pool instanceof PruneableInterface) { + return false; + } + $event = $this->start(__FUNCTION__); + try { + return $event->result = $this->pool->prune(); + } finally { + $event->end = microtime(true); + } + } + + /** + * {@inheritdoc} + */ + public function reset() + { + if (!$this->pool instanceof ResettableInterface) { + return; + } + $event = $this->start(__FUNCTION__); + try { + $this->pool->reset(); + } finally { + $event->end = microtime(true); + } + } + + public function getCalls() + { + return $this->calls; + } + + public function clearCalls() + { + $this->calls = []; + } + + protected function start($name) + { + $this->calls[] = $event = new TraceableAdapterEvent(); + $event->name = $name; + $event->start = microtime(true); + + return $event; + } +} + +class TraceableAdapterEvent +{ + public $name; + public $start; + public $end; + public $result; + public $hits = 0; + public $misses = 0; +} diff --git a/lib/symfony/cache/Adapter/TraceableTagAwareAdapter.php b/lib/symfony/cache/Adapter/TraceableTagAwareAdapter.php new file mode 100644 index 000000000..de68955d8 --- /dev/null +++ b/lib/symfony/cache/Adapter/TraceableTagAwareAdapter.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Adapter; + +/** + * @author Robin Chalas + */ +class TraceableTagAwareAdapter extends TraceableAdapter implements TagAwareAdapterInterface +{ + public function __construct(TagAwareAdapterInterface $pool) + { + parent::__construct($pool); + } + + /** + * {@inheritdoc} + */ + public function invalidateTags(array $tags) + { + $event = $this->start(__FUNCTION__); + try { + return $event->result = $this->pool->invalidateTags($tags); + } finally { + $event->end = microtime(true); + } + } +} diff --git a/lib/symfony/cache/CHANGELOG.md b/lib/symfony/cache/CHANGELOG.md new file mode 100644 index 000000000..11c1b9364 --- /dev/null +++ b/lib/symfony/cache/CHANGELOG.md @@ -0,0 +1,36 @@ +CHANGELOG +========= + +3.4.0 +----- + + * added using options from Memcached DSN + * added PruneableInterface so PSR-6 or PSR-16 cache implementations can declare support for manual stale cache pruning + * added prune logic to FilesystemTrait, PhpFilesTrait, PdoTrait, TagAwareAdapter and ChainTrait + * now FilesystemAdapter, PhpFilesAdapter, FilesystemCache, PhpFilesCache, PdoAdapter, PdoCache, ChainAdapter, and + ChainCache implement PruneableInterface and support manual stale cache pruning + +3.3.0 +----- + + * [EXPERIMENTAL] added CacheItem::getPreviousTags() to get bound tags coming from the pool storage if any + * added PSR-16 "Simple Cache" implementations for all existing PSR-6 adapters + * added Psr6Cache and SimpleCacheAdapter for bidirectional interoperability between PSR-6 and PSR-16 + * added MemcachedAdapter (PSR-6) and MemcachedCache (PSR-16) + * added TraceableAdapter (PSR-6) and TraceableCache (PSR-16) + +3.2.0 +----- + + * added TagAwareAdapter for tags-based invalidation + * added PdoAdapter with PDO and Doctrine DBAL support + * added PhpArrayAdapter and PhpFilesAdapter for OPcache-backed shared memory storage (PHP 7+ only) + * added NullAdapter + +3.1.0 +----- + + * added the component with strict PSR-6 implementations + * added ApcuAdapter, ArrayAdapter, FilesystemAdapter and RedisAdapter + * added AbstractAdapter, ChainAdapter and ProxyAdapter + * added DoctrineAdapter and DoctrineProvider for bidirectional interoperability with Doctrine Cache diff --git a/lib/symfony/cache/CacheItem.php b/lib/symfony/cache/CacheItem.php new file mode 100644 index 000000000..4ab5d1a26 --- /dev/null +++ b/lib/symfony/cache/CacheItem.php @@ -0,0 +1,187 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache; + +use Psr\Cache\CacheItemInterface; +use Psr\Log\LoggerInterface; +use Symfony\Component\Cache\Exception\InvalidArgumentException; + +/** + * @author Nicolas Grekas + */ +final class CacheItem implements CacheItemInterface +{ + protected $key; + protected $value; + protected $isHit = false; + protected $expiry; + protected $defaultLifetime; + protected $tags = []; + protected $prevTags = []; + protected $innerItem; + protected $poolHash; + + /** + * {@inheritdoc} + */ + public function getKey() + { + return $this->key; + } + + /** + * {@inheritdoc} + */ + public function get() + { + return $this->value; + } + + /** + * {@inheritdoc} + */ + public function isHit() + { + return $this->isHit; + } + + /** + * {@inheritdoc} + */ + public function set($value) + { + $this->value = $value; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function expiresAt($expiration) + { + if (null === $expiration) { + $this->expiry = $this->defaultLifetime > 0 ? time() + $this->defaultLifetime : null; + } elseif ($expiration instanceof \DateTimeInterface) { + $this->expiry = (int) $expiration->format('U'); + } else { + throw new InvalidArgumentException(sprintf('Expiration date must implement DateTimeInterface or be null, "%s" given', \is_object($expiration) ? \get_class($expiration) : \gettype($expiration))); + } + + return $this; + } + + /** + * {@inheritdoc} + */ + public function expiresAfter($time) + { + if (null === $time) { + $this->expiry = $this->defaultLifetime > 0 ? time() + $this->defaultLifetime : null; + } elseif ($time instanceof \DateInterval) { + $this->expiry = (int) \DateTime::createFromFormat('U', time())->add($time)->format('U'); + } elseif (\is_int($time)) { + $this->expiry = $time + time(); + } else { + throw new InvalidArgumentException(sprintf('Expiration date must be an integer, a DateInterval or null, "%s" given', \is_object($time) ? \get_class($time) : \gettype($time))); + } + + return $this; + } + + /** + * Adds a tag to a cache item. + * + * @param string|string[] $tags A tag or array of tags + * + * @return static + * + * @throws InvalidArgumentException When $tag is not valid + */ + public function tag($tags) + { + if (!\is_array($tags)) { + $tags = [$tags]; + } + foreach ($tags as $tag) { + if (!\is_string($tag)) { + throw new InvalidArgumentException(sprintf('Cache tag must be string, "%s" given', \is_object($tag) ? \get_class($tag) : \gettype($tag))); + } + if (isset($this->tags[$tag])) { + continue; + } + if ('' === $tag) { + throw new InvalidArgumentException('Cache tag length must be greater than zero'); + } + if (false !== strpbrk($tag, '{}()/\@:')) { + throw new InvalidArgumentException(sprintf('Cache tag "%s" contains reserved characters {}()/\@:', $tag)); + } + $this->tags[$tag] = $tag; + } + + return $this; + } + + /** + * Returns the list of tags bound to the value coming from the pool storage if any. + * + * @return array + */ + public function getPreviousTags() + { + return $this->prevTags; + } + + /** + * Validates a cache key according to PSR-6. + * + * @param string $key The key to validate + * + * @return string + * + * @throws InvalidArgumentException When $key is not valid + */ + public static function validateKey($key) + { + if (!\is_string($key)) { + throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given', \is_object($key) ? \get_class($key) : \gettype($key))); + } + if ('' === $key) { + throw new InvalidArgumentException('Cache key length must be greater than zero'); + } + if (false !== strpbrk($key, '{}()/\@:')) { + throw new InvalidArgumentException(sprintf('Cache key "%s" contains reserved characters {}()/\@:', $key)); + } + + return $key; + } + + /** + * Internal logging helper. + * + * @internal + */ + public static function log(LoggerInterface $logger = null, $message, $context = []) + { + if ($logger) { + $logger->warning($message, $context); + } else { + $replace = []; + foreach ($context as $k => $v) { + if (is_scalar($v)) { + $replace['{'.$k.'}'] = $v; + } + } + @trigger_error(strtr($message, $replace), E_USER_WARNING); + } + } +} diff --git a/lib/symfony/cache/DataCollector/CacheDataCollector.php b/lib/symfony/cache/DataCollector/CacheDataCollector.php new file mode 100644 index 000000000..a2f826d70 --- /dev/null +++ b/lib/symfony/cache/DataCollector/CacheDataCollector.php @@ -0,0 +1,189 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\DataCollector; + +use Symfony\Component\Cache\Adapter\TraceableAdapter; +use Symfony\Component\Cache\Adapter\TraceableAdapterEvent; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\DataCollector\DataCollector; +use Symfony\Component\HttpKernel\DataCollector\LateDataCollectorInterface; + +/** + * @author Aaron Scherer + * @author Tobias Nyholm + */ +class CacheDataCollector extends DataCollector implements LateDataCollectorInterface +{ + /** + * @var TraceableAdapter[] + */ + private $instances = []; + + /** + * @param string $name + * @param TraceableAdapter $instance + */ + public function addInstance($name, TraceableAdapter $instance) + { + $this->instances[$name] = $instance; + } + + /** + * {@inheritdoc} + */ + public function collect(Request $request, Response $response, \Exception $exception = null) + { + $empty = ['calls' => [], 'config' => [], 'options' => [], 'statistics' => []]; + $this->data = ['instances' => $empty, 'total' => $empty]; + foreach ($this->instances as $name => $instance) { + $this->data['instances']['calls'][$name] = $instance->getCalls(); + } + + $this->data['instances']['statistics'] = $this->calculateStatistics(); + $this->data['total']['statistics'] = $this->calculateTotalStatistics(); + } + + public function reset() + { + $this->data = []; + foreach ($this->instances as $instance) { + $instance->clearCalls(); + } + } + + public function lateCollect() + { + $this->data = $this->cloneVar($this->data); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'cache'; + } + + /** + * Method returns amount of logged Cache reads: "get" calls. + * + * @return array + */ + public function getStatistics() + { + return $this->data['instances']['statistics']; + } + + /** + * Method returns the statistic totals. + * + * @return array + */ + public function getTotals() + { + return $this->data['total']['statistics']; + } + + /** + * Method returns all logged Cache call objects. + * + * @return mixed + */ + public function getCalls() + { + return $this->data['instances']['calls']; + } + + /** + * @return array + */ + private function calculateStatistics() + { + $statistics = []; + foreach ($this->data['instances']['calls'] as $name => $calls) { + $statistics[$name] = [ + 'calls' => 0, + 'time' => 0, + 'reads' => 0, + 'writes' => 0, + 'deletes' => 0, + 'hits' => 0, + 'misses' => 0, + ]; + /** @var TraceableAdapterEvent $call */ + foreach ($calls as $call) { + ++$statistics[$name]['calls']; + $statistics[$name]['time'] += $call->end - $call->start; + if ('getItem' === $call->name) { + ++$statistics[$name]['reads']; + if ($call->hits) { + ++$statistics[$name]['hits']; + } else { + ++$statistics[$name]['misses']; + } + } elseif ('getItems' === $call->name) { + $statistics[$name]['reads'] += $call->hits + $call->misses; + $statistics[$name]['hits'] += $call->hits; + $statistics[$name]['misses'] += $call->misses; + } elseif ('hasItem' === $call->name) { + ++$statistics[$name]['reads']; + if (false === $call->result) { + ++$statistics[$name]['misses']; + } else { + ++$statistics[$name]['hits']; + } + } elseif ('save' === $call->name) { + ++$statistics[$name]['writes']; + } elseif ('deleteItem' === $call->name) { + ++$statistics[$name]['deletes']; + } + } + if ($statistics[$name]['reads']) { + $statistics[$name]['hit_read_ratio'] = round(100 * $statistics[$name]['hits'] / $statistics[$name]['reads'], 2); + } else { + $statistics[$name]['hit_read_ratio'] = null; + } + } + + return $statistics; + } + + /** + * @return array + */ + private function calculateTotalStatistics() + { + $statistics = $this->getStatistics(); + $totals = [ + 'calls' => 0, + 'time' => 0, + 'reads' => 0, + 'writes' => 0, + 'deletes' => 0, + 'hits' => 0, + 'misses' => 0, + ]; + foreach ($statistics as $name => $values) { + foreach ($totals as $key => $value) { + $totals[$key] += $statistics[$name][$key]; + } + } + if ($totals['reads']) { + $totals['hit_read_ratio'] = round(100 * $totals['hits'] / $totals['reads'], 2); + } else { + $totals['hit_read_ratio'] = null; + } + + return $totals; + } +} diff --git a/lib/symfony/cache/DoctrineProvider.php b/lib/symfony/cache/DoctrineProvider.php new file mode 100644 index 000000000..cebe95fbc --- /dev/null +++ b/lib/symfony/cache/DoctrineProvider.php @@ -0,0 +1,102 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache; + +use Doctrine\Common\Cache\CacheProvider; +use Psr\Cache\CacheItemPoolInterface; + +/** + * @author Nicolas Grekas + */ +class DoctrineProvider extends CacheProvider implements PruneableInterface, ResettableInterface +{ + private $pool; + + public function __construct(CacheItemPoolInterface $pool) + { + $this->pool = $pool; + } + + /** + * {@inheritdoc} + */ + public function prune() + { + return $this->pool instanceof PruneableInterface && $this->pool->prune(); + } + + /** + * {@inheritdoc} + */ + public function reset() + { + if ($this->pool instanceof ResettableInterface) { + $this->pool->reset(); + } + $this->setNamespace($this->getNamespace()); + } + + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + $item = $this->pool->getItem(rawurlencode($id)); + + return $item->isHit() ? $item->get() : false; + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + return $this->pool->hasItem(rawurlencode($id)); + } + + /** + * {@inheritdoc} + */ + protected function doSave($id, $data, $lifeTime = 0) + { + $item = $this->pool->getItem(rawurlencode($id)); + + if (0 < $lifeTime) { + $item->expiresAfter($lifeTime); + } + + return $this->pool->save($item->set($data)); + } + + /** + * {@inheritdoc} + */ + protected function doDelete($id) + { + return $this->pool->deleteItem(rawurlencode($id)); + } + + /** + * {@inheritdoc} + */ + protected function doFlush() + { + $this->pool->clear(); + } + + /** + * {@inheritdoc} + */ + protected function doGetStats() + { + } +} diff --git a/lib/symfony/cache/Exception/CacheException.php b/lib/symfony/cache/Exception/CacheException.php new file mode 100644 index 000000000..e87b2db8f --- /dev/null +++ b/lib/symfony/cache/Exception/CacheException.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Exception; + +use Psr\Cache\CacheException as Psr6CacheInterface; +use Psr\SimpleCache\CacheException as SimpleCacheInterface; + +class CacheException extends \Exception implements Psr6CacheInterface, SimpleCacheInterface +{ +} diff --git a/lib/symfony/cache/Exception/InvalidArgumentException.php b/lib/symfony/cache/Exception/InvalidArgumentException.php new file mode 100644 index 000000000..828bf3ed7 --- /dev/null +++ b/lib/symfony/cache/Exception/InvalidArgumentException.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Exception; + +use Psr\Cache\InvalidArgumentException as Psr6CacheInterface; +use Psr\SimpleCache\InvalidArgumentException as SimpleCacheInterface; + +class InvalidArgumentException extends \InvalidArgumentException implements Psr6CacheInterface, SimpleCacheInterface +{ +} diff --git a/lib/symfony/cache/LICENSE b/lib/symfony/cache/LICENSE new file mode 100644 index 000000000..3c464ca94 --- /dev/null +++ b/lib/symfony/cache/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2016-2019 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. diff --git a/lib/symfony/cache/PruneableInterface.php b/lib/symfony/cache/PruneableInterface.php new file mode 100644 index 000000000..426152536 --- /dev/null +++ b/lib/symfony/cache/PruneableInterface.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache; + +/** + * Interface extends psr-6 and psr-16 caches to allow for pruning (deletion) of all expired cache items. + */ +interface PruneableInterface +{ + /** + * @return bool + */ + public function prune(); +} diff --git a/lib/symfony/cache/README.md b/lib/symfony/cache/README.md new file mode 100644 index 000000000..c4ab7520f --- /dev/null +++ b/lib/symfony/cache/README.md @@ -0,0 +1,18 @@ +Symfony PSR-6 implementation for caching +======================================== + +This component provides an extended [PSR-6](http://www.php-fig.org/psr/psr-6/) +implementation for adding cache to your applications. It is designed to have a +low overhead so that caching is fastest. It ships with a few caching adapters +for the most widespread and suited to caching backends. It also provides a +`doctrine/cache` proxy adapter to cover more advanced caching needs and a proxy +adapter for greater interoperability between PSR-6 implementations. + +Resources +--------- + + * [Documentation](https://symfony.com/doc/current/components/cache.html) + * [Contributing](https://symfony.com/doc/current/contributing/index.html) + * [Report issues](https://github.com/symfony/symfony/issues) and + [send Pull Requests](https://github.com/symfony/symfony/pulls) + in the [main Symfony repository](https://github.com/symfony/symfony) diff --git a/lib/symfony/cache/ResettableInterface.php b/lib/symfony/cache/ResettableInterface.php new file mode 100644 index 000000000..6be72861e --- /dev/null +++ b/lib/symfony/cache/ResettableInterface.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache; + +/** + * Resets a pool's local state. + */ +interface ResettableInterface +{ + public function reset(); +} diff --git a/lib/symfony/cache/Simple/AbstractCache.php b/lib/symfony/cache/Simple/AbstractCache.php new file mode 100644 index 000000000..23b401c54 --- /dev/null +++ b/lib/symfony/cache/Simple/AbstractCache.php @@ -0,0 +1,190 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Simple; + +use Psr\Log\LoggerAwareInterface; +use Psr\SimpleCache\CacheInterface; +use Symfony\Component\Cache\CacheItem; +use Symfony\Component\Cache\Exception\InvalidArgumentException; +use Symfony\Component\Cache\ResettableInterface; +use Symfony\Component\Cache\Traits\AbstractTrait; + +/** + * @author Nicolas Grekas + */ +abstract class AbstractCache implements CacheInterface, LoggerAwareInterface, ResettableInterface +{ + /** + * @internal + */ + const NS_SEPARATOR = ':'; + + use AbstractTrait { + deleteItems as private; + AbstractTrait::deleteItem as delete; + AbstractTrait::hasItem as has; + } + + private $defaultLifetime; + + /** + * @param string $namespace + * @param int $defaultLifetime + */ + protected function __construct($namespace = '', $defaultLifetime = 0) + { + $this->defaultLifetime = max(0, (int) $defaultLifetime); + $this->namespace = '' === $namespace ? '' : CacheItem::validateKey($namespace).':'; + if (null !== $this->maxIdLength && \strlen($namespace) > $this->maxIdLength - 24) { + throw new InvalidArgumentException(sprintf('Namespace must be %d chars max, %d given ("%s")', $this->maxIdLength - 24, \strlen($namespace), $namespace)); + } + } + + /** + * {@inheritdoc} + */ + public function get($key, $default = null) + { + $id = $this->getId($key); + + try { + foreach ($this->doFetch([$id]) as $value) { + return $value; + } + } catch (\Exception $e) { + CacheItem::log($this->logger, 'Failed to fetch key "{key}"', ['key' => $key, 'exception' => $e]); + } + + return $default; + } + + /** + * {@inheritdoc} + */ + public function set($key, $value, $ttl = null) + { + CacheItem::validateKey($key); + + return $this->setMultiple([$key => $value], $ttl); + } + + /** + * {@inheritdoc} + */ + public function getMultiple($keys, $default = null) + { + if ($keys instanceof \Traversable) { + $keys = iterator_to_array($keys, false); + } elseif (!\is_array($keys)) { + throw new InvalidArgumentException(sprintf('Cache keys must be array or Traversable, "%s" given', \is_object($keys) ? \get_class($keys) : \gettype($keys))); + } + $ids = []; + + foreach ($keys as $key) { + $ids[] = $this->getId($key); + } + try { + $values = $this->doFetch($ids); + } catch (\Exception $e) { + CacheItem::log($this->logger, 'Failed to fetch requested values', ['keys' => $keys, 'exception' => $e]); + $values = []; + } + $ids = array_combine($ids, $keys); + + return $this->generateValues($values, $ids, $default); + } + + /** + * {@inheritdoc} + */ + public function setMultiple($values, $ttl = null) + { + if (!\is_array($values) && !$values instanceof \Traversable) { + throw new InvalidArgumentException(sprintf('Cache values must be array or Traversable, "%s" given', \is_object($values) ? \get_class($values) : \gettype($values))); + } + $valuesById = []; + + foreach ($values as $key => $value) { + if (\is_int($key)) { + $key = (string) $key; + } + $valuesById[$this->getId($key)] = $value; + } + if (false === $ttl = $this->normalizeTtl($ttl)) { + return $this->doDelete(array_keys($valuesById)); + } + + try { + $e = $this->doSave($valuesById, $ttl); + } catch (\Exception $e) { + } + if (true === $e || [] === $e) { + return true; + } + $keys = []; + foreach (\is_array($e) ? $e : array_keys($valuesById) as $id) { + $keys[] = substr($id, \strlen($this->namespace)); + } + CacheItem::log($this->logger, 'Failed to save values', ['keys' => $keys, 'exception' => $e instanceof \Exception ? $e : null]); + + return false; + } + + /** + * {@inheritdoc} + */ + public function deleteMultiple($keys) + { + if ($keys instanceof \Traversable) { + $keys = iterator_to_array($keys, false); + } elseif (!\is_array($keys)) { + throw new InvalidArgumentException(sprintf('Cache keys must be array or Traversable, "%s" given', \is_object($keys) ? \get_class($keys) : \gettype($keys))); + } + + return $this->deleteItems($keys); + } + + private function normalizeTtl($ttl) + { + if (null === $ttl) { + return $this->defaultLifetime; + } + if ($ttl instanceof \DateInterval) { + $ttl = (int) \DateTime::createFromFormat('U', 0)->add($ttl)->format('U'); + } + if (\is_int($ttl)) { + return 0 < $ttl ? $ttl : false; + } + + throw new InvalidArgumentException(sprintf('Expiration date must be an integer, a DateInterval or null, "%s" given', \is_object($ttl) ? \get_class($ttl) : \gettype($ttl))); + } + + private function generateValues($values, &$keys, $default) + { + try { + foreach ($values as $id => $value) { + if (!isset($keys[$id])) { + $id = key($keys); + } + $key = $keys[$id]; + unset($keys[$id]); + yield $key => $value; + } + } catch (\Exception $e) { + CacheItem::log($this->logger, 'Failed to fetch requested values', ['keys' => array_values($keys), 'exception' => $e]); + } + + foreach ($keys as $key) { + yield $key => $default; + } + } +} diff --git a/lib/symfony/cache/Simple/ApcuCache.php b/lib/symfony/cache/Simple/ApcuCache.php new file mode 100644 index 000000000..e583b4434 --- /dev/null +++ b/lib/symfony/cache/Simple/ApcuCache.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Simple; + +use Symfony\Component\Cache\Traits\ApcuTrait; + +class ApcuCache extends AbstractCache +{ + use ApcuTrait; + + /** + * @param string $namespace + * @param int $defaultLifetime + * @param string|null $version + */ + public function __construct($namespace = '', $defaultLifetime = 0, $version = null) + { + $this->init($namespace, $defaultLifetime, $version); + } +} diff --git a/lib/symfony/cache/Simple/ArrayCache.php b/lib/symfony/cache/Simple/ArrayCache.php new file mode 100644 index 000000000..9c8f6d8e3 --- /dev/null +++ b/lib/symfony/cache/Simple/ArrayCache.php @@ -0,0 +1,148 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Simple; + +use Psr\Log\LoggerAwareInterface; +use Psr\SimpleCache\CacheInterface; +use Symfony\Component\Cache\CacheItem; +use Symfony\Component\Cache\Exception\InvalidArgumentException; +use Symfony\Component\Cache\ResettableInterface; +use Symfony\Component\Cache\Traits\ArrayTrait; + +/** + * @author Nicolas Grekas + */ +class ArrayCache implements CacheInterface, LoggerAwareInterface, ResettableInterface +{ + use ArrayTrait { + ArrayTrait::deleteItem as delete; + ArrayTrait::hasItem as has; + } + + private $defaultLifetime; + + /** + * @param int $defaultLifetime + * @param bool $storeSerialized Disabling serialization can lead to cache corruptions when storing mutable values but increases performance otherwise + */ + public function __construct($defaultLifetime = 0, $storeSerialized = true) + { + $this->defaultLifetime = (int) $defaultLifetime; + $this->storeSerialized = $storeSerialized; + } + + /** + * {@inheritdoc} + */ + public function get($key, $default = null) + { + foreach ($this->getMultiple([$key], $default) as $v) { + return $v; + } + } + + /** + * {@inheritdoc} + */ + public function getMultiple($keys, $default = null) + { + if ($keys instanceof \Traversable) { + $keys = iterator_to_array($keys, false); + } elseif (!\is_array($keys)) { + throw new InvalidArgumentException(sprintf('Cache keys must be array or Traversable, "%s" given', \is_object($keys) ? \get_class($keys) : \gettype($keys))); + } + foreach ($keys as $key) { + CacheItem::validateKey($key); + } + + return $this->generateItems($keys, time(), function ($k, $v, $hit) use ($default) { return $hit ? $v : $default; }); + } + + /** + * {@inheritdoc} + */ + public function deleteMultiple($keys) + { + if (!\is_array($keys) && !$keys instanceof \Traversable) { + throw new InvalidArgumentException(sprintf('Cache keys must be array or Traversable, "%s" given', \is_object($keys) ? \get_class($keys) : \gettype($keys))); + } + foreach ($keys as $key) { + $this->delete($key); + } + + return true; + } + + /** + * {@inheritdoc} + */ + public function set($key, $value, $ttl = null) + { + CacheItem::validateKey($key); + + return $this->setMultiple([$key => $value], $ttl); + } + + /** + * {@inheritdoc} + */ + public function setMultiple($values, $ttl = null) + { + if (!\is_array($values) && !$values instanceof \Traversable) { + throw new InvalidArgumentException(sprintf('Cache values must be array or Traversable, "%s" given', \is_object($values) ? \get_class($values) : \gettype($values))); + } + $valuesArray = []; + + foreach ($values as $key => $value) { + \is_int($key) || CacheItem::validateKey($key); + $valuesArray[$key] = $value; + } + if (false === $ttl = $this->normalizeTtl($ttl)) { + return $this->deleteMultiple(array_keys($valuesArray)); + } + if ($this->storeSerialized) { + foreach ($valuesArray as $key => $value) { + try { + $valuesArray[$key] = serialize($value); + } catch (\Exception $e) { + $type = \is_object($value) ? \get_class($value) : \gettype($value); + CacheItem::log($this->logger, 'Failed to save key "{key}" ({type})', ['key' => $key, 'type' => $type, 'exception' => $e]); + + return false; + } + } + } + $expiry = 0 < $ttl ? time() + $ttl : PHP_INT_MAX; + + foreach ($valuesArray as $key => $value) { + $this->values[$key] = $value; + $this->expiries[$key] = $expiry; + } + + return true; + } + + private function normalizeTtl($ttl) + { + if (null === $ttl) { + return $this->defaultLifetime; + } + if ($ttl instanceof \DateInterval) { + $ttl = (int) \DateTime::createFromFormat('U', 0)->add($ttl)->format('U'); + } + if (\is_int($ttl)) { + return 0 < $ttl ? $ttl : false; + } + + throw new InvalidArgumentException(sprintf('Expiration date must be an integer, a DateInterval or null, "%s" given', \is_object($ttl) ? \get_class($ttl) : \gettype($ttl))); + } +} diff --git a/lib/symfony/cache/Simple/ChainCache.php b/lib/symfony/cache/Simple/ChainCache.php new file mode 100644 index 000000000..2e6c7277d --- /dev/null +++ b/lib/symfony/cache/Simple/ChainCache.php @@ -0,0 +1,252 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Simple; + +use Psr\SimpleCache\CacheInterface; +use Symfony\Component\Cache\Exception\InvalidArgumentException; +use Symfony\Component\Cache\PruneableInterface; +use Symfony\Component\Cache\ResettableInterface; + +/** + * Chains several caches together. + * + * Cached items are fetched from the first cache having them in its data store. + * They are saved and deleted in all caches at once. + * + * @author Nicolas Grekas + */ +class ChainCache implements CacheInterface, PruneableInterface, ResettableInterface +{ + private $miss; + private $caches = []; + private $defaultLifetime; + private $cacheCount; + + /** + * @param CacheInterface[] $caches The ordered list of caches used to fetch cached items + * @param int $defaultLifetime The lifetime of items propagated from lower caches to upper ones + */ + public function __construct(array $caches, $defaultLifetime = 0) + { + if (!$caches) { + throw new InvalidArgumentException('At least one cache must be specified.'); + } + + foreach ($caches as $cache) { + if (!$cache instanceof CacheInterface) { + throw new InvalidArgumentException(sprintf('The class "%s" does not implement the "%s" interface.', \get_class($cache), CacheInterface::class)); + } + } + + $this->miss = new \stdClass(); + $this->caches = array_values($caches); + $this->cacheCount = \count($this->caches); + $this->defaultLifetime = 0 < $defaultLifetime ? (int) $defaultLifetime : null; + } + + /** + * {@inheritdoc} + */ + public function get($key, $default = null) + { + $miss = null !== $default && \is_object($default) ? $default : $this->miss; + + foreach ($this->caches as $i => $cache) { + $value = $cache->get($key, $miss); + + if ($miss !== $value) { + while (0 <= --$i) { + $this->caches[$i]->set($key, $value, $this->defaultLifetime); + } + + return $value; + } + } + + return $default; + } + + /** + * {@inheritdoc} + */ + public function getMultiple($keys, $default = null) + { + $miss = null !== $default && \is_object($default) ? $default : $this->miss; + + return $this->generateItems($this->caches[0]->getMultiple($keys, $miss), 0, $miss, $default); + } + + private function generateItems($values, $cacheIndex, $miss, $default) + { + $missing = []; + $nextCacheIndex = $cacheIndex + 1; + $nextCache = isset($this->caches[$nextCacheIndex]) ? $this->caches[$nextCacheIndex] : null; + + foreach ($values as $k => $value) { + if ($miss !== $value) { + yield $k => $value; + } elseif (!$nextCache) { + yield $k => $default; + } else { + $missing[] = $k; + } + } + + if ($missing) { + $cache = $this->caches[$cacheIndex]; + $values = $this->generateItems($nextCache->getMultiple($missing, $miss), $nextCacheIndex, $miss, $default); + + foreach ($values as $k => $value) { + if ($miss !== $value) { + $cache->set($k, $value, $this->defaultLifetime); + yield $k => $value; + } else { + yield $k => $default; + } + } + } + } + + /** + * {@inheritdoc} + */ + public function has($key) + { + foreach ($this->caches as $cache) { + if ($cache->has($key)) { + return true; + } + } + + return false; + } + + /** + * {@inheritdoc} + */ + public function clear() + { + $cleared = true; + $i = $this->cacheCount; + + while ($i--) { + $cleared = $this->caches[$i]->clear() && $cleared; + } + + return $cleared; + } + + /** + * {@inheritdoc} + */ + public function delete($key) + { + $deleted = true; + $i = $this->cacheCount; + + while ($i--) { + $deleted = $this->caches[$i]->delete($key) && $deleted; + } + + return $deleted; + } + + /** + * {@inheritdoc} + */ + public function deleteMultiple($keys) + { + if ($keys instanceof \Traversable) { + $keys = iterator_to_array($keys, false); + } + $deleted = true; + $i = $this->cacheCount; + + while ($i--) { + $deleted = $this->caches[$i]->deleteMultiple($keys) && $deleted; + } + + return $deleted; + } + + /** + * {@inheritdoc} + */ + public function set($key, $value, $ttl = null) + { + $saved = true; + $i = $this->cacheCount; + + while ($i--) { + $saved = $this->caches[$i]->set($key, $value, $ttl) && $saved; + } + + return $saved; + } + + /** + * {@inheritdoc} + */ + public function setMultiple($values, $ttl = null) + { + if ($values instanceof \Traversable) { + $valuesIterator = $values; + $values = function () use ($valuesIterator, &$values) { + $generatedValues = []; + + foreach ($valuesIterator as $key => $value) { + yield $key => $value; + $generatedValues[$key] = $value; + } + + $values = $generatedValues; + }; + $values = $values(); + } + $saved = true; + $i = $this->cacheCount; + + while ($i--) { + $saved = $this->caches[$i]->setMultiple($values, $ttl) && $saved; + } + + return $saved; + } + + /** + * {@inheritdoc} + */ + public function prune() + { + $pruned = true; + + foreach ($this->caches as $cache) { + if ($cache instanceof PruneableInterface) { + $pruned = $cache->prune() && $pruned; + } + } + + return $pruned; + } + + /** + * {@inheritdoc} + */ + public function reset() + { + foreach ($this->caches as $cache) { + if ($cache instanceof ResettableInterface) { + $cache->reset(); + } + } + } +} diff --git a/lib/symfony/cache/Simple/DoctrineCache.php b/lib/symfony/cache/Simple/DoctrineCache.php new file mode 100644 index 000000000..00f0b9c6f --- /dev/null +++ b/lib/symfony/cache/Simple/DoctrineCache.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Simple; + +use Doctrine\Common\Cache\CacheProvider; +use Symfony\Component\Cache\Traits\DoctrineTrait; + +class DoctrineCache extends AbstractCache +{ + use DoctrineTrait; + + /** + * @param CacheProvider $provider + * @param string $namespace + * @param int $defaultLifetime + */ + public function __construct(CacheProvider $provider, $namespace = '', $defaultLifetime = 0) + { + parent::__construct('', $defaultLifetime); + $this->provider = $provider; + $provider->setNamespace($namespace); + } +} diff --git a/lib/symfony/cache/Simple/FilesystemCache.php b/lib/symfony/cache/Simple/FilesystemCache.php new file mode 100644 index 000000000..ccd579534 --- /dev/null +++ b/lib/symfony/cache/Simple/FilesystemCache.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Simple; + +use Symfony\Component\Cache\PruneableInterface; +use Symfony\Component\Cache\Traits\FilesystemTrait; + +class FilesystemCache extends AbstractCache implements PruneableInterface +{ + use FilesystemTrait; + + /** + * @param string $namespace + * @param int $defaultLifetime + * @param string|null $directory + */ + public function __construct($namespace = '', $defaultLifetime = 0, $directory = null) + { + parent::__construct('', $defaultLifetime); + $this->init($namespace, $directory); + } +} diff --git a/lib/symfony/cache/Simple/MemcachedCache.php b/lib/symfony/cache/Simple/MemcachedCache.php new file mode 100644 index 000000000..771774062 --- /dev/null +++ b/lib/symfony/cache/Simple/MemcachedCache.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Simple; + +use Symfony\Component\Cache\Traits\MemcachedTrait; + +class MemcachedCache extends AbstractCache +{ + use MemcachedTrait; + + protected $maxIdLength = 250; + + /** + * @param \Memcached $client + * @param string $namespace + * @param int $defaultLifetime + */ + public function __construct(\Memcached $client, $namespace = '', $defaultLifetime = 0) + { + $this->init($client, $namespace, $defaultLifetime); + } +} diff --git a/lib/symfony/cache/Simple/NullCache.php b/lib/symfony/cache/Simple/NullCache.php new file mode 100644 index 000000000..fa986aebd --- /dev/null +++ b/lib/symfony/cache/Simple/NullCache.php @@ -0,0 +1,86 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Simple; + +use Psr\SimpleCache\CacheInterface; + +/** + * @author Nicolas Grekas + */ +class NullCache implements CacheInterface +{ + /** + * {@inheritdoc} + */ + public function get($key, $default = null) + { + return $default; + } + + /** + * {@inheritdoc} + */ + public function getMultiple($keys, $default = null) + { + foreach ($keys as $key) { + yield $key => $default; + } + } + + /** + * {@inheritdoc} + */ + public function has($key) + { + return false; + } + + /** + * {@inheritdoc} + */ + public function clear() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function delete($key) + { + return true; + } + + /** + * {@inheritdoc} + */ + public function deleteMultiple($keys) + { + return true; + } + + /** + * {@inheritdoc} + */ + public function set($key, $value, $ttl = null) + { + return false; + } + + /** + * {@inheritdoc} + */ + public function setMultiple($values, $ttl = null) + { + return false; + } +} diff --git a/lib/symfony/cache/Simple/PdoCache.php b/lib/symfony/cache/Simple/PdoCache.php new file mode 100644 index 000000000..c92e049a1 --- /dev/null +++ b/lib/symfony/cache/Simple/PdoCache.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Simple; + +use Symfony\Component\Cache\PruneableInterface; +use Symfony\Component\Cache\Traits\PdoTrait; + +class PdoCache extends AbstractCache implements PruneableInterface +{ + use PdoTrait; + + protected $maxIdLength = 255; + + /** + * You can either pass an existing database connection as PDO instance or + * a Doctrine DBAL Connection or a DSN string that will be used to + * lazy-connect to the database when the cache is actually used. + * + * List of available options: + * * db_table: The name of the table [default: cache_items] + * * db_id_col: The column where to store the cache id [default: item_id] + * * db_data_col: The column where to store the cache data [default: item_data] + * * db_lifetime_col: The column where to store the lifetime [default: item_lifetime] + * * db_time_col: The column where to store the timestamp [default: item_time] + * * db_username: The username when lazy-connect [default: ''] + * * db_password: The password when lazy-connect [default: ''] + * * db_connection_options: An array of driver-specific connection options [default: []] + * + * @param \PDO|Connection|string $connOrDsn A \PDO or Connection instance or DSN string or null + * @param string $namespace + * @param int $defaultLifetime + * @param array $options An associative array of options + * + * @throws InvalidArgumentException When first argument is not PDO nor Connection nor string + * @throws InvalidArgumentException When PDO error mode is not PDO::ERRMODE_EXCEPTION + * @throws InvalidArgumentException When namespace contains invalid characters + */ + public function __construct($connOrDsn, $namespace = '', $defaultLifetime = 0, array $options = []) + { + $this->init($connOrDsn, $namespace, $defaultLifetime, $options); + } +} diff --git a/lib/symfony/cache/Simple/PhpArrayCache.php b/lib/symfony/cache/Simple/PhpArrayCache.php new file mode 100644 index 000000000..607555e93 --- /dev/null +++ b/lib/symfony/cache/Simple/PhpArrayCache.php @@ -0,0 +1,259 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Simple; + +use Psr\SimpleCache\CacheInterface; +use Symfony\Component\Cache\Exception\InvalidArgumentException; +use Symfony\Component\Cache\PruneableInterface; +use Symfony\Component\Cache\ResettableInterface; +use Symfony\Component\Cache\Traits\PhpArrayTrait; + +/** + * Caches items at warm up time using a PHP array that is stored in shared memory by OPCache since PHP 7.0. + * Warmed up items are read-only and run-time discovered items are cached using a fallback adapter. + * + * @author Titouan Galopin + * @author Nicolas Grekas + */ +class PhpArrayCache implements CacheInterface, PruneableInterface, ResettableInterface +{ + use PhpArrayTrait; + + /** + * @param string $file The PHP file were values are cached + * @param CacheInterface $fallbackPool A pool to fallback on when an item is not hit + */ + public function __construct($file, CacheInterface $fallbackPool) + { + $this->file = $file; + $this->pool = $fallbackPool; + $this->zendDetectUnicode = filter_var(ini_get('zend.detect_unicode'), FILTER_VALIDATE_BOOLEAN); + } + + /** + * This adapter should only be used on PHP 7.0+ to take advantage of how PHP + * stores arrays in its latest versions. This factory method decorates the given + * fallback pool with this adapter only if the current PHP version is supported. + * + * @param string $file The PHP file were values are cached + * + * @return CacheInterface + */ + public static function create($file, CacheInterface $fallbackPool) + { + // Shared memory is available in PHP 7.0+ with OPCache enabled and in HHVM + if ((\PHP_VERSION_ID >= 70000 && filter_var(ini_get('opcache.enable'), FILTER_VALIDATE_BOOLEAN)) || \defined('HHVM_VERSION')) { + return new static($file, $fallbackPool); + } + + return $fallbackPool; + } + + /** + * {@inheritdoc} + */ + public function get($key, $default = null) + { + if (!\is_string($key)) { + throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given.', \is_object($key) ? \get_class($key) : \gettype($key))); + } + if (null === $this->values) { + $this->initialize(); + } + if (!isset($this->values[$key])) { + return $this->pool->get($key, $default); + } + + $value = $this->values[$key]; + + if ('N;' === $value) { + $value = null; + } elseif (\is_string($value) && isset($value[2]) && ':' === $value[1]) { + try { + $e = null; + $value = unserialize($value); + } catch (\Error $e) { + } catch (\Exception $e) { + } + if (null !== $e) { + return $default; + } + } + + return $value; + } + + /** + * {@inheritdoc} + */ + public function getMultiple($keys, $default = null) + { + if ($keys instanceof \Traversable) { + $keys = iterator_to_array($keys, false); + } elseif (!\is_array($keys)) { + throw new InvalidArgumentException(sprintf('Cache keys must be array or Traversable, "%s" given', \is_object($keys) ? \get_class($keys) : \gettype($keys))); + } + foreach ($keys as $key) { + if (!\is_string($key)) { + throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given.', \is_object($key) ? \get_class($key) : \gettype($key))); + } + } + if (null === $this->values) { + $this->initialize(); + } + + return $this->generateItems($keys, $default); + } + + /** + * {@inheritdoc} + */ + public function has($key) + { + if (!\is_string($key)) { + throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given.', \is_object($key) ? \get_class($key) : \gettype($key))); + } + if (null === $this->values) { + $this->initialize(); + } + + return isset($this->values[$key]) || $this->pool->has($key); + } + + /** + * {@inheritdoc} + */ + public function delete($key) + { + if (!\is_string($key)) { + throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given.', \is_object($key) ? \get_class($key) : \gettype($key))); + } + if (null === $this->values) { + $this->initialize(); + } + + return !isset($this->values[$key]) && $this->pool->delete($key); + } + + /** + * {@inheritdoc} + */ + public function deleteMultiple($keys) + { + if (!\is_array($keys) && !$keys instanceof \Traversable) { + throw new InvalidArgumentException(sprintf('Cache keys must be array or Traversable, "%s" given', \is_object($keys) ? \get_class($keys) : \gettype($keys))); + } + + $deleted = true; + $fallbackKeys = []; + + foreach ($keys as $key) { + if (!\is_string($key)) { + throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given.', \is_object($key) ? \get_class($key) : \gettype($key))); + } + + if (isset($this->values[$key])) { + $deleted = false; + } else { + $fallbackKeys[] = $key; + } + } + if (null === $this->values) { + $this->initialize(); + } + + if ($fallbackKeys) { + $deleted = $this->pool->deleteMultiple($fallbackKeys) && $deleted; + } + + return $deleted; + } + + /** + * {@inheritdoc} + */ + public function set($key, $value, $ttl = null) + { + if (!\is_string($key)) { + throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given.', \is_object($key) ? \get_class($key) : \gettype($key))); + } + if (null === $this->values) { + $this->initialize(); + } + + return !isset($this->values[$key]) && $this->pool->set($key, $value, $ttl); + } + + /** + * {@inheritdoc} + */ + public function setMultiple($values, $ttl = null) + { + if (!\is_array($values) && !$values instanceof \Traversable) { + throw new InvalidArgumentException(sprintf('Cache values must be array or Traversable, "%s" given', \is_object($values) ? \get_class($values) : \gettype($values))); + } + + $saved = true; + $fallbackValues = []; + + foreach ($values as $key => $value) { + if (!\is_string($key) && !\is_int($key)) { + throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given.', \is_object($key) ? \get_class($key) : \gettype($key))); + } + + if (isset($this->values[$key])) { + $saved = false; + } else { + $fallbackValues[$key] = $value; + } + } + + if ($fallbackValues) { + $saved = $this->pool->setMultiple($fallbackValues, $ttl) && $saved; + } + + return $saved; + } + + private function generateItems(array $keys, $default) + { + $fallbackKeys = []; + + foreach ($keys as $key) { + if (isset($this->values[$key])) { + $value = $this->values[$key]; + + if ('N;' === $value) { + yield $key => null; + } elseif (\is_string($value) && isset($value[2]) && ':' === $value[1]) { + try { + yield $key => unserialize($value); + } catch (\Error $e) { + yield $key => $default; + } catch (\Exception $e) { + yield $key => $default; + } + } else { + yield $key => $value; + } + } else { + $fallbackKeys[] = $key; + } + } + + if ($fallbackKeys) { + foreach ($this->pool->getMultiple($fallbackKeys, $default) as $key => $item) { + yield $key => $item; + } + } + } +} diff --git a/lib/symfony/cache/Simple/PhpFilesCache.php b/lib/symfony/cache/Simple/PhpFilesCache.php new file mode 100644 index 000000000..9587f17be --- /dev/null +++ b/lib/symfony/cache/Simple/PhpFilesCache.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Simple; + +use Symfony\Component\Cache\Exception\CacheException; +use Symfony\Component\Cache\PruneableInterface; +use Symfony\Component\Cache\Traits\PhpFilesTrait; + +class PhpFilesCache extends AbstractCache implements PruneableInterface +{ + use PhpFilesTrait; + + /** + * @param string $namespace + * @param int $defaultLifetime + * @param string|null $directory + * + * @throws CacheException if OPcache is not enabled + */ + public function __construct($namespace = '', $defaultLifetime = 0, $directory = null) + { + if (!static::isSupported()) { + throw new CacheException('OPcache is not enabled'); + } + parent::__construct('', $defaultLifetime); + $this->init($namespace, $directory); + + $e = new \Exception(); + $this->includeHandler = function () use ($e) { throw $e; }; + $this->zendDetectUnicode = filter_var(ini_get('zend.detect_unicode'), FILTER_VALIDATE_BOOLEAN); + } +} diff --git a/lib/symfony/cache/Simple/Psr6Cache.php b/lib/symfony/cache/Simple/Psr6Cache.php new file mode 100644 index 000000000..85d75becb --- /dev/null +++ b/lib/symfony/cache/Simple/Psr6Cache.php @@ -0,0 +1,241 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Simple; + +use Psr\Cache\CacheException as Psr6CacheException; +use Psr\Cache\CacheItemPoolInterface; +use Psr\SimpleCache\CacheException as SimpleCacheException; +use Psr\SimpleCache\CacheInterface; +use Symfony\Component\Cache\Adapter\AdapterInterface; +use Symfony\Component\Cache\CacheItem; +use Symfony\Component\Cache\Exception\InvalidArgumentException; +use Symfony\Component\Cache\PruneableInterface; +use Symfony\Component\Cache\ResettableInterface; +use Symfony\Component\Cache\Traits\ProxyTrait; + +/** + * @author Nicolas Grekas + */ +class Psr6Cache implements CacheInterface, PruneableInterface, ResettableInterface +{ + use ProxyTrait; + + private $createCacheItem; + private $cacheItemPrototype; + + public function __construct(CacheItemPoolInterface $pool) + { + $this->pool = $pool; + + if (!$pool instanceof AdapterInterface) { + return; + } + $cacheItemPrototype = &$this->cacheItemPrototype; + $createCacheItem = \Closure::bind( + function ($key, $value, $allowInt = false) use (&$cacheItemPrototype) { + $item = clone $cacheItemPrototype; + $item->key = $allowInt && \is_int($key) ? (string) $key : CacheItem::validateKey($key); + $item->value = $value; + $item->isHit = false; + + return $item; + }, + null, + CacheItem::class + ); + $this->createCacheItem = function ($key, $value, $allowInt = false) use ($createCacheItem) { + if (null === $this->cacheItemPrototype) { + $this->get($allowInt && \is_int($key) ? (string) $key : $key); + } + $this->createCacheItem = $createCacheItem; + + return $createCacheItem($key, $value, $allowInt); + }; + } + + /** + * {@inheritdoc} + */ + public function get($key, $default = null) + { + try { + $item = $this->pool->getItem($key); + } catch (SimpleCacheException $e) { + throw $e; + } catch (Psr6CacheException $e) { + throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e); + } + if (null === $this->cacheItemPrototype) { + $this->cacheItemPrototype = clone $item; + $this->cacheItemPrototype->set(null); + } + + return $item->isHit() ? $item->get() : $default; + } + + /** + * {@inheritdoc} + */ + public function set($key, $value, $ttl = null) + { + try { + if (null !== $f = $this->createCacheItem) { + $item = $f($key, $value); + } else { + $item = $this->pool->getItem($key)->set($value); + } + } catch (SimpleCacheException $e) { + throw $e; + } catch (Psr6CacheException $e) { + throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e); + } + if (null !== $ttl) { + $item->expiresAfter($ttl); + } + + return $this->pool->save($item); + } + + /** + * {@inheritdoc} + */ + public function delete($key) + { + try { + return $this->pool->deleteItem($key); + } catch (SimpleCacheException $e) { + throw $e; + } catch (Psr6CacheException $e) { + throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * {@inheritdoc} + */ + public function clear() + { + return $this->pool->clear(); + } + + /** + * {@inheritdoc} + */ + public function getMultiple($keys, $default = null) + { + if ($keys instanceof \Traversable) { + $keys = iterator_to_array($keys, false); + } elseif (!\is_array($keys)) { + throw new InvalidArgumentException(sprintf('Cache keys must be array or Traversable, "%s" given', \is_object($keys) ? \get_class($keys) : \gettype($keys))); + } + + try { + $items = $this->pool->getItems($keys); + } catch (SimpleCacheException $e) { + throw $e; + } catch (Psr6CacheException $e) { + throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e); + } + $values = []; + + foreach ($items as $key => $item) { + $values[$key] = $item->isHit() ? $item->get() : $default; + } + + return $values; + } + + /** + * {@inheritdoc} + */ + public function setMultiple($values, $ttl = null) + { + $valuesIsArray = \is_array($values); + if (!$valuesIsArray && !$values instanceof \Traversable) { + throw new InvalidArgumentException(sprintf('Cache values must be array or Traversable, "%s" given', \is_object($values) ? \get_class($values) : \gettype($values))); + } + $items = []; + + try { + if (null !== $f = $this->createCacheItem) { + $valuesIsArray = false; + foreach ($values as $key => $value) { + $items[$key] = $f($key, $value, true); + } + } elseif ($valuesIsArray) { + $items = []; + foreach ($values as $key => $value) { + $items[] = (string) $key; + } + $items = $this->pool->getItems($items); + } else { + foreach ($values as $key => $value) { + if (\is_int($key)) { + $key = (string) $key; + } + $items[$key] = $this->pool->getItem($key)->set($value); + } + } + } catch (SimpleCacheException $e) { + throw $e; + } catch (Psr6CacheException $e) { + throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e); + } + $ok = true; + + foreach ($items as $key => $item) { + if ($valuesIsArray) { + $item->set($values[$key]); + } + if (null !== $ttl) { + $item->expiresAfter($ttl); + } + $ok = $this->pool->saveDeferred($item) && $ok; + } + + return $this->pool->commit() && $ok; + } + + /** + * {@inheritdoc} + */ + public function deleteMultiple($keys) + { + if ($keys instanceof \Traversable) { + $keys = iterator_to_array($keys, false); + } elseif (!\is_array($keys)) { + throw new InvalidArgumentException(sprintf('Cache keys must be array or Traversable, "%s" given', \is_object($keys) ? \get_class($keys) : \gettype($keys))); + } + + try { + return $this->pool->deleteItems($keys); + } catch (SimpleCacheException $e) { + throw $e; + } catch (Psr6CacheException $e) { + throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * {@inheritdoc} + */ + public function has($key) + { + try { + return $this->pool->hasItem($key); + } catch (SimpleCacheException $e) { + throw $e; + } catch (Psr6CacheException $e) { + throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e); + } + } +} diff --git a/lib/symfony/cache/Simple/RedisCache.php b/lib/symfony/cache/Simple/RedisCache.php new file mode 100644 index 000000000..e82c0627e --- /dev/null +++ b/lib/symfony/cache/Simple/RedisCache.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Simple; + +use Symfony\Component\Cache\Traits\RedisTrait; + +class RedisCache extends AbstractCache +{ + use RedisTrait; + + /** + * @param \Redis|\RedisArray|\RedisCluster|\Predis\Client $redisClient + * @param string $namespace + * @param int $defaultLifetime + */ + public function __construct($redisClient, $namespace = '', $defaultLifetime = 0) + { + $this->init($redisClient, $namespace, $defaultLifetime); + } +} diff --git a/lib/symfony/cache/Simple/TraceableCache.php b/lib/symfony/cache/Simple/TraceableCache.php new file mode 100644 index 000000000..61b22963e --- /dev/null +++ b/lib/symfony/cache/Simple/TraceableCache.php @@ -0,0 +1,241 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Simple; + +use Psr\SimpleCache\CacheInterface; +use Symfony\Component\Cache\PruneableInterface; +use Symfony\Component\Cache\ResettableInterface; + +/** + * An adapter that collects data about all cache calls. + * + * @author Nicolas Grekas + */ +class TraceableCache implements CacheInterface, PruneableInterface, ResettableInterface +{ + private $pool; + private $miss; + private $calls = []; + + public function __construct(CacheInterface $pool) + { + $this->pool = $pool; + $this->miss = new \stdClass(); + } + + /** + * {@inheritdoc} + */ + public function get($key, $default = null) + { + $miss = null !== $default && \is_object($default) ? $default : $this->miss; + $event = $this->start(__FUNCTION__); + try { + $value = $this->pool->get($key, $miss); + } finally { + $event->end = microtime(true); + } + if ($event->result[$key] = $miss !== $value) { + ++$event->hits; + } else { + ++$event->misses; + $value = $default; + } + + return $value; + } + + /** + * {@inheritdoc} + */ + public function has($key) + { + $event = $this->start(__FUNCTION__); + try { + return $event->result[$key] = $this->pool->has($key); + } finally { + $event->end = microtime(true); + } + } + + /** + * {@inheritdoc} + */ + public function delete($key) + { + $event = $this->start(__FUNCTION__); + try { + return $event->result[$key] = $this->pool->delete($key); + } finally { + $event->end = microtime(true); + } + } + + /** + * {@inheritdoc} + */ + public function set($key, $value, $ttl = null) + { + $event = $this->start(__FUNCTION__); + try { + return $event->result[$key] = $this->pool->set($key, $value, $ttl); + } finally { + $event->end = microtime(true); + } + } + + /** + * {@inheritdoc} + */ + public function setMultiple($values, $ttl = null) + { + $event = $this->start(__FUNCTION__); + $event->result['keys'] = []; + + if ($values instanceof \Traversable) { + $values = function () use ($values, $event) { + foreach ($values as $k => $v) { + $event->result['keys'][] = $k; + yield $k => $v; + } + }; + $values = $values(); + } elseif (\is_array($values)) { + $event->result['keys'] = array_keys($values); + } + + try { + return $event->result['result'] = $this->pool->setMultiple($values, $ttl); + } finally { + $event->end = microtime(true); + } + } + + /** + * {@inheritdoc} + */ + public function getMultiple($keys, $default = null) + { + $miss = null !== $default && \is_object($default) ? $default : $this->miss; + $event = $this->start(__FUNCTION__); + try { + $result = $this->pool->getMultiple($keys, $miss); + } finally { + $event->end = microtime(true); + } + $f = function () use ($result, $event, $miss, $default) { + $event->result = []; + foreach ($result as $key => $value) { + if ($event->result[$key] = $miss !== $value) { + ++$event->hits; + } else { + ++$event->misses; + $value = $default; + } + yield $key => $value; + } + }; + + return $f(); + } + + /** + * {@inheritdoc} + */ + public function clear() + { + $event = $this->start(__FUNCTION__); + try { + return $event->result = $this->pool->clear(); + } finally { + $event->end = microtime(true); + } + } + + /** + * {@inheritdoc} + */ + public function deleteMultiple($keys) + { + $event = $this->start(__FUNCTION__); + if ($keys instanceof \Traversable) { + $keys = $event->result['keys'] = iterator_to_array($keys, false); + } else { + $event->result['keys'] = $keys; + } + try { + return $event->result['result'] = $this->pool->deleteMultiple($keys); + } finally { + $event->end = microtime(true); + } + } + + /** + * {@inheritdoc} + */ + public function prune() + { + if (!$this->pool instanceof PruneableInterface) { + return false; + } + $event = $this->start(__FUNCTION__); + try { + return $event->result = $this->pool->prune(); + } finally { + $event->end = microtime(true); + } + } + + /** + * {@inheritdoc} + */ + public function reset() + { + if (!$this->pool instanceof ResettableInterface) { + return; + } + $event = $this->start(__FUNCTION__); + try { + $this->pool->reset(); + } finally { + $event->end = microtime(true); + } + } + + public function getCalls() + { + try { + return $this->calls; + } finally { + $this->calls = []; + } + } + + private function start($name) + { + $this->calls[] = $event = new TraceableCacheEvent(); + $event->name = $name; + $event->start = microtime(true); + + return $event; + } +} + +class TraceableCacheEvent +{ + public $name; + public $start; + public $end; + public $result; + public $hits = 0; + public $misses = 0; +} diff --git a/lib/symfony/cache/Tests/Adapter/AbstractRedisAdapterTest.php b/lib/symfony/cache/Tests/Adapter/AbstractRedisAdapterTest.php new file mode 100644 index 000000000..5fcec9a26 --- /dev/null +++ b/lib/symfony/cache/Tests/Adapter/AbstractRedisAdapterTest.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests\Adapter; + +use Symfony\Component\Cache\Adapter\RedisAdapter; + +abstract class AbstractRedisAdapterTest extends AdapterTestCase +{ + protected $skippedTests = [ + 'testExpiration' => 'Testing expiration slows down the test suite', + 'testHasItemReturnsFalseWhenDeferredItemIsExpired' => 'Testing expiration slows down the test suite', + 'testDefaultLifeTime' => 'Testing expiration slows down the test suite', + ]; + + protected static $redis; + + public function createCachePool($defaultLifetime = 0) + { + return new RedisAdapter(self::$redis, str_replace('\\', '.', __CLASS__), $defaultLifetime); + } + + public static function setupBeforeClass() + { + if (!\extension_loaded('redis')) { + self::markTestSkipped('Extension redis required.'); + } + if (!@((new \Redis())->connect(getenv('REDIS_HOST')))) { + $e = error_get_last(); + self::markTestSkipped($e['message']); + } + } + + public static function tearDownAfterClass() + { + self::$redis = null; + } +} diff --git a/lib/symfony/cache/Tests/Adapter/AdapterTestCase.php b/lib/symfony/cache/Tests/Adapter/AdapterTestCase.php new file mode 100644 index 000000000..5758a2861 --- /dev/null +++ b/lib/symfony/cache/Tests/Adapter/AdapterTestCase.php @@ -0,0 +1,175 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests\Adapter; + +use Cache\IntegrationTests\CachePoolTest; +use Psr\Cache\CacheItemPoolInterface; +use Symfony\Component\Cache\PruneableInterface; + +abstract class AdapterTestCase extends CachePoolTest +{ + protected function setUp() + { + parent::setUp(); + + if (!\array_key_exists('testDeferredSaveWithoutCommit', $this->skippedTests) && \defined('HHVM_VERSION')) { + $this->skippedTests['testDeferredSaveWithoutCommit'] = 'Destructors are called late on HHVM.'; + } + + if (!\array_key_exists('testPrune', $this->skippedTests) && !$this->createCachePool() instanceof PruneableInterface) { + $this->skippedTests['testPrune'] = 'Not a pruneable cache pool.'; + } + } + + public function testDefaultLifeTime() + { + if (isset($this->skippedTests[__FUNCTION__])) { + $this->markTestSkipped($this->skippedTests[__FUNCTION__]); + } + + $cache = $this->createCachePool(2); + + $item = $cache->getItem('key.dlt'); + $item->set('value'); + $cache->save($item); + sleep(1); + + $item = $cache->getItem('key.dlt'); + $this->assertTrue($item->isHit()); + + sleep(2); + $item = $cache->getItem('key.dlt'); + $this->assertFalse($item->isHit()); + } + + public function testExpiration() + { + if (isset($this->skippedTests[__FUNCTION__])) { + $this->markTestSkipped($this->skippedTests[__FUNCTION__]); + } + + $cache = $this->createCachePool(); + $cache->save($cache->getItem('k1')->set('v1')->expiresAfter(2)); + $cache->save($cache->getItem('k2')->set('v2')->expiresAfter(366 * 86400)); + + sleep(3); + $item = $cache->getItem('k1'); + $this->assertFalse($item->isHit()); + $this->assertNull($item->get(), "Item's value must be null when isHit() is false."); + + $item = $cache->getItem('k2'); + $this->assertTrue($item->isHit()); + $this->assertSame('v2', $item->get()); + } + + public function testNotUnserializable() + { + if (isset($this->skippedTests[__FUNCTION__])) { + $this->markTestSkipped($this->skippedTests[__FUNCTION__]); + } + + $cache = $this->createCachePool(); + + $item = $cache->getItem('foo'); + $cache->save($item->set(new NotUnserializable())); + + $item = $cache->getItem('foo'); + $this->assertFalse($item->isHit()); + + foreach ($cache->getItems(['foo']) as $item) { + } + $cache->save($item->set(new NotUnserializable())); + + foreach ($cache->getItems(['foo']) as $item) { + } + $this->assertFalse($item->isHit()); + } + + public function testPrune() + { + if (isset($this->skippedTests[__FUNCTION__])) { + $this->markTestSkipped($this->skippedTests[__FUNCTION__]); + } + + if (!method_exists($this, 'isPruned')) { + $this->fail('Test classes for pruneable caches must implement `isPruned($cache, $name)` method.'); + } + + /** @var PruneableInterface|CacheItemPoolInterface $cache */ + $cache = $this->createCachePool(); + + $doSet = function ($name, $value, \DateInterval $expiresAfter = null) use ($cache) { + $item = $cache->getItem($name); + $item->set($value); + + if ($expiresAfter) { + $item->expiresAfter($expiresAfter); + } + + $cache->save($item); + }; + + $doSet('foo', 'foo-val', new \DateInterval('PT05S')); + $doSet('bar', 'bar-val', new \DateInterval('PT10S')); + $doSet('baz', 'baz-val', new \DateInterval('PT15S')); + $doSet('qux', 'qux-val', new \DateInterval('PT20S')); + + sleep(30); + $cache->prune(); + $this->assertTrue($this->isPruned($cache, 'foo')); + $this->assertTrue($this->isPruned($cache, 'bar')); + $this->assertTrue($this->isPruned($cache, 'baz')); + $this->assertTrue($this->isPruned($cache, 'qux')); + + $doSet('foo', 'foo-val'); + $doSet('bar', 'bar-val', new \DateInterval('PT20S')); + $doSet('baz', 'baz-val', new \DateInterval('PT40S')); + $doSet('qux', 'qux-val', new \DateInterval('PT80S')); + + $cache->prune(); + $this->assertFalse($this->isPruned($cache, 'foo')); + $this->assertFalse($this->isPruned($cache, 'bar')); + $this->assertFalse($this->isPruned($cache, 'baz')); + $this->assertFalse($this->isPruned($cache, 'qux')); + + sleep(30); + $cache->prune(); + $this->assertFalse($this->isPruned($cache, 'foo')); + $this->assertTrue($this->isPruned($cache, 'bar')); + $this->assertFalse($this->isPruned($cache, 'baz')); + $this->assertFalse($this->isPruned($cache, 'qux')); + + sleep(30); + $cache->prune(); + $this->assertFalse($this->isPruned($cache, 'foo')); + $this->assertTrue($this->isPruned($cache, 'baz')); + $this->assertFalse($this->isPruned($cache, 'qux')); + + sleep(30); + $cache->prune(); + $this->assertFalse($this->isPruned($cache, 'foo')); + $this->assertTrue($this->isPruned($cache, 'qux')); + } +} + +class NotUnserializable implements \Serializable +{ + public function serialize() + { + return serialize(123); + } + + public function unserialize($ser) + { + throw new \Exception(__CLASS__); + } +} diff --git a/lib/symfony/cache/Tests/Adapter/ApcuAdapterTest.php b/lib/symfony/cache/Tests/Adapter/ApcuAdapterTest.php new file mode 100644 index 000000000..5cca73f56 --- /dev/null +++ b/lib/symfony/cache/Tests/Adapter/ApcuAdapterTest.php @@ -0,0 +1,124 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests\Adapter; + +use Psr\Log\NullLogger; +use Symfony\Component\Cache\Adapter\ApcuAdapter; + +class ApcuAdapterTest extends AdapterTestCase +{ + protected $skippedTests = [ + 'testExpiration' => 'Testing expiration slows down the test suite', + 'testHasItemReturnsFalseWhenDeferredItemIsExpired' => 'Testing expiration slows down the test suite', + 'testDefaultLifeTime' => 'Testing expiration slows down the test suite', + ]; + + public function createCachePool($defaultLifetime = 0) + { + if (!\function_exists('apcu_fetch') || !filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN)) { + $this->markTestSkipped('APCu extension is required.'); + } + if ('cli' === \PHP_SAPI && !filter_var(ini_get('apc.enable_cli'), FILTER_VALIDATE_BOOLEAN)) { + if ('testWithCliSapi' !== $this->getName()) { + $this->markTestSkipped('apc.enable_cli=1 is required.'); + } + } + if ('\\' === \DIRECTORY_SEPARATOR) { + $this->markTestSkipped('Fails transiently on Windows.'); + } + + return new ApcuAdapter(str_replace('\\', '.', __CLASS__), $defaultLifetime); + } + + public function testUnserializable() + { + $pool = $this->createCachePool(); + + $item = $pool->getItem('foo'); + $item->set(function () {}); + + $this->assertFalse($pool->save($item)); + + $item = $pool->getItem('foo'); + $this->assertFalse($item->isHit()); + } + + public function testVersion() + { + $namespace = str_replace('\\', '.', \get_class($this)); + + $pool1 = new ApcuAdapter($namespace, 0, 'p1'); + + $item = $pool1->getItem('foo'); + $this->assertFalse($item->isHit()); + $this->assertTrue($pool1->save($item->set('bar'))); + + $item = $pool1->getItem('foo'); + $this->assertTrue($item->isHit()); + $this->assertSame('bar', $item->get()); + + $pool2 = new ApcuAdapter($namespace, 0, 'p2'); + + $item = $pool2->getItem('foo'); + $this->assertFalse($item->isHit()); + $this->assertNull($item->get()); + + $item = $pool1->getItem('foo'); + $this->assertFalse($item->isHit()); + $this->assertNull($item->get()); + } + + public function testNamespace() + { + $namespace = str_replace('\\', '.', \get_class($this)); + + $pool1 = new ApcuAdapter($namespace.'_1', 0, 'p1'); + + $item = $pool1->getItem('foo'); + $this->assertFalse($item->isHit()); + $this->assertTrue($pool1->save($item->set('bar'))); + + $item = $pool1->getItem('foo'); + $this->assertTrue($item->isHit()); + $this->assertSame('bar', $item->get()); + + $pool2 = new ApcuAdapter($namespace.'_2', 0, 'p1'); + + $item = $pool2->getItem('foo'); + $this->assertFalse($item->isHit()); + $this->assertNull($item->get()); + + $item = $pool1->getItem('foo'); + $this->assertTrue($item->isHit()); + $this->assertSame('bar', $item->get()); + } + + public function testWithCliSapi() + { + try { + // disable PHPUnit error handler to mimic a production environment + $isCalled = false; + set_error_handler(function () use (&$isCalled) { + $isCalled = true; + }); + $pool = new ApcuAdapter(str_replace('\\', '.', __CLASS__)); + $pool->setLogger(new NullLogger()); + + $item = $pool->getItem('foo'); + $item->isHit(); + $pool->save($item->set('bar')); + $this->assertFalse($isCalled); + } finally { + restore_error_handler(); + } + } +} diff --git a/lib/symfony/cache/Tests/Adapter/ArrayAdapterTest.php b/lib/symfony/cache/Tests/Adapter/ArrayAdapterTest.php new file mode 100644 index 000000000..e6adc9d01 --- /dev/null +++ b/lib/symfony/cache/Tests/Adapter/ArrayAdapterTest.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests\Adapter; + +use Symfony\Component\Cache\Adapter\ArrayAdapter; + +/** + * @group time-sensitive + */ +class ArrayAdapterTest extends AdapterTestCase +{ + protected $skippedTests = [ + 'testDeferredSaveWithoutCommit' => 'Assumes a shared cache which ArrayAdapter is not.', + 'testSaveWithoutExpire' => 'Assumes a shared cache which ArrayAdapter is not.', + ]; + + public function createCachePool($defaultLifetime = 0) + { + return new ArrayAdapter($defaultLifetime); + } + + public function testGetValuesHitAndMiss() + { + /** @var ArrayAdapter $cache */ + $cache = $this->createCachePool(); + + // Hit + $item = $cache->getItem('foo'); + $item->set('4711'); + $cache->save($item); + + $fooItem = $cache->getItem('foo'); + $this->assertTrue($fooItem->isHit()); + $this->assertEquals('4711', $fooItem->get()); + + // Miss (should be present as NULL in $values) + $cache->getItem('bar'); + + $values = $cache->getValues(); + + $this->assertCount(2, $values); + $this->assertArrayHasKey('foo', $values); + $this->assertSame(serialize('4711'), $values['foo']); + $this->assertArrayHasKey('bar', $values); + $this->assertNull($values['bar']); + } +} diff --git a/lib/symfony/cache/Tests/Adapter/ChainAdapterTest.php b/lib/symfony/cache/Tests/Adapter/ChainAdapterTest.php new file mode 100644 index 000000000..3b42697fe --- /dev/null +++ b/lib/symfony/cache/Tests/Adapter/ChainAdapterTest.php @@ -0,0 +1,118 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests\Adapter; + +use Symfony\Component\Cache\Adapter\AdapterInterface; +use Symfony\Component\Cache\Adapter\ArrayAdapter; +use Symfony\Component\Cache\Adapter\ChainAdapter; +use Symfony\Component\Cache\Adapter\FilesystemAdapter; +use Symfony\Component\Cache\PruneableInterface; +use Symfony\Component\Cache\Tests\Fixtures\ExternalAdapter; + +/** + * @author Kévin Dunglas + * @group time-sensitive + */ +class ChainAdapterTest extends AdapterTestCase +{ + public function createCachePool($defaultLifetime = 0) + { + return new ChainAdapter([new ArrayAdapter($defaultLifetime), new ExternalAdapter(), new FilesystemAdapter('', $defaultLifetime)], $defaultLifetime); + } + + /** + * @expectedException \Symfony\Component\Cache\Exception\InvalidArgumentException + * @expectedExceptionMessage At least one adapter must be specified. + */ + public function testEmptyAdaptersException() + { + new ChainAdapter([]); + } + + /** + * @expectedException \Symfony\Component\Cache\Exception\InvalidArgumentException + * @expectedExceptionMessage The class "stdClass" does not implement + */ + public function testInvalidAdapterException() + { + new ChainAdapter([new \stdClass()]); + } + + public function testPrune() + { + if (isset($this->skippedTests[__FUNCTION__])) { + $this->markTestSkipped($this->skippedTests[__FUNCTION__]); + } + + $cache = new ChainAdapter([ + $this->getPruneableMock(), + $this->getNonPruneableMock(), + $this->getPruneableMock(), + ]); + $this->assertTrue($cache->prune()); + + $cache = new ChainAdapter([ + $this->getPruneableMock(), + $this->getFailingPruneableMock(), + $this->getPruneableMock(), + ]); + $this->assertFalse($cache->prune()); + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject|PruneableCacheInterface + */ + private function getPruneableMock() + { + $pruneable = $this + ->getMockBuilder(PruneableCacheInterface::class) + ->getMock(); + + $pruneable + ->expects($this->atLeastOnce()) + ->method('prune') + ->willReturn(true); + + return $pruneable; + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject|PruneableCacheInterface + */ + private function getFailingPruneableMock() + { + $pruneable = $this + ->getMockBuilder(PruneableCacheInterface::class) + ->getMock(); + + $pruneable + ->expects($this->atLeastOnce()) + ->method('prune') + ->willReturn(false); + + return $pruneable; + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject|AdapterInterface + */ + private function getNonPruneableMock() + { + return $this + ->getMockBuilder(AdapterInterface::class) + ->getMock(); + } +} + +interface PruneableCacheInterface extends PruneableInterface, AdapterInterface +{ +} diff --git a/lib/symfony/cache/Tests/Adapter/DoctrineAdapterTest.php b/lib/symfony/cache/Tests/Adapter/DoctrineAdapterTest.php new file mode 100644 index 000000000..8f520cb59 --- /dev/null +++ b/lib/symfony/cache/Tests/Adapter/DoctrineAdapterTest.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests\Adapter; + +use Symfony\Component\Cache\Adapter\DoctrineAdapter; +use Symfony\Component\Cache\Tests\Fixtures\ArrayCache; + +/** + * @group time-sensitive + */ +class DoctrineAdapterTest extends AdapterTestCase +{ + protected $skippedTests = [ + 'testDeferredSaveWithoutCommit' => 'Assumes a shared cache which ArrayCache is not.', + 'testSaveWithoutExpire' => 'Assumes a shared cache which ArrayCache is not.', + 'testNotUnserializable' => 'ArrayCache does not use serialize/unserialize', + ]; + + public function createCachePool($defaultLifetime = 0) + { + return new DoctrineAdapter(new ArrayCache($defaultLifetime), '', $defaultLifetime); + } +} diff --git a/lib/symfony/cache/Tests/Adapter/FilesystemAdapterTest.php b/lib/symfony/cache/Tests/Adapter/FilesystemAdapterTest.php new file mode 100644 index 000000000..fa8306826 --- /dev/null +++ b/lib/symfony/cache/Tests/Adapter/FilesystemAdapterTest.php @@ -0,0 +1,61 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests\Adapter; + +use Psr\Cache\CacheItemPoolInterface; +use Symfony\Component\Cache\Adapter\FilesystemAdapter; + +/** + * @group time-sensitive + */ +class FilesystemAdapterTest extends AdapterTestCase +{ + public function createCachePool($defaultLifetime = 0) + { + return new FilesystemAdapter('', $defaultLifetime); + } + + public static function tearDownAfterClass() + { + self::rmdir(sys_get_temp_dir().'/symfony-cache'); + } + + public static function rmdir($dir) + { + if (!file_exists($dir)) { + return; + } + if (!$dir || 0 !== strpos(\dirname($dir), sys_get_temp_dir())) { + throw new \Exception(__METHOD__."() operates only on subdirs of system's temp dir"); + } + $children = new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator($dir, \RecursiveDirectoryIterator::SKIP_DOTS), + \RecursiveIteratorIterator::CHILD_FIRST + ); + foreach ($children as $child) { + if ($child->isDir()) { + rmdir($child); + } else { + unlink($child); + } + } + rmdir($dir); + } + + protected function isPruned(CacheItemPoolInterface $cache, $name) + { + $getFileMethod = (new \ReflectionObject($cache))->getMethod('getFile'); + $getFileMethod->setAccessible(true); + + return !file_exists($getFileMethod->invoke($cache, $name)); + } +} diff --git a/lib/symfony/cache/Tests/Adapter/MaxIdLengthAdapterTest.php b/lib/symfony/cache/Tests/Adapter/MaxIdLengthAdapterTest.php new file mode 100644 index 000000000..8bea26810 --- /dev/null +++ b/lib/symfony/cache/Tests/Adapter/MaxIdLengthAdapterTest.php @@ -0,0 +1,85 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests\Adapter; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Cache\Adapter\AbstractAdapter; + +class MaxIdLengthAdapterTest extends TestCase +{ + public function testLongKey() + { + $cache = $this->getMockBuilder(MaxIdLengthAdapter::class) + ->setConstructorArgs([str_repeat('-', 10)]) + ->setMethods(['doHave', 'doFetch', 'doDelete', 'doSave', 'doClear']) + ->getMock(); + + $cache->expects($this->exactly(2)) + ->method('doHave') + ->withConsecutive( + [$this->equalTo('----------:0GTYWa9n4ed8vqNlOT2iEr:')], + [$this->equalTo('----------:---------------------------------------')] + ); + + $cache->hasItem(str_repeat('-', 40)); + $cache->hasItem(str_repeat('-', 39)); + } + + public function testLongKeyVersioning() + { + $cache = $this->getMockBuilder(MaxIdLengthAdapter::class) + ->setConstructorArgs([str_repeat('-', 26)]) + ->getMock(); + + $reflectionClass = new \ReflectionClass(AbstractAdapter::class); + + $reflectionMethod = $reflectionClass->getMethod('getId'); + $reflectionMethod->setAccessible(true); + + // No versioning enabled + $this->assertEquals('--------------------------:------------', $reflectionMethod->invokeArgs($cache, [str_repeat('-', 12)])); + $this->assertLessThanOrEqual(50, \strlen($reflectionMethod->invokeArgs($cache, [str_repeat('-', 12)]))); + $this->assertLessThanOrEqual(50, \strlen($reflectionMethod->invokeArgs($cache, [str_repeat('-', 23)]))); + $this->assertLessThanOrEqual(50, \strlen($reflectionMethod->invokeArgs($cache, [str_repeat('-', 40)]))); + + $reflectionProperty = $reflectionClass->getProperty('versioningIsEnabled'); + $reflectionProperty->setAccessible(true); + $reflectionProperty->setValue($cache, true); + + // Versioning enabled + $this->assertEquals('--------------------------:1:------------', $reflectionMethod->invokeArgs($cache, [str_repeat('-', 12)])); + $this->assertLessThanOrEqual(50, \strlen($reflectionMethod->invokeArgs($cache, [str_repeat('-', 12)]))); + $this->assertLessThanOrEqual(50, \strlen($reflectionMethod->invokeArgs($cache, [str_repeat('-', 23)]))); + $this->assertLessThanOrEqual(50, \strlen($reflectionMethod->invokeArgs($cache, [str_repeat('-', 40)]))); + } + + /** + * @expectedException \Symfony\Component\Cache\Exception\InvalidArgumentException + * @expectedExceptionMessage Namespace must be 26 chars max, 40 given ("----------------------------------------") + */ + public function testTooLongNamespace() + { + $cache = $this->getMockBuilder(MaxIdLengthAdapter::class) + ->setConstructorArgs([str_repeat('-', 40)]) + ->getMock(); + } +} + +abstract class MaxIdLengthAdapter extends AbstractAdapter +{ + protected $maxIdLength = 50; + + public function __construct($ns) + { + parent::__construct($ns); + } +} diff --git a/lib/symfony/cache/Tests/Adapter/MemcachedAdapterTest.php b/lib/symfony/cache/Tests/Adapter/MemcachedAdapterTest.php new file mode 100644 index 000000000..2a88fea18 --- /dev/null +++ b/lib/symfony/cache/Tests/Adapter/MemcachedAdapterTest.php @@ -0,0 +1,200 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests\Adapter; + +use Symfony\Component\Cache\Adapter\AbstractAdapter; +use Symfony\Component\Cache\Adapter\MemcachedAdapter; + +class MemcachedAdapterTest extends AdapterTestCase +{ + protected $skippedTests = [ + 'testHasItemReturnsFalseWhenDeferredItemIsExpired' => 'Testing expiration slows down the test suite', + 'testDefaultLifeTime' => 'Testing expiration slows down the test suite', + ]; + + protected static $client; + + public static function setupBeforeClass() + { + if (!MemcachedAdapter::isSupported()) { + self::markTestSkipped('Extension memcached >=2.2.0 required.'); + } + self::$client = AbstractAdapter::createConnection('memcached://'.getenv('MEMCACHED_HOST'), ['binary_protocol' => false]); + self::$client->get('foo'); + $code = self::$client->getResultCode(); + + if (\Memcached::RES_SUCCESS !== $code && \Memcached::RES_NOTFOUND !== $code) { + self::markTestSkipped('Memcached error: '.strtolower(self::$client->getResultMessage())); + } + } + + public function createCachePool($defaultLifetime = 0) + { + $client = $defaultLifetime ? AbstractAdapter::createConnection('memcached://'.getenv('MEMCACHED_HOST')) : self::$client; + + return new MemcachedAdapter($client, str_replace('\\', '.', __CLASS__), $defaultLifetime); + } + + public function testOptions() + { + $client = MemcachedAdapter::createConnection([], [ + 'libketama_compatible' => false, + 'distribution' => 'modula', + 'compression' => true, + 'serializer' => 'php', + 'hash' => 'md5', + ]); + + $this->assertSame(\Memcached::SERIALIZER_PHP, $client->getOption(\Memcached::OPT_SERIALIZER)); + $this->assertSame(\Memcached::HASH_MD5, $client->getOption(\Memcached::OPT_HASH)); + $this->assertTrue($client->getOption(\Memcached::OPT_COMPRESSION)); + $this->assertSame(0, $client->getOption(\Memcached::OPT_LIBKETAMA_COMPATIBLE)); + $this->assertSame(\Memcached::DISTRIBUTION_MODULA, $client->getOption(\Memcached::OPT_DISTRIBUTION)); + } + + /** + * @dataProvider provideBadOptions + * @expectedException \ErrorException + * @expectedExceptionMessage constant(): Couldn't find constant Memcached:: + */ + public function testBadOptions($name, $value) + { + MemcachedAdapter::createConnection([], [$name => $value]); + } + + public function provideBadOptions() + { + return [ + ['foo', 'bar'], + ['hash', 'zyx'], + ['serializer', 'zyx'], + ['distribution', 'zyx'], + ]; + } + + public function testDefaultOptions() + { + $this->assertTrue(MemcachedAdapter::isSupported()); + + $client = MemcachedAdapter::createConnection([]); + + $this->assertTrue($client->getOption(\Memcached::OPT_COMPRESSION)); + $this->assertSame(1, $client->getOption(\Memcached::OPT_BINARY_PROTOCOL)); + $this->assertSame(1, $client->getOption(\Memcached::OPT_TCP_NODELAY)); + $this->assertSame(1, $client->getOption(\Memcached::OPT_LIBKETAMA_COMPATIBLE)); + } + + /** + * @expectedException \Symfony\Component\Cache\Exception\CacheException + * @expectedExceptionMessage MemcachedAdapter: "serializer" option must be "php" or "igbinary". + */ + public function testOptionSerializer() + { + if (!\Memcached::HAVE_JSON) { + $this->markTestSkipped('Memcached::HAVE_JSON required'); + } + + new MemcachedAdapter(MemcachedAdapter::createConnection([], ['serializer' => 'json'])); + } + + /** + * @dataProvider provideServersSetting + */ + public function testServersSetting($dsn, $host, $port) + { + $client1 = MemcachedAdapter::createConnection($dsn); + $client2 = MemcachedAdapter::createConnection([$dsn]); + $client3 = MemcachedAdapter::createConnection([[$host, $port]]); + $expect = [ + 'host' => $host, + 'port' => $port, + ]; + + $f = function ($s) { return ['host' => $s['host'], 'port' => $s['port']]; }; + $this->assertSame([$expect], array_map($f, $client1->getServerList())); + $this->assertSame([$expect], array_map($f, $client2->getServerList())); + $this->assertSame([$expect], array_map($f, $client3->getServerList())); + } + + public function provideServersSetting() + { + yield [ + 'memcached://127.0.0.1/50', + '127.0.0.1', + 11211, + ]; + yield [ + 'memcached://localhost:11222?weight=25', + 'localhost', + 11222, + ]; + if (filter_var(ini_get('memcached.use_sasl'), FILTER_VALIDATE_BOOLEAN)) { + yield [ + 'memcached://user:password@127.0.0.1?weight=50', + '127.0.0.1', + 11211, + ]; + } + yield [ + 'memcached:///var/run/memcached.sock?weight=25', + '/var/run/memcached.sock', + 0, + ]; + yield [ + 'memcached:///var/local/run/memcached.socket?weight=25', + '/var/local/run/memcached.socket', + 0, + ]; + if (filter_var(ini_get('memcached.use_sasl'), FILTER_VALIDATE_BOOLEAN)) { + yield [ + 'memcached://user:password@/var/local/run/memcached.socket?weight=25', + '/var/local/run/memcached.socket', + 0, + ]; + } + } + + /** + * @dataProvider provideDsnWithOptions + */ + public function testDsnWithOptions($dsn, array $options, array $expectedOptions) + { + $client = MemcachedAdapter::createConnection($dsn, $options); + + foreach ($expectedOptions as $option => $expect) { + $this->assertSame($expect, $client->getOption($option)); + } + } + + public function provideDsnWithOptions() + { + if (!class_exists('\Memcached')) { + self::markTestSkipped('Extension memcached required.'); + } + + yield [ + 'memcached://localhost:11222?retry_timeout=10', + [\Memcached::OPT_RETRY_TIMEOUT => 8], + [\Memcached::OPT_RETRY_TIMEOUT => 10], + ]; + yield [ + 'memcached://localhost:11222?socket_recv_size=1&socket_send_size=2', + [\Memcached::OPT_RETRY_TIMEOUT => 8], + [\Memcached::OPT_SOCKET_RECV_SIZE => 1, \Memcached::OPT_SOCKET_SEND_SIZE => 2, \Memcached::OPT_RETRY_TIMEOUT => 8], + ]; + } + + public function testClear() + { + $this->assertTrue($this->createCachePool()->clear()); + } +} diff --git a/lib/symfony/cache/Tests/Adapter/NamespacedProxyAdapterTest.php b/lib/symfony/cache/Tests/Adapter/NamespacedProxyAdapterTest.php new file mode 100644 index 000000000..c27140333 --- /dev/null +++ b/lib/symfony/cache/Tests/Adapter/NamespacedProxyAdapterTest.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests\Adapter; + +use Symfony\Component\Cache\Adapter\ArrayAdapter; +use Symfony\Component\Cache\Adapter\ProxyAdapter; + +/** + * @group time-sensitive + */ +class NamespacedProxyAdapterTest extends ProxyAdapterTest +{ + public function createCachePool($defaultLifetime = 0) + { + return new ProxyAdapter(new ArrayAdapter($defaultLifetime), 'foo', $defaultLifetime); + } +} diff --git a/lib/symfony/cache/Tests/Adapter/NullAdapterTest.php b/lib/symfony/cache/Tests/Adapter/NullAdapterTest.php new file mode 100644 index 000000000..b771fa0ed --- /dev/null +++ b/lib/symfony/cache/Tests/Adapter/NullAdapterTest.php @@ -0,0 +1,128 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests\Adapter; + +use PHPUnit\Framework\TestCase; +use Psr\Cache\CacheItemInterface; +use Symfony\Component\Cache\Adapter\NullAdapter; + +/** + * @group time-sensitive + */ +class NullAdapterTest extends TestCase +{ + public function createCachePool() + { + return new NullAdapter(); + } + + public function testGetItem() + { + $adapter = $this->createCachePool(); + + $item = $adapter->getItem('key'); + $this->assertFalse($item->isHit()); + $this->assertNull($item->get(), "Item's value must be null when isHit is false."); + } + + public function testHasItem() + { + $this->assertFalse($this->createCachePool()->hasItem('key')); + } + + public function testGetItems() + { + $adapter = $this->createCachePool(); + + $keys = ['foo', 'bar', 'baz', 'biz']; + + /** @var CacheItemInterface[] $items */ + $items = $adapter->getItems($keys); + $count = 0; + + foreach ($items as $key => $item) { + $itemKey = $item->getKey(); + + $this->assertEquals($itemKey, $key, 'Keys must be preserved when fetching multiple items'); + $this->assertContains($key, $keys, 'Cache key can not change.'); + $this->assertFalse($item->isHit()); + + // Remove $key for $keys + foreach ($keys as $k => $v) { + if ($v === $key) { + unset($keys[$k]); + } + } + + ++$count; + } + + $this->assertSame(4, $count); + } + + public function testIsHit() + { + $adapter = $this->createCachePool(); + + $item = $adapter->getItem('key'); + $this->assertFalse($item->isHit()); + } + + public function testClear() + { + $this->assertTrue($this->createCachePool()->clear()); + } + + public function testDeleteItem() + { + $this->assertTrue($this->createCachePool()->deleteItem('key')); + } + + public function testDeleteItems() + { + $this->assertTrue($this->createCachePool()->deleteItems(['key', 'foo', 'bar'])); + } + + public function testSave() + { + $adapter = $this->createCachePool(); + + $item = $adapter->getItem('key'); + $this->assertFalse($item->isHit()); + $this->assertNull($item->get(), "Item's value must be null when isHit is false."); + + $this->assertFalse($adapter->save($item)); + } + + public function testDeferredSave() + { + $adapter = $this->createCachePool(); + + $item = $adapter->getItem('key'); + $this->assertFalse($item->isHit()); + $this->assertNull($item->get(), "Item's value must be null when isHit is false."); + + $this->assertFalse($adapter->saveDeferred($item)); + } + + public function testCommit() + { + $adapter = $this->createCachePool(); + + $item = $adapter->getItem('key'); + $this->assertFalse($item->isHit()); + $this->assertNull($item->get(), "Item's value must be null when isHit is false."); + + $this->assertFalse($adapter->saveDeferred($item)); + $this->assertFalse($this->createCachePool()->commit()); + } +} diff --git a/lib/symfony/cache/Tests/Adapter/PdoAdapterTest.php b/lib/symfony/cache/Tests/Adapter/PdoAdapterTest.php new file mode 100644 index 000000000..b587cf6d6 --- /dev/null +++ b/lib/symfony/cache/Tests/Adapter/PdoAdapterTest.php @@ -0,0 +1,73 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests\Adapter; + +use Symfony\Component\Cache\Adapter\PdoAdapter; +use Symfony\Component\Cache\Tests\Traits\PdoPruneableTrait; + +/** + * @group time-sensitive + */ +class PdoAdapterTest extends AdapterTestCase +{ + use PdoPruneableTrait; + + protected static $dbFile; + + public static function setupBeforeClass() + { + if (!\extension_loaded('pdo_sqlite')) { + self::markTestSkipped('Extension pdo_sqlite required.'); + } + + self::$dbFile = tempnam(sys_get_temp_dir(), 'sf_sqlite_cache'); + + $pool = new PdoAdapter('sqlite:'.self::$dbFile); + $pool->createTable(); + } + + public static function tearDownAfterClass() + { + @unlink(self::$dbFile); + } + + public function createCachePool($defaultLifetime = 0) + { + return new PdoAdapter('sqlite:'.self::$dbFile, 'ns', $defaultLifetime); + } + + public function testCleanupExpiredItems() + { + $pdo = new \PDO('sqlite:'.self::$dbFile); + + $getCacheItemCount = function () use ($pdo) { + return (int) $pdo->query('SELECT COUNT(*) FROM cache_items')->fetch(\PDO::FETCH_COLUMN); + }; + + $this->assertSame(0, $getCacheItemCount()); + + $cache = $this->createCachePool(); + + $item = $cache->getItem('some_nice_key'); + $item->expiresAfter(1); + $item->set(1); + + $cache->save($item); + $this->assertSame(1, $getCacheItemCount()); + + sleep(2); + + $newItem = $cache->getItem($item->getKey()); + $this->assertFalse($newItem->isHit()); + $this->assertSame(0, $getCacheItemCount(), 'PDOAdapter must clean up expired items'); + } +} diff --git a/lib/symfony/cache/Tests/Adapter/PdoDbalAdapterTest.php b/lib/symfony/cache/Tests/Adapter/PdoDbalAdapterTest.php new file mode 100644 index 000000000..d0699f1e3 --- /dev/null +++ b/lib/symfony/cache/Tests/Adapter/PdoDbalAdapterTest.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests\Adapter; + +use Doctrine\DBAL\DriverManager; +use Symfony\Component\Cache\Adapter\PdoAdapter; +use Symfony\Component\Cache\Tests\Traits\PdoPruneableTrait; + +/** + * @group time-sensitive + */ +class PdoDbalAdapterTest extends AdapterTestCase +{ + use PdoPruneableTrait; + + protected static $dbFile; + + public static function setupBeforeClass() + { + if (!\extension_loaded('pdo_sqlite')) { + self::markTestSkipped('Extension pdo_sqlite required.'); + } + + self::$dbFile = tempnam(sys_get_temp_dir(), 'sf_sqlite_cache'); + + $pool = new PdoAdapter(DriverManager::getConnection(['driver' => 'pdo_sqlite', 'path' => self::$dbFile])); + $pool->createTable(); + } + + public static function tearDownAfterClass() + { + @unlink(self::$dbFile); + } + + public function createCachePool($defaultLifetime = 0) + { + return new PdoAdapter(DriverManager::getConnection(['driver' => 'pdo_sqlite', 'path' => self::$dbFile]), '', $defaultLifetime); + } +} diff --git a/lib/symfony/cache/Tests/Adapter/PhpArrayAdapterTest.php b/lib/symfony/cache/Tests/Adapter/PhpArrayAdapterTest.php new file mode 100644 index 000000000..a227adffa --- /dev/null +++ b/lib/symfony/cache/Tests/Adapter/PhpArrayAdapterTest.php @@ -0,0 +1,133 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests\Adapter; + +use Psr\Cache\CacheItemInterface; +use Symfony\Component\Cache\Adapter\NullAdapter; +use Symfony\Component\Cache\Adapter\PhpArrayAdapter; + +/** + * @group time-sensitive + */ +class PhpArrayAdapterTest extends AdapterTestCase +{ + protected $skippedTests = [ + 'testBasicUsage' => 'PhpArrayAdapter is read-only.', + 'testBasicUsageWithLongKey' => 'PhpArrayAdapter is read-only.', + 'testClear' => 'PhpArrayAdapter is read-only.', + 'testClearWithDeferredItems' => 'PhpArrayAdapter is read-only.', + 'testDeleteItem' => 'PhpArrayAdapter is read-only.', + 'testSaveExpired' => 'PhpArrayAdapter is read-only.', + 'testSaveWithoutExpire' => 'PhpArrayAdapter is read-only.', + 'testDeferredSave' => 'PhpArrayAdapter is read-only.', + 'testDeferredSaveWithoutCommit' => 'PhpArrayAdapter is read-only.', + 'testDeleteItems' => 'PhpArrayAdapter is read-only.', + 'testDeleteDeferredItem' => 'PhpArrayAdapter is read-only.', + 'testCommit' => 'PhpArrayAdapter is read-only.', + 'testSaveDeferredWhenChangingValues' => 'PhpArrayAdapter is read-only.', + 'testSaveDeferredOverwrite' => 'PhpArrayAdapter is read-only.', + 'testIsHitDeferred' => 'PhpArrayAdapter is read-only.', + + 'testExpiresAt' => 'PhpArrayAdapter does not support expiration.', + 'testExpiresAtWithNull' => 'PhpArrayAdapter does not support expiration.', + 'testExpiresAfterWithNull' => 'PhpArrayAdapter does not support expiration.', + 'testDeferredExpired' => 'PhpArrayAdapter does not support expiration.', + 'testExpiration' => 'PhpArrayAdapter does not support expiration.', + + 'testGetItemInvalidKeys' => 'PhpArrayAdapter does not throw exceptions on invalid key.', + 'testGetItemsInvalidKeys' => 'PhpArrayAdapter does not throw exceptions on invalid key.', + 'testHasItemInvalidKeys' => 'PhpArrayAdapter does not throw exceptions on invalid key.', + 'testDeleteItemInvalidKeys' => 'PhpArrayAdapter does not throw exceptions on invalid key.', + 'testDeleteItemsInvalidKeys' => 'PhpArrayAdapter does not throw exceptions on invalid key.', + + 'testDefaultLifeTime' => 'PhpArrayAdapter does not allow configuring a default lifetime.', + 'testPrune' => 'PhpArrayAdapter just proxies', + ]; + + protected static $file; + + public static function setupBeforeClass() + { + self::$file = sys_get_temp_dir().'/symfony-cache/php-array-adapter-test.php'; + } + + protected function tearDown() + { + if (file_exists(sys_get_temp_dir().'/symfony-cache')) { + FilesystemAdapterTest::rmdir(sys_get_temp_dir().'/symfony-cache'); + } + } + + public function createCachePool() + { + return new PhpArrayAdapterWrapper(self::$file, new NullAdapter()); + } + + public function testStore() + { + $arrayWithRefs = []; + $arrayWithRefs[0] = 123; + $arrayWithRefs[1] = &$arrayWithRefs[0]; + + $object = (object) [ + 'foo' => 'bar', + 'foo2' => 'bar2', + ]; + + $expected = [ + 'null' => null, + 'serializedString' => serialize($object), + 'arrayWithRefs' => $arrayWithRefs, + 'object' => $object, + 'arrayWithObject' => ['bar' => $object], + ]; + + $adapter = $this->createCachePool(); + $adapter->warmUp($expected); + + foreach ($expected as $key => $value) { + $this->assertSame(serialize($value), serialize($adapter->getItem($key)->get()), 'Warm up should create a PHP file that OPCache can load in memory'); + } + } + + public function testStoredFile() + { + $expected = [ + 'integer' => 42, + 'float' => 42.42, + 'boolean' => true, + 'array_simple' => ['foo', 'bar'], + 'array_associative' => ['foo' => 'bar', 'foo2' => 'bar2'], + ]; + + $adapter = $this->createCachePool(); + $adapter->warmUp($expected); + + $values = eval(substr(file_get_contents(self::$file), 6)); + + $this->assertSame($expected, $values, 'Warm up should create a PHP file that OPCache can load in memory'); + } +} + +class PhpArrayAdapterWrapper extends PhpArrayAdapter +{ + public function save(CacheItemInterface $item) + { + \call_user_func(\Closure::bind(function () use ($item) { + $this->values[$item->getKey()] = $item->get(); + $this->warmUp($this->values); + $this->values = eval(substr(file_get_contents($this->file), 6)); + }, $this, PhpArrayAdapter::class)); + + return true; + } +} diff --git a/lib/symfony/cache/Tests/Adapter/PhpArrayAdapterWithFallbackTest.php b/lib/symfony/cache/Tests/Adapter/PhpArrayAdapterWithFallbackTest.php new file mode 100644 index 000000000..a7feced4e --- /dev/null +++ b/lib/symfony/cache/Tests/Adapter/PhpArrayAdapterWithFallbackTest.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests\Adapter; + +use Symfony\Component\Cache\Adapter\FilesystemAdapter; +use Symfony\Component\Cache\Adapter\PhpArrayAdapter; + +/** + * @group time-sensitive + */ +class PhpArrayAdapterWithFallbackTest extends AdapterTestCase +{ + protected $skippedTests = [ + 'testGetItemInvalidKeys' => 'PhpArrayAdapter does not throw exceptions on invalid key.', + 'testGetItemsInvalidKeys' => 'PhpArrayAdapter does not throw exceptions on invalid key.', + 'testHasItemInvalidKeys' => 'PhpArrayAdapter does not throw exceptions on invalid key.', + 'testDeleteItemInvalidKeys' => 'PhpArrayAdapter does not throw exceptions on invalid key.', + 'testDeleteItemsInvalidKeys' => 'PhpArrayAdapter does not throw exceptions on invalid key.', + 'testPrune' => 'PhpArrayAdapter just proxies', + ]; + + protected static $file; + + public static function setupBeforeClass() + { + self::$file = sys_get_temp_dir().'/symfony-cache/php-array-adapter-test.php'; + } + + protected function tearDown() + { + if (file_exists(sys_get_temp_dir().'/symfony-cache')) { + FilesystemAdapterTest::rmdir(sys_get_temp_dir().'/symfony-cache'); + } + } + + public function createCachePool($defaultLifetime = 0) + { + return new PhpArrayAdapter(self::$file, new FilesystemAdapter('php-array-fallback', $defaultLifetime)); + } +} diff --git a/lib/symfony/cache/Tests/Adapter/PhpFilesAdapterTest.php b/lib/symfony/cache/Tests/Adapter/PhpFilesAdapterTest.php new file mode 100644 index 000000000..247160d53 --- /dev/null +++ b/lib/symfony/cache/Tests/Adapter/PhpFilesAdapterTest.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests\Adapter; + +use Psr\Cache\CacheItemPoolInterface; +use Symfony\Component\Cache\Adapter\PhpFilesAdapter; + +/** + * @group time-sensitive + */ +class PhpFilesAdapterTest extends AdapterTestCase +{ + protected $skippedTests = [ + 'testDefaultLifeTime' => 'PhpFilesAdapter does not allow configuring a default lifetime.', + ]; + + public function createCachePool() + { + if (!PhpFilesAdapter::isSupported()) { + $this->markTestSkipped('OPcache extension is not enabled.'); + } + + return new PhpFilesAdapter('sf-cache'); + } + + public static function tearDownAfterClass() + { + FilesystemAdapterTest::rmdir(sys_get_temp_dir().'/symfony-cache'); + } + + protected function isPruned(CacheItemPoolInterface $cache, $name) + { + $getFileMethod = (new \ReflectionObject($cache))->getMethod('getFile'); + $getFileMethod->setAccessible(true); + + return !file_exists($getFileMethod->invoke($cache, $name)); + } +} diff --git a/lib/symfony/cache/Tests/Adapter/PredisAdapterTest.php b/lib/symfony/cache/Tests/Adapter/PredisAdapterTest.php new file mode 100644 index 000000000..f311a3539 --- /dev/null +++ b/lib/symfony/cache/Tests/Adapter/PredisAdapterTest.php @@ -0,0 +1,53 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests\Adapter; + +use Predis\Connection\StreamConnection; +use Symfony\Component\Cache\Adapter\RedisAdapter; + +class PredisAdapterTest extends AbstractRedisAdapterTest +{ + public static function setupBeforeClass() + { + parent::setupBeforeClass(); + self::$redis = new \Predis\Client(['host' => getenv('REDIS_HOST')]); + } + + public function testCreateConnection() + { + $redisHost = getenv('REDIS_HOST'); + + $redis = RedisAdapter::createConnection('redis://'.$redisHost.'/1', ['class' => \Predis\Client::class, 'timeout' => 3]); + $this->assertInstanceOf(\Predis\Client::class, $redis); + + $connection = $redis->getConnection(); + $this->assertInstanceOf(StreamConnection::class, $connection); + + $params = [ + 'scheme' => 'tcp', + 'host' => $redisHost, + 'path' => '', + 'dbindex' => '1', + 'port' => 6379, + 'class' => 'Predis\Client', + 'timeout' => 3, + 'persistent' => 0, + 'persistent_id' => null, + 'read_timeout' => 0, + 'retry_interval' => 0, + 'lazy' => false, + 'database' => '1', + 'password' => null, + ]; + $this->assertSame($params, $connection->getParameters()->toArray()); + } +} diff --git a/lib/symfony/cache/Tests/Adapter/PredisClusterAdapterTest.php b/lib/symfony/cache/Tests/Adapter/PredisClusterAdapterTest.php new file mode 100644 index 000000000..f723dc446 --- /dev/null +++ b/lib/symfony/cache/Tests/Adapter/PredisClusterAdapterTest.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests\Adapter; + +class PredisClusterAdapterTest extends AbstractRedisAdapterTest +{ + public static function setupBeforeClass() + { + parent::setupBeforeClass(); + self::$redis = new \Predis\Client([['host' => getenv('REDIS_HOST')]]); + } + + public static function tearDownAfterClass() + { + self::$redis = null; + } +} diff --git a/lib/symfony/cache/Tests/Adapter/PredisRedisClusterAdapterTest.php b/lib/symfony/cache/Tests/Adapter/PredisRedisClusterAdapterTest.php new file mode 100644 index 000000000..6bf0348a1 --- /dev/null +++ b/lib/symfony/cache/Tests/Adapter/PredisRedisClusterAdapterTest.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests\Adapter; + +class PredisRedisClusterAdapterTest extends AbstractRedisAdapterTest +{ + public static function setupBeforeClass() + { + if (!$hosts = getenv('REDIS_CLUSTER_HOSTS')) { + self::markTestSkipped('REDIS_CLUSTER_HOSTS env var is not defined.'); + } + self::$redis = new \Predis\Client(explode(' ', $hosts), ['cluster' => 'redis']); + } + + public static function tearDownAfterClass() + { + self::$redis = null; + } +} diff --git a/lib/symfony/cache/Tests/Adapter/ProxyAdapterTest.php b/lib/symfony/cache/Tests/Adapter/ProxyAdapterTest.php new file mode 100644 index 000000000..f69ad6793 --- /dev/null +++ b/lib/symfony/cache/Tests/Adapter/ProxyAdapterTest.php @@ -0,0 +1,71 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests\Adapter; + +use Psr\Cache\CacheItemInterface; +use Symfony\Component\Cache\Adapter\ArrayAdapter; +use Symfony\Component\Cache\Adapter\ProxyAdapter; +use Symfony\Component\Cache\CacheItem; + +/** + * @group time-sensitive + */ +class ProxyAdapterTest extends AdapterTestCase +{ + protected $skippedTests = [ + 'testDeferredSaveWithoutCommit' => 'Assumes a shared cache which ArrayAdapter is not.', + 'testSaveWithoutExpire' => 'Assumes a shared cache which ArrayAdapter is not.', + 'testPrune' => 'ProxyAdapter just proxies', + ]; + + public function createCachePool($defaultLifetime = 0) + { + return new ProxyAdapter(new ArrayAdapter(), '', $defaultLifetime); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage OK bar + */ + public function testProxyfiedItem() + { + $item = new CacheItem(); + $pool = new ProxyAdapter(new TestingArrayAdapter($item)); + + $proxyItem = $pool->getItem('foo'); + + $this->assertNotSame($item, $proxyItem); + $pool->save($proxyItem->set('bar')); + } +} + +class TestingArrayAdapter extends ArrayAdapter +{ + private $item; + + public function __construct(CacheItemInterface $item) + { + $this->item = $item; + } + + public function getItem($key) + { + return $this->item; + } + + public function save(CacheItemInterface $item) + { + if ($item === $this->item) { + throw new \Exception('OK '.$item->get()); + } + } +} diff --git a/lib/symfony/cache/Tests/Adapter/RedisAdapterTest.php b/lib/symfony/cache/Tests/Adapter/RedisAdapterTest.php new file mode 100644 index 000000000..eb2cbd46f --- /dev/null +++ b/lib/symfony/cache/Tests/Adapter/RedisAdapterTest.php @@ -0,0 +1,92 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests\Adapter; + +use Symfony\Component\Cache\Adapter\AbstractAdapter; +use Symfony\Component\Cache\Adapter\RedisAdapter; +use Symfony\Component\Cache\Traits\RedisProxy; + +class RedisAdapterTest extends AbstractRedisAdapterTest +{ + public static function setupBeforeClass() + { + parent::setupBeforeClass(); + self::$redis = AbstractAdapter::createConnection('redis://'.getenv('REDIS_HOST'), ['lazy' => true]); + } + + public function createCachePool($defaultLifetime = 0) + { + $adapter = parent::createCachePool($defaultLifetime); + $this->assertInstanceOf(RedisProxy::class, self::$redis); + + return $adapter; + } + + public function testCreateConnection() + { + $redisHost = getenv('REDIS_HOST'); + + $redis = RedisAdapter::createConnection('redis://'.$redisHost); + $this->assertInstanceOf(\Redis::class, $redis); + $this->assertTrue($redis->isConnected()); + $this->assertSame(0, $redis->getDbNum()); + + $redis = RedisAdapter::createConnection('redis://'.$redisHost.'/2'); + $this->assertSame(2, $redis->getDbNum()); + + $redis = RedisAdapter::createConnection('redis://'.$redisHost, ['timeout' => 3]); + $this->assertEquals(3, $redis->getTimeout()); + + $redis = RedisAdapter::createConnection('redis://'.$redisHost.'?timeout=4'); + $this->assertEquals(4, $redis->getTimeout()); + + $redis = RedisAdapter::createConnection('redis://'.$redisHost, ['read_timeout' => 5]); + $this->assertEquals(5, $redis->getReadTimeout()); + } + + /** + * @dataProvider provideFailedCreateConnection + * @expectedException \Symfony\Component\Cache\Exception\InvalidArgumentException + * @expectedExceptionMessage Redis connection failed + */ + public function testFailedCreateConnection($dsn) + { + RedisAdapter::createConnection($dsn); + } + + public function provideFailedCreateConnection() + { + return [ + ['redis://localhost:1234'], + ['redis://foo@localhost'], + ['redis://localhost/123'], + ]; + } + + /** + * @dataProvider provideInvalidCreateConnection + * @expectedException \Symfony\Component\Cache\Exception\InvalidArgumentException + * @expectedExceptionMessage Invalid Redis DSN + */ + public function testInvalidCreateConnection($dsn) + { + RedisAdapter::createConnection($dsn); + } + + public function provideInvalidCreateConnection() + { + return [ + ['foo://localhost'], + ['redis://'], + ]; + } +} diff --git a/lib/symfony/cache/Tests/Adapter/RedisArrayAdapterTest.php b/lib/symfony/cache/Tests/Adapter/RedisArrayAdapterTest.php new file mode 100644 index 000000000..749b039a0 --- /dev/null +++ b/lib/symfony/cache/Tests/Adapter/RedisArrayAdapterTest.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests\Adapter; + +class RedisArrayAdapterTest extends AbstractRedisAdapterTest +{ + public static function setupBeforeClass() + { + parent::setupBeforeClass(); + if (!class_exists('RedisArray')) { + self::markTestSkipped('The RedisArray class is required.'); + } + self::$redis = new \RedisArray([getenv('REDIS_HOST')], ['lazy_connect' => true]); + } +} diff --git a/lib/symfony/cache/Tests/Adapter/RedisClusterAdapterTest.php b/lib/symfony/cache/Tests/Adapter/RedisClusterAdapterTest.php new file mode 100644 index 000000000..852079c00 --- /dev/null +++ b/lib/symfony/cache/Tests/Adapter/RedisClusterAdapterTest.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests\Adapter; + +class RedisClusterAdapterTest extends AbstractRedisAdapterTest +{ + public static function setupBeforeClass() + { + if (!class_exists('RedisCluster')) { + self::markTestSkipped('The RedisCluster class is required.'); + } + if (!$hosts = getenv('REDIS_CLUSTER_HOSTS')) { + self::markTestSkipped('REDIS_CLUSTER_HOSTS env var is not defined.'); + } + + self::$redis = new \RedisCluster(null, explode(' ', $hosts)); + } +} diff --git a/lib/symfony/cache/Tests/Adapter/SimpleCacheAdapterTest.php b/lib/symfony/cache/Tests/Adapter/SimpleCacheAdapterTest.php new file mode 100644 index 000000000..d8470a2e7 --- /dev/null +++ b/lib/symfony/cache/Tests/Adapter/SimpleCacheAdapterTest.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests\Adapter; + +use Symfony\Component\Cache\Adapter\SimpleCacheAdapter; +use Symfony\Component\Cache\Simple\ArrayCache; +use Symfony\Component\Cache\Simple\FilesystemCache; + +/** + * @group time-sensitive + */ +class SimpleCacheAdapterTest extends AdapterTestCase +{ + protected $skippedTests = [ + 'testPrune' => 'SimpleCache just proxies', + ]; + + public function createCachePool($defaultLifetime = 0) + { + return new SimpleCacheAdapter(new FilesystemCache(), '', $defaultLifetime); + } + + public function testValidCacheKeyWithNamespace() + { + $cache = new SimpleCacheAdapter(new ArrayCache(), 'some_namespace', 0); + $item = $cache->getItem('my_key'); + $item->set('someValue'); + $cache->save($item); + + $this->assertTrue($cache->getItem('my_key')->isHit(), 'Stored item is successfully retrieved.'); + } +} diff --git a/lib/symfony/cache/Tests/Adapter/TagAwareAdapterTest.php b/lib/symfony/cache/Tests/Adapter/TagAwareAdapterTest.php new file mode 100644 index 000000000..488127cc9 --- /dev/null +++ b/lib/symfony/cache/Tests/Adapter/TagAwareAdapterTest.php @@ -0,0 +1,207 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests\Adapter; + +use Symfony\Component\Cache\Adapter\AdapterInterface; +use Symfony\Component\Cache\Adapter\FilesystemAdapter; +use Symfony\Component\Cache\Adapter\TagAwareAdapter; + +/** + * @group time-sensitive + */ +class TagAwareAdapterTest extends AdapterTestCase +{ + public function createCachePool($defaultLifetime = 0) + { + return new TagAwareAdapter(new FilesystemAdapter('', $defaultLifetime)); + } + + public static function tearDownAfterClass() + { + FilesystemAdapterTest::rmdir(sys_get_temp_dir().'/symfony-cache'); + } + + /** + * @expectedException \Psr\Cache\InvalidArgumentException + */ + public function testInvalidTag() + { + $pool = $this->createCachePool(); + $item = $pool->getItem('foo'); + $item->tag(':'); + } + + public function testInvalidateTags() + { + $pool = $this->createCachePool(); + + $i0 = $pool->getItem('i0'); + $i1 = $pool->getItem('i1'); + $i2 = $pool->getItem('i2'); + $i3 = $pool->getItem('i3'); + $foo = $pool->getItem('foo'); + + $pool->save($i0->tag('bar')); + $pool->save($i1->tag('foo')); + $pool->save($i2->tag('foo')->tag('bar')); + $pool->save($i3->tag('foo')->tag('baz')); + $pool->save($foo); + + $pool->invalidateTags(['bar']); + + $this->assertFalse($pool->getItem('i0')->isHit()); + $this->assertTrue($pool->getItem('i1')->isHit()); + $this->assertFalse($pool->getItem('i2')->isHit()); + $this->assertTrue($pool->getItem('i3')->isHit()); + $this->assertTrue($pool->getItem('foo')->isHit()); + + $pool->invalidateTags(['foo']); + + $this->assertFalse($pool->getItem('i1')->isHit()); + $this->assertFalse($pool->getItem('i3')->isHit()); + $this->assertTrue($pool->getItem('foo')->isHit()); + + $anotherPoolInstance = $this->createCachePool(); + + $this->assertFalse($anotherPoolInstance->getItem('i1')->isHit()); + $this->assertFalse($anotherPoolInstance->getItem('i3')->isHit()); + $this->assertTrue($anotherPoolInstance->getItem('foo')->isHit()); + } + + public function testInvalidateCommits() + { + $pool1 = $this->createCachePool(); + + $foo = $pool1->getItem('foo'); + $foo->tag('tag'); + + $pool1->saveDeferred($foo->set('foo')); + $pool1->invalidateTags(['tag']); + + $pool2 = $this->createCachePool(); + $foo = $pool2->getItem('foo'); + + $this->assertTrue($foo->isHit()); + } + + public function testTagsAreCleanedOnSave() + { + $pool = $this->createCachePool(); + + $i = $pool->getItem('k'); + $pool->save($i->tag('foo')); + + $i = $pool->getItem('k'); + $pool->save($i->tag('bar')); + + $pool->invalidateTags(['foo']); + $this->assertTrue($pool->getItem('k')->isHit()); + } + + public function testTagsAreCleanedOnDelete() + { + $pool = $this->createCachePool(); + + $i = $pool->getItem('k'); + $pool->save($i->tag('foo')); + $pool->deleteItem('k'); + + $pool->save($pool->getItem('k')); + $pool->invalidateTags(['foo']); + + $this->assertTrue($pool->getItem('k')->isHit()); + } + + public function testTagItemExpiry() + { + $pool = $this->createCachePool(10); + + $item = $pool->getItem('foo'); + $item->tag(['baz']); + $item->expiresAfter(100); + + $pool->save($item); + $pool->invalidateTags(['baz']); + $this->assertFalse($pool->getItem('foo')->isHit()); + + sleep(20); + + $this->assertFalse($pool->getItem('foo')->isHit()); + } + + public function testGetPreviousTags() + { + $pool = $this->createCachePool(); + + $i = $pool->getItem('k'); + $pool->save($i->tag('foo')); + + $i = $pool->getItem('k'); + $this->assertSame(['foo' => 'foo'], $i->getPreviousTags()); + } + + public function testPrune() + { + $cache = new TagAwareAdapter($this->getPruneableMock()); + $this->assertTrue($cache->prune()); + + $cache = new TagAwareAdapter($this->getNonPruneableMock()); + $this->assertFalse($cache->prune()); + + $cache = new TagAwareAdapter($this->getFailingPruneableMock()); + $this->assertFalse($cache->prune()); + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject|PruneableCacheInterface + */ + private function getPruneableMock() + { + $pruneable = $this + ->getMockBuilder(PruneableCacheInterface::class) + ->getMock(); + + $pruneable + ->expects($this->atLeastOnce()) + ->method('prune') + ->willReturn(true); + + return $pruneable; + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject|PruneableCacheInterface + */ + private function getFailingPruneableMock() + { + $pruneable = $this + ->getMockBuilder(PruneableCacheInterface::class) + ->getMock(); + + $pruneable + ->expects($this->atLeastOnce()) + ->method('prune') + ->willReturn(false); + + return $pruneable; + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject|AdapterInterface + */ + private function getNonPruneableMock() + { + return $this + ->getMockBuilder(AdapterInterface::class) + ->getMock(); + } +} diff --git a/lib/symfony/cache/Tests/Adapter/TagAwareAndProxyAdapterIntegrationTest.php b/lib/symfony/cache/Tests/Adapter/TagAwareAndProxyAdapterIntegrationTest.php new file mode 100644 index 000000000..b11c1f287 --- /dev/null +++ b/lib/symfony/cache/Tests/Adapter/TagAwareAndProxyAdapterIntegrationTest.php @@ -0,0 +1,38 @@ +getItem('foo'); + $item->tag(['tag1', 'tag2']); + $item->set('bar'); + $cache->save($item); + + $this->assertSame('bar', $cache->getItem('foo')->get()); + } + + public function dataProvider() + { + return [ + [new ArrayAdapter()], + // also testing with a non-AdapterInterface implementation + // because the ProxyAdapter behaves slightly different for those + [new ExternalAdapter()], + ]; + } +} diff --git a/lib/symfony/cache/Tests/Adapter/TraceableAdapterTest.php b/lib/symfony/cache/Tests/Adapter/TraceableAdapterTest.php new file mode 100644 index 000000000..35eba7d77 --- /dev/null +++ b/lib/symfony/cache/Tests/Adapter/TraceableAdapterTest.php @@ -0,0 +1,191 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests\Adapter; + +use Symfony\Component\Cache\Adapter\FilesystemAdapter; +use Symfony\Component\Cache\Adapter\TraceableAdapter; + +/** + * @group time-sensitive + */ +class TraceableAdapterTest extends AdapterTestCase +{ + protected $skippedTests = [ + 'testPrune' => 'TraceableAdapter just proxies', + ]; + + public function createCachePool($defaultLifetime = 0) + { + return new TraceableAdapter(new FilesystemAdapter('', $defaultLifetime)); + } + + public function testGetItemMissTrace() + { + $pool = $this->createCachePool(); + $pool->getItem('k'); + $calls = $pool->getCalls(); + $this->assertCount(1, $calls); + + $call = $calls[0]; + $this->assertSame('getItem', $call->name); + $this->assertSame(['k' => false], $call->result); + $this->assertSame(0, $call->hits); + $this->assertSame(1, $call->misses); + $this->assertNotEmpty($call->start); + $this->assertNotEmpty($call->end); + } + + public function testGetItemHitTrace() + { + $pool = $this->createCachePool(); + $item = $pool->getItem('k')->set('foo'); + $pool->save($item); + $pool->getItem('k'); + $calls = $pool->getCalls(); + $this->assertCount(3, $calls); + + $call = $calls[2]; + $this->assertSame(1, $call->hits); + $this->assertSame(0, $call->misses); + } + + public function testGetItemsMissTrace() + { + $pool = $this->createCachePool(); + $arg = ['k0', 'k1']; + $items = $pool->getItems($arg); + foreach ($items as $item) { + } + $calls = $pool->getCalls(); + $this->assertCount(1, $calls); + + $call = $calls[0]; + $this->assertSame('getItems', $call->name); + $this->assertSame(['k0' => false, 'k1' => false], $call->result); + $this->assertSame(2, $call->misses); + $this->assertNotEmpty($call->start); + $this->assertNotEmpty($call->end); + } + + public function testHasItemMissTrace() + { + $pool = $this->createCachePool(); + $pool->hasItem('k'); + $calls = $pool->getCalls(); + $this->assertCount(1, $calls); + + $call = $calls[0]; + $this->assertSame('hasItem', $call->name); + $this->assertSame(['k' => false], $call->result); + $this->assertNotEmpty($call->start); + $this->assertNotEmpty($call->end); + } + + public function testHasItemHitTrace() + { + $pool = $this->createCachePool(); + $item = $pool->getItem('k')->set('foo'); + $pool->save($item); + $pool->hasItem('k'); + $calls = $pool->getCalls(); + $this->assertCount(3, $calls); + + $call = $calls[2]; + $this->assertSame('hasItem', $call->name); + $this->assertSame(['k' => true], $call->result); + $this->assertNotEmpty($call->start); + $this->assertNotEmpty($call->end); + } + + public function testDeleteItemTrace() + { + $pool = $this->createCachePool(); + $pool->deleteItem('k'); + $calls = $pool->getCalls(); + $this->assertCount(1, $calls); + + $call = $calls[0]; + $this->assertSame('deleteItem', $call->name); + $this->assertSame(['k' => true], $call->result); + $this->assertSame(0, $call->hits); + $this->assertSame(0, $call->misses); + $this->assertNotEmpty($call->start); + $this->assertNotEmpty($call->end); + } + + public function testDeleteItemsTrace() + { + $pool = $this->createCachePool(); + $arg = ['k0', 'k1']; + $pool->deleteItems($arg); + $calls = $pool->getCalls(); + $this->assertCount(1, $calls); + + $call = $calls[0]; + $this->assertSame('deleteItems', $call->name); + $this->assertSame(['keys' => $arg, 'result' => true], $call->result); + $this->assertSame(0, $call->hits); + $this->assertSame(0, $call->misses); + $this->assertNotEmpty($call->start); + $this->assertNotEmpty($call->end); + } + + public function testSaveTrace() + { + $pool = $this->createCachePool(); + $item = $pool->getItem('k')->set('foo'); + $pool->save($item); + $calls = $pool->getCalls(); + $this->assertCount(2, $calls); + + $call = $calls[1]; + $this->assertSame('save', $call->name); + $this->assertSame(['k' => true], $call->result); + $this->assertSame(0, $call->hits); + $this->assertSame(0, $call->misses); + $this->assertNotEmpty($call->start); + $this->assertNotEmpty($call->end); + } + + public function testSaveDeferredTrace() + { + $pool = $this->createCachePool(); + $item = $pool->getItem('k')->set('foo'); + $pool->saveDeferred($item); + $calls = $pool->getCalls(); + $this->assertCount(2, $calls); + + $call = $calls[1]; + $this->assertSame('saveDeferred', $call->name); + $this->assertSame(['k' => true], $call->result); + $this->assertSame(0, $call->hits); + $this->assertSame(0, $call->misses); + $this->assertNotEmpty($call->start); + $this->assertNotEmpty($call->end); + } + + public function testCommitTrace() + { + $pool = $this->createCachePool(); + $pool->commit(); + $calls = $pool->getCalls(); + $this->assertCount(1, $calls); + + $call = $calls[0]; + $this->assertSame('commit', $call->name); + $this->assertTrue($call->result); + $this->assertSame(0, $call->hits); + $this->assertSame(0, $call->misses); + $this->assertNotEmpty($call->start); + $this->assertNotEmpty($call->end); + } +} diff --git a/lib/symfony/cache/Tests/Adapter/TraceableTagAwareAdapterTest.php b/lib/symfony/cache/Tests/Adapter/TraceableTagAwareAdapterTest.php new file mode 100644 index 000000000..5cd4185cb --- /dev/null +++ b/lib/symfony/cache/Tests/Adapter/TraceableTagAwareAdapterTest.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests\Adapter; + +use Symfony\Component\Cache\Adapter\FilesystemAdapter; +use Symfony\Component\Cache\Adapter\TagAwareAdapter; +use Symfony\Component\Cache\Adapter\TraceableTagAwareAdapter; + +/** + * @group time-sensitive + */ +class TraceableTagAwareAdapterTest extends TraceableAdapterTest +{ + public function testInvalidateTags() + { + $pool = new TraceableTagAwareAdapter(new TagAwareAdapter(new FilesystemAdapter())); + $pool->invalidateTags(['foo']); + $calls = $pool->getCalls(); + $this->assertCount(1, $calls); + + $call = $calls[0]; + $this->assertSame('invalidateTags', $call->name); + $this->assertSame(0, $call->hits); + $this->assertSame(0, $call->misses); + $this->assertNotEmpty($call->start); + $this->assertNotEmpty($call->end); + } +} diff --git a/lib/symfony/cache/Tests/CacheItemTest.php b/lib/symfony/cache/Tests/CacheItemTest.php new file mode 100644 index 000000000..fff5202b1 --- /dev/null +++ b/lib/symfony/cache/Tests/CacheItemTest.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Cache\CacheItem; + +class CacheItemTest extends TestCase +{ + public function testValidKey() + { + $this->assertSame('foo', CacheItem::validateKey('foo')); + } + + /** + * @dataProvider provideInvalidKey + * @expectedException \Symfony\Component\Cache\Exception\InvalidArgumentException + * @expectedExceptionMessage Cache key + */ + public function testInvalidKey($key) + { + CacheItem::validateKey($key); + } + + public function provideInvalidKey() + { + return [ + [''], + ['{'], + ['}'], + ['('], + [')'], + ['/'], + ['\\'], + ['@'], + [':'], + [true], + [null], + [1], + [1.1], + [[[]]], + [new \Exception('foo')], + ]; + } + + public function testTag() + { + $item = new CacheItem(); + + $this->assertSame($item, $item->tag('foo')); + $this->assertSame($item, $item->tag(['bar', 'baz'])); + + \call_user_func(\Closure::bind(function () use ($item) { + $this->assertSame(['foo' => 'foo', 'bar' => 'bar', 'baz' => 'baz'], $item->tags); + }, $this, CacheItem::class)); + } + + /** + * @dataProvider provideInvalidKey + * @expectedException \Symfony\Component\Cache\Exception\InvalidArgumentException + * @expectedExceptionMessage Cache tag + */ + public function testInvalidTag($tag) + { + $item = new CacheItem(); + $item->tag($tag); + } +} diff --git a/lib/symfony/cache/Tests/DoctrineProviderTest.php b/lib/symfony/cache/Tests/DoctrineProviderTest.php new file mode 100644 index 000000000..91a5516ab --- /dev/null +++ b/lib/symfony/cache/Tests/DoctrineProviderTest.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests; + +use Doctrine\Common\Cache\CacheProvider; +use PHPUnit\Framework\TestCase; +use Symfony\Component\Cache\Adapter\ArrayAdapter; +use Symfony\Component\Cache\DoctrineProvider; + +class DoctrineProviderTest extends TestCase +{ + public function testProvider() + { + $pool = new ArrayAdapter(); + $cache = new DoctrineProvider($pool); + + $this->assertInstanceOf(CacheProvider::class, $cache); + + $key = '{}()/\@:'; + + $this->assertTrue($cache->delete($key)); + $this->assertFalse($cache->contains($key)); + + $this->assertTrue($cache->save($key, 'bar')); + $this->assertTrue($cache->contains($key)); + $this->assertSame('bar', $cache->fetch($key)); + + $this->assertTrue($cache->delete($key)); + $this->assertFalse($cache->fetch($key)); + $this->assertTrue($cache->save($key, 'bar')); + + $cache->flushAll(); + $this->assertFalse($cache->fetch($key)); + $this->assertFalse($cache->contains($key)); + } +} diff --git a/lib/symfony/cache/Tests/Fixtures/ArrayCache.php b/lib/symfony/cache/Tests/Fixtures/ArrayCache.php new file mode 100644 index 000000000..13b4f3306 --- /dev/null +++ b/lib/symfony/cache/Tests/Fixtures/ArrayCache.php @@ -0,0 +1,52 @@ +doContains($id) ? $this->data[$id][0] : false; + } + + protected function doContains($id) + { + if (!isset($this->data[$id])) { + return false; + } + + $expiry = $this->data[$id][1]; + + return !$expiry || time() < $expiry || !$this->doDelete($id); + } + + protected function doSave($id, $data, $lifeTime = 0) + { + $this->data[$id] = [$data, $lifeTime ? time() + $lifeTime : false]; + + return true; + } + + protected function doDelete($id) + { + unset($this->data[$id]); + + return true; + } + + protected function doFlush() + { + $this->data = []; + + return true; + } + + protected function doGetStats() + { + return null; + } +} diff --git a/lib/symfony/cache/Tests/Fixtures/ExternalAdapter.php b/lib/symfony/cache/Tests/Fixtures/ExternalAdapter.php new file mode 100644 index 000000000..779a374ec --- /dev/null +++ b/lib/symfony/cache/Tests/Fixtures/ExternalAdapter.php @@ -0,0 +1,76 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests\Fixtures; + +use Psr\Cache\CacheItemInterface; +use Psr\Cache\CacheItemPoolInterface; +use Symfony\Component\Cache\Adapter\ArrayAdapter; + +/** + * Adapter not implementing the {@see \Symfony\Component\Cache\Adapter\AdapterInterface}. + * + * @author Kévin Dunglas + */ +class ExternalAdapter implements CacheItemPoolInterface +{ + private $cache; + + public function __construct() + { + $this->cache = new ArrayAdapter(); + } + + public function getItem($key) + { + return $this->cache->getItem($key); + } + + public function getItems(array $keys = []) + { + return $this->cache->getItems($keys); + } + + public function hasItem($key) + { + return $this->cache->hasItem($key); + } + + public function clear() + { + return $this->cache->clear(); + } + + public function deleteItem($key) + { + return $this->cache->deleteItem($key); + } + + public function deleteItems(array $keys) + { + return $this->cache->deleteItems($keys); + } + + public function save(CacheItemInterface $item) + { + return $this->cache->save($item); + } + + public function saveDeferred(CacheItemInterface $item) + { + return $this->cache->saveDeferred($item); + } + + public function commit() + { + return $this->cache->commit(); + } +} diff --git a/lib/symfony/cache/Tests/Simple/AbstractRedisCacheTest.php b/lib/symfony/cache/Tests/Simple/AbstractRedisCacheTest.php new file mode 100644 index 000000000..dd5e1509c --- /dev/null +++ b/lib/symfony/cache/Tests/Simple/AbstractRedisCacheTest.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests\Simple; + +use Symfony\Component\Cache\Simple\RedisCache; + +abstract class AbstractRedisCacheTest extends CacheTestCase +{ + protected $skippedTests = [ + 'testSetTtl' => 'Testing expiration slows down the test suite', + 'testSetMultipleTtl' => 'Testing expiration slows down the test suite', + 'testDefaultLifeTime' => 'Testing expiration slows down the test suite', + ]; + + protected static $redis; + + public function createSimpleCache($defaultLifetime = 0) + { + return new RedisCache(self::$redis, str_replace('\\', '.', __CLASS__), $defaultLifetime); + } + + public static function setupBeforeClass() + { + if (!\extension_loaded('redis')) { + self::markTestSkipped('Extension redis required.'); + } + if (!@((new \Redis())->connect(getenv('REDIS_HOST')))) { + $e = error_get_last(); + self::markTestSkipped($e['message']); + } + } + + public static function tearDownAfterClass() + { + self::$redis = null; + } +} diff --git a/lib/symfony/cache/Tests/Simple/ApcuCacheTest.php b/lib/symfony/cache/Tests/Simple/ApcuCacheTest.php new file mode 100644 index 000000000..f37b95a09 --- /dev/null +++ b/lib/symfony/cache/Tests/Simple/ApcuCacheTest.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests\Simple; + +use Symfony\Component\Cache\Simple\ApcuCache; + +class ApcuCacheTest extends CacheTestCase +{ + protected $skippedTests = [ + 'testSetTtl' => 'Testing expiration slows down the test suite', + 'testSetMultipleTtl' => 'Testing expiration slows down the test suite', + 'testDefaultLifeTime' => 'Testing expiration slows down the test suite', + ]; + + public function createSimpleCache($defaultLifetime = 0) + { + if (!\function_exists('apcu_fetch') || !filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) || ('cli' === \PHP_SAPI && !filter_var(ini_get('apc.enable_cli'), FILTER_VALIDATE_BOOLEAN))) { + $this->markTestSkipped('APCu extension is required.'); + } + if ('\\' === \DIRECTORY_SEPARATOR) { + $this->markTestSkipped('Fails transiently on Windows.'); + } + + return new ApcuCache(str_replace('\\', '.', __CLASS__), $defaultLifetime); + } +} diff --git a/lib/symfony/cache/Tests/Simple/ArrayCacheTest.php b/lib/symfony/cache/Tests/Simple/ArrayCacheTest.php new file mode 100644 index 000000000..26c3e14d0 --- /dev/null +++ b/lib/symfony/cache/Tests/Simple/ArrayCacheTest.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests\Simple; + +use Symfony\Component\Cache\Simple\ArrayCache; + +/** + * @group time-sensitive + */ +class ArrayCacheTest extends CacheTestCase +{ + public function createSimpleCache($defaultLifetime = 0) + { + return new ArrayCache($defaultLifetime); + } +} diff --git a/lib/symfony/cache/Tests/Simple/CacheTestCase.php b/lib/symfony/cache/Tests/Simple/CacheTestCase.php new file mode 100644 index 000000000..ff9944a34 --- /dev/null +++ b/lib/symfony/cache/Tests/Simple/CacheTestCase.php @@ -0,0 +1,150 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests\Simple; + +use Cache\IntegrationTests\SimpleCacheTest; +use Psr\SimpleCache\CacheInterface; +use Symfony\Component\Cache\PruneableInterface; + +abstract class CacheTestCase extends SimpleCacheTest +{ + protected function setUp() + { + parent::setUp(); + + if (!\array_key_exists('testPrune', $this->skippedTests) && !$this->createSimpleCache() instanceof PruneableInterface) { + $this->skippedTests['testPrune'] = 'Not a pruneable cache pool.'; + } + } + + public static function validKeys() + { + if (\defined('HHVM_VERSION')) { + return parent::validKeys(); + } + + return array_merge(parent::validKeys(), [["a\0b"]]); + } + + public function testDefaultLifeTime() + { + if (isset($this->skippedTests[__FUNCTION__])) { + $this->markTestSkipped($this->skippedTests[__FUNCTION__]); + } + + $cache = $this->createSimpleCache(2); + $cache->clear(); + + $cache->set('key.dlt', 'value'); + sleep(1); + + $this->assertSame('value', $cache->get('key.dlt')); + + sleep(2); + $this->assertNull($cache->get('key.dlt')); + + $cache->clear(); + } + + public function testNotUnserializable() + { + if (isset($this->skippedTests[__FUNCTION__])) { + $this->markTestSkipped($this->skippedTests[__FUNCTION__]); + } + + $cache = $this->createSimpleCache(); + $cache->clear(); + + $cache->set('foo', new NotUnserializable()); + + $this->assertNull($cache->get('foo')); + + $cache->setMultiple(['foo' => new NotUnserializable()]); + + foreach ($cache->getMultiple(['foo']) as $value) { + } + $this->assertNull($value); + + $cache->clear(); + } + + public function testPrune() + { + if (isset($this->skippedTests[__FUNCTION__])) { + $this->markTestSkipped($this->skippedTests[__FUNCTION__]); + } + + if (!method_exists($this, 'isPruned')) { + $this->fail('Test classes for pruneable caches must implement `isPruned($cache, $name)` method.'); + } + + /** @var PruneableInterface|CacheInterface $cache */ + $cache = $this->createSimpleCache(); + $cache->clear(); + + $cache->set('foo', 'foo-val', new \DateInterval('PT05S')); + $cache->set('bar', 'bar-val', new \DateInterval('PT10S')); + $cache->set('baz', 'baz-val', new \DateInterval('PT15S')); + $cache->set('qux', 'qux-val', new \DateInterval('PT20S')); + + sleep(30); + $cache->prune(); + $this->assertTrue($this->isPruned($cache, 'foo')); + $this->assertTrue($this->isPruned($cache, 'bar')); + $this->assertTrue($this->isPruned($cache, 'baz')); + $this->assertTrue($this->isPruned($cache, 'qux')); + + $cache->set('foo', 'foo-val'); + $cache->set('bar', 'bar-val', new \DateInterval('PT20S')); + $cache->set('baz', 'baz-val', new \DateInterval('PT40S')); + $cache->set('qux', 'qux-val', new \DateInterval('PT80S')); + + $cache->prune(); + $this->assertFalse($this->isPruned($cache, 'foo')); + $this->assertFalse($this->isPruned($cache, 'bar')); + $this->assertFalse($this->isPruned($cache, 'baz')); + $this->assertFalse($this->isPruned($cache, 'qux')); + + sleep(30); + $cache->prune(); + $this->assertFalse($this->isPruned($cache, 'foo')); + $this->assertTrue($this->isPruned($cache, 'bar')); + $this->assertFalse($this->isPruned($cache, 'baz')); + $this->assertFalse($this->isPruned($cache, 'qux')); + + sleep(30); + $cache->prune(); + $this->assertFalse($this->isPruned($cache, 'foo')); + $this->assertTrue($this->isPruned($cache, 'baz')); + $this->assertFalse($this->isPruned($cache, 'qux')); + + sleep(30); + $cache->prune(); + $this->assertFalse($this->isPruned($cache, 'foo')); + $this->assertTrue($this->isPruned($cache, 'qux')); + + $cache->clear(); + } +} + +class NotUnserializable implements \Serializable +{ + public function serialize() + { + return serialize(123); + } + + public function unserialize($ser) + { + throw new \Exception(__CLASS__); + } +} diff --git a/lib/symfony/cache/Tests/Simple/ChainCacheTest.php b/lib/symfony/cache/Tests/Simple/ChainCacheTest.php new file mode 100644 index 000000000..aa7788791 --- /dev/null +++ b/lib/symfony/cache/Tests/Simple/ChainCacheTest.php @@ -0,0 +1,116 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests\Simple; + +use Psr\SimpleCache\CacheInterface; +use Symfony\Component\Cache\PruneableInterface; +use Symfony\Component\Cache\Simple\ArrayCache; +use Symfony\Component\Cache\Simple\ChainCache; +use Symfony\Component\Cache\Simple\FilesystemCache; + +/** + * @group time-sensitive + */ +class ChainCacheTest extends CacheTestCase +{ + public function createSimpleCache($defaultLifetime = 0) + { + return new ChainCache([new ArrayCache($defaultLifetime), new FilesystemCache('', $defaultLifetime)], $defaultLifetime); + } + + /** + * @expectedException \Symfony\Component\Cache\Exception\InvalidArgumentException + * @expectedExceptionMessage At least one cache must be specified. + */ + public function testEmptyCachesException() + { + new ChainCache([]); + } + + /** + * @expectedException \Symfony\Component\Cache\Exception\InvalidArgumentException + * @expectedExceptionMessage The class "stdClass" does not implement + */ + public function testInvalidCacheException() + { + new ChainCache([new \stdClass()]); + } + + public function testPrune() + { + if (isset($this->skippedTests[__FUNCTION__])) { + $this->markTestSkipped($this->skippedTests[__FUNCTION__]); + } + + $cache = new ChainCache([ + $this->getPruneableMock(), + $this->getNonPruneableMock(), + $this->getPruneableMock(), + ]); + $this->assertTrue($cache->prune()); + + $cache = new ChainCache([ + $this->getPruneableMock(), + $this->getFailingPruneableMock(), + $this->getPruneableMock(), + ]); + $this->assertFalse($cache->prune()); + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject|PruneableCacheInterface + */ + private function getPruneableMock() + { + $pruneable = $this + ->getMockBuilder(PruneableCacheInterface::class) + ->getMock(); + + $pruneable + ->expects($this->atLeastOnce()) + ->method('prune') + ->willReturn(true); + + return $pruneable; + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject|PruneableCacheInterface + */ + private function getFailingPruneableMock() + { + $pruneable = $this + ->getMockBuilder(PruneableCacheInterface::class) + ->getMock(); + + $pruneable + ->expects($this->atLeastOnce()) + ->method('prune') + ->willReturn(false); + + return $pruneable; + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject|CacheInterface + */ + private function getNonPruneableMock() + { + return $this + ->getMockBuilder(CacheInterface::class) + ->getMock(); + } +} + +interface PruneableCacheInterface extends PruneableInterface, CacheInterface +{ +} diff --git a/lib/symfony/cache/Tests/Simple/DoctrineCacheTest.php b/lib/symfony/cache/Tests/Simple/DoctrineCacheTest.php new file mode 100644 index 000000000..af4331d69 --- /dev/null +++ b/lib/symfony/cache/Tests/Simple/DoctrineCacheTest.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests\Simple; + +use Symfony\Component\Cache\Simple\DoctrineCache; +use Symfony\Component\Cache\Tests\Fixtures\ArrayCache; + +/** + * @group time-sensitive + */ +class DoctrineCacheTest extends CacheTestCase +{ + protected $skippedTests = [ + 'testObjectDoesNotChangeInCache' => 'ArrayCache does not use serialize/unserialize', + 'testNotUnserializable' => 'ArrayCache does not use serialize/unserialize', + ]; + + public function createSimpleCache($defaultLifetime = 0) + { + return new DoctrineCache(new ArrayCache($defaultLifetime), '', $defaultLifetime); + } +} diff --git a/lib/symfony/cache/Tests/Simple/FilesystemCacheTest.php b/lib/symfony/cache/Tests/Simple/FilesystemCacheTest.php new file mode 100644 index 000000000..620305a58 --- /dev/null +++ b/lib/symfony/cache/Tests/Simple/FilesystemCacheTest.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests\Simple; + +use Psr\SimpleCache\CacheInterface; +use Symfony\Component\Cache\Simple\FilesystemCache; + +/** + * @group time-sensitive + */ +class FilesystemCacheTest extends CacheTestCase +{ + public function createSimpleCache($defaultLifetime = 0) + { + return new FilesystemCache('', $defaultLifetime); + } + + protected function isPruned(CacheInterface $cache, $name) + { + $getFileMethod = (new \ReflectionObject($cache))->getMethod('getFile'); + $getFileMethod->setAccessible(true); + + return !file_exists($getFileMethod->invoke($cache, $name)); + } +} diff --git a/lib/symfony/cache/Tests/Simple/MemcachedCacheTest.php b/lib/symfony/cache/Tests/Simple/MemcachedCacheTest.php new file mode 100644 index 000000000..f83f0a2e3 --- /dev/null +++ b/lib/symfony/cache/Tests/Simple/MemcachedCacheTest.php @@ -0,0 +1,174 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests\Simple; + +use Symfony\Component\Cache\Adapter\AbstractAdapter; +use Symfony\Component\Cache\Simple\MemcachedCache; + +class MemcachedCacheTest extends CacheTestCase +{ + protected $skippedTests = [ + 'testSetTtl' => 'Testing expiration slows down the test suite', + 'testSetMultipleTtl' => 'Testing expiration slows down the test suite', + 'testDefaultLifeTime' => 'Testing expiration slows down the test suite', + ]; + + protected static $client; + + public static function setupBeforeClass() + { + if (!MemcachedCache::isSupported()) { + self::markTestSkipped('Extension memcached >=2.2.0 required.'); + } + self::$client = AbstractAdapter::createConnection('memcached://'.getenv('MEMCACHED_HOST')); + self::$client->get('foo'); + $code = self::$client->getResultCode(); + + if (\Memcached::RES_SUCCESS !== $code && \Memcached::RES_NOTFOUND !== $code) { + self::markTestSkipped('Memcached error: '.strtolower(self::$client->getResultMessage())); + } + } + + public function createSimpleCache($defaultLifetime = 0) + { + $client = $defaultLifetime ? AbstractAdapter::createConnection('memcached://'.getenv('MEMCACHED_HOST'), ['binary_protocol' => false]) : self::$client; + + return new MemcachedCache($client, str_replace('\\', '.', __CLASS__), $defaultLifetime); + } + + public function testCreatePersistentConnectionShouldNotDupServerList() + { + $instance = MemcachedCache::createConnection('memcached://'.getenv('MEMCACHED_HOST'), ['persistent_id' => 'persistent']); + $this->assertCount(1, $instance->getServerList()); + + $instance = MemcachedCache::createConnection('memcached://'.getenv('MEMCACHED_HOST'), ['persistent_id' => 'persistent']); + $this->assertCount(1, $instance->getServerList()); + } + + public function testOptions() + { + $client = MemcachedCache::createConnection([], [ + 'libketama_compatible' => false, + 'distribution' => 'modula', + 'compression' => true, + 'serializer' => 'php', + 'hash' => 'md5', + ]); + + $this->assertSame(\Memcached::SERIALIZER_PHP, $client->getOption(\Memcached::OPT_SERIALIZER)); + $this->assertSame(\Memcached::HASH_MD5, $client->getOption(\Memcached::OPT_HASH)); + $this->assertTrue($client->getOption(\Memcached::OPT_COMPRESSION)); + $this->assertSame(0, $client->getOption(\Memcached::OPT_LIBKETAMA_COMPATIBLE)); + $this->assertSame(\Memcached::DISTRIBUTION_MODULA, $client->getOption(\Memcached::OPT_DISTRIBUTION)); + } + + /** + * @dataProvider provideBadOptions + * @expectedException \ErrorException + * @expectedExceptionMessage constant(): Couldn't find constant Memcached:: + */ + public function testBadOptions($name, $value) + { + MemcachedCache::createConnection([], [$name => $value]); + } + + public function provideBadOptions() + { + return [ + ['foo', 'bar'], + ['hash', 'zyx'], + ['serializer', 'zyx'], + ['distribution', 'zyx'], + ]; + } + + public function testDefaultOptions() + { + $this->assertTrue(MemcachedCache::isSupported()); + + $client = MemcachedCache::createConnection([]); + + $this->assertTrue($client->getOption(\Memcached::OPT_COMPRESSION)); + $this->assertSame(1, $client->getOption(\Memcached::OPT_BINARY_PROTOCOL)); + $this->assertSame(1, $client->getOption(\Memcached::OPT_LIBKETAMA_COMPATIBLE)); + } + + /** + * @expectedException \Symfony\Component\Cache\Exception\CacheException + * @expectedExceptionMessage MemcachedAdapter: "serializer" option must be "php" or "igbinary". + */ + public function testOptionSerializer() + { + if (!\Memcached::HAVE_JSON) { + $this->markTestSkipped('Memcached::HAVE_JSON required'); + } + + new MemcachedCache(MemcachedCache::createConnection([], ['serializer' => 'json'])); + } + + /** + * @dataProvider provideServersSetting + */ + public function testServersSetting($dsn, $host, $port) + { + $client1 = MemcachedCache::createConnection($dsn); + $client2 = MemcachedCache::createConnection([$dsn]); + $client3 = MemcachedCache::createConnection([[$host, $port]]); + $expect = [ + 'host' => $host, + 'port' => $port, + ]; + + $f = function ($s) { return ['host' => $s['host'], 'port' => $s['port']]; }; + $this->assertSame([$expect], array_map($f, $client1->getServerList())); + $this->assertSame([$expect], array_map($f, $client2->getServerList())); + $this->assertSame([$expect], array_map($f, $client3->getServerList())); + } + + public function provideServersSetting() + { + yield [ + 'memcached://127.0.0.1/50', + '127.0.0.1', + 11211, + ]; + yield [ + 'memcached://localhost:11222?weight=25', + 'localhost', + 11222, + ]; + if (filter_var(ini_get('memcached.use_sasl'), FILTER_VALIDATE_BOOLEAN)) { + yield [ + 'memcached://user:password@127.0.0.1?weight=50', + '127.0.0.1', + 11211, + ]; + } + yield [ + 'memcached:///var/run/memcached.sock?weight=25', + '/var/run/memcached.sock', + 0, + ]; + yield [ + 'memcached:///var/local/run/memcached.socket?weight=25', + '/var/local/run/memcached.socket', + 0, + ]; + if (filter_var(ini_get('memcached.use_sasl'), FILTER_VALIDATE_BOOLEAN)) { + yield [ + 'memcached://user:password@/var/local/run/memcached.socket?weight=25', + '/var/local/run/memcached.socket', + 0, + ]; + } + } +} diff --git a/lib/symfony/cache/Tests/Simple/MemcachedCacheTextModeTest.php b/lib/symfony/cache/Tests/Simple/MemcachedCacheTextModeTest.php new file mode 100644 index 000000000..13865a609 --- /dev/null +++ b/lib/symfony/cache/Tests/Simple/MemcachedCacheTextModeTest.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests\Simple; + +use Symfony\Component\Cache\Adapter\AbstractAdapter; +use Symfony\Component\Cache\Simple\MemcachedCache; + +class MemcachedCacheTextModeTest extends MemcachedCacheTest +{ + public function createSimpleCache($defaultLifetime = 0) + { + $client = AbstractAdapter::createConnection('memcached://'.getenv('MEMCACHED_HOST'), ['binary_protocol' => false]); + + return new MemcachedCache($client, str_replace('\\', '.', __CLASS__), $defaultLifetime); + } +} diff --git a/lib/symfony/cache/Tests/Simple/NullCacheTest.php b/lib/symfony/cache/Tests/Simple/NullCacheTest.php new file mode 100644 index 000000000..31f42c32b --- /dev/null +++ b/lib/symfony/cache/Tests/Simple/NullCacheTest.php @@ -0,0 +1,96 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests\Simple; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Cache\Simple\NullCache; + +/** + * @group time-sensitive + */ +class NullCacheTest extends TestCase +{ + public function createCachePool() + { + return new NullCache(); + } + + public function testGetItem() + { + $cache = $this->createCachePool(); + + $this->assertNull($cache->get('key')); + } + + public function testHas() + { + $this->assertFalse($this->createCachePool()->has('key')); + } + + public function testGetMultiple() + { + $cache = $this->createCachePool(); + + $keys = ['foo', 'bar', 'baz', 'biz']; + + $default = new \stdClass(); + $items = $cache->getMultiple($keys, $default); + $count = 0; + + foreach ($items as $key => $item) { + $this->assertContains($key, $keys, 'Cache key can not change.'); + $this->assertSame($default, $item); + + // Remove $key for $keys + foreach ($keys as $k => $v) { + if ($v === $key) { + unset($keys[$k]); + } + } + + ++$count; + } + + $this->assertSame(4, $count); + } + + public function testClear() + { + $this->assertTrue($this->createCachePool()->clear()); + } + + public function testDelete() + { + $this->assertTrue($this->createCachePool()->delete('key')); + } + + public function testDeleteMultiple() + { + $this->assertTrue($this->createCachePool()->deleteMultiple(['key', 'foo', 'bar'])); + } + + public function testSet() + { + $cache = $this->createCachePool(); + + $this->assertFalse($cache->set('key', 'val')); + $this->assertNull($cache->get('key')); + } + + public function testSetMultiple() + { + $cache = $this->createCachePool(); + + $this->assertFalse($cache->setMultiple(['key' => 'val'])); + $this->assertNull($cache->get('key')); + } +} diff --git a/lib/symfony/cache/Tests/Simple/PdoCacheTest.php b/lib/symfony/cache/Tests/Simple/PdoCacheTest.php new file mode 100644 index 000000000..665db09f6 --- /dev/null +++ b/lib/symfony/cache/Tests/Simple/PdoCacheTest.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests\Simple; + +use Symfony\Component\Cache\Simple\PdoCache; +use Symfony\Component\Cache\Tests\Traits\PdoPruneableTrait; + +/** + * @group time-sensitive + */ +class PdoCacheTest extends CacheTestCase +{ + use PdoPruneableTrait; + + protected static $dbFile; + + public static function setupBeforeClass() + { + if (!\extension_loaded('pdo_sqlite')) { + self::markTestSkipped('Extension pdo_sqlite required.'); + } + + self::$dbFile = tempnam(sys_get_temp_dir(), 'sf_sqlite_cache'); + + $pool = new PdoCache('sqlite:'.self::$dbFile); + $pool->createTable(); + } + + public static function tearDownAfterClass() + { + @unlink(self::$dbFile); + } + + public function createSimpleCache($defaultLifetime = 0) + { + return new PdoCache('sqlite:'.self::$dbFile, 'ns', $defaultLifetime); + } +} diff --git a/lib/symfony/cache/Tests/Simple/PdoDbalCacheTest.php b/lib/symfony/cache/Tests/Simple/PdoDbalCacheTest.php new file mode 100644 index 000000000..ce1a9ae4f --- /dev/null +++ b/lib/symfony/cache/Tests/Simple/PdoDbalCacheTest.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests\Simple; + +use Doctrine\DBAL\DriverManager; +use Symfony\Component\Cache\Simple\PdoCache; +use Symfony\Component\Cache\Tests\Traits\PdoPruneableTrait; + +/** + * @group time-sensitive + */ +class PdoDbalCacheTest extends CacheTestCase +{ + use PdoPruneableTrait; + + protected static $dbFile; + + public static function setupBeforeClass() + { + if (!\extension_loaded('pdo_sqlite')) { + self::markTestSkipped('Extension pdo_sqlite required.'); + } + + self::$dbFile = tempnam(sys_get_temp_dir(), 'sf_sqlite_cache'); + + $pool = new PdoCache(DriverManager::getConnection(['driver' => 'pdo_sqlite', 'path' => self::$dbFile])); + $pool->createTable(); + } + + public static function tearDownAfterClass() + { + @unlink(self::$dbFile); + } + + public function createSimpleCache($defaultLifetime = 0) + { + return new PdoCache(DriverManager::getConnection(['driver' => 'pdo_sqlite', 'path' => self::$dbFile]), '', $defaultLifetime); + } +} diff --git a/lib/symfony/cache/Tests/Simple/PhpArrayCacheTest.php b/lib/symfony/cache/Tests/Simple/PhpArrayCacheTest.php new file mode 100644 index 000000000..a1bab079c --- /dev/null +++ b/lib/symfony/cache/Tests/Simple/PhpArrayCacheTest.php @@ -0,0 +1,143 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests\Simple; + +use Symfony\Component\Cache\Simple\NullCache; +use Symfony\Component\Cache\Simple\PhpArrayCache; +use Symfony\Component\Cache\Tests\Adapter\FilesystemAdapterTest; + +/** + * @group time-sensitive + */ +class PhpArrayCacheTest extends CacheTestCase +{ + protected $skippedTests = [ + 'testBasicUsageWithLongKey' => 'PhpArrayCache does no writes', + + 'testDelete' => 'PhpArrayCache does no writes', + 'testDeleteMultiple' => 'PhpArrayCache does no writes', + 'testDeleteMultipleGenerator' => 'PhpArrayCache does no writes', + + 'testSetTtl' => 'PhpArrayCache does no expiration', + 'testSetMultipleTtl' => 'PhpArrayCache does no expiration', + 'testSetExpiredTtl' => 'PhpArrayCache does no expiration', + 'testSetMultipleExpiredTtl' => 'PhpArrayCache does no expiration', + + 'testGetInvalidKeys' => 'PhpArrayCache does no validation', + 'testGetMultipleInvalidKeys' => 'PhpArrayCache does no validation', + 'testSetInvalidKeys' => 'PhpArrayCache does no validation', + 'testDeleteInvalidKeys' => 'PhpArrayCache does no validation', + 'testDeleteMultipleInvalidKeys' => 'PhpArrayCache does no validation', + 'testSetInvalidTtl' => 'PhpArrayCache does no validation', + 'testSetMultipleInvalidKeys' => 'PhpArrayCache does no validation', + 'testSetMultipleInvalidTtl' => 'PhpArrayCache does no validation', + 'testHasInvalidKeys' => 'PhpArrayCache does no validation', + 'testSetValidData' => 'PhpArrayCache does no validation', + + 'testDefaultLifeTime' => 'PhpArrayCache does not allow configuring a default lifetime.', + 'testPrune' => 'PhpArrayCache just proxies', + ]; + + protected static $file; + + public static function setupBeforeClass() + { + self::$file = sys_get_temp_dir().'/symfony-cache/php-array-adapter-test.php'; + } + + protected function tearDown() + { + if (file_exists(sys_get_temp_dir().'/symfony-cache')) { + FilesystemAdapterTest::rmdir(sys_get_temp_dir().'/symfony-cache'); + } + } + + public function createSimpleCache() + { + return new PhpArrayCacheWrapper(self::$file, new NullCache()); + } + + public function testStore() + { + $arrayWithRefs = []; + $arrayWithRefs[0] = 123; + $arrayWithRefs[1] = &$arrayWithRefs[0]; + + $object = (object) [ + 'foo' => 'bar', + 'foo2' => 'bar2', + ]; + + $expected = [ + 'null' => null, + 'serializedString' => serialize($object), + 'arrayWithRefs' => $arrayWithRefs, + 'object' => $object, + 'arrayWithObject' => ['bar' => $object], + ]; + + $cache = new PhpArrayCache(self::$file, new NullCache()); + $cache->warmUp($expected); + + foreach ($expected as $key => $value) { + $this->assertSame(serialize($value), serialize($cache->get($key)), 'Warm up should create a PHP file that OPCache can load in memory'); + } + } + + public function testStoredFile() + { + $expected = [ + 'integer' => 42, + 'float' => 42.42, + 'boolean' => true, + 'array_simple' => ['foo', 'bar'], + 'array_associative' => ['foo' => 'bar', 'foo2' => 'bar2'], + ]; + + $cache = new PhpArrayCache(self::$file, new NullCache()); + $cache->warmUp($expected); + + $values = eval(substr(file_get_contents(self::$file), 6)); + + $this->assertSame($expected, $values, 'Warm up should create a PHP file that OPCache can load in memory'); + } +} + +class PhpArrayCacheWrapper extends PhpArrayCache +{ + public function set($key, $value, $ttl = null) + { + \call_user_func(\Closure::bind(function () use ($key, $value) { + $this->values[$key] = $value; + $this->warmUp($this->values); + $this->values = eval(substr(file_get_contents($this->file), 6)); + }, $this, PhpArrayCache::class)); + + return true; + } + + public function setMultiple($values, $ttl = null) + { + if (!\is_array($values) && !$values instanceof \Traversable) { + return parent::setMultiple($values, $ttl); + } + \call_user_func(\Closure::bind(function () use ($values) { + foreach ($values as $key => $value) { + $this->values[$key] = $value; + } + $this->warmUp($this->values); + $this->values = eval(substr(file_get_contents($this->file), 6)); + }, $this, PhpArrayCache::class)); + + return true; + } +} diff --git a/lib/symfony/cache/Tests/Simple/PhpArrayCacheWithFallbackTest.php b/lib/symfony/cache/Tests/Simple/PhpArrayCacheWithFallbackTest.php new file mode 100644 index 000000000..abee5e787 --- /dev/null +++ b/lib/symfony/cache/Tests/Simple/PhpArrayCacheWithFallbackTest.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests\Simple; + +use Symfony\Component\Cache\Simple\FilesystemCache; +use Symfony\Component\Cache\Simple\PhpArrayCache; +use Symfony\Component\Cache\Tests\Adapter\FilesystemAdapterTest; + +/** + * @group time-sensitive + */ +class PhpArrayCacheWithFallbackTest extends CacheTestCase +{ + protected $skippedTests = [ + 'testGetInvalidKeys' => 'PhpArrayCache does no validation', + 'testGetMultipleInvalidKeys' => 'PhpArrayCache does no validation', + 'testDeleteInvalidKeys' => 'PhpArrayCache does no validation', + 'testDeleteMultipleInvalidKeys' => 'PhpArrayCache does no validation', + //'testSetValidData' => 'PhpArrayCache does no validation', + 'testSetInvalidKeys' => 'PhpArrayCache does no validation', + 'testSetInvalidTtl' => 'PhpArrayCache does no validation', + 'testSetMultipleInvalidKeys' => 'PhpArrayCache does no validation', + 'testSetMultipleInvalidTtl' => 'PhpArrayCache does no validation', + 'testHasInvalidKeys' => 'PhpArrayCache does no validation', + 'testPrune' => 'PhpArrayCache just proxies', + ]; + + protected static $file; + + public static function setupBeforeClass() + { + self::$file = sys_get_temp_dir().'/symfony-cache/php-array-adapter-test.php'; + } + + protected function tearDown() + { + if (file_exists(sys_get_temp_dir().'/symfony-cache')) { + FilesystemAdapterTest::rmdir(sys_get_temp_dir().'/symfony-cache'); + } + } + + public function createSimpleCache($defaultLifetime = 0) + { + return new PhpArrayCache(self::$file, new FilesystemCache('php-array-fallback', $defaultLifetime)); + } +} diff --git a/lib/symfony/cache/Tests/Simple/PhpFilesCacheTest.php b/lib/symfony/cache/Tests/Simple/PhpFilesCacheTest.php new file mode 100644 index 000000000..936f29a43 --- /dev/null +++ b/lib/symfony/cache/Tests/Simple/PhpFilesCacheTest.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests\Simple; + +use Psr\SimpleCache\CacheInterface; +use Symfony\Component\Cache\Simple\PhpFilesCache; + +/** + * @group time-sensitive + */ +class PhpFilesCacheTest extends CacheTestCase +{ + protected $skippedTests = [ + 'testDefaultLifeTime' => 'PhpFilesCache does not allow configuring a default lifetime.', + ]; + + public function createSimpleCache() + { + if (!PhpFilesCache::isSupported()) { + $this->markTestSkipped('OPcache extension is not enabled.'); + } + + return new PhpFilesCache('sf-cache'); + } + + protected function isPruned(CacheInterface $cache, $name) + { + $getFileMethod = (new \ReflectionObject($cache))->getMethod('getFile'); + $getFileMethod->setAccessible(true); + + return !file_exists($getFileMethod->invoke($cache, $name)); + } +} diff --git a/lib/symfony/cache/Tests/Simple/Psr6CacheTest.php b/lib/symfony/cache/Tests/Simple/Psr6CacheTest.php new file mode 100644 index 000000000..1bc75c906 --- /dev/null +++ b/lib/symfony/cache/Tests/Simple/Psr6CacheTest.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests\Simple; + +use Symfony\Component\Cache\Adapter\FilesystemAdapter; +use Symfony\Component\Cache\Simple\Psr6Cache; + +/** + * @group time-sensitive + */ +class Psr6CacheTest extends CacheTestCase +{ + protected $skippedTests = [ + 'testPrune' => 'Psr6Cache just proxies', + ]; + + public function createSimpleCache($defaultLifetime = 0) + { + return new Psr6Cache(new FilesystemAdapter('', $defaultLifetime)); + } +} diff --git a/lib/symfony/cache/Tests/Simple/RedisArrayCacheTest.php b/lib/symfony/cache/Tests/Simple/RedisArrayCacheTest.php new file mode 100644 index 000000000..bda39d990 --- /dev/null +++ b/lib/symfony/cache/Tests/Simple/RedisArrayCacheTest.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests\Simple; + +class RedisArrayCacheTest extends AbstractRedisCacheTest +{ + public static function setupBeforeClass() + { + parent::setupBeforeClass(); + if (!class_exists('RedisArray')) { + self::markTestSkipped('The RedisArray class is required.'); + } + self::$redis = new \RedisArray([getenv('REDIS_HOST')], ['lazy_connect' => true]); + } +} diff --git a/lib/symfony/cache/Tests/Simple/RedisCacheTest.php b/lib/symfony/cache/Tests/Simple/RedisCacheTest.php new file mode 100644 index 000000000..407d916c7 --- /dev/null +++ b/lib/symfony/cache/Tests/Simple/RedisCacheTest.php @@ -0,0 +1,82 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests\Simple; + +use Symfony\Component\Cache\Simple\RedisCache; + +class RedisCacheTest extends AbstractRedisCacheTest +{ + public static function setupBeforeClass() + { + parent::setupBeforeClass(); + self::$redis = RedisCache::createConnection('redis://'.getenv('REDIS_HOST')); + } + + public function testCreateConnection() + { + $redisHost = getenv('REDIS_HOST'); + + $redis = RedisCache::createConnection('redis://'.$redisHost); + $this->assertInstanceOf(\Redis::class, $redis); + $this->assertTrue($redis->isConnected()); + $this->assertSame(0, $redis->getDbNum()); + + $redis = RedisCache::createConnection('redis://'.$redisHost.'/2'); + $this->assertSame(2, $redis->getDbNum()); + + $redis = RedisCache::createConnection('redis://'.$redisHost, ['timeout' => 3]); + $this->assertEquals(3, $redis->getTimeout()); + + $redis = RedisCache::createConnection('redis://'.$redisHost.'?timeout=4'); + $this->assertEquals(4, $redis->getTimeout()); + + $redis = RedisCache::createConnection('redis://'.$redisHost, ['read_timeout' => 5]); + $this->assertEquals(5, $redis->getReadTimeout()); + } + + /** + * @dataProvider provideFailedCreateConnection + * @expectedException \Symfony\Component\Cache\Exception\InvalidArgumentException + * @expectedExceptionMessage Redis connection failed + */ + public function testFailedCreateConnection($dsn) + { + RedisCache::createConnection($dsn); + } + + public function provideFailedCreateConnection() + { + return [ + ['redis://localhost:1234'], + ['redis://foo@localhost'], + ['redis://localhost/123'], + ]; + } + + /** + * @dataProvider provideInvalidCreateConnection + * @expectedException \Symfony\Component\Cache\Exception\InvalidArgumentException + * @expectedExceptionMessage Invalid Redis DSN + */ + public function testInvalidCreateConnection($dsn) + { + RedisCache::createConnection($dsn); + } + + public function provideInvalidCreateConnection() + { + return [ + ['foo://localhost'], + ['redis://'], + ]; + } +} diff --git a/lib/symfony/cache/Tests/Simple/RedisClusterCacheTest.php b/lib/symfony/cache/Tests/Simple/RedisClusterCacheTest.php new file mode 100644 index 000000000..99d4e518f --- /dev/null +++ b/lib/symfony/cache/Tests/Simple/RedisClusterCacheTest.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests\Simple; + +class RedisClusterCacheTest extends AbstractRedisCacheTest +{ + public static function setupBeforeClass() + { + if (!class_exists('RedisCluster')) { + self::markTestSkipped('The RedisCluster class is required.'); + } + if (!$hosts = getenv('REDIS_CLUSTER_HOSTS')) { + self::markTestSkipped('REDIS_CLUSTER_HOSTS env var is not defined.'); + } + + self::$redis = new \RedisCluster(null, explode(' ', $hosts)); + } +} diff --git a/lib/symfony/cache/Tests/Simple/TraceableCacheTest.php b/lib/symfony/cache/Tests/Simple/TraceableCacheTest.php new file mode 100644 index 000000000..e684caf36 --- /dev/null +++ b/lib/symfony/cache/Tests/Simple/TraceableCacheTest.php @@ -0,0 +1,171 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests\Simple; + +use Symfony\Component\Cache\Simple\FilesystemCache; +use Symfony\Component\Cache\Simple\TraceableCache; + +/** + * @group time-sensitive + */ +class TraceableCacheTest extends CacheTestCase +{ + protected $skippedTests = [ + 'testPrune' => 'TraceableCache just proxies', + ]; + + public function createSimpleCache($defaultLifetime = 0) + { + return new TraceableCache(new FilesystemCache('', $defaultLifetime)); + } + + public function testGetMissTrace() + { + $pool = $this->createSimpleCache(); + $pool->get('k'); + $calls = $pool->getCalls(); + $this->assertCount(1, $calls); + + $call = $calls[0]; + $this->assertSame('get', $call->name); + $this->assertSame(['k' => false], $call->result); + $this->assertSame(0, $call->hits); + $this->assertSame(1, $call->misses); + $this->assertNotEmpty($call->start); + $this->assertNotEmpty($call->end); + } + + public function testGetHitTrace() + { + $pool = $this->createSimpleCache(); + $pool->set('k', 'foo'); + $pool->get('k'); + $calls = $pool->getCalls(); + $this->assertCount(2, $calls); + + $call = $calls[1]; + $this->assertSame(1, $call->hits); + $this->assertSame(0, $call->misses); + } + + public function testGetMultipleMissTrace() + { + $pool = $this->createSimpleCache(); + $pool->set('k1', 123); + $values = $pool->getMultiple(['k0', 'k1']); + foreach ($values as $value) { + } + $calls = $pool->getCalls(); + $this->assertCount(2, $calls); + + $call = $calls[1]; + $this->assertSame('getMultiple', $call->name); + $this->assertSame(['k1' => true, 'k0' => false], $call->result); + $this->assertSame(1, $call->misses); + $this->assertNotEmpty($call->start); + $this->assertNotEmpty($call->end); + } + + public function testHasMissTrace() + { + $pool = $this->createSimpleCache(); + $pool->has('k'); + $calls = $pool->getCalls(); + $this->assertCount(1, $calls); + + $call = $calls[0]; + $this->assertSame('has', $call->name); + $this->assertSame(['k' => false], $call->result); + $this->assertNotEmpty($call->start); + $this->assertNotEmpty($call->end); + } + + public function testHasHitTrace() + { + $pool = $this->createSimpleCache(); + $pool->set('k', 'foo'); + $pool->has('k'); + $calls = $pool->getCalls(); + $this->assertCount(2, $calls); + + $call = $calls[1]; + $this->assertSame('has', $call->name); + $this->assertSame(['k' => true], $call->result); + $this->assertNotEmpty($call->start); + $this->assertNotEmpty($call->end); + } + + public function testDeleteTrace() + { + $pool = $this->createSimpleCache(); + $pool->delete('k'); + $calls = $pool->getCalls(); + $this->assertCount(1, $calls); + + $call = $calls[0]; + $this->assertSame('delete', $call->name); + $this->assertSame(['k' => true], $call->result); + $this->assertSame(0, $call->hits); + $this->assertSame(0, $call->misses); + $this->assertNotEmpty($call->start); + $this->assertNotEmpty($call->end); + } + + public function testDeleteMultipleTrace() + { + $pool = $this->createSimpleCache(); + $arg = ['k0', 'k1']; + $pool->deleteMultiple($arg); + $calls = $pool->getCalls(); + $this->assertCount(1, $calls); + + $call = $calls[0]; + $this->assertSame('deleteMultiple', $call->name); + $this->assertSame(['keys' => $arg, 'result' => true], $call->result); + $this->assertSame(0, $call->hits); + $this->assertSame(0, $call->misses); + $this->assertNotEmpty($call->start); + $this->assertNotEmpty($call->end); + } + + public function testTraceSetTrace() + { + $pool = $this->createSimpleCache(); + $pool->set('k', 'foo'); + $calls = $pool->getCalls(); + $this->assertCount(1, $calls); + + $call = $calls[0]; + $this->assertSame('set', $call->name); + $this->assertSame(['k' => true], $call->result); + $this->assertSame(0, $call->hits); + $this->assertSame(0, $call->misses); + $this->assertNotEmpty($call->start); + $this->assertNotEmpty($call->end); + } + + public function testSetMultipleTrace() + { + $pool = $this->createSimpleCache(); + $pool->setMultiple(['k' => 'foo']); + $calls = $pool->getCalls(); + $this->assertCount(1, $calls); + + $call = $calls[0]; + $this->assertSame('setMultiple', $call->name); + $this->assertSame(['keys' => ['k'], 'result' => true], $call->result); + $this->assertSame(0, $call->hits); + $this->assertSame(0, $call->misses); + $this->assertNotEmpty($call->start); + $this->assertNotEmpty($call->end); + } +} diff --git a/lib/symfony/cache/Tests/Traits/PdoPruneableTrait.php b/lib/symfony/cache/Tests/Traits/PdoPruneableTrait.php new file mode 100644 index 000000000..3b1e1128b --- /dev/null +++ b/lib/symfony/cache/Tests/Traits/PdoPruneableTrait.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests\Traits; + +trait PdoPruneableTrait +{ + protected function isPruned($cache, $name) + { + $o = new \ReflectionObject($cache); + + if (!$o->hasMethod('getConnection')) { + self::fail('Cache does not have "getConnection()" method.'); + } + + $getPdoConn = $o->getMethod('getConnection'); + $getPdoConn->setAccessible(true); + + /** @var \Doctrine\DBAL\Statement $select */ + $select = $getPdoConn->invoke($cache)->prepare('SELECT 1 FROM cache_items WHERE item_id LIKE :id'); + $select->bindValue(':id', sprintf('%%%s', $name)); + $select->execute(); + + return 0 === \count($select->fetchAll(\PDO::FETCH_COLUMN)); + } +} diff --git a/lib/symfony/cache/Traits/AbstractTrait.php b/lib/symfony/cache/Traits/AbstractTrait.php new file mode 100644 index 000000000..cd1f20413 --- /dev/null +++ b/lib/symfony/cache/Traits/AbstractTrait.php @@ -0,0 +1,268 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Traits; + +use Psr\Log\LoggerAwareTrait; +use Symfony\Component\Cache\CacheItem; + +/** + * @author Nicolas Grekas + * + * @internal + */ +trait AbstractTrait +{ + use LoggerAwareTrait; + + private $namespace; + private $namespaceVersion = ''; + private $versioningIsEnabled = false; + private $deferred = []; + + /** + * @var int|null The maximum length to enforce for identifiers or null when no limit applies + */ + protected $maxIdLength; + + /** + * Fetches several cache items. + * + * @param array $ids The cache identifiers to fetch + * + * @return array|\Traversable The corresponding values found in the cache + */ + abstract protected function doFetch(array $ids); + + /** + * Confirms if the cache contains specified cache item. + * + * @param string $id The identifier for which to check existence + * + * @return bool True if item exists in the cache, false otherwise + */ + abstract protected function doHave($id); + + /** + * Deletes all items in the pool. + * + * @param string $namespace The prefix used for all identifiers managed by this pool + * + * @return bool True if the pool was successfully cleared, false otherwise + */ + abstract protected function doClear($namespace); + + /** + * Removes multiple items from the pool. + * + * @param array $ids An array of identifiers that should be removed from the pool + * + * @return bool True if the items were successfully removed, false otherwise + */ + abstract protected function doDelete(array $ids); + + /** + * Persists several cache items immediately. + * + * @param array $values The values to cache, indexed by their cache identifier + * @param int $lifetime The lifetime of the cached values, 0 for persisting until manual cleaning + * + * @return array|bool The identifiers that failed to be cached or a boolean stating if caching succeeded or not + */ + abstract protected function doSave(array $values, $lifetime); + + /** + * {@inheritdoc} + */ + public function hasItem($key) + { + $id = $this->getId($key); + + if (isset($this->deferred[$key])) { + $this->commit(); + } + + try { + return $this->doHave($id); + } catch (\Exception $e) { + CacheItem::log($this->logger, 'Failed to check if key "{key}" is cached', ['key' => $key, 'exception' => $e]); + + return false; + } + } + + /** + * {@inheritdoc} + */ + public function clear() + { + $this->deferred = []; + if ($cleared = $this->versioningIsEnabled) { + $namespaceVersion = substr_replace(base64_encode(pack('V', mt_rand())), static::NS_SEPARATOR, 5); + try { + $cleared = $this->doSave([static::NS_SEPARATOR.$this->namespace => $namespaceVersion], 0); + } catch (\Exception $e) { + $cleared = false; + } + if ($cleared = true === $cleared || [] === $cleared) { + $this->namespaceVersion = $namespaceVersion; + } + } + + try { + return $this->doClear($this->namespace) || $cleared; + } catch (\Exception $e) { + CacheItem::log($this->logger, 'Failed to clear the cache', ['exception' => $e]); + + return false; + } + } + + /** + * {@inheritdoc} + */ + public function deleteItem($key) + { + return $this->deleteItems([$key]); + } + + /** + * {@inheritdoc} + */ + public function deleteItems(array $keys) + { + $ids = []; + + foreach ($keys as $key) { + $ids[$key] = $this->getId($key); + unset($this->deferred[$key]); + } + + try { + if ($this->doDelete($ids)) { + return true; + } + } catch (\Exception $e) { + } + + $ok = true; + + // When bulk-delete failed, retry each item individually + foreach ($ids as $key => $id) { + try { + $e = null; + if ($this->doDelete([$id])) { + continue; + } + } catch (\Exception $e) { + } + CacheItem::log($this->logger, 'Failed to delete key "{key}"', ['key' => $key, 'exception' => $e]); + $ok = false; + } + + return $ok; + } + + /** + * Enables/disables versioning of items. + * + * When versioning is enabled, clearing the cache is atomic and doesn't require listing existing keys to proceed, + * but old keys may need garbage collection and extra round-trips to the back-end are required. + * + * Calling this method also clears the memoized namespace version and thus forces a resynchonization of it. + * + * @param bool $enable + * + * @return bool the previous state of versioning + */ + public function enableVersioning($enable = true) + { + $wasEnabled = $this->versioningIsEnabled; + $this->versioningIsEnabled = (bool) $enable; + $this->namespaceVersion = ''; + + return $wasEnabled; + } + + /** + * {@inheritdoc} + */ + public function reset() + { + if ($this->deferred) { + $this->commit(); + } + $this->namespaceVersion = ''; + } + + /** + * Like the native unserialize() function but throws an exception if anything goes wrong. + * + * @param string $value + * + * @return mixed + * + * @throws \Exception + */ + protected static function unserialize($value) + { + if ('b:0;' === $value) { + return false; + } + $unserializeCallbackHandler = ini_set('unserialize_callback_func', __CLASS__.'::handleUnserializeCallback'); + try { + if (false !== $value = unserialize($value)) { + return $value; + } + throw new \DomainException('Failed to unserialize cached value'); + } catch (\Error $e) { + throw new \ErrorException($e->getMessage(), $e->getCode(), E_ERROR, $e->getFile(), $e->getLine()); + } finally { + ini_set('unserialize_callback_func', $unserializeCallbackHandler); + } + } + + private function getId($key) + { + CacheItem::validateKey($key); + + if ($this->versioningIsEnabled && '' === $this->namespaceVersion) { + $this->namespaceVersion = '1'.static::NS_SEPARATOR; + try { + foreach ($this->doFetch([static::NS_SEPARATOR.$this->namespace]) as $v) { + $this->namespaceVersion = $v; + } + if ('1'.static::NS_SEPARATOR === $this->namespaceVersion) { + $this->namespaceVersion = substr_replace(base64_encode(pack('V', time())), static::NS_SEPARATOR, 5); + $this->doSave([static::NS_SEPARATOR.$this->namespace => $this->namespaceVersion], 0); + } + } catch (\Exception $e) { + } + } + + if (null === $this->maxIdLength) { + return $this->namespace.$this->namespaceVersion.$key; + } + if (\strlen($id = $this->namespace.$this->namespaceVersion.$key) > $this->maxIdLength) { + $id = $this->namespace.$this->namespaceVersion.substr_replace(base64_encode(hash('sha256', $key, true)), static::NS_SEPARATOR, -(\strlen($this->namespaceVersion) + 22)); + } + + return $id; + } + + /** + * @internal + */ + public static function handleUnserializeCallback($class) + { + throw new \DomainException('Class not found: '.$class); + } +} diff --git a/lib/symfony/cache/Traits/ApcuTrait.php b/lib/symfony/cache/Traits/ApcuTrait.php new file mode 100644 index 000000000..88dd22a91 --- /dev/null +++ b/lib/symfony/cache/Traits/ApcuTrait.php @@ -0,0 +1,117 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Traits; + +use Symfony\Component\Cache\CacheItem; +use Symfony\Component\Cache\Exception\CacheException; + +/** + * @author Nicolas Grekas + * + * @internal + */ +trait ApcuTrait +{ + public static function isSupported() + { + return \function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN); + } + + private function init($namespace, $defaultLifetime, $version) + { + if (!static::isSupported()) { + throw new CacheException('APCu is not enabled'); + } + if ('cli' === \PHP_SAPI) { + ini_set('apc.use_request_time', 0); + } + parent::__construct($namespace, $defaultLifetime); + + if (null !== $version) { + CacheItem::validateKey($version); + + if (!apcu_exists($version.'@'.$namespace)) { + $this->doClear($namespace); + apcu_add($version.'@'.$namespace, null); + } + } + } + + /** + * {@inheritdoc} + */ + protected function doFetch(array $ids) + { + try { + foreach (apcu_fetch($ids, $ok) ?: [] as $k => $v) { + if (null !== $v || $ok) { + yield $k => $v; + } + } + } catch (\Error $e) { + throw new \ErrorException($e->getMessage(), $e->getCode(), E_ERROR, $e->getFile(), $e->getLine()); + } + } + + /** + * {@inheritdoc} + */ + protected function doHave($id) + { + return apcu_exists($id); + } + + /** + * {@inheritdoc} + */ + protected function doClear($namespace) + { + return isset($namespace[0]) && class_exists('APCuIterator', false) && ('cli' !== \PHP_SAPI || filter_var(ini_get('apc.enable_cli'), FILTER_VALIDATE_BOOLEAN)) + ? apcu_delete(new \APCuIterator(sprintf('/^%s/', preg_quote($namespace, '/')), APC_ITER_KEY)) + : apcu_clear_cache(); + } + + /** + * {@inheritdoc} + */ + protected function doDelete(array $ids) + { + foreach ($ids as $id) { + apcu_delete($id); + } + + return true; + } + + /** + * {@inheritdoc} + */ + protected function doSave(array $values, $lifetime) + { + try { + if (false === $failures = apcu_store($values, null, $lifetime)) { + $failures = $values; + } + + return array_keys($failures); + } catch (\Error $e) { + } catch (\Exception $e) { + } + + if (1 === \count($values)) { + // Workaround https://github.com/krakjoe/apcu/issues/170 + apcu_delete(key($values)); + } + + throw $e; + } +} diff --git a/lib/symfony/cache/Traits/ArrayTrait.php b/lib/symfony/cache/Traits/ArrayTrait.php new file mode 100644 index 000000000..0a60968e9 --- /dev/null +++ b/lib/symfony/cache/Traits/ArrayTrait.php @@ -0,0 +1,108 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Traits; + +use Psr\Log\LoggerAwareTrait; +use Symfony\Component\Cache\CacheItem; + +/** + * @author Nicolas Grekas + * + * @internal + */ +trait ArrayTrait +{ + use LoggerAwareTrait; + + private $storeSerialized; + private $values = []; + private $expiries = []; + + /** + * Returns all cached values, with cache miss as null. + * + * @return array + */ + public function getValues() + { + return $this->values; + } + + /** + * {@inheritdoc} + */ + public function hasItem($key) + { + CacheItem::validateKey($key); + + return isset($this->expiries[$key]) && ($this->expiries[$key] > time() || !$this->deleteItem($key)); + } + + /** + * {@inheritdoc} + */ + public function clear() + { + $this->values = $this->expiries = []; + + return true; + } + + /** + * {@inheritdoc} + */ + public function deleteItem($key) + { + CacheItem::validateKey($key); + + unset($this->values[$key], $this->expiries[$key]); + + return true; + } + + /** + * {@inheritdoc} + */ + public function reset() + { + $this->clear(); + } + + private function generateItems(array $keys, $now, $f) + { + foreach ($keys as $i => $key) { + try { + if (!$isHit = isset($this->expiries[$key]) && ($this->expiries[$key] > $now || !$this->deleteItem($key))) { + $this->values[$key] = $value = null; + } elseif (!$this->storeSerialized) { + $value = $this->values[$key]; + } elseif ('b:0;' === $value = $this->values[$key]) { + $value = false; + } elseif (false === $value = unserialize($value)) { + $this->values[$key] = $value = null; + $isHit = false; + } + } catch (\Exception $e) { + CacheItem::log($this->logger, 'Failed to unserialize key "{key}"', ['key' => $key, 'exception' => $e]); + $this->values[$key] = $value = null; + $isHit = false; + } + unset($keys[$i]); + + yield $key => $f($key, $value, $isHit); + } + + foreach ($keys as $key) { + yield $key => $f($key, null, false); + } + } +} diff --git a/lib/symfony/cache/Traits/DoctrineTrait.php b/lib/symfony/cache/Traits/DoctrineTrait.php new file mode 100644 index 000000000..c87ecabaf --- /dev/null +++ b/lib/symfony/cache/Traits/DoctrineTrait.php @@ -0,0 +1,98 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Traits; + +/** + * @author Nicolas Grekas + * + * @internal + */ +trait DoctrineTrait +{ + private $provider; + + /** + * {@inheritdoc} + */ + public function reset() + { + parent::reset(); + $this->provider->setNamespace($this->provider->getNamespace()); + } + + /** + * {@inheritdoc} + */ + protected function doFetch(array $ids) + { + $unserializeCallbackHandler = ini_set('unserialize_callback_func', parent::class.'::handleUnserializeCallback'); + try { + return $this->provider->fetchMultiple($ids); + } catch (\Error $e) { + $trace = $e->getTrace(); + + if (isset($trace[0]['function']) && !isset($trace[0]['class'])) { + switch ($trace[0]['function']) { + case 'unserialize': + case 'apcu_fetch': + case 'apc_fetch': + throw new \ErrorException($e->getMessage(), $e->getCode(), E_ERROR, $e->getFile(), $e->getLine()); + } + } + + throw $e; + } finally { + ini_set('unserialize_callback_func', $unserializeCallbackHandler); + } + } + + /** + * {@inheritdoc} + */ + protected function doHave($id) + { + return $this->provider->contains($id); + } + + /** + * {@inheritdoc} + */ + protected function doClear($namespace) + { + $namespace = $this->provider->getNamespace(); + + return isset($namespace[0]) + ? $this->provider->deleteAll() + : $this->provider->flushAll(); + } + + /** + * {@inheritdoc} + */ + protected function doDelete(array $ids) + { + $ok = true; + foreach ($ids as $id) { + $ok = $this->provider->delete($id) && $ok; + } + + return $ok; + } + + /** + * {@inheritdoc} + */ + protected function doSave(array $values, $lifetime) + { + return $this->provider->saveMultiple($values, $lifetime); + } +} diff --git a/lib/symfony/cache/Traits/FilesystemCommonTrait.php b/lib/symfony/cache/Traits/FilesystemCommonTrait.php new file mode 100644 index 000000000..5510898b8 --- /dev/null +++ b/lib/symfony/cache/Traits/FilesystemCommonTrait.php @@ -0,0 +1,138 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Traits; + +use Symfony\Component\Cache\Exception\InvalidArgumentException; + +/** + * @author Nicolas Grekas + * + * @internal + */ +trait FilesystemCommonTrait +{ + private $directory; + private $tmp; + + private function init($namespace, $directory) + { + if (!isset($directory[0])) { + $directory = sys_get_temp_dir().'/symfony-cache'; + } else { + $directory = realpath($directory) ?: $directory; + } + if (isset($namespace[0])) { + if (preg_match('#[^-+_.A-Za-z0-9]#', $namespace, $match)) { + throw new InvalidArgumentException(sprintf('Namespace contains "%s" but only characters in [-+_.A-Za-z0-9] are allowed.', $match[0])); + } + $directory .= \DIRECTORY_SEPARATOR.$namespace; + } + if (!file_exists($directory)) { + @mkdir($directory, 0777, true); + } + $directory .= \DIRECTORY_SEPARATOR; + // On Windows the whole path is limited to 258 chars + if ('\\' === \DIRECTORY_SEPARATOR && \strlen($directory) > 234) { + throw new InvalidArgumentException(sprintf('Cache directory too long (%s)', $directory)); + } + + $this->directory = $directory; + } + + /** + * {@inheritdoc} + */ + protected function doClear($namespace) + { + $ok = true; + + foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->directory, \FilesystemIterator::SKIP_DOTS)) as $file) { + $ok = ($file->isDir() || @unlink($file) || !file_exists($file)) && $ok; + } + + return $ok; + } + + /** + * {@inheritdoc} + */ + protected function doDelete(array $ids) + { + $ok = true; + + foreach ($ids as $id) { + $file = $this->getFile($id); + $ok = (!file_exists($file) || @unlink($file) || !file_exists($file)) && $ok; + } + + return $ok; + } + + private function write($file, $data, $expiresAt = null) + { + set_error_handler(__CLASS__.'::throwError'); + try { + if (null === $this->tmp) { + $this->tmp = $this->directory.uniqid('', true); + } + file_put_contents($this->tmp, $data); + + if (null !== $expiresAt) { + touch($this->tmp, $expiresAt); + } + + return rename($this->tmp, $file); + } finally { + restore_error_handler(); + } + } + + private function getFile($id, $mkdir = false) + { + $hash = str_replace('/', '-', base64_encode(hash('sha256', static::class.$id, true))); + $dir = $this->directory.strtoupper($hash[0].\DIRECTORY_SEPARATOR.$hash[1].\DIRECTORY_SEPARATOR); + + if ($mkdir && !file_exists($dir)) { + @mkdir($dir, 0777, true); + } + + return $dir.substr($hash, 2, 20); + } + + /** + * @internal + */ + public static function throwError($type, $message, $file, $line) + { + throw new \ErrorException($message, 0, $type, $file, $line); + } + + public function __sleep() + { + throw new \BadMethodCallException('Cannot serialize '.__CLASS__); + } + + public function __wakeup() + { + throw new \BadMethodCallException('Cannot unserialize '.__CLASS__); + } + + public function __destruct() + { + if (method_exists(parent::class, '__destruct')) { + parent::__destruct(); + } + if (null !== $this->tmp && file_exists($this->tmp)) { + unlink($this->tmp); + } + } +} diff --git a/lib/symfony/cache/Traits/FilesystemTrait.php b/lib/symfony/cache/Traits/FilesystemTrait.php new file mode 100644 index 000000000..3e0dc86bd --- /dev/null +++ b/lib/symfony/cache/Traits/FilesystemTrait.php @@ -0,0 +1,107 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Traits; + +use Symfony\Component\Cache\Exception\CacheException; + +/** + * @author Nicolas Grekas + * @author Rob Frawley 2nd + * + * @internal + */ +trait FilesystemTrait +{ + use FilesystemCommonTrait; + + /** + * @return bool + */ + public function prune() + { + $time = time(); + $pruned = true; + + foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->directory, \FilesystemIterator::SKIP_DOTS), \RecursiveIteratorIterator::LEAVES_ONLY) as $file) { + if (!$h = @fopen($file, 'rb')) { + continue; + } + + if (($expiresAt = (int) fgets($h)) && $time >= $expiresAt) { + fclose($h); + $pruned = @unlink($file) && !file_exists($file) && $pruned; + } else { + fclose($h); + } + } + + return $pruned; + } + + /** + * {@inheritdoc} + */ + protected function doFetch(array $ids) + { + $values = []; + $now = time(); + + foreach ($ids as $id) { + $file = $this->getFile($id); + if (!file_exists($file) || !$h = @fopen($file, 'rb')) { + continue; + } + if (($expiresAt = (int) fgets($h)) && $now >= $expiresAt) { + fclose($h); + @unlink($file); + } else { + $i = rawurldecode(rtrim(fgets($h))); + $value = stream_get_contents($h); + fclose($h); + if ($i === $id) { + $values[$id] = parent::unserialize($value); + } + } + } + + return $values; + } + + /** + * {@inheritdoc} + */ + protected function doHave($id) + { + $file = $this->getFile($id); + + return file_exists($file) && (@filemtime($file) > time() || $this->doFetch([$id])); + } + + /** + * {@inheritdoc} + */ + protected function doSave(array $values, $lifetime) + { + $ok = true; + $expiresAt = $lifetime ? (time() + $lifetime) : 0; + + foreach ($values as $id => $value) { + $ok = $this->write($this->getFile($id, true), $expiresAt."\n".rawurlencode($id)."\n".serialize($value), $expiresAt) && $ok; + } + + if (!$ok && !is_writable($this->directory)) { + throw new CacheException(sprintf('Cache directory is not writable (%s)', $this->directory)); + } + + return $ok; + } +} diff --git a/lib/symfony/cache/Traits/MemcachedTrait.php b/lib/symfony/cache/Traits/MemcachedTrait.php new file mode 100644 index 000000000..9b7a84ab5 --- /dev/null +++ b/lib/symfony/cache/Traits/MemcachedTrait.php @@ -0,0 +1,296 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Traits; + +use Symfony\Component\Cache\Exception\CacheException; +use Symfony\Component\Cache\Exception\InvalidArgumentException; + +/** + * @author Rob Frawley 2nd + * @author Nicolas Grekas + * + * @internal + */ +trait MemcachedTrait +{ + private static $defaultClientOptions = [ + 'persistent_id' => null, + 'username' => null, + 'password' => null, + 'serializer' => 'php', + ]; + + private $client; + private $lazyClient; + + public static function isSupported() + { + return \extension_loaded('memcached') && version_compare(phpversion('memcached'), '2.2.0', '>='); + } + + private function init(\Memcached $client, $namespace, $defaultLifetime) + { + if (!static::isSupported()) { + throw new CacheException('Memcached >= 2.2.0 is required'); + } + if ('Memcached' === \get_class($client)) { + $opt = $client->getOption(\Memcached::OPT_SERIALIZER); + if (\Memcached::SERIALIZER_PHP !== $opt && \Memcached::SERIALIZER_IGBINARY !== $opt) { + throw new CacheException('MemcachedAdapter: "serializer" option must be "php" or "igbinary".'); + } + $this->maxIdLength -= \strlen($client->getOption(\Memcached::OPT_PREFIX_KEY)); + $this->client = $client; + } else { + $this->lazyClient = $client; + } + + parent::__construct($namespace, $defaultLifetime); + $this->enableVersioning(); + } + + /** + * Creates a Memcached instance. + * + * By default, the binary protocol, no block, and libketama compatible options are enabled. + * + * Examples for servers: + * - 'memcached://user:pass@localhost?weight=33' + * - [['localhost', 11211, 33]] + * + * @param array[]|string|string[] $servers An array of servers, a DSN, or an array of DSNs + * @param array $options An array of options + * + * @return \Memcached + * + * @throws \ErrorException When invalid options or servers are provided + */ + public static function createConnection($servers, array $options = []) + { + if (\is_string($servers)) { + $servers = [$servers]; + } elseif (!\is_array($servers)) { + throw new InvalidArgumentException(sprintf('MemcachedAdapter::createClient() expects array or string as first argument, %s given.', \gettype($servers))); + } + if (!static::isSupported()) { + throw new CacheException('Memcached >= 2.2.0 is required'); + } + set_error_handler(function ($type, $msg, $file, $line) { throw new \ErrorException($msg, 0, $type, $file, $line); }); + try { + $options += static::$defaultClientOptions; + $client = new \Memcached($options['persistent_id']); + $username = $options['username']; + $password = $options['password']; + + // parse any DSN in $servers + foreach ($servers as $i => $dsn) { + if (\is_array($dsn)) { + continue; + } + if (0 !== strpos($dsn, 'memcached://')) { + throw new InvalidArgumentException(sprintf('Invalid Memcached DSN: %s does not start with "memcached://"', $dsn)); + } + $params = preg_replace_callback('#^memcached://(?:([^@]*+)@)?#', function ($m) use (&$username, &$password) { + if (!empty($m[1])) { + list($username, $password) = explode(':', $m[1], 2) + [1 => null]; + } + + return 'file://'; + }, $dsn); + if (false === $params = parse_url($params)) { + throw new InvalidArgumentException(sprintf('Invalid Memcached DSN: %s', $dsn)); + } + if (!isset($params['host']) && !isset($params['path'])) { + throw new InvalidArgumentException(sprintf('Invalid Memcached DSN: %s', $dsn)); + } + if (isset($params['path']) && preg_match('#/(\d+)$#', $params['path'], $m)) { + $params['weight'] = $m[1]; + $params['path'] = substr($params['path'], 0, -\strlen($m[0])); + } + $params += [ + 'host' => isset($params['host']) ? $params['host'] : $params['path'], + 'port' => isset($params['host']) ? 11211 : null, + 'weight' => 0, + ]; + if (isset($params['query'])) { + parse_str($params['query'], $query); + $params += $query; + $options = $query + $options; + } + + $servers[$i] = [$params['host'], $params['port'], $params['weight']]; + } + + // set client's options + unset($options['persistent_id'], $options['username'], $options['password'], $options['weight'], $options['lazy']); + $options = array_change_key_case($options, CASE_UPPER); + $client->setOption(\Memcached::OPT_BINARY_PROTOCOL, true); + $client->setOption(\Memcached::OPT_NO_BLOCK, true); + $client->setOption(\Memcached::OPT_TCP_NODELAY, true); + if (!\array_key_exists('LIBKETAMA_COMPATIBLE', $options) && !\array_key_exists(\Memcached::OPT_LIBKETAMA_COMPATIBLE, $options)) { + $client->setOption(\Memcached::OPT_LIBKETAMA_COMPATIBLE, true); + } + foreach ($options as $name => $value) { + if (\is_int($name)) { + continue; + } + if ('HASH' === $name || 'SERIALIZER' === $name || 'DISTRIBUTION' === $name) { + $value = \constant('Memcached::'.$name.'_'.strtoupper($value)); + } + $opt = \constant('Memcached::OPT_'.$name); + + unset($options[$name]); + $options[$opt] = $value; + } + $client->setOptions($options); + + // set client's servers, taking care of persistent connections + if (!$client->isPristine()) { + $oldServers = []; + foreach ($client->getServerList() as $server) { + $oldServers[] = [$server['host'], $server['port']]; + } + + $newServers = []; + foreach ($servers as $server) { + if (1 < \count($server)) { + $server = array_values($server); + unset($server[2]); + $server[1] = (int) $server[1]; + } + $newServers[] = $server; + } + + if ($oldServers !== $newServers) { + $client->resetServerList(); + $client->addServers($servers); + } + } else { + $client->addServers($servers); + } + + if (null !== $username || null !== $password) { + if (!method_exists($client, 'setSaslAuthData')) { + trigger_error('Missing SASL support: the memcached extension must be compiled with --enable-memcached-sasl.'); + } + $client->setSaslAuthData($username, $password); + } + + return $client; + } finally { + restore_error_handler(); + } + } + + /** + * {@inheritdoc} + */ + protected function doSave(array $values, $lifetime) + { + if ($lifetime && $lifetime > 30 * 86400) { + $lifetime += time(); + } + + $encodedValues = []; + foreach ($values as $key => $value) { + $encodedValues[rawurlencode($key)] = $value; + } + + return $this->checkResultCode($this->getClient()->setMulti($encodedValues, $lifetime)); + } + + /** + * {@inheritdoc} + */ + protected function doFetch(array $ids) + { + $unserializeCallbackHandler = ini_set('unserialize_callback_func', __CLASS__.'::handleUnserializeCallback'); + try { + $encodedIds = array_map('rawurlencode', $ids); + + $encodedResult = $this->checkResultCode($this->getClient()->getMulti($encodedIds)); + + $result = []; + foreach ($encodedResult as $key => $value) { + $result[rawurldecode($key)] = $value; + } + + return $result; + } catch (\Error $e) { + throw new \ErrorException($e->getMessage(), $e->getCode(), E_ERROR, $e->getFile(), $e->getLine()); + } finally { + ini_set('unserialize_callback_func', $unserializeCallbackHandler); + } + } + + /** + * {@inheritdoc} + */ + protected function doHave($id) + { + return false !== $this->getClient()->get(rawurlencode($id)) || $this->checkResultCode(\Memcached::RES_SUCCESS === $this->client->getResultCode()); + } + + /** + * {@inheritdoc} + */ + protected function doDelete(array $ids) + { + $ok = true; + $encodedIds = array_map('rawurlencode', $ids); + foreach ($this->checkResultCode($this->getClient()->deleteMulti($encodedIds)) as $result) { + if (\Memcached::RES_SUCCESS !== $result && \Memcached::RES_NOTFOUND !== $result) { + $ok = false; + } + } + + return $ok; + } + + /** + * {@inheritdoc} + */ + protected function doClear($namespace) + { + return '' === $namespace && $this->getClient()->flush(); + } + + private function checkResultCode($result) + { + $code = $this->client->getResultCode(); + + if (\Memcached::RES_SUCCESS === $code || \Memcached::RES_NOTFOUND === $code) { + return $result; + } + + throw new CacheException(sprintf('MemcachedAdapter client error: %s.', strtolower($this->client->getResultMessage()))); + } + + /** + * @return \Memcached + */ + private function getClient() + { + if ($this->client) { + return $this->client; + } + + $opt = $this->lazyClient->getOption(\Memcached::OPT_SERIALIZER); + if (\Memcached::SERIALIZER_PHP !== $opt && \Memcached::SERIALIZER_IGBINARY !== $opt) { + throw new CacheException('MemcachedAdapter: "serializer" option must be "php" or "igbinary".'); + } + if ('' !== $prefix = (string) $this->lazyClient->getOption(\Memcached::OPT_PREFIX_KEY)) { + throw new CacheException(sprintf('MemcachedAdapter: "prefix_key" option must be empty when using proxified connections, "%s" given.', $prefix)); + } + + return $this->client = $this->lazyClient; + } +} diff --git a/lib/symfony/cache/Traits/PdoTrait.php b/lib/symfony/cache/Traits/PdoTrait.php new file mode 100644 index 000000000..b190fef8b --- /dev/null +++ b/lib/symfony/cache/Traits/PdoTrait.php @@ -0,0 +1,404 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Traits; + +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Driver\ServerInfoAwareConnection; +use Doctrine\DBAL\Schema\Schema; +use Symfony\Component\Cache\Exception\InvalidArgumentException; + +/** + * @internal + */ +trait PdoTrait +{ + private $conn; + private $dsn; + private $driver; + private $serverVersion; + private $table = 'cache_items'; + private $idCol = 'item_id'; + private $dataCol = 'item_data'; + private $lifetimeCol = 'item_lifetime'; + private $timeCol = 'item_time'; + private $username = ''; + private $password = ''; + private $connectionOptions = []; + private $namespace; + + private function init($connOrDsn, $namespace, $defaultLifetime, array $options) + { + if (isset($namespace[0]) && preg_match('#[^-+.A-Za-z0-9]#', $namespace, $match)) { + throw new InvalidArgumentException(sprintf('Namespace contains "%s" but only characters in [-+.A-Za-z0-9] are allowed.', $match[0])); + } + + if ($connOrDsn instanceof \PDO) { + if (\PDO::ERRMODE_EXCEPTION !== $connOrDsn->getAttribute(\PDO::ATTR_ERRMODE)) { + throw new InvalidArgumentException(sprintf('"%s" requires PDO error mode attribute be set to throw Exceptions (i.e. $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION))', __CLASS__)); + } + + $this->conn = $connOrDsn; + } elseif ($connOrDsn instanceof Connection) { + $this->conn = $connOrDsn; + } elseif (\is_string($connOrDsn)) { + $this->dsn = $connOrDsn; + } else { + throw new InvalidArgumentException(sprintf('"%s" requires PDO or Doctrine\DBAL\Connection instance or DSN string as first argument, "%s" given.', __CLASS__, \is_object($connOrDsn) ? \get_class($connOrDsn) : \gettype($connOrDsn))); + } + + $this->table = isset($options['db_table']) ? $options['db_table'] : $this->table; + $this->idCol = isset($options['db_id_col']) ? $options['db_id_col'] : $this->idCol; + $this->dataCol = isset($options['db_data_col']) ? $options['db_data_col'] : $this->dataCol; + $this->lifetimeCol = isset($options['db_lifetime_col']) ? $options['db_lifetime_col'] : $this->lifetimeCol; + $this->timeCol = isset($options['db_time_col']) ? $options['db_time_col'] : $this->timeCol; + $this->username = isset($options['db_username']) ? $options['db_username'] : $this->username; + $this->password = isset($options['db_password']) ? $options['db_password'] : $this->password; + $this->connectionOptions = isset($options['db_connection_options']) ? $options['db_connection_options'] : $this->connectionOptions; + $this->namespace = $namespace; + + parent::__construct($namespace, $defaultLifetime); + } + + /** + * Creates the table to store cache items which can be called once for setup. + * + * Cache ID are saved in a column of maximum length 255. Cache data is + * saved in a BLOB. + * + * @throws \PDOException When the table already exists + * @throws DBALException When the table already exists + * @throws \DomainException When an unsupported PDO driver is used + */ + public function createTable() + { + // connect if we are not yet + $conn = $this->getConnection(); + + if ($conn instanceof Connection) { + $types = [ + 'mysql' => 'binary', + 'sqlite' => 'text', + 'pgsql' => 'string', + 'oci' => 'string', + 'sqlsrv' => 'string', + ]; + if (!isset($types[$this->driver])) { + throw new \DomainException(sprintf('Creating the cache table is currently not implemented for PDO driver "%s".', $this->driver)); + } + + $schema = new Schema(); + $table = $schema->createTable($this->table); + $table->addColumn($this->idCol, $types[$this->driver], ['length' => 255]); + $table->addColumn($this->dataCol, 'blob', ['length' => 16777215]); + $table->addColumn($this->lifetimeCol, 'integer', ['unsigned' => true, 'notnull' => false]); + $table->addColumn($this->timeCol, 'integer', ['unsigned' => true]); + $table->setPrimaryKey([$this->idCol]); + + foreach ($schema->toSql($conn->getDatabasePlatform()) as $sql) { + $conn->exec($sql); + } + + return; + } + + switch ($this->driver) { + case 'mysql': + // We use varbinary for the ID column because it prevents unwanted conversions: + // - character set conversions between server and client + // - trailing space removal + // - case-insensitivity + // - language processing like é == e + $sql = "CREATE TABLE $this->table ($this->idCol VARBINARY(255) NOT NULL PRIMARY KEY, $this->dataCol MEDIUMBLOB NOT NULL, $this->lifetimeCol INTEGER UNSIGNED, $this->timeCol INTEGER UNSIGNED NOT NULL) COLLATE utf8_bin, ENGINE = InnoDB"; + break; + case 'sqlite': + $sql = "CREATE TABLE $this->table ($this->idCol TEXT NOT NULL PRIMARY KEY, $this->dataCol BLOB NOT NULL, $this->lifetimeCol INTEGER, $this->timeCol INTEGER NOT NULL)"; + break; + case 'pgsql': + $sql = "CREATE TABLE $this->table ($this->idCol VARCHAR(255) NOT NULL PRIMARY KEY, $this->dataCol BYTEA NOT NULL, $this->lifetimeCol INTEGER, $this->timeCol INTEGER NOT NULL)"; + break; + case 'oci': + $sql = "CREATE TABLE $this->table ($this->idCol VARCHAR2(255) NOT NULL PRIMARY KEY, $this->dataCol BLOB NOT NULL, $this->lifetimeCol INTEGER, $this->timeCol INTEGER NOT NULL)"; + break; + case 'sqlsrv': + $sql = "CREATE TABLE $this->table ($this->idCol VARCHAR(255) NOT NULL PRIMARY KEY, $this->dataCol VARBINARY(MAX) NOT NULL, $this->lifetimeCol INTEGER, $this->timeCol INTEGER NOT NULL)"; + break; + default: + throw new \DomainException(sprintf('Creating the cache table is currently not implemented for PDO driver "%s".', $this->driver)); + } + + $conn->exec($sql); + } + + /** + * {@inheritdoc} + */ + public function prune() + { + $deleteSql = "DELETE FROM $this->table WHERE $this->lifetimeCol + $this->timeCol <= :time"; + + if ('' !== $this->namespace) { + $deleteSql .= " AND $this->idCol LIKE :namespace"; + } + + $delete = $this->getConnection()->prepare($deleteSql); + $delete->bindValue(':time', time(), \PDO::PARAM_INT); + + if ('' !== $this->namespace) { + $delete->bindValue(':namespace', sprintf('%s%%', $this->namespace), \PDO::PARAM_STR); + } + + return $delete->execute(); + } + + /** + * {@inheritdoc} + */ + protected function doFetch(array $ids) + { + $now = time(); + $expired = []; + + $sql = str_pad('', (\count($ids) << 1) - 1, '?,'); + $sql = "SELECT $this->idCol, CASE WHEN $this->lifetimeCol IS NULL OR $this->lifetimeCol + $this->timeCol > ? THEN $this->dataCol ELSE NULL END FROM $this->table WHERE $this->idCol IN ($sql)"; + $stmt = $this->getConnection()->prepare($sql); + $stmt->bindValue($i = 1, $now, \PDO::PARAM_INT); + foreach ($ids as $id) { + $stmt->bindValue(++$i, $id); + } + $stmt->execute(); + + while ($row = $stmt->fetch(\PDO::FETCH_NUM)) { + if (null === $row[1]) { + $expired[] = $row[0]; + } else { + yield $row[0] => parent::unserialize(\is_resource($row[1]) ? stream_get_contents($row[1]) : $row[1]); + } + } + + if ($expired) { + $sql = str_pad('', (\count($expired) << 1) - 1, '?,'); + $sql = "DELETE FROM $this->table WHERE $this->lifetimeCol + $this->timeCol <= ? AND $this->idCol IN ($sql)"; + $stmt = $this->getConnection()->prepare($sql); + $stmt->bindValue($i = 1, $now, \PDO::PARAM_INT); + foreach ($expired as $id) { + $stmt->bindValue(++$i, $id); + } + $stmt->execute(); + } + } + + /** + * {@inheritdoc} + */ + protected function doHave($id) + { + $sql = "SELECT 1 FROM $this->table WHERE $this->idCol = :id AND ($this->lifetimeCol IS NULL OR $this->lifetimeCol + $this->timeCol > :time)"; + $stmt = $this->getConnection()->prepare($sql); + + $stmt->bindValue(':id', $id); + $stmt->bindValue(':time', time(), \PDO::PARAM_INT); + $stmt->execute(); + + return (bool) $stmt->fetchColumn(); + } + + /** + * {@inheritdoc} + */ + protected function doClear($namespace) + { + $conn = $this->getConnection(); + + if ('' === $namespace) { + if ('sqlite' === $this->driver) { + $sql = "DELETE FROM $this->table"; + } else { + $sql = "TRUNCATE TABLE $this->table"; + } + } else { + $sql = "DELETE FROM $this->table WHERE $this->idCol LIKE '$namespace%'"; + } + + $conn->exec($sql); + + return true; + } + + /** + * {@inheritdoc} + */ + protected function doDelete(array $ids) + { + $sql = str_pad('', (\count($ids) << 1) - 1, '?,'); + $sql = "DELETE FROM $this->table WHERE $this->idCol IN ($sql)"; + $stmt = $this->getConnection()->prepare($sql); + $stmt->execute(array_values($ids)); + + return true; + } + + /** + * {@inheritdoc} + */ + protected function doSave(array $values, $lifetime) + { + $serialized = []; + $failed = []; + + foreach ($values as $id => $value) { + try { + $serialized[$id] = serialize($value); + } catch (\Exception $e) { + $failed[] = $id; + } + } + + if (!$serialized) { + return $failed; + } + + $conn = $this->getConnection(); + $driver = $this->driver; + $insertSql = "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time)"; + + switch (true) { + case 'mysql' === $driver: + $sql = $insertSql." ON DUPLICATE KEY UPDATE $this->dataCol = VALUES($this->dataCol), $this->lifetimeCol = VALUES($this->lifetimeCol), $this->timeCol = VALUES($this->timeCol)"; + break; + case 'oci' === $driver: + // DUAL is Oracle specific dummy table + $sql = "MERGE INTO $this->table USING DUAL ON ($this->idCol = ?) ". + "WHEN NOT MATCHED THEN INSERT ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (?, ?, ?, ?) ". + "WHEN MATCHED THEN UPDATE SET $this->dataCol = ?, $this->lifetimeCol = ?, $this->timeCol = ?"; + break; + case 'sqlsrv' === $driver && version_compare($this->getServerVersion(), '10', '>='): + // MERGE is only available since SQL Server 2008 and must be terminated by semicolon + // It also requires HOLDLOCK according to http://weblogs.sqlteam.com/dang/archive/2009/01/31/UPSERT-Race-Condition-With-MERGE.aspx + $sql = "MERGE INTO $this->table WITH (HOLDLOCK) USING (SELECT 1 AS dummy) AS src ON ($this->idCol = ?) ". + "WHEN NOT MATCHED THEN INSERT ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (?, ?, ?, ?) ". + "WHEN MATCHED THEN UPDATE SET $this->dataCol = ?, $this->lifetimeCol = ?, $this->timeCol = ?;"; + break; + case 'sqlite' === $driver: + $sql = 'INSERT OR REPLACE'.substr($insertSql, 6); + break; + case 'pgsql' === $driver && version_compare($this->getServerVersion(), '9.5', '>='): + $sql = $insertSql." ON CONFLICT ($this->idCol) DO UPDATE SET ($this->dataCol, $this->lifetimeCol, $this->timeCol) = (EXCLUDED.$this->dataCol, EXCLUDED.$this->lifetimeCol, EXCLUDED.$this->timeCol)"; + break; + default: + $driver = null; + $sql = "UPDATE $this->table SET $this->dataCol = :data, $this->lifetimeCol = :lifetime, $this->timeCol = :time WHERE $this->idCol = :id"; + break; + } + + $now = time(); + $lifetime = $lifetime ?: null; + $stmt = $conn->prepare($sql); + + if ('sqlsrv' === $driver || 'oci' === $driver) { + $stmt->bindParam(1, $id); + $stmt->bindParam(2, $id); + $stmt->bindParam(3, $data, \PDO::PARAM_LOB); + $stmt->bindValue(4, $lifetime, \PDO::PARAM_INT); + $stmt->bindValue(5, $now, \PDO::PARAM_INT); + $stmt->bindParam(6, $data, \PDO::PARAM_LOB); + $stmt->bindValue(7, $lifetime, \PDO::PARAM_INT); + $stmt->bindValue(8, $now, \PDO::PARAM_INT); + } else { + $stmt->bindParam(':id', $id); + $stmt->bindParam(':data', $data, \PDO::PARAM_LOB); + $stmt->bindValue(':lifetime', $lifetime, \PDO::PARAM_INT); + $stmt->bindValue(':time', $now, \PDO::PARAM_INT); + } + if (null === $driver) { + $insertStmt = $conn->prepare($insertSql); + + $insertStmt->bindParam(':id', $id); + $insertStmt->bindParam(':data', $data, \PDO::PARAM_LOB); + $insertStmt->bindValue(':lifetime', $lifetime, \PDO::PARAM_INT); + $insertStmt->bindValue(':time', $now, \PDO::PARAM_INT); + } + + foreach ($serialized as $id => $data) { + $stmt->execute(); + + if (null === $driver && !$stmt->rowCount()) { + try { + $insertStmt->execute(); + } catch (DBALException $e) { + } catch (\PDOException $e) { + // A concurrent write won, let it be + } + } + } + + return $failed; + } + + /** + * @return \PDO|Connection + */ + private function getConnection() + { + if (null === $this->conn) { + $this->conn = new \PDO($this->dsn, $this->username, $this->password, $this->connectionOptions); + $this->conn->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); + } + if (null === $this->driver) { + if ($this->conn instanceof \PDO) { + $this->driver = $this->conn->getAttribute(\PDO::ATTR_DRIVER_NAME); + } else { + switch ($this->driver = $this->conn->getDriver()->getName()) { + case 'mysqli': + case 'pdo_mysql': + case 'drizzle_pdo_mysql': + $this->driver = 'mysql'; + break; + case 'pdo_sqlite': + $this->driver = 'sqlite'; + break; + case 'pdo_pgsql': + $this->driver = 'pgsql'; + break; + case 'oci8': + case 'pdo_oracle': + $this->driver = 'oci'; + break; + case 'pdo_sqlsrv': + $this->driver = 'sqlsrv'; + break; + } + } + } + + return $this->conn; + } + + /** + * @return string + */ + private function getServerVersion() + { + if (null === $this->serverVersion) { + $conn = $this->conn instanceof \PDO ? $this->conn : $this->conn->getWrappedConnection(); + if ($conn instanceof \PDO) { + $this->serverVersion = $conn->getAttribute(\PDO::ATTR_SERVER_VERSION); + } elseif ($conn instanceof ServerInfoAwareConnection) { + $this->serverVersion = $conn->getServerVersion(); + } else { + $this->serverVersion = '0'; + } + } + + return $this->serverVersion; + } +} diff --git a/lib/symfony/cache/Traits/PhpArrayTrait.php b/lib/symfony/cache/Traits/PhpArrayTrait.php new file mode 100644 index 000000000..ea996a217 --- /dev/null +++ b/lib/symfony/cache/Traits/PhpArrayTrait.php @@ -0,0 +1,142 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Traits; + +use Symfony\Component\Cache\CacheItem; +use Symfony\Component\Cache\Exception\InvalidArgumentException; + +/** + * @author Titouan Galopin + * @author Nicolas Grekas + * + * @internal + */ +trait PhpArrayTrait +{ + use ProxyTrait; + + private $file; + private $values; + private $zendDetectUnicode; + + /** + * Store an array of cached values. + * + * @param array $values The cached values + */ + public function warmUp(array $values) + { + if (file_exists($this->file)) { + if (!is_file($this->file)) { + throw new InvalidArgumentException(sprintf('Cache path exists and is not a file: %s.', $this->file)); + } + + if (!is_writable($this->file)) { + throw new InvalidArgumentException(sprintf('Cache file is not writable: %s.', $this->file)); + } + } else { + $directory = \dirname($this->file); + + if (!is_dir($directory) && !@mkdir($directory, 0777, true)) { + throw new InvalidArgumentException(sprintf('Cache directory does not exist and cannot be created: %s.', $directory)); + } + + if (!is_writable($directory)) { + throw new InvalidArgumentException(sprintf('Cache directory is not writable: %s.', $directory)); + } + } + + $dump = <<<'EOF' + $value) { + CacheItem::validateKey(\is_int($key) ? (string) $key : $key); + + if (null === $value || \is_object($value)) { + try { + $value = serialize($value); + } catch (\Exception $e) { + throw new InvalidArgumentException(sprintf('Cache key "%s" has non-serializable %s value.', $key, \get_class($value)), 0, $e); + } + } elseif (\is_array($value)) { + try { + $serialized = serialize($value); + $unserialized = unserialize($serialized); + } catch (\Exception $e) { + throw new InvalidArgumentException(sprintf('Cache key "%s" has non-serializable array value.', $key), 0, $e); + } + // Store arrays serialized if they contain any objects or references + if ($unserialized !== $value || (false !== strpos($serialized, ';R:') && preg_match('/;R:[1-9]/', $serialized))) { + $value = $serialized; + } + } elseif (\is_string($value)) { + // Serialize strings if they could be confused with serialized objects or arrays + if ('N;' === $value || (isset($value[2]) && ':' === $value[1])) { + $value = serialize($value); + } + } elseif (!is_scalar($value)) { + throw new InvalidArgumentException(sprintf('Cache key "%s" has non-serializable %s value.', $key, \gettype($value))); + } + + $dump .= var_export($key, true).' => '.var_export($value, true).",\n"; + } + + $dump .= "\n];\n"; + $dump = str_replace("' . \"\\0\" . '", "\0", $dump); + + $tmpFile = uniqid($this->file, true); + + file_put_contents($tmpFile, $dump); + @chmod($tmpFile, 0666 & ~umask()); + unset($serialized, $unserialized, $value, $dump); + + @rename($tmpFile, $this->file); + + $this->initialize(); + } + + /** + * {@inheritdoc} + */ + public function clear() + { + $this->values = []; + + $cleared = @unlink($this->file) || !file_exists($this->file); + + return $this->pool->clear() && $cleared; + } + + /** + * Load the cache file. + */ + private function initialize() + { + if ($this->zendDetectUnicode) { + $zmb = ini_set('zend.detect_unicode', 0); + } + try { + $this->values = file_exists($this->file) ? (include $this->file ?: []) : []; + } finally { + if ($this->zendDetectUnicode) { + ini_set('zend.detect_unicode', $zmb); + } + } + } +} diff --git a/lib/symfony/cache/Traits/PhpFilesTrait.php b/lib/symfony/cache/Traits/PhpFilesTrait.php new file mode 100644 index 000000000..3cc02b242 --- /dev/null +++ b/lib/symfony/cache/Traits/PhpFilesTrait.php @@ -0,0 +1,158 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Traits; + +use Symfony\Component\Cache\Exception\CacheException; +use Symfony\Component\Cache\Exception\InvalidArgumentException; + +/** + * @author Piotr Stankowski + * @author Nicolas Grekas + * @author Rob Frawley 2nd + * + * @internal + */ +trait PhpFilesTrait +{ + use FilesystemCommonTrait; + + private $includeHandler; + private $zendDetectUnicode; + + public static function isSupported() + { + return \function_exists('opcache_invalidate') && filter_var(ini_get('opcache.enable'), FILTER_VALIDATE_BOOLEAN); + } + + /** + * @return bool + */ + public function prune() + { + $time = time(); + $pruned = true; + $allowCompile = 'cli' !== \PHP_SAPI || filter_var(ini_get('opcache.enable_cli'), FILTER_VALIDATE_BOOLEAN); + + set_error_handler($this->includeHandler); + try { + foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->directory, \FilesystemIterator::SKIP_DOTS), \RecursiveIteratorIterator::LEAVES_ONLY) as $file) { + list($expiresAt) = include $file; + + if ($time >= $expiresAt) { + $pruned = @unlink($file) && !file_exists($file) && $pruned; + + if ($allowCompile) { + @opcache_invalidate($file, true); + } + } + } + } finally { + restore_error_handler(); + } + + return $pruned; + } + + /** + * {@inheritdoc} + */ + protected function doFetch(array $ids) + { + $values = []; + $now = time(); + + if ($this->zendDetectUnicode) { + $zmb = ini_set('zend.detect_unicode', 0); + } + set_error_handler($this->includeHandler); + try { + foreach ($ids as $id) { + try { + $file = $this->getFile($id); + list($expiresAt, $values[$id]) = include $file; + if ($now >= $expiresAt) { + unset($values[$id]); + } + } catch (\Exception $e) { + continue; + } + } + } finally { + restore_error_handler(); + if ($this->zendDetectUnicode) { + ini_set('zend.detect_unicode', $zmb); + } + } + + foreach ($values as $id => $value) { + if ('N;' === $value) { + $values[$id] = null; + } elseif (\is_string($value) && isset($value[2]) && ':' === $value[1]) { + $values[$id] = parent::unserialize($value); + } + } + + return $values; + } + + /** + * {@inheritdoc} + */ + protected function doHave($id) + { + return (bool) $this->doFetch([$id]); + } + + /** + * {@inheritdoc} + */ + protected function doSave(array $values, $lifetime) + { + $ok = true; + $data = [$lifetime ? time() + $lifetime : PHP_INT_MAX, '']; + $allowCompile = 'cli' !== \PHP_SAPI || filter_var(ini_get('opcache.enable_cli'), FILTER_VALIDATE_BOOLEAN); + + foreach ($values as $key => $value) { + if (null === $value || \is_object($value)) { + $value = serialize($value); + } elseif (\is_array($value)) { + $serialized = serialize($value); + $unserialized = parent::unserialize($serialized); + // Store arrays serialized if they contain any objects or references + if ($unserialized !== $value || (false !== strpos($serialized, ';R:') && preg_match('/;R:[1-9]/', $serialized))) { + $value = $serialized; + } + } elseif (\is_string($value)) { + // Serialize strings if they could be confused with serialized objects or arrays + if ('N;' === $value || (isset($value[2]) && ':' === $value[1])) { + $value = serialize($value); + } + } elseif (!is_scalar($value)) { + throw new InvalidArgumentException(sprintf('Cache key "%s" has non-serializable %s value.', $key, \gettype($value))); + } + + $data[1] = $value; + $file = $this->getFile($key, true); + $ok = $this->write($file, 'directory)) { + throw new CacheException(sprintf('Cache directory is not writable (%s)', $this->directory)); + } + + return $ok; + } +} diff --git a/lib/symfony/cache/Traits/ProxyTrait.php b/lib/symfony/cache/Traits/ProxyTrait.php new file mode 100644 index 000000000..d9e085b9e --- /dev/null +++ b/lib/symfony/cache/Traits/ProxyTrait.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Traits; + +use Symfony\Component\Cache\PruneableInterface; +use Symfony\Component\Cache\ResettableInterface; + +/** + * @author Nicolas Grekas + * + * @internal + */ +trait ProxyTrait +{ + private $pool; + + /** + * {@inheritdoc} + */ + public function prune() + { + return $this->pool instanceof PruneableInterface && $this->pool->prune(); + } + + /** + * {@inheritdoc} + */ + public function reset() + { + if ($this->pool instanceof ResettableInterface) { + $this->pool->reset(); + } + } +} diff --git a/lib/symfony/cache/Traits/RedisProxy.php b/lib/symfony/cache/Traits/RedisProxy.php new file mode 100644 index 000000000..98ea3aba7 --- /dev/null +++ b/lib/symfony/cache/Traits/RedisProxy.php @@ -0,0 +1,65 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Traits; + +/** + * @author Nicolas Grekas + * + * @internal + */ +class RedisProxy +{ + private $redis; + private $initializer; + private $ready = false; + + public function __construct(\Redis $redis, \Closure $initializer) + { + $this->redis = $redis; + $this->initializer = $initializer; + } + + public function __call($method, array $args) + { + $this->ready ?: $this->ready = $this->initializer->__invoke($this->redis); + + return \call_user_func_array([$this->redis, $method], $args); + } + + public function hscan($strKey, &$iIterator, $strPattern = null, $iCount = null) + { + $this->ready ?: $this->ready = $this->initializer->__invoke($this->redis); + + return $this->redis->hscan($strKey, $iIterator, $strPattern, $iCount); + } + + public function scan(&$iIterator, $strPattern = null, $iCount = null) + { + $this->ready ?: $this->ready = $this->initializer->__invoke($this->redis); + + return $this->redis->scan($iIterator, $strPattern, $iCount); + } + + public function sscan($strKey, &$iIterator, $strPattern = null, $iCount = null) + { + $this->ready ?: $this->ready = $this->initializer->__invoke($this->redis); + + return $this->redis->sscan($strKey, $iIterator, $strPattern, $iCount); + } + + public function zscan($strKey, &$iIterator, $strPattern = null, $iCount = null) + { + $this->ready ?: $this->ready = $this->initializer->__invoke($this->redis); + + return $this->redis->zscan($strKey, $iIterator, $strPattern, $iCount); + } +} diff --git a/lib/symfony/cache/Traits/RedisTrait.php b/lib/symfony/cache/Traits/RedisTrait.php new file mode 100644 index 000000000..395a9b805 --- /dev/null +++ b/lib/symfony/cache/Traits/RedisTrait.php @@ -0,0 +1,376 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Traits; + +use Predis\Connection\Aggregate\ClusterInterface; +use Predis\Connection\Aggregate\RedisCluster; +use Predis\Connection\Factory; +use Predis\Response\Status; +use Symfony\Component\Cache\Exception\CacheException; +use Symfony\Component\Cache\Exception\InvalidArgumentException; + +/** + * @author Aurimas Niekis + * @author Nicolas Grekas + * + * @internal + */ +trait RedisTrait +{ + private static $defaultConnectionOptions = [ + 'class' => null, + 'persistent' => 0, + 'persistent_id' => null, + 'timeout' => 30, + 'read_timeout' => 0, + 'retry_interval' => 0, + 'lazy' => false, + ]; + private $redis; + + /** + * @param \Redis|\RedisArray|\RedisCluster|\Predis\Client $redisClient + */ + private function init($redisClient, $namespace = '', $defaultLifetime = 0) + { + parent::__construct($namespace, $defaultLifetime); + + if (preg_match('#[^-+_.A-Za-z0-9]#', $namespace, $match)) { + throw new InvalidArgumentException(sprintf('RedisAdapter namespace contains "%s" but only characters in [-+_.A-Za-z0-9] are allowed.', $match[0])); + } + if (!$redisClient instanceof \Redis && !$redisClient instanceof \RedisArray && !$redisClient instanceof \RedisCluster && !$redisClient instanceof \Predis\Client && !$redisClient instanceof RedisProxy) { + throw new InvalidArgumentException(sprintf('%s() expects parameter 1 to be Redis, RedisArray, RedisCluster or Predis\Client, %s given', __METHOD__, \is_object($redisClient) ? \get_class($redisClient) : \gettype($redisClient))); + } + $this->redis = $redisClient; + } + + /** + * Creates a Redis connection using a DSN configuration. + * + * Example DSN: + * - redis://localhost + * - redis://example.com:1234 + * - redis://secret@example.com/13 + * - redis:///var/run/redis.sock + * - redis://secret@/var/run/redis.sock/13 + * + * @param string $dsn + * @param array $options See self::$defaultConnectionOptions + * + * @throws InvalidArgumentException when the DSN is invalid + * + * @return \Redis|\Predis\Client According to the "class" option + */ + public static function createConnection($dsn, array $options = []) + { + if (0 !== strpos($dsn, 'redis://')) { + throw new InvalidArgumentException(sprintf('Invalid Redis DSN: %s does not start with "redis://"', $dsn)); + } + $params = preg_replace_callback('#^redis://(?:(?:[^:@]*+:)?([^@]*+)@)?#', function ($m) use (&$auth) { + if (isset($m[1])) { + $auth = $m[1]; + } + + return 'file://'; + }, $dsn); + if (false === $params = parse_url($params)) { + throw new InvalidArgumentException(sprintf('Invalid Redis DSN: %s', $dsn)); + } + if (!isset($params['host']) && !isset($params['path'])) { + throw new InvalidArgumentException(sprintf('Invalid Redis DSN: %s', $dsn)); + } + if (isset($params['path']) && preg_match('#/(\d+)$#', $params['path'], $m)) { + $params['dbindex'] = $m[1]; + $params['path'] = substr($params['path'], 0, -\strlen($m[0])); + } + if (isset($params['host'])) { + $scheme = 'tcp'; + } else { + $scheme = 'unix'; + } + $params += [ + 'host' => isset($params['host']) ? $params['host'] : $params['path'], + 'port' => isset($params['host']) ? 6379 : null, + 'dbindex' => 0, + ]; + if (isset($params['query'])) { + parse_str($params['query'], $query); + $params += $query; + } + $params += $options + self::$defaultConnectionOptions; + if (null === $params['class'] && !\extension_loaded('redis') && !class_exists(\Predis\Client::class)) { + throw new CacheException(sprintf('Cannot find the "redis" extension, and "predis/predis" is not installed: %s', $dsn)); + } + $class = null === $params['class'] ? (\extension_loaded('redis') ? \Redis::class : \Predis\Client::class) : $params['class']; + + if (is_a($class, \Redis::class, true)) { + $connect = $params['persistent'] || $params['persistent_id'] ? 'pconnect' : 'connect'; + $redis = new $class(); + + $initializer = function ($redis) use ($connect, $params, $dsn, $auth) { + try { + @$redis->{$connect}($params['host'], $params['port'], $params['timeout'], $params['persistent_id'], $params['retry_interval']); + } catch (\RedisException $e) { + throw new InvalidArgumentException(sprintf('Redis connection failed (%s): %s', $e->getMessage(), $dsn)); + } + + set_error_handler(function ($type, $msg) use (&$error) { $error = $msg; }); + $isConnected = $redis->isConnected(); + restore_error_handler(); + if (!$isConnected) { + $error = preg_match('/^Redis::p?connect\(\): (.*)/', $error, $error) ? sprintf(' (%s)', $error[1]) : ''; + throw new InvalidArgumentException(sprintf('Redis connection failed%s: %s', $error, $dsn)); + } + + if ((null !== $auth && !$redis->auth($auth)) + || ($params['dbindex'] && !$redis->select($params['dbindex'])) + || ($params['read_timeout'] && !$redis->setOption(\Redis::OPT_READ_TIMEOUT, $params['read_timeout'])) + ) { + $e = preg_replace('/^ERR /', '', $redis->getLastError()); + throw new InvalidArgumentException(sprintf('Redis connection failed (%s): %s', $e, $dsn)); + } + + return true; + }; + + if ($params['lazy']) { + $redis = new RedisProxy($redis, $initializer); + } else { + $initializer($redis); + } + } elseif (is_a($class, \Predis\Client::class, true)) { + $params['scheme'] = $scheme; + $params['database'] = $params['dbindex'] ?: null; + $params['password'] = $auth; + $redis = new $class((new Factory())->create($params)); + } elseif (class_exists($class, false)) { + throw new InvalidArgumentException(sprintf('"%s" is not a subclass of "Redis" or "Predis\Client"', $class)); + } else { + throw new InvalidArgumentException(sprintf('Class "%s" does not exist', $class)); + } + + return $redis; + } + + /** + * {@inheritdoc} + */ + protected function doFetch(array $ids) + { + if (!$ids) { + return []; + } + + $result = []; + + if ($this->redis instanceof \Predis\Client && $this->redis->getConnection() instanceof ClusterInterface) { + $values = $this->pipeline(function () use ($ids) { + foreach ($ids as $id) { + yield 'get' => [$id]; + } + }); + } else { + $values = array_combine($ids, $this->redis->mget($ids)); + } + + foreach ($values as $id => $v) { + if ($v) { + $result[$id] = parent::unserialize($v); + } + } + + return $result; + } + + /** + * {@inheritdoc} + */ + protected function doHave($id) + { + return (bool) $this->redis->exists($id); + } + + /** + * {@inheritdoc} + */ + protected function doClear($namespace) + { + $cleared = true; + $hosts = [$this->redis]; + $evalArgs = [[$namespace], 0]; + + if ($this->redis instanceof \Predis\Client) { + $evalArgs = [0, $namespace]; + + $connection = $this->redis->getConnection(); + if ($connection instanceof ClusterInterface && $connection instanceof \Traversable) { + $hosts = []; + foreach ($connection as $c) { + $hosts[] = new \Predis\Client($c); + } + } + } elseif ($this->redis instanceof \RedisArray) { + $hosts = []; + foreach ($this->redis->_hosts() as $host) { + $hosts[] = $this->redis->_instance($host); + } + } elseif ($this->redis instanceof \RedisCluster) { + $hosts = []; + foreach ($this->redis->_masters() as $host) { + $hosts[] = $h = new \Redis(); + $h->connect($host[0], $host[1]); + } + } + foreach ($hosts as $host) { + if (!isset($namespace[0])) { + $cleared = $host->flushDb() && $cleared; + continue; + } + + $info = $host->info('Server'); + $info = isset($info['Server']) ? $info['Server'] : $info; + + if (!version_compare($info['redis_version'], '2.8', '>=')) { + // As documented in Redis documentation (http://redis.io/commands/keys) using KEYS + // can hang your server when it is executed against large databases (millions of items). + // Whenever you hit this scale, you should really consider upgrading to Redis 2.8 or above. + $cleared = $host->eval("local keys=redis.call('KEYS',ARGV[1]..'*') for i=1,#keys,5000 do redis.call('DEL',unpack(keys,i,math.min(i+4999,#keys))) end return 1", $evalArgs[0], $evalArgs[1]) && $cleared; + continue; + } + + $cursor = null; + do { + $keys = $host instanceof \Predis\Client ? $host->scan($cursor, 'MATCH', $namespace.'*', 'COUNT', 1000) : $host->scan($cursor, $namespace.'*', 1000); + if (isset($keys[1]) && \is_array($keys[1])) { + $cursor = $keys[0]; + $keys = $keys[1]; + } + if ($keys) { + $this->doDelete($keys); + } + } while ($cursor = (int) $cursor); + } + + return $cleared; + } + + /** + * {@inheritdoc} + */ + protected function doDelete(array $ids) + { + if (!$ids) { + return true; + } + + if ($this->redis instanceof \Predis\Client && $this->redis->getConnection() instanceof ClusterInterface) { + $this->pipeline(function () use ($ids) { + foreach ($ids as $id) { + yield 'del' => [$id]; + } + })->rewind(); + } else { + $this->redis->del($ids); + } + + return true; + } + + /** + * {@inheritdoc} + */ + protected function doSave(array $values, $lifetime) + { + $serialized = []; + $failed = []; + + foreach ($values as $id => $value) { + try { + $serialized[$id] = serialize($value); + } catch (\Exception $e) { + $failed[] = $id; + } + } + + if (!$serialized) { + return $failed; + } + + $results = $this->pipeline(function () use ($serialized, $lifetime) { + foreach ($serialized as $id => $value) { + if (0 >= $lifetime) { + yield 'set' => [$id, $value]; + } else { + yield 'setEx' => [$id, $lifetime, $value]; + } + } + }); + foreach ($results as $id => $result) { + if (true !== $result && (!$result instanceof Status || $result !== Status::get('OK'))) { + $failed[] = $id; + } + } + + return $failed; + } + + private function pipeline(\Closure $generator) + { + $ids = []; + + if ($this->redis instanceof \RedisCluster || ($this->redis instanceof \Predis\Client && $this->redis->getConnection() instanceof RedisCluster)) { + // phpredis & predis don't support pipelining with RedisCluster + // see https://github.com/phpredis/phpredis/blob/develop/cluster.markdown#pipelining + // see https://github.com/nrk/predis/issues/267#issuecomment-123781423 + $results = []; + foreach ($generator() as $command => $args) { + $results[] = \call_user_func_array([$this->redis, $command], $args); + $ids[] = $args[0]; + } + } elseif ($this->redis instanceof \Predis\Client) { + $results = $this->redis->pipeline(function ($redis) use ($generator, &$ids) { + foreach ($generator() as $command => $args) { + \call_user_func_array([$redis, $command], $args); + $ids[] = $args[0]; + } + }); + } elseif ($this->redis instanceof \RedisArray) { + $connections = $results = $ids = []; + foreach ($generator() as $command => $args) { + if (!isset($connections[$h = $this->redis->_target($args[0])])) { + $connections[$h] = [$this->redis->_instance($h), -1]; + $connections[$h][0]->multi(\Redis::PIPELINE); + } + \call_user_func_array([$connections[$h][0], $command], $args); + $results[] = [$h, ++$connections[$h][1]]; + $ids[] = $args[0]; + } + foreach ($connections as $h => $c) { + $connections[$h] = $c[0]->exec(); + } + foreach ($results as $k => list($h, $c)) { + $results[$k] = $connections[$h][$c]; + } + } else { + $this->redis->multi(\Redis::PIPELINE); + foreach ($generator() as $command => $args) { + \call_user_func_array([$this->redis, $command], $args); + $ids[] = $args[0]; + } + $results = $this->redis->exec(); + } + + foreach ($ids as $k => $id) { + yield $id => $results[$k]; + } + } +} diff --git a/lib/symfony/cache/composer.json b/lib/symfony/cache/composer.json new file mode 100644 index 000000000..e13cd9675 --- /dev/null +++ b/lib/symfony/cache/composer.json @@ -0,0 +1,50 @@ +{ + "name": "symfony/cache", + "type": "library", + "description": "Symfony Cache component with PSR-6, PSR-16, and tags", + "keywords": ["caching", "psr6"], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "provide": { + "psr/cache-implementation": "1.0", + "psr/simple-cache-implementation": "1.0" + }, + "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" + }, + "require-dev": { + "cache/integration-tests": "dev-master", + "doctrine/cache": "~1.6", + "doctrine/dbal": "~2.4", + "predis/predis": "~1.0" + }, + "conflict": { + "symfony/var-dumper": "<3.3" + }, + "autoload": { + "psr-4": { "Symfony\\Component\\Cache\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + } +} diff --git a/lib/symfony/cache/phpunit.xml.dist b/lib/symfony/cache/phpunit.xml.dist new file mode 100644 index 000000000..c35458ca4 --- /dev/null +++ b/lib/symfony/cache/phpunit.xml.dist @@ -0,0 +1,50 @@ + + + + + + + + + + + + ./Tests/ + + + + + + ./ + + ./Tests + ./vendor + + + + + + + + + + + Cache\IntegrationTests + Doctrine\Common\Cache + Symfony\Component\Cache + Symfony\Component\Cache\Tests\Fixtures + Symfony\Component\Cache\Traits + + + + + + + diff --git a/lib/symfony/class-loader/.gitignore b/lib/symfony/class-loader/.gitignore new file mode 100644 index 000000000..c49a5d8df --- /dev/null +++ b/lib/symfony/class-loader/.gitignore @@ -0,0 +1,3 @@ +vendor/ +composer.lock +phpunit.xml diff --git a/lib/symfony/class-loader/ApcClassLoader.php b/lib/symfony/class-loader/ApcClassLoader.php new file mode 100644 index 000000000..83038d749 --- /dev/null +++ b/lib/symfony/class-loader/ApcClassLoader.php @@ -0,0 +1,143 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\ClassLoader; + +@trigger_error('The '.__NAMESPACE__.'\ApcClassLoader class is deprecated since Symfony 3.3 and will be removed in 4.0. Use `composer install --apcu-autoloader` instead.', E_USER_DEPRECATED); + +/** + * ApcClassLoader implements a wrapping autoloader cached in APC for PHP 5.3. + * + * It expects an object implementing a findFile method to find the file. This + * allows using it as a wrapper around the other loaders of the component (the + * ClassLoader for instance) but also around any other autoloaders following + * this convention (the Composer one for instance). + * + * // with a Symfony autoloader + * use Symfony\Component\ClassLoader\ClassLoader; + * + * $loader = new ClassLoader(); + * $loader->addPrefix('Symfony\Component', __DIR__.'/component'); + * $loader->addPrefix('Symfony', __DIR__.'/framework'); + * + * // or with a Composer autoloader + * use Composer\Autoload\ClassLoader; + * + * $loader = new ClassLoader(); + * $loader->add('Symfony\Component', __DIR__.'/component'); + * $loader->add('Symfony', __DIR__.'/framework'); + * + * $cachedLoader = new ApcClassLoader('my_prefix', $loader); + * + * // activate the cached autoloader + * $cachedLoader->register(); + * + * // eventually deactivate the non-cached loader if it was registered previously + * // to be sure to use the cached one. + * $loader->unregister(); + * + * @author Fabien Potencier + * @author Kris Wallsmith + * + * @deprecated since version 3.3, to be removed in 4.0. Use `composer install --apcu-autoloader` instead. + */ +class ApcClassLoader +{ + private $prefix; + + /** + * A class loader object that implements the findFile() method. + * + * @var object + */ + protected $decorated; + + /** + * @param string $prefix The APC namespace prefix to use + * @param object $decorated A class loader object that implements the findFile() method + * + * @throws \RuntimeException + * @throws \InvalidArgumentException + */ + public function __construct($prefix, $decorated) + { + if (!\function_exists('apcu_fetch')) { + throw new \RuntimeException('Unable to use ApcClassLoader as APC is not installed.'); + } + + if (!method_exists($decorated, 'findFile')) { + throw new \InvalidArgumentException('The class finder must implement a "findFile" method.'); + } + + $this->prefix = $prefix; + $this->decorated = $decorated; + } + + /** + * Registers this instance as an autoloader. + * + * @param bool $prepend Whether to prepend the autoloader or not + */ + public function register($prepend = false) + { + spl_autoload_register([$this, 'loadClass'], true, $prepend); + } + + /** + * Unregisters this instance as an autoloader. + */ + public function unregister() + { + spl_autoload_unregister([$this, 'loadClass']); + } + + /** + * Loads the given class or interface. + * + * @param string $class The name of the class + * + * @return bool|null True, if loaded + */ + public function loadClass($class) + { + if ($file = $this->findFile($class)) { + require $file; + + return true; + } + } + + /** + * Finds a file by class name while caching lookups to APC. + * + * @param string $class A class name to resolve to file + * + * @return string|null + */ + public function findFile($class) + { + $file = apcu_fetch($this->prefix.$class, $success); + + if (!$success) { + apcu_store($this->prefix.$class, $file = $this->decorated->findFile($class) ?: null); + } + + return $file; + } + + /** + * Passes through all unknown calls onto the decorated object. + */ + public function __call($method, $args) + { + return \call_user_func_array([$this->decorated, $method], $args); + } +} diff --git a/lib/symfony/class-loader/CHANGELOG.md b/lib/symfony/class-loader/CHANGELOG.md new file mode 100644 index 000000000..203ec9eed --- /dev/null +++ b/lib/symfony/class-loader/CHANGELOG.md @@ -0,0 +1,41 @@ +CHANGELOG +========= + +3.3.0 +----- + + * deprecated the component: use Composer instead + +3.0.0 +----- + + * The DebugClassLoader class has been removed + * The DebugUniversalClassLoader class has been removed + * The UniversalClassLoader class has been removed + * The ApcUniversalClassLoader class has been removed + +2.4.0 +----- + + * deprecated the UniversalClassLoader in favor of the ClassLoader class instead + * deprecated the ApcUniversalClassLoader in favor of the ApcClassLoader class instead + * deprecated the DebugUniversalClassLoader in favor of the DebugClassLoader class from the Debug component + * deprecated the DebugClassLoader as it has been moved to the Debug component instead + +2.3.0 +----- + + * added a WinCacheClassLoader for WinCache + +2.1.0 +----- + + * added a DebugClassLoader able to wrap any autoloader providing a findFile + method + * added a new ApcClassLoader and XcacheClassLoader using composition to wrap + other loaders + * added a new ClassLoader which does not distinguish between namespaced and + pear-like classes (as the PEAR convention is a subset of PSR-0) and + supports using Composer's namespace maps + * added a class map generator + * added support for loading globally-installed PEAR packages diff --git a/lib/symfony/class-loader/ClassCollectionLoader.php b/lib/symfony/class-loader/ClassCollectionLoader.php new file mode 100644 index 000000000..5edab01cb --- /dev/null +++ b/lib/symfony/class-loader/ClassCollectionLoader.php @@ -0,0 +1,448 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\ClassLoader; + +if (\PHP_VERSION_ID >= 70000) { + @trigger_error('The '.__NAMESPACE__.'\ClassCollectionLoader class is deprecated since Symfony 3.3 and will be removed in 4.0.', E_USER_DEPRECATED); +} + +/** + * ClassCollectionLoader. + * + * @author Fabien Potencier + * + * @deprecated since version 3.3, to be removed in 4.0. + */ +class ClassCollectionLoader +{ + private static $loaded; + private static $seen; + private static $useTokenizer = true; + + /** + * Loads a list of classes and caches them in one big file. + * + * @param array $classes An array of classes to load + * @param string $cacheDir A cache directory + * @param string $name The cache name prefix + * @param bool $autoReload Whether to flush the cache when the cache is stale or not + * @param bool $adaptive Whether to remove already declared classes or not + * @param string $extension File extension of the resulting file + * + * @throws \InvalidArgumentException When class can't be loaded + */ + public static function load($classes, $cacheDir, $name, $autoReload, $adaptive = false, $extension = '.php') + { + // each $name can only be loaded once per PHP process + if (isset(self::$loaded[$name])) { + return; + } + + self::$loaded[$name] = true; + + if ($adaptive) { + $declared = array_merge(get_declared_classes(), get_declared_interfaces(), get_declared_traits()); + + // don't include already declared classes + $classes = array_diff($classes, $declared); + + // the cache is different depending on which classes are already declared + $name .= '-'.substr(hash('sha256', implode('|', $classes)), 0, 5); + } + + $classes = array_unique($classes); + + // cache the core classes + if (!is_dir($cacheDir) && !@mkdir($cacheDir, 0777, true) && !is_dir($cacheDir)) { + throw new \RuntimeException(sprintf('Class Collection Loader was not able to create directory "%s"', $cacheDir)); + } + $cacheDir = rtrim(realpath($cacheDir) ?: $cacheDir, '/'.\DIRECTORY_SEPARATOR); + $cache = $cacheDir.'/'.$name.$extension; + + // auto-reload + $reload = false; + if ($autoReload) { + $metadata = $cache.'.meta'; + if (!is_file($metadata) || !is_file($cache)) { + $reload = true; + } else { + $time = filemtime($cache); + $meta = unserialize(file_get_contents($metadata)); + + sort($meta[1]); + sort($classes); + + if ($meta[1] != $classes) { + $reload = true; + } else { + foreach ($meta[0] as $resource) { + if (!is_file($resource) || filemtime($resource) > $time) { + $reload = true; + + break; + } + } + } + } + } + + if (!$reload && file_exists($cache)) { + require_once $cache; + + return; + } + if (!$adaptive) { + $declared = array_merge(get_declared_classes(), get_declared_interfaces(), get_declared_traits()); + } + + $files = self::inline($classes, $cache, $declared); + + if ($autoReload) { + // save the resources + self::writeCacheFile($metadata, serialize([array_values($files), $classes])); + } + } + + /** + * Generates a file where classes and their parents are inlined. + * + * @param array $classes An array of classes to load + * @param string $cache The file where classes are inlined + * @param array $excluded An array of classes that won't be inlined + * + * @return array The source map of inlined classes, with classes as keys and files as values + * + * @throws \RuntimeException When class can't be loaded + */ + public static function inline($classes, $cache, array $excluded) + { + $declared = []; + foreach (self::getOrderedClasses($excluded) as $class) { + $declared[$class->getName()] = true; + } + + // cache the core classes + $cacheDir = \dirname($cache); + if (!is_dir($cacheDir) && !@mkdir($cacheDir, 0777, true) && !is_dir($cacheDir)) { + throw new \RuntimeException(sprintf('Class Collection Loader was not able to create directory "%s"', $cacheDir)); + } + + $spacesRegex = '(?:\s*+(?:(?:\#|//)[^\n]*+\n|/\*(?:(?getName()])) { + continue; + } + $declared[$class->getName()] = true; + + $files[$class->getName()] = $file = $class->getFileName(); + $c = file_get_contents($file); + + if (preg_match($dontInlineRegex, $c)) { + $file = explode('/', str_replace(\DIRECTORY_SEPARATOR, '/', $file)); + + for ($i = 0; isset($file[$i], $cacheDir[$i]); ++$i) { + if ($file[$i] !== $cacheDir[$i]) { + break; + } + } + if (1 >= $i) { + $file = var_export(implode('/', $file), true); + } else { + $file = \array_slice($file, $i); + $file = str_repeat('../', \count($cacheDir) - $i).implode('/', $file); + $file = '__DIR__.'.var_export('/'.$file, true); + } + + $c = "\nnamespace {require $file;}"; + } else { + $c = preg_replace(['/^\s*<\?php/', '/\?>\s*$/'], '', $c); + + // fakes namespace declaration for global code + if (!$class->inNamespace()) { + $c = "\nnamespace\n{\n".$c."\n}\n"; + } + + $c = self::fixNamespaceDeclarations('= 70000) { + // PHP 7 memory manager will not release after token_get_all(), see https://bugs.php.net/70098 + unset($tokens, $rawChunk); + gc_mem_caches(); + } + + return $output; + } + + /** + * This method is only useful for testing. + */ + public static function enableTokenizer($bool) + { + self::$useTokenizer = (bool) $bool; + } + + /** + * Strips leading & trailing ws, multiple EOL, multiple ws. + * + * @param string $code Original PHP code + * + * @return string compressed code + */ + private static function compressCode($code) + { + return preg_replace( + ['/^\s+/m', '/\s+$/m', '/([\n\r]+ *[\n\r]+)+/', '/[ \t]+/'], + ['', '', "\n", ' '], + $code + ); + } + + /** + * Writes a cache file. + * + * @param string $file Filename + * @param string $content Temporary file content + * + * @throws \RuntimeException when a cache file cannot be written + */ + private static function writeCacheFile($file, $content) + { + $dir = \dirname($file); + if (!is_writable($dir)) { + throw new \RuntimeException(sprintf('Cache directory "%s" is not writable.', $dir)); + } + + $tmpFile = tempnam($dir, basename($file)); + + if (false !== @file_put_contents($tmpFile, $content) && @rename($tmpFile, $file)) { + @chmod($file, 0666 & ~umask()); + + return; + } + + throw new \RuntimeException(sprintf('Failed to write cache file "%s".', $file)); + } + + /** + * Gets an ordered array of passed classes including all their dependencies. + * + * @return \ReflectionClass[] An array of sorted \ReflectionClass instances (dependencies added if needed) + * + * @throws \InvalidArgumentException When a class can't be loaded + */ + private static function getOrderedClasses(array $classes) + { + $map = []; + self::$seen = []; + foreach ($classes as $class) { + try { + $reflectionClass = new \ReflectionClass($class); + } catch (\ReflectionException $e) { + throw new \InvalidArgumentException(sprintf('Unable to load class "%s"', $class)); + } + + $map = array_merge($map, self::getClassHierarchy($reflectionClass)); + } + + return $map; + } + + private static function getClassHierarchy(\ReflectionClass $class) + { + if (isset(self::$seen[$class->getName()])) { + return []; + } + + self::$seen[$class->getName()] = true; + + $classes = [$class]; + $parent = $class; + while (($parent = $parent->getParentClass()) && $parent->isUserDefined() && !isset(self::$seen[$parent->getName()])) { + self::$seen[$parent->getName()] = true; + + array_unshift($classes, $parent); + } + + $traits = []; + + foreach ($classes as $c) { + foreach (self::resolveDependencies(self::computeTraitDeps($c), $c) as $trait) { + if ($trait !== $c) { + $traits[] = $trait; + } + } + } + + return array_merge(self::getInterfaces($class), $traits, $classes); + } + + private static function getInterfaces(\ReflectionClass $class) + { + $classes = []; + + foreach ($class->getInterfaces() as $interface) { + $classes = array_merge($classes, self::getInterfaces($interface)); + } + + if ($class->isUserDefined() && $class->isInterface() && !isset(self::$seen[$class->getName()])) { + self::$seen[$class->getName()] = true; + + $classes[] = $class; + } + + return $classes; + } + + private static function computeTraitDeps(\ReflectionClass $class) + { + $traits = $class->getTraits(); + $deps = [$class->getName() => $traits]; + while ($trait = array_pop($traits)) { + if ($trait->isUserDefined() && !isset(self::$seen[$trait->getName()])) { + self::$seen[$trait->getName()] = true; + $traitDeps = $trait->getTraits(); + $deps[$trait->getName()] = $traitDeps; + $traits = array_merge($traits, $traitDeps); + } + } + + return $deps; + } + + /** + * Dependencies resolution. + * + * This function does not check for circular dependencies as it should never + * occur with PHP traits. + * + * @param array $tree The dependency tree + * @param \ReflectionClass $node The node + * @param \ArrayObject $resolved An array of already resolved dependencies + * @param \ArrayObject $unresolved An array of dependencies to be resolved + * + * @return \ArrayObject The dependencies for the given node + * + * @throws \RuntimeException if a circular dependency is detected + */ + private static function resolveDependencies(array $tree, $node, \ArrayObject $resolved = null, \ArrayObject $unresolved = null) + { + if (null === $resolved) { + $resolved = new \ArrayObject(); + } + if (null === $unresolved) { + $unresolved = new \ArrayObject(); + } + $nodeName = $node->getName(); + + if (isset($tree[$nodeName])) { + $unresolved[$nodeName] = $node; + foreach ($tree[$nodeName] as $dependency) { + if (!$resolved->offsetExists($dependency->getName())) { + self::resolveDependencies($tree, $dependency, $resolved, $unresolved); + } + } + $resolved[$nodeName] = $node; + unset($unresolved[$nodeName]); + } + + return $resolved; + } +} diff --git a/lib/symfony/class-loader/ClassLoader.php b/lib/symfony/class-loader/ClassLoader.php new file mode 100644 index 000000000..d727278ee --- /dev/null +++ b/lib/symfony/class-loader/ClassLoader.php @@ -0,0 +1,207 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\ClassLoader; + +@trigger_error('The '.__NAMESPACE__.'\ClassLoader class is deprecated since Symfony 3.3 and will be removed in 4.0. Use Composer instead.', E_USER_DEPRECATED); + +/** + * ClassLoader implements an PSR-0 class loader. + * + * See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md + * + * $loader = new ClassLoader(); + * + * // register classes with namespaces + * $loader->addPrefix('Symfony\Component', __DIR__.'/component'); + * $loader->addPrefix('Symfony', __DIR__.'/framework'); + * + * // activate the autoloader + * $loader->register(); + * + * // to enable searching the include path (e.g. 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. + * + * @author Fabien Potencier + * @author Jordi Boggiano + * + * @deprecated since version 3.3, to be removed in 4.0. + */ +class ClassLoader +{ + private $prefixes = []; + private $fallbackDirs = []; + private $useIncludePath = false; + + /** + * Returns prefixes. + * + * @return array + */ + public function getPrefixes() + { + return $this->prefixes; + } + + /** + * Returns fallback directories. + * + * @return array + */ + public function getFallbackDirs() + { + return $this->fallbackDirs; + } + + /** + * Adds prefixes. + * + * @param array $prefixes Prefixes to add + */ + public function addPrefixes(array $prefixes) + { + foreach ($prefixes as $prefix => $path) { + $this->addPrefix($prefix, $path); + } + } + + /** + * Registers a set of classes. + * + * @param string $prefix The classes prefix + * @param array|string $paths The location(s) of the classes + */ + public function addPrefix($prefix, $paths) + { + if (!$prefix) { + foreach ((array) $paths as $path) { + $this->fallbackDirs[] = $path; + } + + return; + } + if (isset($this->prefixes[$prefix])) { + if (\is_array($paths)) { + $this->prefixes[$prefix] = array_unique(array_merge( + $this->prefixes[$prefix], + $paths + )); + } elseif (!\in_array($paths, $this->prefixes[$prefix])) { + $this->prefixes[$prefix][] = $paths; + } + } else { + $this->prefixes[$prefix] = array_unique((array) $paths); + } + } + + /** + * Turns on searching the include for class files. + * + * @param bool $useIncludePath + */ + public function setUseIncludePath($useIncludePath) + { + $this->useIncludePath = (bool) $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; + } + + /** + * Registers this instance as an autoloader. + * + * @param bool $prepend Whether to prepend the autoloader or not + */ + public function register($prepend = false) + { + spl_autoload_register([$this, 'loadClass'], true, $prepend); + } + + /** + * Unregisters this instance as an autoloader. + */ + public function unregister() + { + spl_autoload_unregister([$this, 'loadClass']); + } + + /** + * Loads the given class or interface. + * + * @param string $class The name of the class + * + * @return bool|null True, if loaded + */ + public function loadClass($class) + { + if ($file = $this->findFile($class)) { + require $file; + + return true; + } + } + + /** + * Finds the path to the file where the class is defined. + * + * @param string $class The name of the class + * + * @return string|null The path, if found + */ + public function findFile($class) + { + if (false !== $pos = strrpos($class, '\\')) { + // namespaced class name + $classPath = str_replace('\\', \DIRECTORY_SEPARATOR, substr($class, 0, $pos)).\DIRECTORY_SEPARATOR; + $className = substr($class, $pos + 1); + } else { + // PEAR-like class name + $classPath = null; + $className = $class; + } + + $classPath .= str_replace('_', \DIRECTORY_SEPARATOR, $className).'.php'; + + foreach ($this->prefixes as $prefix => $dirs) { + if ($class === strstr($class, $prefix)) { + foreach ($dirs as $dir) { + if (file_exists($dir.\DIRECTORY_SEPARATOR.$classPath)) { + return $dir.\DIRECTORY_SEPARATOR.$classPath; + } + } + } + } + + foreach ($this->fallbackDirs as $dir) { + if (file_exists($dir.\DIRECTORY_SEPARATOR.$classPath)) { + return $dir.\DIRECTORY_SEPARATOR.$classPath; + } + } + + if ($this->useIncludePath && $file = stream_resolve_include_path($classPath)) { + return $file; + } + } +} diff --git a/lib/symfony/class-loader/ClassMapGenerator.php b/lib/symfony/class-loader/ClassMapGenerator.php new file mode 100644 index 000000000..f32ca3ca2 --- /dev/null +++ b/lib/symfony/class-loader/ClassMapGenerator.php @@ -0,0 +1,160 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\ClassLoader; + +@trigger_error('The '.__NAMESPACE__.'\ClassMapGenerator class is deprecated since Symfony 3.3 and will be removed in 4.0. Use Composer instead.', E_USER_DEPRECATED); + +/** + * ClassMapGenerator. + * + * @author Gyula Sallai + * + * @deprecated since version 3.3, to be removed in 4.0. + */ +class ClassMapGenerator +{ + /** + * Generate a class map file. + * + * @param array|string $dirs Directories or a single path to search in + * @param string $file The name of the class map file + */ + public static function dump($dirs, $file) + { + $dirs = (array) $dirs; + $maps = []; + + foreach ($dirs as $dir) { + $maps = array_merge($maps, static::createMap($dir)); + } + + file_put_contents($file, sprintf('isFile()) { + continue; + } + + $path = $file->getRealPath() ?: $file->getPathname(); + + if ('php' !== pathinfo($path, PATHINFO_EXTENSION)) { + continue; + } + + $classes = self::findClasses($path); + + if (\PHP_VERSION_ID >= 70000) { + // PHP 7 memory manager will not release after token_get_all(), see https://bugs.php.net/70098 + gc_mem_caches(); + } + + foreach ($classes as $class) { + $map[$class] = $path; + } + } + + return $map; + } + + /** + * Extract the classes in the given file. + * + * @param string $path The file to check + * + * @return array The found classes + */ + private static function findClasses($path) + { + $contents = file_get_contents($path); + $tokens = token_get_all($contents); + + $classes = []; + + $namespace = ''; + for ($i = 0; isset($tokens[$i]); ++$i) { + $token = $tokens[$i]; + + if (!isset($token[1])) { + continue; + } + + $class = ''; + + switch ($token[0]) { + case T_NAMESPACE: + $namespace = ''; + // If there is a namespace, extract it + while (isset($tokens[++$i][1])) { + if (\in_array($tokens[$i][0], [T_STRING, T_NS_SEPARATOR])) { + $namespace .= $tokens[$i][1]; + } + } + $namespace .= '\\'; + break; + case T_CLASS: + case T_INTERFACE: + case T_TRAIT: + // Skip usage of ::class constant + $isClassConstant = false; + for ($j = $i - 1; $j > 0; --$j) { + if (!isset($tokens[$j][1])) { + break; + } + + if (T_DOUBLE_COLON === $tokens[$j][0]) { + $isClassConstant = true; + break; + } elseif (!\in_array($tokens[$j][0], [T_WHITESPACE, T_DOC_COMMENT, T_COMMENT])) { + break; + } + } + + if ($isClassConstant) { + break; + } + + // Find the classname + while (isset($tokens[++$i][1])) { + $t = $tokens[$i]; + if (T_STRING === $t[0]) { + $class .= $t[1]; + } elseif ('' !== $class && T_WHITESPACE === $t[0]) { + break; + } + } + + $classes[] = ltrim($namespace.$class, '\\'); + break; + default: + break; + } + } + + return $classes; + } +} diff --git a/lib/symfony/class-loader/LICENSE b/lib/symfony/class-loader/LICENSE new file mode 100644 index 000000000..a677f4376 --- /dev/null +++ b/lib/symfony/class-loader/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2019 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. diff --git a/lib/symfony/class-loader/MapClassLoader.php b/lib/symfony/class-loader/MapClassLoader.php new file mode 100644 index 000000000..a9719d6bd --- /dev/null +++ b/lib/symfony/class-loader/MapClassLoader.php @@ -0,0 +1,70 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\ClassLoader; + +@trigger_error('The '.__NAMESPACE__.'\MapClassLoader class is deprecated since Symfony 3.3 and will be removed in 4.0. Use Composer instead.', E_USER_DEPRECATED); + +/** + * A class loader that uses a mapping file to look up paths. + * + * @author Fabien Potencier + * + * @deprecated since version 3.3, to be removed in 4.0. + */ +class MapClassLoader +{ + private $map = []; + + /** + * @param array $map A map where keys are classes and values the absolute file path + */ + public function __construct(array $map) + { + $this->map = $map; + } + + /** + * Registers this instance as an autoloader. + * + * @param bool $prepend Whether to prepend the autoloader or not + */ + public function register($prepend = false) + { + spl_autoload_register([$this, 'loadClass'], true, $prepend); + } + + /** + * Loads the given class or interface. + * + * @param string $class The name of the class + */ + public function loadClass($class) + { + if (isset($this->map[$class])) { + require $this->map[$class]; + } + } + + /** + * Finds the path to the file where the class is defined. + * + * @param string $class The name of the class + * + * @return string|null The path, if found + */ + public function findFile($class) + { + if (isset($this->map[$class])) { + return $this->map[$class]; + } + } +} diff --git a/lib/symfony/class-loader/Psr4ClassLoader.php b/lib/symfony/class-loader/Psr4ClassLoader.php new file mode 100644 index 000000000..7ea521d82 --- /dev/null +++ b/lib/symfony/class-loader/Psr4ClassLoader.php @@ -0,0 +1,94 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\ClassLoader; + +@trigger_error('The '.__NAMESPACE__.'\Psr4ClassLoader class is deprecated since Symfony 3.3 and will be removed in 4.0. Use Composer instead.', E_USER_DEPRECATED); + +/** + * A PSR-4 compatible class loader. + * + * See http://www.php-fig.org/psr/psr-4/ + * + * @author Alexander M. Turek + * + * @deprecated since version 3.3, to be removed in 4.0. + */ +class Psr4ClassLoader +{ + private $prefixes = []; + + /** + * @param string $prefix + * @param string $baseDir + */ + public function addPrefix($prefix, $baseDir) + { + $prefix = trim($prefix, '\\').'\\'; + $baseDir = rtrim($baseDir, \DIRECTORY_SEPARATOR).\DIRECTORY_SEPARATOR; + $this->prefixes[] = [$prefix, $baseDir]; + } + + /** + * @param string $class + * + * @return string|null + */ + public function findFile($class) + { + $class = ltrim($class, '\\'); + + foreach ($this->prefixes as list($currentPrefix, $currentBaseDir)) { + if (0 === strpos($class, $currentPrefix)) { + $classWithoutPrefix = substr($class, \strlen($currentPrefix)); + $file = $currentBaseDir.str_replace('\\', \DIRECTORY_SEPARATOR, $classWithoutPrefix).'.php'; + if (file_exists($file)) { + return $file; + } + } + } + } + + /** + * @param string $class + * + * @return bool + */ + public function loadClass($class) + { + $file = $this->findFile($class); + if (null !== $file) { + require $file; + + return true; + } + + return false; + } + + /** + * Registers this instance as an autoloader. + * + * @param bool $prepend + */ + public function register($prepend = false) + { + spl_autoload_register([$this, 'loadClass'], true, $prepend); + } + + /** + * Removes this instance from the registered autoloaders. + */ + public function unregister() + { + spl_autoload_unregister([$this, 'loadClass']); + } +} diff --git a/lib/symfony/class-loader/README.md b/lib/symfony/class-loader/README.md new file mode 100644 index 000000000..d61992b6a --- /dev/null +++ b/lib/symfony/class-loader/README.md @@ -0,0 +1,14 @@ +ClassLoader Component +===================== + +The ClassLoader component provides tools to autoload your classes and cache +their locations for performance. + +Resources +--------- + + * [Documentation](https://symfony.com/doc/current/components/class_loader/index.html) + * [Contributing](https://symfony.com/doc/current/contributing/index.html) + * [Report issues](https://github.com/symfony/symfony/issues) and + [send Pull Requests](https://github.com/symfony/symfony/pulls) + in the [main Symfony repository](https://github.com/symfony/symfony) diff --git a/lib/symfony/class-loader/Tests/ApcClassLoaderTest.php b/lib/symfony/class-loader/Tests/ApcClassLoaderTest.php new file mode 100644 index 000000000..6706acbd3 --- /dev/null +++ b/lib/symfony/class-loader/Tests/ApcClassLoaderTest.php @@ -0,0 +1,200 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\ClassLoader\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\ClassLoader\ApcClassLoader; +use Symfony\Component\ClassLoader\ClassLoader; + +/** + * @group legacy + */ +class ApcClassLoaderTest extends TestCase +{ + protected function setUp() + { + if (!(filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) && filter_var(ini_get('apc.enable_cli'), FILTER_VALIDATE_BOOLEAN))) { + $this->markTestSkipped('The apc extension is not enabled.'); + } else { + apcu_clear_cache(); + } + } + + protected function tearDown() + { + if (filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) && filter_var(ini_get('apc.enable_cli'), FILTER_VALIDATE_BOOLEAN)) { + apcu_clear_cache(); + } + } + + public function testConstructor() + { + $loader = new ClassLoader(); + $loader->addPrefix('Apc\Namespaced', __DIR__.\DIRECTORY_SEPARATOR.'Fixtures'); + + $loader = new ApcClassLoader('test.prefix.', $loader); + + $this->assertEquals($loader->findFile('\Apc\Namespaced\FooBar'), apcu_fetch('test.prefix.\Apc\Namespaced\FooBar'), '__construct() takes a prefix as its first argument'); + } + + /** + * @dataProvider getLoadClassTests + */ + public function testLoadClass($className, $testClassName, $message) + { + $loader = new ClassLoader(); + $loader->addPrefix('Apc\Namespaced', __DIR__.\DIRECTORY_SEPARATOR.'Fixtures'); + $loader->addPrefix('Apc_Pearlike_', __DIR__.\DIRECTORY_SEPARATOR.'Fixtures'); + + $loader = new ApcClassLoader('test.prefix.', $loader); + $loader->loadClass($testClassName); + $this->assertTrue(class_exists($className), $message); + } + + public function getLoadClassTests() + { + return [ + ['\\Apc\\Namespaced\\Foo', 'Apc\\Namespaced\\Foo', '->loadClass() loads Apc\Namespaced\Foo class'], + ['Apc_Pearlike_Foo', 'Apc_Pearlike_Foo', '->loadClass() loads Apc_Pearlike_Foo class'], + ]; + } + + /** + * @dataProvider getLoadClassFromFallbackTests + */ + public function testLoadClassFromFallback($className, $testClassName, $message) + { + $loader = new ClassLoader(); + $loader->addPrefix('Apc\Namespaced', __DIR__.\DIRECTORY_SEPARATOR.'Fixtures'); + $loader->addPrefix('Apc_Pearlike_', __DIR__.\DIRECTORY_SEPARATOR.'Fixtures'); + $loader->addPrefix('', [__DIR__.\DIRECTORY_SEPARATOR.'Fixtures/Apc/fallback']); + + $loader = new ApcClassLoader('test.prefix.fallback', $loader); + $loader->loadClass($testClassName); + + $this->assertTrue(class_exists($className), $message); + } + + public function getLoadClassFromFallbackTests() + { + return [ + ['\\Apc\\Namespaced\\Baz', 'Apc\\Namespaced\\Baz', '->loadClass() loads Apc\Namespaced\Baz class'], + ['Apc_Pearlike_Baz', 'Apc_Pearlike_Baz', '->loadClass() loads Apc_Pearlike_Baz class'], + ['\\Apc\\Namespaced\\FooBar', 'Apc\\Namespaced\\FooBar', '->loadClass() loads Apc\Namespaced\Baz class from fallback dir'], + ['Apc_Pearlike_FooBar', 'Apc_Pearlike_FooBar', '->loadClass() loads Apc_Pearlike_Baz class from fallback dir'], + ]; + } + + /** + * @dataProvider getLoadClassNamespaceCollisionTests + */ + public function testLoadClassNamespaceCollision($namespaces, $className, $message) + { + $loader = new ClassLoader(); + $loader->addPrefixes($namespaces); + + $loader = new ApcClassLoader('test.prefix.collision.', $loader); + $loader->loadClass($className); + + $this->assertTrue(class_exists($className), $message); + } + + public function getLoadClassNamespaceCollisionTests() + { + return [ + [ + [ + 'Apc\\NamespaceCollision\\A' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/Apc/alpha', + 'Apc\\NamespaceCollision\\A\\B' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/Apc/beta', + ], + 'Apc\NamespaceCollision\A\Foo', + '->loadClass() loads NamespaceCollision\A\Foo from alpha.', + ], + [ + [ + 'Apc\\NamespaceCollision\\A\\B' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/Apc/beta', + 'Apc\\NamespaceCollision\\A' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/Apc/alpha', + ], + 'Apc\NamespaceCollision\A\Bar', + '->loadClass() loads NamespaceCollision\A\Bar from alpha.', + ], + [ + [ + 'Apc\\NamespaceCollision\\A' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/Apc/alpha', + 'Apc\\NamespaceCollision\\A\\B' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/Apc/beta', + ], + 'Apc\NamespaceCollision\A\B\Foo', + '->loadClass() loads NamespaceCollision\A\B\Foo from beta.', + ], + [ + [ + 'Apc\\NamespaceCollision\\A\\B' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/Apc/beta', + 'Apc\\NamespaceCollision\\A' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/Apc/alpha', + ], + 'Apc\NamespaceCollision\A\B\Bar', + '->loadClass() loads NamespaceCollision\A\B\Bar from beta.', + ], + ]; + } + + /** + * @dataProvider getLoadClassPrefixCollisionTests + */ + public function testLoadClassPrefixCollision($prefixes, $className, $message) + { + $loader = new ClassLoader(); + $loader->addPrefixes($prefixes); + + $loader = new ApcClassLoader('test.prefix.collision.', $loader); + $loader->loadClass($className); + + $this->assertTrue(class_exists($className), $message); + } + + public function getLoadClassPrefixCollisionTests() + { + return [ + [ + [ + 'ApcPrefixCollision_A_' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/Apc/alpha/Apc', + 'ApcPrefixCollision_A_B_' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/Apc/beta/Apc', + ], + 'ApcPrefixCollision_A_Foo', + '->loadClass() loads ApcPrefixCollision_A_Foo from alpha.', + ], + [ + [ + 'ApcPrefixCollision_A_B_' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/Apc/beta/Apc', + 'ApcPrefixCollision_A_' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/Apc/alpha/Apc', + ], + 'ApcPrefixCollision_A_Bar', + '->loadClass() loads ApcPrefixCollision_A_Bar from alpha.', + ], + [ + [ + 'ApcPrefixCollision_A_' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/Apc/alpha/Apc', + 'ApcPrefixCollision_A_B_' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/Apc/beta/Apc', + ], + 'ApcPrefixCollision_A_B_Foo', + '->loadClass() loads ApcPrefixCollision_A_B_Foo from beta.', + ], + [ + [ + 'ApcPrefixCollision_A_B_' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/Apc/beta/Apc', + 'ApcPrefixCollision_A_' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/Apc/alpha/Apc', + ], + 'ApcPrefixCollision_A_B_Bar', + '->loadClass() loads ApcPrefixCollision_A_B_Bar from beta.', + ], + ]; + } +} diff --git a/lib/symfony/class-loader/Tests/ClassCollectionLoaderTest.php b/lib/symfony/class-loader/Tests/ClassCollectionLoaderTest.php new file mode 100644 index 000000000..816f3c394 --- /dev/null +++ b/lib/symfony/class-loader/Tests/ClassCollectionLoaderTest.php @@ -0,0 +1,319 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\ClassLoader\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\ClassLoader\ClassCollectionLoader; +use Symfony\Component\ClassLoader\Tests\Fixtures\DeclaredClass; +use Symfony\Component\ClassLoader\Tests\Fixtures\WarmedClass; + +require_once __DIR__.'/Fixtures/ClassesWithParents/GInterface.php'; +require_once __DIR__.'/Fixtures/ClassesWithParents/CInterface.php'; +require_once __DIR__.'/Fixtures/ClassesWithParents/B.php'; +require_once __DIR__.'/Fixtures/ClassesWithParents/A.php'; + +/** + * @group legacy + */ +class ClassCollectionLoaderTest extends TestCase +{ + public function testTraitDependencies() + { + require_once __DIR__.'/Fixtures/deps/traits.php'; + + $r = new \ReflectionClass('Symfony\Component\ClassLoader\ClassCollectionLoader'); + $m = $r->getMethod('getOrderedClasses'); + $m->setAccessible(true); + + $ordered = $m->invoke(null, ['CTFoo']); + + $this->assertEquals( + ['TD', 'TC', 'TB', 'TA', 'TZ', 'CTFoo'], + array_map(function ($class) { return $class->getName(); }, $ordered) + ); + + $ordered = $m->invoke(null, ['CTBar']); + + $this->assertEquals( + ['TD', 'TZ', 'TC', 'TB', 'TA', 'CTBar'], + array_map(function ($class) { return $class->getName(); }, $ordered) + ); + } + + /** + * @dataProvider getDifferentOrders + */ + public function testClassReordering(array $classes) + { + $expected = [ + 'ClassesWithParents\\GInterface', + 'ClassesWithParents\\CInterface', + 'ClassesWithParents\\B', + 'ClassesWithParents\\A', + ]; + + $r = new \ReflectionClass('Symfony\Component\ClassLoader\ClassCollectionLoader'); + $m = $r->getMethod('getOrderedClasses'); + $m->setAccessible(true); + + $ordered = $m->invoke(null, $classes); + + $this->assertEquals($expected, array_map(function ($class) { return $class->getName(); }, $ordered)); + } + + public function getDifferentOrders() + { + return [ + [[ + 'ClassesWithParents\\A', + 'ClassesWithParents\\CInterface', + 'ClassesWithParents\\GInterface', + 'ClassesWithParents\\B', + ]], + [[ + 'ClassesWithParents\\B', + 'ClassesWithParents\\A', + 'ClassesWithParents\\CInterface', + ]], + [[ + 'ClassesWithParents\\CInterface', + 'ClassesWithParents\\B', + 'ClassesWithParents\\A', + ]], + [[ + 'ClassesWithParents\\A', + ]], + ]; + } + + /** + * @dataProvider getDifferentOrdersForTraits + */ + public function testClassWithTraitsReordering(array $classes) + { + require_once __DIR__.'/Fixtures/ClassesWithParents/ATrait.php'; + require_once __DIR__.'/Fixtures/ClassesWithParents/BTrait.php'; + require_once __DIR__.'/Fixtures/ClassesWithParents/CTrait.php'; + require_once __DIR__.'/Fixtures/ClassesWithParents/D.php'; + require_once __DIR__.'/Fixtures/ClassesWithParents/E.php'; + + $expected = [ + 'ClassesWithParents\\GInterface', + 'ClassesWithParents\\CInterface', + 'ClassesWithParents\\ATrait', + 'ClassesWithParents\\BTrait', + 'ClassesWithParents\\CTrait', + 'ClassesWithParents\\B', + 'ClassesWithParents\\A', + 'ClassesWithParents\\D', + 'ClassesWithParents\\E', + ]; + + $r = new \ReflectionClass('Symfony\Component\ClassLoader\ClassCollectionLoader'); + $m = $r->getMethod('getOrderedClasses'); + $m->setAccessible(true); + + $ordered = $m->invoke(null, $classes); + + $this->assertEquals($expected, array_map(function ($class) { return $class->getName(); }, $ordered)); + } + + public function getDifferentOrdersForTraits() + { + return [ + [[ + 'ClassesWithParents\\E', + 'ClassesWithParents\\ATrait', + ]], + [[ + 'ClassesWithParents\\E', + ]], + ]; + } + + public function testFixClassWithTraitsOrdering() + { + require_once __DIR__.'/Fixtures/ClassesWithParents/CTrait.php'; + require_once __DIR__.'/Fixtures/ClassesWithParents/F.php'; + require_once __DIR__.'/Fixtures/ClassesWithParents/G.php'; + + $classes = [ + 'ClassesWithParents\\F', + 'ClassesWithParents\\G', + ]; + + $expected = [ + 'ClassesWithParents\\CTrait', + 'ClassesWithParents\\F', + 'ClassesWithParents\\G', + ]; + + $r = new \ReflectionClass('Symfony\Component\ClassLoader\ClassCollectionLoader'); + $m = $r->getMethod('getOrderedClasses'); + $m->setAccessible(true); + + $ordered = $m->invoke(null, $classes); + + $this->assertEquals($expected, array_map(function ($class) { return $class->getName(); }, $ordered)); + } + + /** + * @dataProvider getFixNamespaceDeclarationsData + */ + public function testFixNamespaceDeclarations($source, $expected) + { + $this->assertEquals('assertEquals('assertEquals(<<<'EOF' +namespace Namespaced +{ +class WithComments +{ +public static $loaded = true; +} +$string ='string should not be modified {$string}'; +$heredoc = (<<assertTrue(class_exists(WarmedClass::class, true)); + + @unlink($cache = sys_get_temp_dir().'/inline.php'); + + $classes = [WarmedClass::class]; + $excluded = [DeclaredClass::class]; + + ClassCollectionLoader::inline($classes, $cache, $excluded); + + $this->assertSame(<<<'EOTXT' + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\ClassLoader\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\ClassLoader\ClassLoader; + +/** + * @group legacy + */ +class ClassLoaderTest extends TestCase +{ + public function testGetPrefixes() + { + $loader = new ClassLoader(); + $loader->addPrefix('Foo', __DIR__.\DIRECTORY_SEPARATOR.'Fixtures'); + $loader->addPrefix('Bar', __DIR__.\DIRECTORY_SEPARATOR.'Fixtures'); + $loader->addPrefix('Bas', __DIR__.\DIRECTORY_SEPARATOR.'Fixtures'); + $prefixes = $loader->getPrefixes(); + $this->assertArrayHasKey('Foo', $prefixes); + $this->assertArrayNotHasKey('Foo1', $prefixes); + $this->assertArrayHasKey('Bar', $prefixes); + $this->assertArrayHasKey('Bas', $prefixes); + } + + public function testGetFallbackDirs() + { + $loader = new ClassLoader(); + $loader->addPrefix(null, __DIR__.\DIRECTORY_SEPARATOR.'Fixtures'); + $loader->addPrefix(null, __DIR__.\DIRECTORY_SEPARATOR.'Fixtures'); + $fallback_dirs = $loader->getFallbackDirs(); + $this->assertCount(2, $fallback_dirs); + } + + /** + * @dataProvider getLoadClassTests + */ + public function testLoadClass($className, $testClassName, $message) + { + $loader = new ClassLoader(); + $loader->addPrefix('Namespaced2\\', __DIR__.\DIRECTORY_SEPARATOR.'Fixtures'); + $loader->addPrefix('Pearlike2_', __DIR__.\DIRECTORY_SEPARATOR.'Fixtures'); + $loader->loadClass($testClassName); + $this->assertTrue(class_exists($className), $message); + } + + public function getLoadClassTests() + { + return [ + ['\\Namespaced2\\Foo', 'Namespaced2\\Foo', '->loadClass() loads Namespaced2\Foo class'], + ['\\Pearlike2_Foo', 'Pearlike2_Foo', '->loadClass() loads Pearlike2_Foo class'], + ]; + } + + /** + * @dataProvider getLoadNonexistentClassTests + */ + public function testLoadNonexistentClass($className, $testClassName, $message) + { + $loader = new ClassLoader(); + $loader->addPrefix('Namespaced2\\', __DIR__.\DIRECTORY_SEPARATOR.'Fixtures'); + $loader->addPrefix('Pearlike2_', __DIR__.\DIRECTORY_SEPARATOR.'Fixtures'); + $loader->loadClass($testClassName); + $this->assertFalse(class_exists($className), $message); + } + + public function getLoadNonexistentClassTests() + { + return [ + ['\\Pearlike3_Bar', '\\Pearlike3_Bar', '->loadClass() loads non existing Pearlike3_Bar class with a leading slash'], + ]; + } + + public function testAddPrefixSingle() + { + $loader = new ClassLoader(); + $loader->addPrefix('Foo', __DIR__.\DIRECTORY_SEPARATOR.'Fixtures'); + $loader->addPrefix('Foo', __DIR__.\DIRECTORY_SEPARATOR.'Fixtures'); + $prefixes = $loader->getPrefixes(); + $this->assertArrayHasKey('Foo', $prefixes); + $this->assertCount(1, $prefixes['Foo']); + } + + public function testAddPrefixesSingle() + { + $loader = new ClassLoader(); + $loader->addPrefixes(['Foo' => ['foo', 'foo']]); + $loader->addPrefixes(['Foo' => ['foo']]); + $prefixes = $loader->getPrefixes(); + $this->assertArrayHasKey('Foo', $prefixes); + $this->assertCount(1, $prefixes['Foo'], print_r($prefixes, true)); + } + + public function testAddPrefixMulti() + { + $loader = new ClassLoader(); + $loader->addPrefix('Foo', 'foo'); + $loader->addPrefix('Foo', 'bar'); + $prefixes = $loader->getPrefixes(); + $this->assertArrayHasKey('Foo', $prefixes); + $this->assertCount(2, $prefixes['Foo']); + $this->assertContains('foo', $prefixes['Foo']); + $this->assertContains('bar', $prefixes['Foo']); + } + + public function testUseIncludePath() + { + $loader = new ClassLoader(); + $this->assertFalse($loader->getUseIncludePath()); + + $this->assertNull($loader->findFile('Foo')); + + $includePath = get_include_path(); + + $loader->setUseIncludePath(true); + $this->assertTrue($loader->getUseIncludePath()); + + set_include_path(__DIR__.'/Fixtures/includepath'.PATH_SEPARATOR.$includePath); + + $this->assertEquals(__DIR__.\DIRECTORY_SEPARATOR.'Fixtures'.\DIRECTORY_SEPARATOR.'includepath'.\DIRECTORY_SEPARATOR.'Foo.php', $loader->findFile('Foo')); + + set_include_path($includePath); + } + + /** + * @dataProvider getLoadClassFromFallbackTests + */ + public function testLoadClassFromFallback($className, $testClassName, $message) + { + $loader = new ClassLoader(); + $loader->addPrefix('Namespaced2\\', __DIR__.\DIRECTORY_SEPARATOR.'Fixtures'); + $loader->addPrefix('Pearlike2_', __DIR__.\DIRECTORY_SEPARATOR.'Fixtures'); + $loader->addPrefix('', [__DIR__.\DIRECTORY_SEPARATOR.'Fixtures/fallback']); + $loader->loadClass($testClassName); + $this->assertTrue(class_exists($className), $message); + } + + public function getLoadClassFromFallbackTests() + { + return [ + ['\\Namespaced2\\Baz', 'Namespaced2\\Baz', '->loadClass() loads Namespaced2\Baz class'], + ['\\Pearlike2_Baz', 'Pearlike2_Baz', '->loadClass() loads Pearlike2_Baz class'], + ['\\Namespaced2\\FooBar', 'Namespaced2\\FooBar', '->loadClass() loads Namespaced2\Baz class from fallback dir'], + ['\\Pearlike2_FooBar', 'Pearlike2_FooBar', '->loadClass() loads Pearlike2_Baz class from fallback dir'], + ]; + } + + /** + * @dataProvider getLoadClassNamespaceCollisionTests + */ + public function testLoadClassNamespaceCollision($namespaces, $className, $message) + { + $loader = new ClassLoader(); + $loader->addPrefixes($namespaces); + + $loader->loadClass($className); + $this->assertTrue(class_exists($className), $message); + } + + public function getLoadClassNamespaceCollisionTests() + { + return [ + [ + [ + 'NamespaceCollision\\C' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/alpha', + 'NamespaceCollision\\C\\B' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/beta', + ], + 'NamespaceCollision\C\Foo', + '->loadClass() loads NamespaceCollision\C\Foo from alpha.', + ], + [ + [ + 'NamespaceCollision\\C\\B' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/beta', + 'NamespaceCollision\\C' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/alpha', + ], + 'NamespaceCollision\C\Bar', + '->loadClass() loads NamespaceCollision\C\Bar from alpha.', + ], + [ + [ + 'NamespaceCollision\\C' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/alpha', + 'NamespaceCollision\\C\\B' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/beta', + ], + 'NamespaceCollision\C\B\Foo', + '->loadClass() loads NamespaceCollision\C\B\Foo from beta.', + ], + [ + [ + 'NamespaceCollision\\C\\B' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/beta', + 'NamespaceCollision\\C' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/alpha', + ], + 'NamespaceCollision\C\B\Bar', + '->loadClass() loads NamespaceCollision\C\B\Bar from beta.', + ], + [ + [ + 'PrefixCollision_C_' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/alpha', + 'PrefixCollision_C_B_' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/beta', + ], + 'PrefixCollision_C_Foo', + '->loadClass() loads PrefixCollision_C_Foo from alpha.', + ], + [ + [ + 'PrefixCollision_C_B_' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/beta', + 'PrefixCollision_C_' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/alpha', + ], + 'PrefixCollision_C_Bar', + '->loadClass() loads PrefixCollision_C_Bar from alpha.', + ], + [ + [ + 'PrefixCollision_C_' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/alpha', + 'PrefixCollision_C_B_' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/beta', + ], + 'PrefixCollision_C_B_Foo', + '->loadClass() loads PrefixCollision_C_B_Foo from beta.', + ], + [ + [ + 'PrefixCollision_C_B_' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/beta', + 'PrefixCollision_C_' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/alpha', + ], + 'PrefixCollision_C_B_Bar', + '->loadClass() loads PrefixCollision_C_B_Bar from beta.', + ], + ]; + } +} diff --git a/lib/symfony/class-loader/Tests/ClassMapGeneratorTest.php b/lib/symfony/class-loader/Tests/ClassMapGeneratorTest.php new file mode 100644 index 000000000..911066018 --- /dev/null +++ b/lib/symfony/class-loader/Tests/ClassMapGeneratorTest.php @@ -0,0 +1,151 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\ClassLoader\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\ClassLoader\ClassMapGenerator; + +/** + * @group legacy + */ +class ClassMapGeneratorTest extends TestCase +{ + /** + * @var string|null + */ + private $workspace = null; + + public function prepare_workspace() + { + $this->workspace = sys_get_temp_dir().'/'.microtime(true).'.'.mt_rand(); + mkdir($this->workspace, 0777, true); + $this->workspace = realpath($this->workspace); + } + + /** + * @param string $file + */ + private function clean($file) + { + if (is_dir($file) && !is_link($file)) { + $dir = new \FilesystemIterator($file); + foreach ($dir as $childFile) { + $this->clean($childFile); + } + + rmdir($file); + } else { + unlink($file); + } + } + + /** + * @dataProvider getTestCreateMapTests + */ + public function testDump($directory) + { + $this->prepare_workspace(); + + $file = $this->workspace.'/file'; + + $generator = new ClassMapGenerator(); + $generator->dump($directory, $file); + $this->assertFileExists($file); + + $this->clean($this->workspace); + } + + /** + * @dataProvider getTestCreateMapTests + */ + public function testCreateMap($directory, $expected) + { + $this->assertEqualsNormalized($expected, ClassMapGenerator::createMap($directory)); + } + + public function getTestCreateMapTests() + { + $data = [ + [__DIR__.'/Fixtures/Namespaced', [ + 'Namespaced\\Bar' => realpath(__DIR__).'/Fixtures/Namespaced/Bar.php', + 'Namespaced\\Foo' => realpath(__DIR__).'/Fixtures/Namespaced/Foo.php', + 'Namespaced\\Baz' => realpath(__DIR__).'/Fixtures/Namespaced/Baz.php', + 'Namespaced\\WithComments' => realpath(__DIR__).'/Fixtures/Namespaced/WithComments.php', + 'Namespaced\\WithStrictTypes' => realpath(__DIR__).'/Fixtures/Namespaced/WithStrictTypes.php', + 'Namespaced\\WithHaltCompiler' => realpath(__DIR__).'/Fixtures/Namespaced/WithHaltCompiler.php', + 'Namespaced\\WithDirMagic' => realpath(__DIR__).'/Fixtures/Namespaced/WithDirMagic.php', + 'Namespaced\\WithFileMagic' => realpath(__DIR__).'/Fixtures/Namespaced/WithFileMagic.php', + ]], + [__DIR__.'/Fixtures/beta/NamespaceCollision', [ + 'NamespaceCollision\\A\\B\\Bar' => realpath(__DIR__).'/Fixtures/beta/NamespaceCollision/A/B/Bar.php', + 'NamespaceCollision\\A\\B\\Foo' => realpath(__DIR__).'/Fixtures/beta/NamespaceCollision/A/B/Foo.php', + 'NamespaceCollision\\C\\B\\Bar' => realpath(__DIR__).'/Fixtures/beta/NamespaceCollision/C/B/Bar.php', + 'NamespaceCollision\\C\\B\\Foo' => realpath(__DIR__).'/Fixtures/beta/NamespaceCollision/C/B/Foo.php', + ]], + [__DIR__.'/Fixtures/Pearlike', [ + 'Pearlike_Foo' => realpath(__DIR__).'/Fixtures/Pearlike/Foo.php', + 'Pearlike_Bar' => realpath(__DIR__).'/Fixtures/Pearlike/Bar.php', + 'Pearlike_Baz' => realpath(__DIR__).'/Fixtures/Pearlike/Baz.php', + 'Pearlike_WithComments' => realpath(__DIR__).'/Fixtures/Pearlike/WithComments.php', + ]], + [__DIR__.'/Fixtures/classmap', [ + 'Foo\\Bar\\A' => realpath(__DIR__).'/Fixtures/classmap/sameNsMultipleClasses.php', + 'Foo\\Bar\\B' => realpath(__DIR__).'/Fixtures/classmap/sameNsMultipleClasses.php', + 'A' => realpath(__DIR__).'/Fixtures/classmap/multipleNs.php', + 'Alpha\\A' => realpath(__DIR__).'/Fixtures/classmap/multipleNs.php', + 'Alpha\\B' => realpath(__DIR__).'/Fixtures/classmap/multipleNs.php', + 'Beta\\A' => realpath(__DIR__).'/Fixtures/classmap/multipleNs.php', + 'Beta\\B' => realpath(__DIR__).'/Fixtures/classmap/multipleNs.php', + 'ClassMap\\SomeInterface' => realpath(__DIR__).'/Fixtures/classmap/SomeInterface.php', + 'ClassMap\\SomeParent' => realpath(__DIR__).'/Fixtures/classmap/SomeParent.php', + 'ClassMap\\SomeClass' => realpath(__DIR__).'/Fixtures/classmap/SomeClass.php', + ]], + [__DIR__.'/Fixtures/php5.4', [ + 'TFoo' => __DIR__.'/Fixtures/php5.4/traits.php', + 'CFoo' => __DIR__.'/Fixtures/php5.4/traits.php', + 'Foo\\TBar' => __DIR__.'/Fixtures/php5.4/traits.php', + 'Foo\\IBar' => __DIR__.'/Fixtures/php5.4/traits.php', + 'Foo\\TFooBar' => __DIR__.'/Fixtures/php5.4/traits.php', + 'Foo\\CBar' => __DIR__.'/Fixtures/php5.4/traits.php', + ]], + [__DIR__.'/Fixtures/php5.5', [ + 'ClassCons\\Foo' => __DIR__.'/Fixtures/php5.5/class_cons.php', + ]], + ]; + + return $data; + } + + public function testCreateMapFinderSupport() + { + $finder = new \Symfony\Component\Finder\Finder(); + $finder->files()->in(__DIR__.'/Fixtures/beta/NamespaceCollision'); + + $this->assertEqualsNormalized([ + 'NamespaceCollision\\A\\B\\Bar' => realpath(__DIR__).'/Fixtures/beta/NamespaceCollision/A/B/Bar.php', + 'NamespaceCollision\\A\\B\\Foo' => realpath(__DIR__).'/Fixtures/beta/NamespaceCollision/A/B/Foo.php', + 'NamespaceCollision\\C\\B\\Bar' => realpath(__DIR__).'/Fixtures/beta/NamespaceCollision/C/B/Bar.php', + 'NamespaceCollision\\C\\B\\Foo' => realpath(__DIR__).'/Fixtures/beta/NamespaceCollision/C/B/Foo.php', + ], ClassMapGenerator::createMap($finder)); + } + + protected function assertEqualsNormalized($expected, $actual, $message = '') + { + foreach ($expected as $ns => $path) { + $expected[$ns] = str_replace('\\', '/', $path); + } + foreach ($actual as $ns => $path) { + $actual[$ns] = str_replace('\\', '/', $path); + } + $this->assertEquals($expected, $actual, $message); + } +} diff --git a/lib/symfony/class-loader/Tests/Fixtures/Apc/Namespaced/Bar.php b/lib/symfony/class-loader/Tests/Fixtures/Apc/Namespaced/Bar.php new file mode 100644 index 000000000..4259f1451 --- /dev/null +++ b/lib/symfony/class-loader/Tests/Fixtures/Apc/Namespaced/Bar.php @@ -0,0 +1,17 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Apc\Namespaced; + +class Bar +{ + public static $loaded = true; +} diff --git a/lib/symfony/class-loader/Tests/Fixtures/Apc/Namespaced/Baz.php b/lib/symfony/class-loader/Tests/Fixtures/Apc/Namespaced/Baz.php new file mode 100644 index 000000000..3ddb595e2 --- /dev/null +++ b/lib/symfony/class-loader/Tests/Fixtures/Apc/Namespaced/Baz.php @@ -0,0 +1,17 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Apc\Namespaced; + +class Baz +{ + public static $loaded = true; +} diff --git a/lib/symfony/class-loader/Tests/Fixtures/Apc/Namespaced/Foo.php b/lib/symfony/class-loader/Tests/Fixtures/Apc/Namespaced/Foo.php new file mode 100644 index 000000000..cf0a4b741 --- /dev/null +++ b/lib/symfony/class-loader/Tests/Fixtures/Apc/Namespaced/Foo.php @@ -0,0 +1,17 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Apc\Namespaced; + +class Foo +{ + public static $loaded = true; +} diff --git a/lib/symfony/class-loader/Tests/Fixtures/Apc/Namespaced/FooBar.php b/lib/symfony/class-loader/Tests/Fixtures/Apc/Namespaced/FooBar.php new file mode 100644 index 000000000..bbbc81515 --- /dev/null +++ b/lib/symfony/class-loader/Tests/Fixtures/Apc/Namespaced/FooBar.php @@ -0,0 +1,17 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Apc\Namespaced; + +class FooBar +{ + public static $loaded = true; +} diff --git a/lib/symfony/class-loader/Tests/Fixtures/Apc/Pearlike/Bar.php b/lib/symfony/class-loader/Tests/Fixtures/Apc/Pearlike/Bar.php new file mode 100644 index 000000000..e774cb9bf --- /dev/null +++ b/lib/symfony/class-loader/Tests/Fixtures/Apc/Pearlike/Bar.php @@ -0,0 +1,6 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Apc\NamespaceCollision\A; + +class Bar +{ + public static $loaded = true; +} diff --git a/lib/symfony/class-loader/Tests/Fixtures/Apc/alpha/Apc/NamespaceCollision/A/Foo.php b/lib/symfony/class-loader/Tests/Fixtures/Apc/alpha/Apc/NamespaceCollision/A/Foo.php new file mode 100644 index 000000000..184a1b1da --- /dev/null +++ b/lib/symfony/class-loader/Tests/Fixtures/Apc/alpha/Apc/NamespaceCollision/A/Foo.php @@ -0,0 +1,17 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Apc\NamespaceCollision\A; + +class Foo +{ + public static $loaded = true; +} diff --git a/lib/symfony/class-loader/Tests/Fixtures/Apc/beta/Apc/ApcPrefixCollision/A/B/Bar.php b/lib/symfony/class-loader/Tests/Fixtures/Apc/beta/Apc/ApcPrefixCollision/A/B/Bar.php new file mode 100644 index 000000000..3892f7068 --- /dev/null +++ b/lib/symfony/class-loader/Tests/Fixtures/Apc/beta/Apc/ApcPrefixCollision/A/B/Bar.php @@ -0,0 +1,6 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Apc\NamespaceCollision\A\B; + +class Bar +{ + public static $loaded = true; +} diff --git a/lib/symfony/class-loader/Tests/Fixtures/Apc/beta/Apc/NamespaceCollision/A/B/Foo.php b/lib/symfony/class-loader/Tests/Fixtures/Apc/beta/Apc/NamespaceCollision/A/B/Foo.php new file mode 100644 index 000000000..450eeb50b --- /dev/null +++ b/lib/symfony/class-loader/Tests/Fixtures/Apc/beta/Apc/NamespaceCollision/A/B/Foo.php @@ -0,0 +1,17 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Apc\NamespaceCollision\A\B; + +class Foo +{ + public static $loaded = true; +} diff --git a/lib/symfony/class-loader/Tests/Fixtures/Apc/fallback/Apc/Pearlike/FooBar.php b/lib/symfony/class-loader/Tests/Fixtures/Apc/fallback/Apc/Pearlike/FooBar.php new file mode 100644 index 000000000..96f2f76c6 --- /dev/null +++ b/lib/symfony/class-loader/Tests/Fixtures/Apc/fallback/Apc/Pearlike/FooBar.php @@ -0,0 +1,6 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Apc\Namespaced; + +class FooBar +{ + public static $loaded = true; +} diff --git a/lib/symfony/class-loader/Tests/Fixtures/ClassesWithParents/A.php b/lib/symfony/class-loader/Tests/Fixtures/ClassesWithParents/A.php new file mode 100644 index 000000000..b0f942595 --- /dev/null +++ b/lib/symfony/class-loader/Tests/Fixtures/ClassesWithParents/A.php @@ -0,0 +1,7 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Namespaced; + +class Bar +{ + public static $loaded = true; +} diff --git a/lib/symfony/class-loader/Tests/Fixtures/Namespaced/Baz.php b/lib/symfony/class-loader/Tests/Fixtures/Namespaced/Baz.php new file mode 100644 index 000000000..0b0bbd057 --- /dev/null +++ b/lib/symfony/class-loader/Tests/Fixtures/Namespaced/Baz.php @@ -0,0 +1,17 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Namespaced; + +class Baz +{ + public static $loaded = true; +} diff --git a/lib/symfony/class-loader/Tests/Fixtures/Namespaced/Foo.php b/lib/symfony/class-loader/Tests/Fixtures/Namespaced/Foo.php new file mode 100644 index 000000000..df5e1f4ce --- /dev/null +++ b/lib/symfony/class-loader/Tests/Fixtures/Namespaced/Foo.php @@ -0,0 +1,17 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Namespaced; + +class Foo +{ + public static $loaded = true; +} diff --git a/lib/symfony/class-loader/Tests/Fixtures/Namespaced/WithComments.php b/lib/symfony/class-loader/Tests/Fixtures/Namespaced/WithComments.php new file mode 100644 index 000000000..361e53de1 --- /dev/null +++ b/lib/symfony/class-loader/Tests/Fixtures/Namespaced/WithComments.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Namespaced; + +class WithComments +{ + /** @Boolean */ + public static $loaded = true; +} + +$string = 'string should not be modified {$string}'; + +$heredoc = (<< + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +class Pearlike_WithComments +{ + /** @Boolean */ + public static $loaded = true; +} diff --git a/lib/symfony/class-loader/Tests/Fixtures/Pearlike2/Bar.php b/lib/symfony/class-loader/Tests/Fixtures/Pearlike2/Bar.php new file mode 100644 index 000000000..7f5f79773 --- /dev/null +++ b/lib/symfony/class-loader/Tests/Fixtures/Pearlike2/Bar.php @@ -0,0 +1,6 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace NamespaceCollision\A; + +class Bar +{ + public static $loaded = true; +} diff --git a/lib/symfony/class-loader/Tests/Fixtures/alpha/NamespaceCollision/A/Foo.php b/lib/symfony/class-loader/Tests/Fixtures/alpha/NamespaceCollision/A/Foo.php new file mode 100644 index 000000000..aee6a080d --- /dev/null +++ b/lib/symfony/class-loader/Tests/Fixtures/alpha/NamespaceCollision/A/Foo.php @@ -0,0 +1,17 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace NamespaceCollision\A; + +class Foo +{ + public static $loaded = true; +} diff --git a/lib/symfony/class-loader/Tests/Fixtures/alpha/NamespaceCollision/C/Bar.php b/lib/symfony/class-loader/Tests/Fixtures/alpha/NamespaceCollision/C/Bar.php new file mode 100644 index 000000000..c1b8dd65d --- /dev/null +++ b/lib/symfony/class-loader/Tests/Fixtures/alpha/NamespaceCollision/C/Bar.php @@ -0,0 +1,8 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace NamespaceCollision\A\B; + +class Bar +{ + public static $loaded = true; +} diff --git a/lib/symfony/class-loader/Tests/Fixtures/beta/NamespaceCollision/A/B/Foo.php b/lib/symfony/class-loader/Tests/Fixtures/beta/NamespaceCollision/A/B/Foo.php new file mode 100644 index 000000000..f5f2d727e --- /dev/null +++ b/lib/symfony/class-loader/Tests/Fixtures/beta/NamespaceCollision/A/B/Foo.php @@ -0,0 +1,17 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace NamespaceCollision\A\B; + +class Foo +{ + public static $loaded = true; +} diff --git a/lib/symfony/class-loader/Tests/Fixtures/beta/NamespaceCollision/C/B/Bar.php b/lib/symfony/class-loader/Tests/Fixtures/beta/NamespaceCollision/C/B/Bar.php new file mode 100644 index 000000000..4bb03dc7f --- /dev/null +++ b/lib/symfony/class-loader/Tests/Fixtures/beta/NamespaceCollision/C/B/Bar.php @@ -0,0 +1,8 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace ClassMap; + +class SomeClass extends SomeParent implements SomeInterface +{ +} diff --git a/lib/symfony/class-loader/Tests/Fixtures/classmap/SomeInterface.php b/lib/symfony/class-loader/Tests/Fixtures/classmap/SomeInterface.php new file mode 100644 index 000000000..1fe5e09aa --- /dev/null +++ b/lib/symfony/class-loader/Tests/Fixtures/classmap/SomeInterface.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace ClassMap; + +interface SomeInterface +{ +} diff --git a/lib/symfony/class-loader/Tests/Fixtures/classmap/SomeParent.php b/lib/symfony/class-loader/Tests/Fixtures/classmap/SomeParent.php new file mode 100644 index 000000000..ce2f9fc6c --- /dev/null +++ b/lib/symfony/class-loader/Tests/Fixtures/classmap/SomeParent.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace ClassMap; + +abstract class SomeParent +{ +} diff --git a/lib/symfony/class-loader/Tests/Fixtures/classmap/multipleNs.php b/lib/symfony/class-loader/Tests/Fixtures/classmap/multipleNs.php new file mode 100644 index 000000000..c7cec646f --- /dev/null +++ b/lib/symfony/class-loader/Tests/Fixtures/classmap/multipleNs.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Foo\Bar; + +class A +{ +} +class B +{ +} diff --git a/lib/symfony/class-loader/Tests/Fixtures/deps/traits.php b/lib/symfony/class-loader/Tests/Fixtures/deps/traits.php new file mode 100644 index 000000000..82b30a6f9 --- /dev/null +++ b/lib/symfony/class-loader/Tests/Fixtures/deps/traits.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Namespaced; + +class FooBar +{ + public static $loaded = true; +} diff --git a/lib/symfony/class-loader/Tests/Fixtures/fallback/Namespaced2/FooBar.php b/lib/symfony/class-loader/Tests/Fixtures/fallback/Namespaced2/FooBar.php new file mode 100644 index 000000000..1036d4359 --- /dev/null +++ b/lib/symfony/class-loader/Tests/Fixtures/fallback/Namespaced2/FooBar.php @@ -0,0 +1,8 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\ClassLoader\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\ClassLoader\Psr4ClassLoader; + +/** + * @group legacy + */ +class Psr4ClassLoaderTest extends TestCase +{ + /** + * @param string $className + * @dataProvider getLoadClassTests + */ + public function testLoadClass($className) + { + $loader = new Psr4ClassLoader(); + $loader->addPrefix( + 'Acme\\DemoLib', + __DIR__.\DIRECTORY_SEPARATOR.'Fixtures'.\DIRECTORY_SEPARATOR.'psr-4' + ); + $loader->loadClass($className); + $this->assertTrue(class_exists($className), sprintf('loadClass() should load %s', $className)); + } + + /** + * @return array + */ + public function getLoadClassTests() + { + return [ + ['Acme\\DemoLib\\Foo'], + ['Acme\\DemoLib\\Class_With_Underscores'], + ['Acme\\DemoLib\\Lets\\Go\\Deeper\\Foo'], + ['Acme\\DemoLib\\Lets\\Go\\Deeper\\Class_With_Underscores'], + ]; + } + + /** + * @param string $className + * @dataProvider getLoadNonexistentClassTests + */ + public function testLoadNonexistentClass($className) + { + $loader = new Psr4ClassLoader(); + $loader->addPrefix( + 'Acme\\DemoLib', + __DIR__.\DIRECTORY_SEPARATOR.'Fixtures'.\DIRECTORY_SEPARATOR.'psr-4' + ); + $loader->loadClass($className); + $this->assertFalse(class_exists($className), sprintf('loadClass() should not load %s', $className)); + } + + /** + * @return array + */ + public function getLoadNonexistentClassTests() + { + return [ + ['Acme\\DemoLib\\I_Do_Not_Exist'], + ['UnknownVendor\\SomeLib\\I_Do_Not_Exist'], + ]; + } +} diff --git a/lib/symfony/class-loader/WinCacheClassLoader.php b/lib/symfony/class-loader/WinCacheClassLoader.php new file mode 100644 index 000000000..a7149ce9d --- /dev/null +++ b/lib/symfony/class-loader/WinCacheClassLoader.php @@ -0,0 +1,142 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\ClassLoader; + +@trigger_error('The '.__NAMESPACE__.'\WinCacheClassLoader class is deprecated since Symfony 3.3 and will be removed in 4.0. Use `composer install --apcu-autoloader` instead.', E_USER_DEPRECATED); + +/** + * WinCacheClassLoader implements a wrapping autoloader cached in WinCache. + * + * It expects an object implementing a findFile method to find the file. This + * allow using it as a wrapper around the other loaders of the component (the + * ClassLoader for instance) but also around any other autoloaders following + * this convention (the Composer one for instance). + * + * // with a Symfony autoloader + * $loader = new ClassLoader(); + * $loader->addPrefix('Symfony\Component', __DIR__.'/component'); + * $loader->addPrefix('Symfony', __DIR__.'/framework'); + * + * // or with a Composer autoloader + * use Composer\Autoload\ClassLoader; + * + * $loader = new ClassLoader(); + * $loader->add('Symfony\Component', __DIR__.'/component'); + * $loader->add('Symfony', __DIR__.'/framework'); + * + * $cachedLoader = new WinCacheClassLoader('my_prefix', $loader); + * + * // activate the cached autoloader + * $cachedLoader->register(); + * + * // eventually deactivate the non-cached loader if it was registered previously + * // to be sure to use the cached one. + * $loader->unregister(); + * + * @author Fabien Potencier + * @author Kris Wallsmith + * @author Artem Ryzhkov + * + * @deprecated since version 3.3, to be removed in 4.0. Use `composer install --apcu-autoloader` instead. + */ +class WinCacheClassLoader +{ + private $prefix; + + /** + * A class loader object that implements the findFile() method. + * + * @var object + */ + protected $decorated; + + /** + * @param string $prefix The WinCache namespace prefix to use + * @param object $decorated A class loader object that implements the findFile() method + * + * @throws \RuntimeException + * @throws \InvalidArgumentException + */ + public function __construct($prefix, $decorated) + { + if (!\extension_loaded('wincache')) { + throw new \RuntimeException('Unable to use WinCacheClassLoader as WinCache is not enabled.'); + } + + if (!method_exists($decorated, 'findFile')) { + throw new \InvalidArgumentException('The class finder must implement a "findFile" method.'); + } + + $this->prefix = $prefix; + $this->decorated = $decorated; + } + + /** + * Registers this instance as an autoloader. + * + * @param bool $prepend Whether to prepend the autoloader or not + */ + public function register($prepend = false) + { + spl_autoload_register([$this, 'loadClass'], true, $prepend); + } + + /** + * Unregisters this instance as an autoloader. + */ + public function unregister() + { + spl_autoload_unregister([$this, 'loadClass']); + } + + /** + * Loads the given class or interface. + * + * @param string $class The name of the class + * + * @return bool|null True, if loaded + */ + public function loadClass($class) + { + if ($file = $this->findFile($class)) { + require $file; + + return true; + } + } + + /** + * Finds a file by class name while caching lookups to WinCache. + * + * @param string $class A class name to resolve to file + * + * @return string|null + */ + public function findFile($class) + { + $file = wincache_ucache_get($this->prefix.$class, $success); + + if (!$success) { + wincache_ucache_set($this->prefix.$class, $file = $this->decorated->findFile($class) ?: null, 0); + } + + return $file; + } + + /** + * Passes through all unknown calls onto the decorated object. + */ + public function __call($method, $args) + { + return \call_user_func_array([$this->decorated, $method], $args); + } +} diff --git a/lib/symfony/class-loader/XcacheClassLoader.php b/lib/symfony/class-loader/XcacheClassLoader.php new file mode 100644 index 000000000..56965df4c --- /dev/null +++ b/lib/symfony/class-loader/XcacheClassLoader.php @@ -0,0 +1,137 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\ClassLoader; + +@trigger_error('The '.__NAMESPACE__.'\XcacheClassLoader class is deprecated since Symfony 3.3 and will be removed in 4.0. Use `composer install --apcu-autoloader` instead.', E_USER_DEPRECATED); + +/** + * XcacheClassLoader implements a wrapping autoloader cached in XCache for PHP 5.3. + * + * It expects an object implementing a findFile method to find the file. This + * allows using it as a wrapper around the other loaders of the component (the + * ClassLoader for instance) but also around any other autoloaders following + * this convention (the Composer one for instance). + * + * // with a Symfony autoloader + * $loader = new ClassLoader(); + * $loader->addPrefix('Symfony\Component', __DIR__.'/component'); + * $loader->addPrefix('Symfony', __DIR__.'/framework'); + * + * // or with a Composer autoloader + * use Composer\Autoload\ClassLoader; + * + * $loader = new ClassLoader(); + * $loader->add('Symfony\Component', __DIR__.'/component'); + * $loader->add('Symfony', __DIR__.'/framework'); + * + * $cachedLoader = new XcacheClassLoader('my_prefix', $loader); + * + * // activate the cached autoloader + * $cachedLoader->register(); + * + * // eventually deactivate the non-cached loader if it was registered previously + * // to be sure to use the cached one. + * $loader->unregister(); + * + * @author Fabien Potencier + * @author Kris Wallsmith + * @author Kim Hemsø Rasmussen + * + * @deprecated since version 3.3, to be removed in 4.0. Use `composer install --apcu-autoloader` instead. + */ +class XcacheClassLoader +{ + private $prefix; + private $decorated; + + /** + * @param string $prefix The XCache namespace prefix to use + * @param object $decorated A class loader object that implements the findFile() method + * + * @throws \RuntimeException + * @throws \InvalidArgumentException + */ + public function __construct($prefix, $decorated) + { + if (!\extension_loaded('xcache')) { + throw new \RuntimeException('Unable to use XcacheClassLoader as XCache is not enabled.'); + } + + if (!method_exists($decorated, 'findFile')) { + throw new \InvalidArgumentException('The class finder must implement a "findFile" method.'); + } + + $this->prefix = $prefix; + $this->decorated = $decorated; + } + + /** + * Registers this instance as an autoloader. + * + * @param bool $prepend Whether to prepend the autoloader or not + */ + public function register($prepend = false) + { + spl_autoload_register([$this, 'loadClass'], true, $prepend); + } + + /** + * Unregisters this instance as an autoloader. + */ + public function unregister() + { + spl_autoload_unregister([$this, 'loadClass']); + } + + /** + * Loads the given class or interface. + * + * @param string $class The name of the class + * + * @return bool|null True, if loaded + */ + public function loadClass($class) + { + if ($file = $this->findFile($class)) { + require $file; + + return true; + } + } + + /** + * Finds a file by class name while caching lookups to Xcache. + * + * @param string $class A class name to resolve to file + * + * @return string|null + */ + public function findFile($class) + { + if (xcache_isset($this->prefix.$class)) { + $file = xcache_get($this->prefix.$class); + } else { + $file = $this->decorated->findFile($class) ?: null; + xcache_set($this->prefix.$class, $file); + } + + return $file; + } + + /** + * Passes through all unknown calls onto the decorated object. + */ + public function __call($method, $args) + { + return \call_user_func_array([$this->decorated, $method], $args); + } +} diff --git a/lib/symfony/class-loader/composer.json b/lib/symfony/class-loader/composer.json new file mode 100644 index 000000000..97f150fbf --- /dev/null +++ b/lib/symfony/class-loader/composer.json @@ -0,0 +1,40 @@ +{ + "name": "symfony/class-loader", + "type": "library", + "description": "Symfony ClassLoader Component", + "keywords": [], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "minimum-stability": "dev", + "require": { + "php": "^5.5.9|>=7.0.8" + }, + "require-dev": { + "symfony/finder": "~2.8|~3.0|~4.0", + "symfony/polyfill-apcu": "~1.1" + }, + "suggest": { + "symfony/polyfill-apcu": "For using ApcClassLoader on HHVM" + }, + "autoload": { + "psr-4": { "Symfony\\Component\\ClassLoader\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + } +} diff --git a/lib/symfony/class-loader/phpunit.xml.dist b/lib/symfony/class-loader/phpunit.xml.dist new file mode 100644 index 000000000..2ad0b802d --- /dev/null +++ b/lib/symfony/class-loader/phpunit.xml.dist @@ -0,0 +1,31 @@ + + + + + + + + + + ./Tests/ + + + + + + ./ + + ./Resources + ./Tests + ./vendor + + + + diff --git a/lib/symfony/config/.gitignore b/lib/symfony/config/.gitignore new file mode 100644 index 000000000..c49a5d8df --- /dev/null +++ b/lib/symfony/config/.gitignore @@ -0,0 +1,3 @@ +vendor/ +composer.lock +phpunit.xml diff --git a/lib/symfony/config/CHANGELOG.md b/lib/symfony/config/CHANGELOG.md new file mode 100644 index 000000000..6cb610c47 --- /dev/null +++ b/lib/symfony/config/CHANGELOG.md @@ -0,0 +1,78 @@ +CHANGELOG +========= + +3.4.0 +----- + + * added `setDeprecated()` method to indicate a deprecated node + * added `XmlUtils::parse()` method to parse an XML string + * deprecated `ConfigCachePass` + +3.3.0 +----- + + * added `ReflectionClassResource` class + * added second `$exists` constructor argument to `ClassExistenceResource` + * made `ClassExistenceResource` work with interfaces and traits + * added `ConfigCachePass` (originally in FrameworkBundle) + * added `castToArray()` helper to turn any config value into an array + +3.0.0 +----- + + * removed `ReferenceDumper` class + * removed the `ResourceInterface::isFresh()` method + * removed `BCResourceInterfaceChecker` class + * removed `ResourceInterface::getResource()` method + +2.8.0 +----- + +The edge case of defining just one value for nodes of type Enum is now allowed: + +```php +$rootNode + ->children() + ->enumNode('variable') + ->values(['value']) + ->end() + ->end() +; +``` + +Before: `InvalidArgumentException` (variable must contain at least two +distinct elements). +After: the code will work as expected and it will restrict the values of the +`variable` option to just `value`. + + * deprecated the `ResourceInterface::isFresh()` method. If you implement custom resource types and they + can be validated that way, make them implement the new `SelfCheckingResourceInterface`. + * deprecated the getResource() method in ResourceInterface. You can still call this method + on concrete classes implementing the interface, but it does not make sense at the interface + level as you need to know about the particular type of resource at hand to understand the + semantics of the returned value. + +2.7.0 +----- + + * added `ConfigCacheInterface`, `ConfigCacheFactoryInterface` and a basic `ConfigCacheFactory` + implementation to delegate creation of ConfigCache instances + +2.2.0 +----- + + * added `ArrayNodeDefinition::canBeEnabled()` and `ArrayNodeDefinition::canBeDisabled()` + to ease configuration when some sections are respectively disabled / enabled + by default. + * added a `normalizeKeys()` method for array nodes (to avoid key normalization) + * added numerical type handling for config definitions + * added convenience methods for optional configuration sections to `ArrayNodeDefinition` + * added a utils class for XML manipulations + +2.1.0 +----- + + * added a way to add documentation on configuration + * implemented `Serializable` on resources + * `LoaderResolverInterface` is now used instead of `LoaderResolver` for type + hinting diff --git a/lib/symfony/config/ConfigCache.php b/lib/symfony/config/ConfigCache.php new file mode 100644 index 000000000..b2a39076f --- /dev/null +++ b/lib/symfony/config/ConfigCache.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config; + +use Symfony\Component\Config\Resource\SelfCheckingResourceChecker; + +/** + * ConfigCache caches arbitrary content in files on disk. + * + * When in debug mode, those metadata resources that implement + * \Symfony\Component\Config\Resource\SelfCheckingResourceInterface will + * be used to check cache freshness. + * + * @author Fabien Potencier + * @author Matthias Pigulla + */ +class ConfigCache extends ResourceCheckerConfigCache +{ + private $debug; + + /** + * @param string $file The absolute cache path + * @param bool $debug Whether debugging is enabled or not + */ + public function __construct($file, $debug) + { + $this->debug = (bool) $debug; + + $checkers = []; + if (true === $this->debug) { + $checkers = [new SelfCheckingResourceChecker()]; + } + + parent::__construct($file, $checkers); + } + + /** + * Checks if the cache is still fresh. + * + * This implementation always returns true when debug is off and the + * cache file exists. + * + * @return bool true if the cache is fresh, false otherwise + */ + public function isFresh() + { + if (!$this->debug && is_file($this->getPath())) { + return true; + } + + return parent::isFresh(); + } +} diff --git a/lib/symfony/config/ConfigCacheFactory.php b/lib/symfony/config/ConfigCacheFactory.php new file mode 100644 index 000000000..7903cca93 --- /dev/null +++ b/lib/symfony/config/ConfigCacheFactory.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config; + +/** + * Basic implementation of ConfigCacheFactoryInterface that + * creates an instance of the default ConfigCache. + * + * This factory and/or cache do not support cache validation + * by means of ResourceChecker instances (that is, service-based). + * + * @author Matthias Pigulla + */ +class ConfigCacheFactory implements ConfigCacheFactoryInterface +{ + private $debug; + + /** + * @param bool $debug The debug flag to pass to ConfigCache + */ + public function __construct($debug) + { + $this->debug = $debug; + } + + /** + * {@inheritdoc} + */ + public function cache($file, $callback) + { + if (!\is_callable($callback)) { + throw new \InvalidArgumentException(sprintf('Invalid type for callback argument. Expected callable, but got "%s".', \gettype($callback))); + } + + $cache = new ConfigCache($file, $this->debug); + if (!$cache->isFresh()) { + \call_user_func($callback, $cache); + } + + return $cache; + } +} diff --git a/lib/symfony/config/ConfigCacheFactoryInterface.php b/lib/symfony/config/ConfigCacheFactoryInterface.php new file mode 100644 index 000000000..8e80142b7 --- /dev/null +++ b/lib/symfony/config/ConfigCacheFactoryInterface.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config; + +/** + * Interface for a ConfigCache factory. This factory creates + * an instance of ConfigCacheInterface and initializes the + * cache if necessary. + * + * @author Matthias Pigulla + */ +interface ConfigCacheFactoryInterface +{ + /** + * Creates a cache instance and (re-)initializes it if necessary. + * + * @param string $file The absolute cache file path + * @param callable $callable The callable to be executed when the cache needs to be filled (i. e. is not fresh). The cache will be passed as the only parameter to this callback + * + * @return ConfigCacheInterface The cache instance + */ + public function cache($file, $callable); +} diff --git a/lib/symfony/config/ConfigCacheInterface.php b/lib/symfony/config/ConfigCacheInterface.php new file mode 100644 index 000000000..7c47ad70a --- /dev/null +++ b/lib/symfony/config/ConfigCacheInterface.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config; + +use Symfony\Component\Config\Resource\ResourceInterface; + +/** + * Interface for ConfigCache. + * + * @author Matthias Pigulla + */ +interface ConfigCacheInterface +{ + /** + * Gets the cache file path. + * + * @return string The cache file path + */ + public function getPath(); + + /** + * Checks if the cache is still fresh. + * + * This check should take the metadata passed to the write() method into consideration. + * + * @return bool Whether the cache is still fresh + */ + public function isFresh(); + + /** + * Writes the given content into the cache file. Metadata will be stored + * independently and can be used to check cache freshness at a later time. + * + * @param string $content The content to write into the cache + * @param ResourceInterface[]|null $metadata An array of ResourceInterface instances + * + * @throws \RuntimeException When the cache file cannot be written + */ + public function write($content, array $metadata = null); +} diff --git a/lib/symfony/config/Definition/ArrayNode.php b/lib/symfony/config/Definition/ArrayNode.php new file mode 100644 index 000000000..2afa629cd --- /dev/null +++ b/lib/symfony/config/Definition/ArrayNode.php @@ -0,0 +1,383 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition; + +use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; +use Symfony\Component\Config\Definition\Exception\InvalidTypeException; +use Symfony\Component\Config\Definition\Exception\UnsetKeyException; + +/** + * Represents an Array node in the config tree. + * + * @author Johannes M. Schmitt + */ +class ArrayNode extends BaseNode implements PrototypeNodeInterface +{ + protected $xmlRemappings = []; + protected $children = []; + protected $allowFalse = false; + protected $allowNewKeys = true; + protected $addIfNotSet = false; + protected $performDeepMerging = true; + protected $ignoreExtraKeys = false; + protected $removeExtraKeys = true; + protected $normalizeKeys = true; + + public function setNormalizeKeys($normalizeKeys) + { + $this->normalizeKeys = (bool) $normalizeKeys; + } + + /** + * Normalizes keys between the different configuration formats. + * + * Namely, you mostly have foo_bar in YAML while you have foo-bar in XML. + * After running this method, all keys are normalized to foo_bar. + * + * If you have a mixed key like foo-bar_moo, it will not be altered. + * The key will also not be altered if the target key already exists. + * + * @param mixed $value + * + * @return array The value with normalized keys + */ + protected function preNormalize($value) + { + if (!$this->normalizeKeys || !\is_array($value)) { + return $value; + } + + $normalized = []; + + foreach ($value as $k => $v) { + if (false !== strpos($k, '-') && false === strpos($k, '_') && !\array_key_exists($normalizedKey = str_replace('-', '_', $k), $value)) { + $normalized[$normalizedKey] = $v; + } else { + $normalized[$k] = $v; + } + } + + return $normalized; + } + + /** + * Retrieves the children of this node. + * + * @return array The children + */ + public function getChildren() + { + return $this->children; + } + + /** + * Sets the xml remappings that should be performed. + * + * @param array $remappings An array of the form [[string, string]] + */ + public function setXmlRemappings(array $remappings) + { + $this->xmlRemappings = $remappings; + } + + /** + * Gets the xml remappings that should be performed. + * + * @return array an array of the form [[string, string]] + */ + public function getXmlRemappings() + { + return $this->xmlRemappings; + } + + /** + * Sets whether to add default values for this array if it has not been + * defined in any of the configuration files. + * + * @param bool $boolean + */ + public function setAddIfNotSet($boolean) + { + $this->addIfNotSet = (bool) $boolean; + } + + /** + * Sets whether false is allowed as value indicating that the array should be unset. + * + * @param bool $allow + */ + public function setAllowFalse($allow) + { + $this->allowFalse = (bool) $allow; + } + + /** + * Sets whether new keys can be defined in subsequent configurations. + * + * @param bool $allow + */ + public function setAllowNewKeys($allow) + { + $this->allowNewKeys = (bool) $allow; + } + + /** + * Sets if deep merging should occur. + * + * @param bool $boolean + */ + public function setPerformDeepMerging($boolean) + { + $this->performDeepMerging = (bool) $boolean; + } + + /** + * Whether extra keys should just be ignored without an exception. + * + * @param bool $boolean To allow extra keys + * @param bool $remove To remove extra keys + */ + public function setIgnoreExtraKeys($boolean, $remove = true) + { + $this->ignoreExtraKeys = (bool) $boolean; + $this->removeExtraKeys = $this->ignoreExtraKeys && $remove; + } + + /** + * {@inheritdoc} + */ + public function setName($name) + { + $this->name = $name; + } + + /** + * {@inheritdoc} + */ + public function hasDefaultValue() + { + return $this->addIfNotSet; + } + + /** + * {@inheritdoc} + */ + public function getDefaultValue() + { + if (!$this->hasDefaultValue()) { + throw new \RuntimeException(sprintf('The node at path "%s" has no default value.', $this->getPath())); + } + + $defaults = []; + foreach ($this->children as $name => $child) { + if ($child->hasDefaultValue()) { + $defaults[$name] = $child->getDefaultValue(); + } + } + + return $defaults; + } + + /** + * Adds a child node. + * + * @throws \InvalidArgumentException when the child node has no name + * @throws \InvalidArgumentException when the child node's name is not unique + */ + public function addChild(NodeInterface $node) + { + $name = $node->getName(); + if (!\strlen($name)) { + throw new \InvalidArgumentException('Child nodes must be named.'); + } + if (isset($this->children[$name])) { + throw new \InvalidArgumentException(sprintf('A child node named "%s" already exists.', $name)); + } + + $this->children[$name] = $node; + } + + /** + * Finalizes the value of this node. + * + * @param mixed $value + * + * @return mixed The finalised value + * + * @throws UnsetKeyException + * @throws InvalidConfigurationException if the node doesn't have enough children + */ + protected function finalizeValue($value) + { + if (false === $value) { + throw new UnsetKeyException(sprintf('Unsetting key for path "%s", value: %s', $this->getPath(), json_encode($value))); + } + + foreach ($this->children as $name => $child) { + if (!\array_key_exists($name, $value)) { + if ($child->isRequired()) { + $ex = new InvalidConfigurationException(sprintf('The child node "%s" at path "%s" must be configured.', $name, $this->getPath())); + $ex->setPath($this->getPath()); + + throw $ex; + } + + if ($child->hasDefaultValue()) { + $value[$name] = $child->getDefaultValue(); + } + + continue; + } + + if ($child->isDeprecated()) { + @trigger_error($child->getDeprecationMessage($name, $this->getPath()), E_USER_DEPRECATED); + } + + try { + $value[$name] = $child->finalize($value[$name]); + } catch (UnsetKeyException $e) { + unset($value[$name]); + } + } + + return $value; + } + + /** + * Validates the type of the value. + * + * @param mixed $value + * + * @throws InvalidTypeException + */ + protected function validateType($value) + { + if (!\is_array($value) && (!$this->allowFalse || false !== $value)) { + $ex = new InvalidTypeException(sprintf('Invalid type for path "%s". Expected array, but got %s', $this->getPath(), \gettype($value))); + if ($hint = $this->getInfo()) { + $ex->addHint($hint); + } + $ex->setPath($this->getPath()); + + throw $ex; + } + } + + /** + * Normalizes the value. + * + * @param mixed $value The value to normalize + * + * @return mixed The normalized value + * + * @throws InvalidConfigurationException + */ + protected function normalizeValue($value) + { + if (false === $value) { + return $value; + } + + $value = $this->remapXml($value); + + $normalized = []; + foreach ($value as $name => $val) { + if (isset($this->children[$name])) { + try { + $normalized[$name] = $this->children[$name]->normalize($val); + } catch (UnsetKeyException $e) { + } + unset($value[$name]); + } elseif (!$this->removeExtraKeys) { + $normalized[$name] = $val; + } + } + + // if extra fields are present, throw exception + if (\count($value) && !$this->ignoreExtraKeys) { + $ex = new InvalidConfigurationException(sprintf('Unrecognized option%s "%s" under "%s"', 1 === \count($value) ? '' : 's', implode(', ', array_keys($value)), $this->getPath())); + $ex->setPath($this->getPath()); + + throw $ex; + } + + return $normalized; + } + + /** + * Remaps multiple singular values to a single plural value. + * + * @param array $value The source values + * + * @return array The remapped values + */ + protected function remapXml($value) + { + foreach ($this->xmlRemappings as list($singular, $plural)) { + if (!isset($value[$singular])) { + continue; + } + + $value[$plural] = Processor::normalizeConfig($value, $singular, $plural); + unset($value[$singular]); + } + + return $value; + } + + /** + * Merges values together. + * + * @param mixed $leftSide The left side to merge + * @param mixed $rightSide The right side to merge + * + * @return mixed The merged values + * + * @throws InvalidConfigurationException + * @throws \RuntimeException + */ + protected function mergeValues($leftSide, $rightSide) + { + if (false === $rightSide) { + // if this is still false after the last config has been merged the + // finalization pass will take care of removing this key entirely + return false; + } + + if (false === $leftSide || !$this->performDeepMerging) { + return $rightSide; + } + + foreach ($rightSide as $k => $v) { + // no conflict + if (!\array_key_exists($k, $leftSide)) { + if (!$this->allowNewKeys) { + $ex = new InvalidConfigurationException(sprintf('You are not allowed to define new elements for path "%s". Please define all elements for this path in one config file. If you are trying to overwrite an element, make sure you redefine it with the same name.', $this->getPath())); + $ex->setPath($this->getPath()); + + throw $ex; + } + + $leftSide[$k] = $v; + continue; + } + + if (!isset($this->children[$k])) { + throw new \RuntimeException('merge() expects a normalized config array.'); + } + + $leftSide[$k] = $this->children[$k]->merge($leftSide[$k], $v); + } + + return $leftSide; + } +} diff --git a/lib/symfony/config/Definition/BaseNode.php b/lib/symfony/config/Definition/BaseNode.php new file mode 100644 index 000000000..905406d12 --- /dev/null +++ b/lib/symfony/config/Definition/BaseNode.php @@ -0,0 +1,362 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition; + +use Symfony\Component\Config\Definition\Exception\Exception; +use Symfony\Component\Config\Definition\Exception\ForbiddenOverwriteException; +use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; +use Symfony\Component\Config\Definition\Exception\InvalidTypeException; + +/** + * The base node class. + * + * @author Johannes M. Schmitt + */ +abstract class BaseNode implements NodeInterface +{ + protected $name; + protected $parent; + protected $normalizationClosures = []; + protected $finalValidationClosures = []; + protected $allowOverwrite = true; + protected $required = false; + protected $deprecationMessage = null; + protected $equivalentValues = []; + protected $attributes = []; + + /** + * @param string|null $name The name of the node + * @param NodeInterface|null $parent The parent of this node + * + * @throws \InvalidArgumentException if the name contains a period + */ + public function __construct($name, NodeInterface $parent = null) + { + if (false !== strpos($name = (string) $name, '.')) { + throw new \InvalidArgumentException('The name must not contain ".".'); + } + + $this->name = $name; + $this->parent = $parent; + } + + public function setAttribute($key, $value) + { + $this->attributes[$key] = $value; + } + + public function getAttribute($key, $default = null) + { + return isset($this->attributes[$key]) ? $this->attributes[$key] : $default; + } + + public function hasAttribute($key) + { + return isset($this->attributes[$key]); + } + + public function getAttributes() + { + return $this->attributes; + } + + public function setAttributes(array $attributes) + { + $this->attributes = $attributes; + } + + public function removeAttribute($key) + { + unset($this->attributes[$key]); + } + + /** + * Sets an info message. + * + * @param string $info + */ + public function setInfo($info) + { + $this->setAttribute('info', $info); + } + + /** + * Returns info message. + * + * @return string The info text + */ + public function getInfo() + { + return $this->getAttribute('info'); + } + + /** + * Sets the example configuration for this node. + * + * @param string|array $example + */ + public function setExample($example) + { + $this->setAttribute('example', $example); + } + + /** + * Retrieves the example configuration for this node. + * + * @return string|array The example + */ + public function getExample() + { + return $this->getAttribute('example'); + } + + /** + * Adds an equivalent value. + * + * @param mixed $originalValue + * @param mixed $equivalentValue + */ + public function addEquivalentValue($originalValue, $equivalentValue) + { + $this->equivalentValues[] = [$originalValue, $equivalentValue]; + } + + /** + * Set this node as required. + * + * @param bool $boolean Required node + */ + public function setRequired($boolean) + { + $this->required = (bool) $boolean; + } + + /** + * Sets this node as deprecated. + * + * You can use %node% and %path% placeholders in your message to display, + * respectively, the node name and its complete path. + * + * @param string|null $message Deprecated message + */ + public function setDeprecated($message) + { + $this->deprecationMessage = $message; + } + + /** + * Sets if this node can be overridden. + * + * @param bool $allow + */ + public function setAllowOverwrite($allow) + { + $this->allowOverwrite = (bool) $allow; + } + + /** + * Sets the closures used for normalization. + * + * @param \Closure[] $closures An array of Closures used for normalization + */ + public function setNormalizationClosures(array $closures) + { + $this->normalizationClosures = $closures; + } + + /** + * Sets the closures used for final validation. + * + * @param \Closure[] $closures An array of Closures used for final validation + */ + public function setFinalValidationClosures(array $closures) + { + $this->finalValidationClosures = $closures; + } + + /** + * {@inheritdoc} + */ + public function isRequired() + { + return $this->required; + } + + /** + * Checks if this node is deprecated. + * + * @return bool + */ + public function isDeprecated() + { + return null !== $this->deprecationMessage; + } + + /** + * Returns the deprecated message. + * + * @param string $node the configuration node name + * @param string $path the path of the node + * + * @return string + */ + public function getDeprecationMessage($node, $path) + { + return strtr($this->deprecationMessage, ['%node%' => $node, '%path%' => $path]); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return $this->name; + } + + /** + * {@inheritdoc} + */ + public function getPath() + { + $path = $this->name; + + if (null !== $this->parent) { + $path = $this->parent->getPath().'.'.$path; + } + + return $path; + } + + /** + * {@inheritdoc} + */ + final public function merge($leftSide, $rightSide) + { + if (!$this->allowOverwrite) { + throw new ForbiddenOverwriteException(sprintf('Configuration path "%s" cannot be overwritten. You have to define all options for this path, and any of its sub-paths in one configuration section.', $this->getPath())); + } + + $this->validateType($leftSide); + $this->validateType($rightSide); + + return $this->mergeValues($leftSide, $rightSide); + } + + /** + * {@inheritdoc} + */ + final public function normalize($value) + { + $value = $this->preNormalize($value); + + // run custom normalization closures + foreach ($this->normalizationClosures as $closure) { + $value = $closure($value); + } + + // replace value with their equivalent + foreach ($this->equivalentValues as $data) { + if ($data[0] === $value) { + $value = $data[1]; + } + } + + // validate type + $this->validateType($value); + + // normalize value + return $this->normalizeValue($value); + } + + /** + * Normalizes the value before any other normalization is applied. + * + * @param $value + * + * @return The normalized array value + */ + protected function preNormalize($value) + { + return $value; + } + + /** + * Returns parent node for this node. + * + * @return NodeInterface|null + */ + public function getParent() + { + return $this->parent; + } + + /** + * {@inheritdoc} + */ + final public function finalize($value) + { + $this->validateType($value); + + $value = $this->finalizeValue($value); + + // Perform validation on the final value if a closure has been set. + // The closure is also allowed to return another value. + foreach ($this->finalValidationClosures as $closure) { + try { + $value = $closure($value); + } catch (Exception $e) { + throw $e; + } catch (\Exception $e) { + throw new InvalidConfigurationException(sprintf('Invalid configuration for path "%s": %s', $this->getPath(), $e->getMessage()), $e->getCode(), $e); + } + } + + return $value; + } + + /** + * Validates the type of a Node. + * + * @param mixed $value The value to validate + * + * @throws InvalidTypeException when the value is invalid + */ + abstract protected function validateType($value); + + /** + * Normalizes the value. + * + * @param mixed $value The value to normalize + * + * @return mixed The normalized value + */ + abstract protected function normalizeValue($value); + + /** + * Merges two values together. + * + * @param mixed $leftSide + * @param mixed $rightSide + * + * @return mixed The merged value + */ + abstract protected function mergeValues($leftSide, $rightSide); + + /** + * Finalizes a value. + * + * @param mixed $value The value to finalize + * + * @return mixed The finalized value + */ + abstract protected function finalizeValue($value); +} diff --git a/lib/symfony/config/Definition/BooleanNode.php b/lib/symfony/config/Definition/BooleanNode.php new file mode 100644 index 000000000..85f467b6b --- /dev/null +++ b/lib/symfony/config/Definition/BooleanNode.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition; + +use Symfony\Component\Config\Definition\Exception\InvalidTypeException; + +/** + * This node represents a Boolean value in the config tree. + * + * @author Johannes M. Schmitt + */ +class BooleanNode extends ScalarNode +{ + /** + * {@inheritdoc} + */ + protected function validateType($value) + { + if (!\is_bool($value)) { + $ex = new InvalidTypeException(sprintf('Invalid type for path "%s". Expected boolean, but got %s.', $this->getPath(), \gettype($value))); + if ($hint = $this->getInfo()) { + $ex->addHint($hint); + } + $ex->setPath($this->getPath()); + + throw $ex; + } + } + + /** + * {@inheritdoc} + */ + protected function isValueEmpty($value) + { + // a boolean value cannot be empty + return false; + } +} diff --git a/lib/symfony/config/Definition/Builder/ArrayNodeDefinition.php b/lib/symfony/config/Definition/Builder/ArrayNodeDefinition.php new file mode 100644 index 000000000..115054a24 --- /dev/null +++ b/lib/symfony/config/Definition/Builder/ArrayNodeDefinition.php @@ -0,0 +1,522 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Builder; + +use Symfony\Component\Config\Definition\ArrayNode; +use Symfony\Component\Config\Definition\Exception\InvalidDefinitionException; +use Symfony\Component\Config\Definition\PrototypedArrayNode; + +/** + * This class provides a fluent interface for defining an array node. + * + * @author Johannes M. Schmitt + */ +class ArrayNodeDefinition extends NodeDefinition implements ParentNodeDefinitionInterface +{ + protected $performDeepMerging = true; + protected $ignoreExtraKeys = false; + protected $removeExtraKeys = true; + protected $children = []; + protected $prototype; + protected $atLeastOne = false; + protected $allowNewKeys = true; + protected $key; + protected $removeKeyItem; + protected $addDefaults = false; + protected $addDefaultChildren = false; + protected $nodeBuilder; + protected $normalizeKeys = true; + + /** + * {@inheritdoc} + */ + public function __construct($name, NodeParentInterface $parent = null) + { + parent::__construct($name, $parent); + + $this->nullEquivalent = []; + $this->trueEquivalent = []; + } + + /** + * {@inheritdoc} + */ + public function setBuilder(NodeBuilder $builder) + { + $this->nodeBuilder = $builder; + } + + /** + * {@inheritdoc} + */ + public function children() + { + return $this->getNodeBuilder(); + } + + /** + * Sets a prototype for child nodes. + * + * @param string $type The type of node + * + * @return NodeDefinition + */ + public function prototype($type) + { + return $this->prototype = $this->getNodeBuilder()->node(null, $type)->setParent($this); + } + + /** + * @return VariableNodeDefinition + */ + public function variablePrototype() + { + return $this->prototype('variable'); + } + + /** + * @return ScalarNodeDefinition + */ + public function scalarPrototype() + { + return $this->prototype('scalar'); + } + + /** + * @return BooleanNodeDefinition + */ + public function booleanPrototype() + { + return $this->prototype('boolean'); + } + + /** + * @return IntegerNodeDefinition + */ + public function integerPrototype() + { + return $this->prototype('integer'); + } + + /** + * @return FloatNodeDefinition + */ + public function floatPrototype() + { + return $this->prototype('float'); + } + + /** + * @return ArrayNodeDefinition + */ + public function arrayPrototype() + { + return $this->prototype('array'); + } + + /** + * @return EnumNodeDefinition + */ + public function enumPrototype() + { + return $this->prototype('enum'); + } + + /** + * Adds the default value if the node is not set in the configuration. + * + * This method is applicable to concrete nodes only (not to prototype nodes). + * If this function has been called and the node is not set during the finalization + * phase, it's default value will be derived from its children default values. + * + * @return $this + */ + public function addDefaultsIfNotSet() + { + $this->addDefaults = true; + + return $this; + } + + /** + * Adds children with a default value when none are defined. + * + * This method is applicable to prototype nodes only. + * + * @param int|string|array|null $children The number of children|The child name|The children names to be added + * + * @return $this + */ + public function addDefaultChildrenIfNoneSet($children = null) + { + $this->addDefaultChildren = $children; + + return $this; + } + + /** + * Requires the node to have at least one element. + * + * This method is applicable to prototype nodes only. + * + * @return $this + */ + public function requiresAtLeastOneElement() + { + $this->atLeastOne = true; + + return $this; + } + + /** + * Disallows adding news keys in a subsequent configuration. + * + * If used all keys have to be defined in the same configuration file. + * + * @return $this + */ + public function disallowNewKeysInSubsequentConfigs() + { + $this->allowNewKeys = false; + + return $this; + } + + /** + * Sets a normalization rule for XML configurations. + * + * @param string $singular The key to remap + * @param string $plural The plural of the key for irregular plurals + * + * @return $this + */ + public function fixXmlConfig($singular, $plural = null) + { + $this->normalization()->remap($singular, $plural); + + return $this; + } + + /** + * Sets the attribute which value is to be used as key. + * + * This is useful when you have an indexed array that should be an + * associative array. You can select an item from within the array + * to be the key of the particular item. For example, if "id" is the + * "key", then: + * + * [ + * ['id' => 'my_name', 'foo' => 'bar'], + * ]; + * + * becomes + * + * [ + * 'my_name' => ['foo' => 'bar'], + * ]; + * + * If you'd like "'id' => 'my_name'" to still be present in the resulting + * array, then you can set the second argument of this method to false. + * + * This method is applicable to prototype nodes only. + * + * @param string $name The name of the key + * @param bool $removeKeyItem Whether or not the key item should be removed + * + * @return $this + */ + public function useAttributeAsKey($name, $removeKeyItem = true) + { + $this->key = $name; + $this->removeKeyItem = $removeKeyItem; + + return $this; + } + + /** + * Sets whether the node can be unset. + * + * @param bool $allow + * + * @return $this + */ + public function canBeUnset($allow = true) + { + $this->merge()->allowUnset($allow); + + return $this; + } + + /** + * Adds an "enabled" boolean to enable the current section. + * + * By default, the section is disabled. If any configuration is specified then + * the node will be automatically enabled: + * + * enableableArrayNode: {enabled: true, ...} # The config is enabled & default values get overridden + * enableableArrayNode: ~ # The config is enabled & use the default values + * enableableArrayNode: true # The config is enabled & use the default values + * enableableArrayNode: {other: value, ...} # The config is enabled & default values get overridden + * enableableArrayNode: {enabled: false, ...} # The config is disabled + * enableableArrayNode: false # The config is disabled + * + * @return $this + */ + public function canBeEnabled() + { + $this + ->addDefaultsIfNotSet() + ->treatFalseLike(['enabled' => false]) + ->treatTrueLike(['enabled' => true]) + ->treatNullLike(['enabled' => true]) + ->beforeNormalization() + ->ifArray() + ->then(function ($v) { + $v['enabled'] = isset($v['enabled']) ? $v['enabled'] : true; + + return $v; + }) + ->end() + ->children() + ->booleanNode('enabled') + ->defaultFalse() + ; + + return $this; + } + + /** + * Adds an "enabled" boolean to enable the current section. + * + * By default, the section is enabled. + * + * @return $this + */ + public function canBeDisabled() + { + $this + ->addDefaultsIfNotSet() + ->treatFalseLike(['enabled' => false]) + ->treatTrueLike(['enabled' => true]) + ->treatNullLike(['enabled' => true]) + ->children() + ->booleanNode('enabled') + ->defaultTrue() + ; + + return $this; + } + + /** + * Disables the deep merging of the node. + * + * @return $this + */ + public function performNoDeepMerging() + { + $this->performDeepMerging = false; + + return $this; + } + + /** + * Allows extra config keys to be specified under an array without + * throwing an exception. + * + * Those config values are ignored and removed from the resulting + * array. This should be used only in special cases where you want + * to send an entire configuration array through a special tree that + * processes only part of the array. + * + * @param bool $remove Whether to remove the extra keys + * + * @return $this + */ + public function ignoreExtraKeys($remove = true) + { + $this->ignoreExtraKeys = true; + $this->removeExtraKeys = $remove; + + return $this; + } + + /** + * Sets key normalization. + * + * @param bool $bool Whether to enable key normalization + * + * @return $this + */ + public function normalizeKeys($bool) + { + $this->normalizeKeys = (bool) $bool; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function append(NodeDefinition $node) + { + $this->children[$node->name] = $node->setParent($this); + + return $this; + } + + /** + * Returns a node builder to be used to add children and prototype. + * + * @return NodeBuilder The node builder + */ + protected function getNodeBuilder() + { + if (null === $this->nodeBuilder) { + $this->nodeBuilder = new NodeBuilder(); + } + + return $this->nodeBuilder->setParent($this); + } + + /** + * {@inheritdoc} + */ + protected function createNode() + { + if (null === $this->prototype) { + $node = new ArrayNode($this->name, $this->parent); + + $this->validateConcreteNode($node); + + $node->setAddIfNotSet($this->addDefaults); + + foreach ($this->children as $child) { + $child->parent = $node; + $node->addChild($child->getNode()); + } + } else { + $node = new PrototypedArrayNode($this->name, $this->parent); + + $this->validatePrototypeNode($node); + + if (null !== $this->key) { + $node->setKeyAttribute($this->key, $this->removeKeyItem); + } + + if (false === $this->allowEmptyValue) { + @trigger_error(sprintf('Using %s::cannotBeEmpty() at path "%s" has no effect, consider requiresAtLeastOneElement() instead. In 4.0 both methods will behave the same.', __CLASS__, $node->getPath()), E_USER_DEPRECATED); + } + + if (true === $this->atLeastOne) { + $node->setMinNumberOfElements(1); + } + + if ($this->default) { + $node->setDefaultValue($this->defaultValue); + } + + if (false !== $this->addDefaultChildren) { + $node->setAddChildrenIfNoneSet($this->addDefaultChildren); + if ($this->prototype instanceof static && null === $this->prototype->prototype) { + $this->prototype->addDefaultsIfNotSet(); + } + } + + $this->prototype->parent = $node; + $node->setPrototype($this->prototype->getNode()); + } + + $node->setAllowNewKeys($this->allowNewKeys); + $node->addEquivalentValue(null, $this->nullEquivalent); + $node->addEquivalentValue(true, $this->trueEquivalent); + $node->addEquivalentValue(false, $this->falseEquivalent); + $node->setPerformDeepMerging($this->performDeepMerging); + $node->setRequired($this->required); + $node->setDeprecated($this->deprecationMessage); + $node->setIgnoreExtraKeys($this->ignoreExtraKeys, $this->removeExtraKeys); + $node->setNormalizeKeys($this->normalizeKeys); + + if (null !== $this->normalization) { + $node->setNormalizationClosures($this->normalization->before); + $node->setXmlRemappings($this->normalization->remappings); + } + + if (null !== $this->merge) { + $node->setAllowOverwrite($this->merge->allowOverwrite); + $node->setAllowFalse($this->merge->allowFalse); + } + + if (null !== $this->validation) { + $node->setFinalValidationClosures($this->validation->rules); + } + + return $node; + } + + /** + * Validate the configuration of a concrete node. + * + * @throws InvalidDefinitionException + */ + protected function validateConcreteNode(ArrayNode $node) + { + $path = $node->getPath(); + + if (null !== $this->key) { + throw new InvalidDefinitionException(sprintf('->useAttributeAsKey() is not applicable to concrete nodes at path "%s"', $path)); + } + + if (false === $this->allowEmptyValue) { + @trigger_error(sprintf('->cannotBeEmpty() is not applicable to concrete nodes at path "%s". In 4.0 it will throw an exception.', $path), E_USER_DEPRECATED); + } + + if (true === $this->atLeastOne) { + throw new InvalidDefinitionException(sprintf('->requiresAtLeastOneElement() is not applicable to concrete nodes at path "%s"', $path)); + } + + if ($this->default) { + throw new InvalidDefinitionException(sprintf('->defaultValue() is not applicable to concrete nodes at path "%s"', $path)); + } + + if (false !== $this->addDefaultChildren) { + throw new InvalidDefinitionException(sprintf('->addDefaultChildrenIfNoneSet() is not applicable to concrete nodes at path "%s"', $path)); + } + } + + /** + * Validate the configuration of a prototype node. + * + * @throws InvalidDefinitionException + */ + protected function validatePrototypeNode(PrototypedArrayNode $node) + { + $path = $node->getPath(); + + if ($this->addDefaults) { + throw new InvalidDefinitionException(sprintf('->addDefaultsIfNotSet() is not applicable to prototype nodes at path "%s"', $path)); + } + + if (false !== $this->addDefaultChildren) { + if ($this->default) { + throw new InvalidDefinitionException(sprintf('A default value and default children might not be used together at path "%s"', $path)); + } + + if (null !== $this->key && (null === $this->addDefaultChildren || \is_int($this->addDefaultChildren) && $this->addDefaultChildren > 0)) { + throw new InvalidDefinitionException(sprintf('->addDefaultChildrenIfNoneSet() should set default children names as ->useAttributeAsKey() is used at path "%s"', $path)); + } + + if (null === $this->key && (\is_string($this->addDefaultChildren) || \is_array($this->addDefaultChildren))) { + throw new InvalidDefinitionException(sprintf('->addDefaultChildrenIfNoneSet() might not set default children names as ->useAttributeAsKey() is not used at path "%s"', $path)); + } + } + } +} diff --git a/lib/symfony/config/Definition/Builder/BooleanNodeDefinition.php b/lib/symfony/config/Definition/Builder/BooleanNodeDefinition.php new file mode 100644 index 000000000..28e56579a --- /dev/null +++ b/lib/symfony/config/Definition/Builder/BooleanNodeDefinition.php @@ -0,0 +1,53 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Builder; + +use Symfony\Component\Config\Definition\BooleanNode; +use Symfony\Component\Config\Definition\Exception\InvalidDefinitionException; + +/** + * This class provides a fluent interface for defining a node. + * + * @author Johannes M. Schmitt + */ +class BooleanNodeDefinition extends ScalarNodeDefinition +{ + /** + * {@inheritdoc} + */ + public function __construct($name, NodeParentInterface $parent = null) + { + parent::__construct($name, $parent); + + $this->nullEquivalent = true; + } + + /** + * Instantiate a Node. + * + * @return BooleanNode The node + */ + protected function instantiateNode() + { + return new BooleanNode($this->name, $this->parent); + } + + /** + * {@inheritdoc} + * + * @throws InvalidDefinitionException + */ + public function cannotBeEmpty() + { + throw new InvalidDefinitionException('->cannotBeEmpty() is not applicable to BooleanNodeDefinition.'); + } +} diff --git a/lib/symfony/config/Definition/Builder/EnumNodeDefinition.php b/lib/symfony/config/Definition/Builder/EnumNodeDefinition.php new file mode 100644 index 000000000..817906f50 --- /dev/null +++ b/lib/symfony/config/Definition/Builder/EnumNodeDefinition.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Builder; + +use Symfony\Component\Config\Definition\EnumNode; + +/** + * Enum Node Definition. + * + * @author Johannes M. Schmitt + */ +class EnumNodeDefinition extends ScalarNodeDefinition +{ + private $values; + + /** + * @return $this + */ + public function values(array $values) + { + $values = array_unique($values); + + if (empty($values)) { + throw new \InvalidArgumentException('->values() must be called with at least one value.'); + } + + $this->values = $values; + + return $this; + } + + /** + * Instantiate a Node. + * + * @return EnumNode The node + * + * @throws \RuntimeException + */ + protected function instantiateNode() + { + if (null === $this->values) { + throw new \RuntimeException('You must call ->values() on enum nodes.'); + } + + return new EnumNode($this->name, $this->parent, $this->values); + } +} diff --git a/lib/symfony/config/Definition/Builder/ExprBuilder.php b/lib/symfony/config/Definition/Builder/ExprBuilder.php new file mode 100644 index 000000000..05949d2b5 --- /dev/null +++ b/lib/symfony/config/Definition/Builder/ExprBuilder.php @@ -0,0 +1,248 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Builder; + +use Symfony\Component\Config\Definition\Exception\UnsetKeyException; + +/** + * This class builds an if expression. + * + * @author Johannes M. Schmitt + * @author Christophe Coevoet + */ +class ExprBuilder +{ + protected $node; + public $ifPart; + public $thenPart; + + public function __construct(NodeDefinition $node) + { + $this->node = $node; + } + + /** + * Marks the expression as being always used. + * + * @return $this + */ + public function always(\Closure $then = null) + { + $this->ifPart = function ($v) { return true; }; + + if (null !== $then) { + $this->thenPart = $then; + } + + return $this; + } + + /** + * Sets a closure to use as tests. + * + * The default one tests if the value is true. + * + * @return $this + */ + public function ifTrue(\Closure $closure = null) + { + if (null === $closure) { + $closure = function ($v) { return true === $v; }; + } + + $this->ifPart = $closure; + + return $this; + } + + /** + * Tests if the value is a string. + * + * @return $this + */ + public function ifString() + { + $this->ifPart = function ($v) { return \is_string($v); }; + + return $this; + } + + /** + * Tests if the value is null. + * + * @return $this + */ + public function ifNull() + { + $this->ifPart = function ($v) { return null === $v; }; + + return $this; + } + + /** + * Tests if the value is empty. + * + * @return ExprBuilder + */ + public function ifEmpty() + { + $this->ifPart = function ($v) { return empty($v); }; + + return $this; + } + + /** + * Tests if the value is an array. + * + * @return $this + */ + public function ifArray() + { + $this->ifPart = function ($v) { return \is_array($v); }; + + return $this; + } + + /** + * Tests if the value is in an array. + * + * @return $this + */ + public function ifInArray(array $array) + { + $this->ifPart = function ($v) use ($array) { return \in_array($v, $array, true); }; + + return $this; + } + + /** + * Tests if the value is not in an array. + * + * @return $this + */ + public function ifNotInArray(array $array) + { + $this->ifPart = function ($v) use ($array) { return !\in_array($v, $array, true); }; + + return $this; + } + + /** + * Transforms variables of any type into an array. + * + * @return $this + */ + public function castToArray() + { + $this->ifPart = function ($v) { return !\is_array($v); }; + $this->thenPart = function ($v) { return [$v]; }; + + return $this; + } + + /** + * Sets the closure to run if the test pass. + * + * @return $this + */ + public function then(\Closure $closure) + { + $this->thenPart = $closure; + + return $this; + } + + /** + * Sets a closure returning an empty array. + * + * @return $this + */ + public function thenEmptyArray() + { + $this->thenPart = function ($v) { return []; }; + + return $this; + } + + /** + * Sets a closure marking the value as invalid at processing time. + * + * if you want to add the value of the node in your message just use a %s placeholder. + * + * @param string $message + * + * @return $this + * + * @throws \InvalidArgumentException + */ + public function thenInvalid($message) + { + $this->thenPart = function ($v) use ($message) { throw new \InvalidArgumentException(sprintf($message, json_encode($v))); }; + + return $this; + } + + /** + * Sets a closure unsetting this key of the array at processing time. + * + * @return $this + * + * @throws UnsetKeyException + */ + public function thenUnset() + { + $this->thenPart = function ($v) { throw new UnsetKeyException('Unsetting key'); }; + + return $this; + } + + /** + * Returns the related node. + * + * @return NodeDefinition|ArrayNodeDefinition|VariableNodeDefinition + * + * @throws \RuntimeException + */ + public function end() + { + if (null === $this->ifPart) { + throw new \RuntimeException('You must specify an if part.'); + } + if (null === $this->thenPart) { + throw new \RuntimeException('You must specify a then part.'); + } + + return $this->node; + } + + /** + * Builds the expressions. + * + * @param ExprBuilder[] $expressions An array of ExprBuilder instances to build + * + * @return array + */ + public static function buildExpressions(array $expressions) + { + foreach ($expressions as $k => $expr) { + if ($expr instanceof self) { + $if = $expr->ifPart; + $then = $expr->thenPart; + $expressions[$k] = function ($v) use ($if, $then) { + return $if($v) ? $then($v) : $v; + }; + } + } + + return $expressions; + } +} diff --git a/lib/symfony/config/Definition/Builder/FloatNodeDefinition.php b/lib/symfony/config/Definition/Builder/FloatNodeDefinition.php new file mode 100644 index 000000000..c0bed462b --- /dev/null +++ b/lib/symfony/config/Definition/Builder/FloatNodeDefinition.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Builder; + +use Symfony\Component\Config\Definition\FloatNode; + +/** + * This class provides a fluent interface for defining a float node. + * + * @author Jeanmonod David + */ +class FloatNodeDefinition extends NumericNodeDefinition +{ + /** + * Instantiates a Node. + * + * @return FloatNode The node + */ + protected function instantiateNode() + { + return new FloatNode($this->name, $this->parent, $this->min, $this->max); + } +} diff --git a/lib/symfony/config/Definition/Builder/IntegerNodeDefinition.php b/lib/symfony/config/Definition/Builder/IntegerNodeDefinition.php new file mode 100644 index 000000000..f6c3c147f --- /dev/null +++ b/lib/symfony/config/Definition/Builder/IntegerNodeDefinition.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Builder; + +use Symfony\Component\Config\Definition\IntegerNode; + +/** + * This class provides a fluent interface for defining an integer node. + * + * @author Jeanmonod David + */ +class IntegerNodeDefinition extends NumericNodeDefinition +{ + /** + * Instantiates a Node. + * + * @return IntegerNode The node + */ + protected function instantiateNode() + { + return new IntegerNode($this->name, $this->parent, $this->min, $this->max); + } +} diff --git a/lib/symfony/config/Definition/Builder/MergeBuilder.php b/lib/symfony/config/Definition/Builder/MergeBuilder.php new file mode 100644 index 000000000..105e2d647 --- /dev/null +++ b/lib/symfony/config/Definition/Builder/MergeBuilder.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Builder; + +/** + * This class builds merge conditions. + * + * @author Johannes M. Schmitt + */ +class MergeBuilder +{ + protected $node; + public $allowFalse = false; + public $allowOverwrite = true; + + public function __construct(NodeDefinition $node) + { + $this->node = $node; + } + + /** + * Sets whether the node can be unset. + * + * @param bool $allow + * + * @return $this + */ + public function allowUnset($allow = true) + { + $this->allowFalse = $allow; + + return $this; + } + + /** + * Sets whether the node can be overwritten. + * + * @param bool $deny Whether the overwriting is forbidden or not + * + * @return $this + */ + public function denyOverwrite($deny = true) + { + $this->allowOverwrite = !$deny; + + return $this; + } + + /** + * Returns the related node. + * + * @return NodeDefinition|ArrayNodeDefinition|VariableNodeDefinition + */ + public function end() + { + return $this->node; + } +} diff --git a/lib/symfony/config/Definition/Builder/NodeBuilder.php b/lib/symfony/config/Definition/Builder/NodeBuilder.php new file mode 100644 index 000000000..63c0d696b --- /dev/null +++ b/lib/symfony/config/Definition/Builder/NodeBuilder.php @@ -0,0 +1,238 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Builder; + +/** + * This class provides a fluent interface for building a node. + * + * @author Johannes M. Schmitt + */ +class NodeBuilder implements NodeParentInterface +{ + protected $parent; + protected $nodeMapping; + + public function __construct() + { + $this->nodeMapping = [ + 'variable' => __NAMESPACE__.'\\VariableNodeDefinition', + 'scalar' => __NAMESPACE__.'\\ScalarNodeDefinition', + 'boolean' => __NAMESPACE__.'\\BooleanNodeDefinition', + 'integer' => __NAMESPACE__.'\\IntegerNodeDefinition', + 'float' => __NAMESPACE__.'\\FloatNodeDefinition', + 'array' => __NAMESPACE__.'\\ArrayNodeDefinition', + 'enum' => __NAMESPACE__.'\\EnumNodeDefinition', + ]; + } + + /** + * Set the parent node. + * + * @return $this + */ + public function setParent(ParentNodeDefinitionInterface $parent = null) + { + $this->parent = $parent; + + return $this; + } + + /** + * Creates a child array node. + * + * @param string $name The name of the node + * + * @return ArrayNodeDefinition The child node + */ + public function arrayNode($name) + { + return $this->node($name, 'array'); + } + + /** + * Creates a child scalar node. + * + * @param string $name The name of the node + * + * @return ScalarNodeDefinition The child node + */ + public function scalarNode($name) + { + return $this->node($name, 'scalar'); + } + + /** + * Creates a child Boolean node. + * + * @param string $name The name of the node + * + * @return BooleanNodeDefinition The child node + */ + public function booleanNode($name) + { + return $this->node($name, 'boolean'); + } + + /** + * Creates a child integer node. + * + * @param string $name The name of the node + * + * @return IntegerNodeDefinition The child node + */ + public function integerNode($name) + { + return $this->node($name, 'integer'); + } + + /** + * Creates a child float node. + * + * @param string $name The name of the node + * + * @return FloatNodeDefinition The child node + */ + public function floatNode($name) + { + return $this->node($name, 'float'); + } + + /** + * Creates a child EnumNode. + * + * @param string $name + * + * @return EnumNodeDefinition + */ + public function enumNode($name) + { + return $this->node($name, 'enum'); + } + + /** + * Creates a child variable node. + * + * @param string $name The name of the node + * + * @return VariableNodeDefinition The builder of the child node + */ + public function variableNode($name) + { + return $this->node($name, 'variable'); + } + + /** + * Returns the parent node. + * + * @return NodeDefinition&ParentNodeDefinitionInterface The parent node + */ + public function end() + { + return $this->parent; + } + + /** + * Creates a child node. + * + * @param string|null $name The name of the node + * @param string $type The type of the node + * + * @return NodeDefinition The child node + * + * @throws \RuntimeException When the node type is not registered + * @throws \RuntimeException When the node class is not found + */ + public function node($name, $type) + { + $class = $this->getNodeClass($type); + + $node = new $class($name); + + $this->append($node); + + return $node; + } + + /** + * Appends a node definition. + * + * Usage: + * + * $node = new ArrayNodeDefinition('name') + * ->children() + * ->scalarNode('foo')->end() + * ->scalarNode('baz')->end() + * ->append($this->getBarNodeDefinition()) + * ->end() + * ; + * + * @return $this + */ + public function append(NodeDefinition $node) + { + if ($node instanceof ParentNodeDefinitionInterface) { + $builder = clone $this; + $builder->setParent(null); + $node->setBuilder($builder); + } + + if (null !== $this->parent) { + $this->parent->append($node); + // Make this builder the node parent to allow for a fluid interface + $node->setParent($this); + } + + return $this; + } + + /** + * Adds or overrides a node Type. + * + * @param string $type The name of the type + * @param string $class The fully qualified name the node definition class + * + * @return $this + */ + public function setNodeClass($type, $class) + { + $this->nodeMapping[strtolower($type)] = $class; + + return $this; + } + + /** + * Returns the class name of the node definition. + * + * @param string $type The node type + * + * @return string The node definition class name + * + * @throws \RuntimeException When the node type is not registered + * @throws \RuntimeException When the node class is not found + */ + protected function getNodeClass($type) + { + $type = strtolower($type); + + if (!isset($this->nodeMapping[$type])) { + throw new \RuntimeException(sprintf('The node type "%s" is not registered.', $type)); + } + + $class = $this->nodeMapping[$type]; + + if (!class_exists($class)) { + throw new \RuntimeException(sprintf('The node class "%s" does not exist.', $class)); + } + + return $class; + } +} diff --git a/lib/symfony/config/Definition/Builder/NodeDefinition.php b/lib/symfony/config/Definition/Builder/NodeDefinition.php new file mode 100644 index 000000000..cc245d748 --- /dev/null +++ b/lib/symfony/config/Definition/Builder/NodeDefinition.php @@ -0,0 +1,353 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Builder; + +use Symfony\Component\Config\Definition\Exception\InvalidDefinitionException; +use Symfony\Component\Config\Definition\NodeInterface; + +/** + * This class provides a fluent interface for defining a node. + * + * @author Johannes M. Schmitt + */ +abstract class NodeDefinition implements NodeParentInterface +{ + protected $name; + protected $normalization; + protected $validation; + protected $defaultValue; + protected $default = false; + protected $required = false; + protected $deprecationMessage = null; + protected $merge; + protected $allowEmptyValue = true; + protected $nullEquivalent; + protected $trueEquivalent = true; + protected $falseEquivalent = false; + protected $parent; + protected $attributes = []; + + /** + * @param string|null $name The name of the node + * @param NodeParentInterface|null $parent The parent + */ + public function __construct($name, NodeParentInterface $parent = null) + { + $this->parent = $parent; + $this->name = $name; + } + + /** + * Sets the parent node. + * + * @return $this + */ + public function setParent(NodeParentInterface $parent) + { + $this->parent = $parent; + + return $this; + } + + /** + * Sets info message. + * + * @param string $info The info text + * + * @return $this + */ + public function info($info) + { + return $this->attribute('info', $info); + } + + /** + * Sets example configuration. + * + * @param string|array $example + * + * @return $this + */ + public function example($example) + { + return $this->attribute('example', $example); + } + + /** + * Sets an attribute on the node. + * + * @param string $key + * @param mixed $value + * + * @return $this + */ + public function attribute($key, $value) + { + $this->attributes[$key] = $value; + + return $this; + } + + /** + * Returns the parent node. + * + * @return NodeParentInterface|NodeBuilder|NodeDefinition|ArrayNodeDefinition|VariableNodeDefinition|null The builder of the parent node + */ + public function end() + { + return $this->parent; + } + + /** + * Creates the node. + * + * @param bool $forceRootNode Whether to force this node as the root node + * + * @return NodeInterface + */ + public function getNode($forceRootNode = false) + { + if ($forceRootNode) { + $this->parent = null; + } + + if (null !== $this->normalization) { + $this->normalization->before = ExprBuilder::buildExpressions($this->normalization->before); + } + + if (null !== $this->validation) { + $this->validation->rules = ExprBuilder::buildExpressions($this->validation->rules); + } + + $node = $this->createNode(); + $node->setAttributes($this->attributes); + + return $node; + } + + /** + * Sets the default value. + * + * @param mixed $value The default value + * + * @return $this + */ + public function defaultValue($value) + { + $this->default = true; + $this->defaultValue = $value; + + return $this; + } + + /** + * Sets the node as required. + * + * @return $this + */ + public function isRequired() + { + $this->required = true; + + return $this; + } + + /** + * Sets the node as deprecated. + * + * You can use %node% and %path% placeholders in your message to display, + * respectively, the node name and its complete path. + * + * @param string $message Deprecation message + * + * @return $this + */ + public function setDeprecated($message = 'The child node "%node%" at path "%path%" is deprecated.') + { + $this->deprecationMessage = $message; + + return $this; + } + + /** + * Sets the equivalent value used when the node contains null. + * + * @param mixed $value + * + * @return $this + */ + public function treatNullLike($value) + { + $this->nullEquivalent = $value; + + return $this; + } + + /** + * Sets the equivalent value used when the node contains true. + * + * @param mixed $value + * + * @return $this + */ + public function treatTrueLike($value) + { + $this->trueEquivalent = $value; + + return $this; + } + + /** + * Sets the equivalent value used when the node contains false. + * + * @param mixed $value + * + * @return $this + */ + public function treatFalseLike($value) + { + $this->falseEquivalent = $value; + + return $this; + } + + /** + * Sets null as the default value. + * + * @return $this + */ + public function defaultNull() + { + return $this->defaultValue(null); + } + + /** + * Sets true as the default value. + * + * @return $this + */ + public function defaultTrue() + { + return $this->defaultValue(true); + } + + /** + * Sets false as the default value. + * + * @return $this + */ + public function defaultFalse() + { + return $this->defaultValue(false); + } + + /** + * Sets an expression to run before the normalization. + * + * @return ExprBuilder + */ + public function beforeNormalization() + { + return $this->normalization()->before(); + } + + /** + * Denies the node value being empty. + * + * @return $this + */ + public function cannotBeEmpty() + { + $this->allowEmptyValue = false; + + return $this; + } + + /** + * Sets an expression to run for the validation. + * + * The expression receives the value of the node and must return it. It can + * modify it. + * An exception should be thrown when the node is not valid. + * + * @return ExprBuilder + */ + public function validate() + { + return $this->validation()->rule(); + } + + /** + * Sets whether the node can be overwritten. + * + * @param bool $deny Whether the overwriting is forbidden or not + * + * @return $this + */ + public function cannotBeOverwritten($deny = true) + { + $this->merge()->denyOverwrite($deny); + + return $this; + } + + /** + * Gets the builder for validation rules. + * + * @return ValidationBuilder + */ + protected function validation() + { + if (null === $this->validation) { + $this->validation = new ValidationBuilder($this); + } + + return $this->validation; + } + + /** + * Gets the builder for merging rules. + * + * @return MergeBuilder + */ + protected function merge() + { + if (null === $this->merge) { + $this->merge = new MergeBuilder($this); + } + + return $this->merge; + } + + /** + * Gets the builder for normalization rules. + * + * @return NormalizationBuilder + */ + protected function normalization() + { + if (null === $this->normalization) { + $this->normalization = new NormalizationBuilder($this); + } + + return $this->normalization; + } + + /** + * Instantiate and configure the node according to this definition. + * + * @return NodeInterface The node instance + * + * @throws InvalidDefinitionException When the definition is invalid + */ + abstract protected function createNode(); +} diff --git a/lib/symfony/config/Definition/Builder/NodeParentInterface.php b/lib/symfony/config/Definition/Builder/NodeParentInterface.php new file mode 100644 index 000000000..305e99316 --- /dev/null +++ b/lib/symfony/config/Definition/Builder/NodeParentInterface.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Builder; + +/** + * An interface that must be implemented by all node parents. + * + * @author Victor Berchet + */ +interface NodeParentInterface +{ +} diff --git a/lib/symfony/config/Definition/Builder/NormalizationBuilder.php b/lib/symfony/config/Definition/Builder/NormalizationBuilder.php new file mode 100644 index 000000000..d3cdca90d --- /dev/null +++ b/lib/symfony/config/Definition/Builder/NormalizationBuilder.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Builder; + +/** + * This class builds normalization conditions. + * + * @author Johannes M. Schmitt + */ +class NormalizationBuilder +{ + protected $node; + public $before = []; + public $remappings = []; + + public function __construct(NodeDefinition $node) + { + $this->node = $node; + } + + /** + * Registers a key to remap to its plural form. + * + * @param string $key The key to remap + * @param string $plural The plural of the key in case of irregular plural + * + * @return $this + */ + public function remap($key, $plural = null) + { + $this->remappings[] = [$key, null === $plural ? $key.'s' : $plural]; + + return $this; + } + + /** + * Registers a closure to run before the normalization or an expression builder to build it if null is provided. + * + * @return ExprBuilder|$this + */ + public function before(\Closure $closure = null) + { + if (null !== $closure) { + $this->before[] = $closure; + + return $this; + } + + return $this->before[] = new ExprBuilder($this->node); + } +} diff --git a/lib/symfony/config/Definition/Builder/NumericNodeDefinition.php b/lib/symfony/config/Definition/Builder/NumericNodeDefinition.php new file mode 100644 index 000000000..0d0207ca4 --- /dev/null +++ b/lib/symfony/config/Definition/Builder/NumericNodeDefinition.php @@ -0,0 +1,73 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Builder; + +use Symfony\Component\Config\Definition\Exception\InvalidDefinitionException; + +/** + * Abstract class that contains common code of integer and float node definitions. + * + * @author David Jeanmonod + */ +abstract class NumericNodeDefinition extends ScalarNodeDefinition +{ + protected $min; + protected $max; + + /** + * Ensures that the value is smaller than the given reference. + * + * @param mixed $max + * + * @return $this + * + * @throws \InvalidArgumentException when the constraint is inconsistent + */ + public function max($max) + { + if (isset($this->min) && $this->min > $max) { + throw new \InvalidArgumentException(sprintf('You cannot define a max(%s) as you already have a min(%s)', $max, $this->min)); + } + $this->max = $max; + + return $this; + } + + /** + * Ensures that the value is bigger than the given reference. + * + * @param mixed $min + * + * @return $this + * + * @throws \InvalidArgumentException when the constraint is inconsistent + */ + public function min($min) + { + if (isset($this->max) && $this->max < $min) { + throw new \InvalidArgumentException(sprintf('You cannot define a min(%s) as you already have a max(%s)', $min, $this->max)); + } + $this->min = $min; + + return $this; + } + + /** + * {@inheritdoc} + * + * @throws InvalidDefinitionException + */ + public function cannotBeEmpty() + { + throw new InvalidDefinitionException('->cannotBeEmpty() is not applicable to NumericNodeDefinition.'); + } +} diff --git a/lib/symfony/config/Definition/Builder/ParentNodeDefinitionInterface.php b/lib/symfony/config/Definition/Builder/ParentNodeDefinitionInterface.php new file mode 100644 index 000000000..1bf2ad4bf --- /dev/null +++ b/lib/symfony/config/Definition/Builder/ParentNodeDefinitionInterface.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Builder; + +/** + * An interface that must be implemented by nodes which can have children. + * + * @author Victor Berchet + */ +interface ParentNodeDefinitionInterface +{ + /** + * Returns a builder to add children nodes. + * + * @return NodeBuilder + */ + public function children(); + + /** + * Appends a node definition. + * + * Usage: + * + * $node = $parentNode + * ->children() + * ->scalarNode('foo')->end() + * ->scalarNode('baz')->end() + * ->append($this->getBarNodeDefinition()) + * ->end() + * ; + * + * @return $this + */ + public function append(NodeDefinition $node); + + /** + * Sets a custom children builder. + */ + public function setBuilder(NodeBuilder $builder); +} diff --git a/lib/symfony/config/Definition/Builder/ScalarNodeDefinition.php b/lib/symfony/config/Definition/Builder/ScalarNodeDefinition.php new file mode 100644 index 000000000..6170555cc --- /dev/null +++ b/lib/symfony/config/Definition/Builder/ScalarNodeDefinition.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Builder; + +use Symfony\Component\Config\Definition\ScalarNode; + +/** + * This class provides a fluent interface for defining a node. + * + * @author Johannes M. Schmitt + */ +class ScalarNodeDefinition extends VariableNodeDefinition +{ + /** + * Instantiate a Node. + * + * @return ScalarNode The node + */ + protected function instantiateNode() + { + return new ScalarNode($this->name, $this->parent); + } +} diff --git a/lib/symfony/config/Definition/Builder/TreeBuilder.php b/lib/symfony/config/Definition/Builder/TreeBuilder.php new file mode 100644 index 000000000..384477c50 --- /dev/null +++ b/lib/symfony/config/Definition/Builder/TreeBuilder.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Builder; + +use Symfony\Component\Config\Definition\NodeInterface; + +/** + * This is the entry class for building a config tree. + * + * @author Johannes M. Schmitt + */ +class TreeBuilder implements NodeParentInterface +{ + protected $tree; + protected $root; + + /** + * @deprecated since 3.4. To be removed in 4.0 + */ + protected $builder; + + /** + * Creates the root node. + * + * @param string $name The name of the root node + * @param string $type The type of the root node + * @param NodeBuilder $builder A custom node builder instance + * + * @return ArrayNodeDefinition|NodeDefinition The root node (as an ArrayNodeDefinition when the type is 'array') + * + * @throws \RuntimeException When the node type is not supported + */ + public function root($name, $type = 'array', NodeBuilder $builder = null) + { + $builder = $builder ?: new NodeBuilder(); + + return $this->root = $builder->node($name, $type)->setParent($this); + } + + /** + * Builds the tree. + * + * @return NodeInterface + * + * @throws \RuntimeException + */ + public function buildTree() + { + if (null === $this->root) { + throw new \RuntimeException('The configuration tree has no root node.'); + } + if (null !== $this->tree) { + return $this->tree; + } + + return $this->tree = $this->root->getNode(true); + } +} diff --git a/lib/symfony/config/Definition/Builder/ValidationBuilder.php b/lib/symfony/config/Definition/Builder/ValidationBuilder.php new file mode 100644 index 000000000..4efc726c0 --- /dev/null +++ b/lib/symfony/config/Definition/Builder/ValidationBuilder.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Builder; + +/** + * This class builds validation conditions. + * + * @author Christophe Coevoet + */ +class ValidationBuilder +{ + protected $node; + public $rules = []; + + public function __construct(NodeDefinition $node) + { + $this->node = $node; + } + + /** + * Registers a closure to run as normalization or an expression builder to build it if null is provided. + * + * @return ExprBuilder|$this + */ + public function rule(\Closure $closure = null) + { + if (null !== $closure) { + $this->rules[] = $closure; + + return $this; + } + + return $this->rules[] = new ExprBuilder($this->node); + } +} diff --git a/lib/symfony/config/Definition/Builder/VariableNodeDefinition.php b/lib/symfony/config/Definition/Builder/VariableNodeDefinition.php new file mode 100644 index 000000000..26565e177 --- /dev/null +++ b/lib/symfony/config/Definition/Builder/VariableNodeDefinition.php @@ -0,0 +1,65 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Builder; + +use Symfony\Component\Config\Definition\VariableNode; + +/** + * This class provides a fluent interface for defining a node. + * + * @author Johannes M. Schmitt + */ +class VariableNodeDefinition extends NodeDefinition +{ + /** + * Instantiate a Node. + * + * @return VariableNode The node + */ + protected function instantiateNode() + { + return new VariableNode($this->name, $this->parent); + } + + /** + * {@inheritdoc} + */ + protected function createNode() + { + $node = $this->instantiateNode(); + + if (null !== $this->normalization) { + $node->setNormalizationClosures($this->normalization->before); + } + + if (null !== $this->merge) { + $node->setAllowOverwrite($this->merge->allowOverwrite); + } + + if (true === $this->default) { + $node->setDefaultValue($this->defaultValue); + } + + $node->setAllowEmptyValue($this->allowEmptyValue); + $node->addEquivalentValue(null, $this->nullEquivalent); + $node->addEquivalentValue(true, $this->trueEquivalent); + $node->addEquivalentValue(false, $this->falseEquivalent); + $node->setRequired($this->required); + $node->setDeprecated($this->deprecationMessage); + + if (null !== $this->validation) { + $node->setFinalValidationClosures($this->validation->rules); + } + + return $node; + } +} diff --git a/lib/symfony/config/Definition/ConfigurationInterface.php b/lib/symfony/config/Definition/ConfigurationInterface.php new file mode 100644 index 000000000..d6456edb8 --- /dev/null +++ b/lib/symfony/config/Definition/ConfigurationInterface.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition; + +/** + * Configuration interface. + * + * @author Victor Berchet + */ +interface ConfigurationInterface +{ + /** + * Generates the configuration tree builder. + * + * @return \Symfony\Component\Config\Definition\Builder\TreeBuilder The tree builder + */ + public function getConfigTreeBuilder(); +} diff --git a/lib/symfony/config/Definition/Dumper/XmlReferenceDumper.php b/lib/symfony/config/Definition/Dumper/XmlReferenceDumper.php new file mode 100644 index 000000000..da05530ac --- /dev/null +++ b/lib/symfony/config/Definition/Dumper/XmlReferenceDumper.php @@ -0,0 +1,311 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Dumper; + +use Symfony\Component\Config\Definition\ArrayNode; +use Symfony\Component\Config\Definition\ConfigurationInterface; +use Symfony\Component\Config\Definition\EnumNode; +use Symfony\Component\Config\Definition\NodeInterface; +use Symfony\Component\Config\Definition\PrototypedArrayNode; + +/** + * Dumps a XML reference configuration for the given configuration/node instance. + * + * @author Wouter J + */ +class XmlReferenceDumper +{ + private $reference; + + public function dump(ConfigurationInterface $configuration, $namespace = null) + { + return $this->dumpNode($configuration->getConfigTreeBuilder()->buildTree(), $namespace); + } + + public function dumpNode(NodeInterface $node, $namespace = null) + { + $this->reference = ''; + $this->writeNode($node, 0, true, $namespace); + $ref = $this->reference; + $this->reference = null; + + return $ref; + } + + /** + * @param NodeInterface $node + * @param int $depth + * @param bool $root If the node is the root node + * @param string $namespace The namespace of the node + */ + private function writeNode(NodeInterface $node, $depth = 0, $root = false, $namespace = null) + { + $rootName = ($root ? 'config' : $node->getName()); + $rootNamespace = ($namespace ?: ($root ? 'http://example.org/schema/dic/'.$node->getName() : null)); + + // xml remapping + if ($node->getParent()) { + $remapping = array_filter($node->getParent()->getXmlRemappings(), function ($mapping) use ($rootName) { + return $rootName === $mapping[1]; + }); + + if (\count($remapping)) { + list($singular) = current($remapping); + $rootName = $singular; + } + } + $rootName = str_replace('_', '-', $rootName); + + $rootAttributes = []; + $rootAttributeComments = []; + $rootChildren = []; + $rootComments = []; + + if ($node instanceof ArrayNode) { + $children = $node->getChildren(); + + // comments about the root node + if ($rootInfo = $node->getInfo()) { + $rootComments[] = $rootInfo; + } + + if ($rootNamespace) { + $rootComments[] = 'Namespace: '.$rootNamespace; + } + + // render prototyped nodes + if ($node instanceof PrototypedArrayNode) { + $prototype = $node->getPrototype(); + + $info = 'prototype'; + if (null !== $prototype->getInfo()) { + $info .= ': '.$prototype->getInfo(); + } + array_unshift($rootComments, $info); + + if ($key = $node->getKeyAttribute()) { + $rootAttributes[$key] = str_replace('-', ' ', $rootName).' '.$key; + } + + if ($prototype instanceof PrototypedArrayNode) { + $prototype->setName($key); + $children = [$key => $prototype]; + } elseif ($prototype instanceof ArrayNode) { + $children = $prototype->getChildren(); + } else { + if ($prototype->hasDefaultValue()) { + $prototypeValue = $prototype->getDefaultValue(); + } else { + switch (\get_class($prototype)) { + case 'Symfony\Component\Config\Definition\ScalarNode': + $prototypeValue = 'scalar value'; + break; + + case 'Symfony\Component\Config\Definition\FloatNode': + case 'Symfony\Component\Config\Definition\IntegerNode': + $prototypeValue = 'numeric value'; + break; + + case 'Symfony\Component\Config\Definition\BooleanNode': + $prototypeValue = 'true|false'; + break; + + case 'Symfony\Component\Config\Definition\EnumNode': + $prototypeValue = implode('|', array_map('json_encode', $prototype->getValues())); + break; + + default: + $prototypeValue = 'value'; + } + } + } + } + + // get attributes and elements + foreach ($children as $child) { + if (!$child instanceof ArrayNode) { + // get attributes + + // metadata + $name = str_replace('_', '-', $child->getName()); + $value = '%%%%not_defined%%%%'; // use a string which isn't used in the normal world + + // comments + $comments = []; + if ($info = $child->getInfo()) { + $comments[] = $info; + } + + if ($example = $child->getExample()) { + $comments[] = 'Example: '.$example; + } + + if ($child->isRequired()) { + $comments[] = 'Required'; + } + + if ($child->isDeprecated()) { + $comments[] = sprintf('Deprecated (%s)', $child->getDeprecationMessage($child->getName(), $node->getPath())); + } + + if ($child instanceof EnumNode) { + $comments[] = 'One of '.implode('; ', array_map('json_encode', $child->getValues())); + } + + if (\count($comments)) { + $rootAttributeComments[$name] = implode(";\n", $comments); + } + + // default values + if ($child->hasDefaultValue()) { + $value = $child->getDefaultValue(); + } + + // append attribute + $rootAttributes[$name] = $value; + } else { + // get elements + $rootChildren[] = $child; + } + } + } + + // render comments + + // root node comment + if (\count($rootComments)) { + foreach ($rootComments as $comment) { + $this->writeLine('', $depth); + } + } + + // attribute comments + if (\count($rootAttributeComments)) { + foreach ($rootAttributeComments as $attrName => $comment) { + $commentDepth = $depth + 4 + \strlen($attrName) + 2; + $commentLines = explode("\n", $comment); + $multiline = (\count($commentLines) > 1); + $comment = implode(PHP_EOL.str_repeat(' ', $commentDepth), $commentLines); + + if ($multiline) { + $this->writeLine('', $depth); + } else { + $this->writeLine('', $depth); + } + } + } + + // render start tag + attributes + $rootIsVariablePrototype = isset($prototypeValue); + $rootIsEmptyTag = (0 === \count($rootChildren) && !$rootIsVariablePrototype); + $rootOpenTag = '<'.$rootName; + if (1 >= ($attributesCount = \count($rootAttributes))) { + if (1 === $attributesCount) { + $rootOpenTag .= sprintf(' %s="%s"', current(array_keys($rootAttributes)), $this->writeValue(current($rootAttributes))); + } + + $rootOpenTag .= $rootIsEmptyTag ? ' />' : '>'; + + if ($rootIsVariablePrototype) { + $rootOpenTag .= $prototypeValue.''; + } + + $this->writeLine($rootOpenTag, $depth); + } else { + $this->writeLine($rootOpenTag, $depth); + + $i = 1; + + foreach ($rootAttributes as $attrName => $attrValue) { + $attr = sprintf('%s="%s"', $attrName, $this->writeValue($attrValue)); + + $this->writeLine($attr, $depth + 4); + + if ($attributesCount === $i++) { + $this->writeLine($rootIsEmptyTag ? '/>' : '>', $depth); + + if ($rootIsVariablePrototype) { + $rootOpenTag .= $prototypeValue.''; + } + } + } + } + + // render children tags + foreach ($rootChildren as $child) { + $this->writeLine(''); + $this->writeNode($child, $depth + 4); + } + + // render end tag + if (!$rootIsEmptyTag && !$rootIsVariablePrototype) { + $this->writeLine(''); + + $rootEndTag = ''; + $this->writeLine($rootEndTag, $depth); + } + } + + /** + * Outputs a single config reference line. + * + * @param string $text + * @param int $indent + */ + private function writeLine($text, $indent = 0) + { + $indent = \strlen($text) + $indent; + $format = '%'.$indent.'s'; + + $this->reference .= sprintf($format, $text).PHP_EOL; + } + + /** + * Renders the string conversion of the value. + * + * @param mixed $value + * + * @return string + */ + private function writeValue($value) + { + if ('%%%%not_defined%%%%' === $value) { + return ''; + } + + if (\is_string($value) || is_numeric($value)) { + return $value; + } + + if (false === $value) { + return 'false'; + } + + if (true === $value) { + return 'true'; + } + + if (null === $value) { + return 'null'; + } + + if (empty($value)) { + return ''; + } + + if (\is_array($value)) { + return implode(',', $value); + } + } +} diff --git a/lib/symfony/config/Definition/Dumper/YamlReferenceDumper.php b/lib/symfony/config/Definition/Dumper/YamlReferenceDumper.php new file mode 100644 index 000000000..7aa97909f --- /dev/null +++ b/lib/symfony/config/Definition/Dumper/YamlReferenceDumper.php @@ -0,0 +1,256 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Dumper; + +use Symfony\Component\Config\Definition\ArrayNode; +use Symfony\Component\Config\Definition\ConfigurationInterface; +use Symfony\Component\Config\Definition\EnumNode; +use Symfony\Component\Config\Definition\NodeInterface; +use Symfony\Component\Config\Definition\PrototypedArrayNode; +use Symfony\Component\Config\Definition\ScalarNode; +use Symfony\Component\Yaml\Inline; + +/** + * Dumps a Yaml reference configuration for the given configuration/node instance. + * + * @author Kevin Bond + */ +class YamlReferenceDumper +{ + private $reference; + + public function dump(ConfigurationInterface $configuration) + { + return $this->dumpNode($configuration->getConfigTreeBuilder()->buildTree()); + } + + public function dumpAtPath(ConfigurationInterface $configuration, $path) + { + $rootNode = $node = $configuration->getConfigTreeBuilder()->buildTree(); + + foreach (explode('.', $path) as $step) { + if (!$node instanceof ArrayNode) { + throw new \UnexpectedValueException(sprintf('Unable to find node at path "%s.%s"', $rootNode->getName(), $path)); + } + + /** @var NodeInterface[] $children */ + $children = $node instanceof PrototypedArrayNode ? $this->getPrototypeChildren($node) : $node->getChildren(); + + foreach ($children as $child) { + if ($child->getName() === $step) { + $node = $child; + + continue 2; + } + } + + throw new \UnexpectedValueException(sprintf('Unable to find node at path "%s.%s"', $rootNode->getName(), $path)); + } + + return $this->dumpNode($node); + } + + public function dumpNode(NodeInterface $node) + { + $this->reference = ''; + $this->writeNode($node); + $ref = $this->reference; + $this->reference = null; + + return $ref; + } + + /** + * @param NodeInterface $node + * @param NodeInterface|null $parentNode + * @param int $depth + * @param bool $prototypedArray + */ + private function writeNode(NodeInterface $node, NodeInterface $parentNode = null, $depth = 0, $prototypedArray = false) + { + $comments = []; + $default = ''; + $defaultArray = null; + $children = null; + $example = $node->getExample(); + + // defaults + if ($node instanceof ArrayNode) { + $children = $node->getChildren(); + + if ($node instanceof PrototypedArrayNode) { + $children = $this->getPrototypeChildren($node); + } + + if (!$children) { + if ($node->hasDefaultValue() && \count($defaultArray = $node->getDefaultValue())) { + $default = ''; + } elseif (!\is_array($example)) { + $default = '[]'; + } + } + } elseif ($node instanceof EnumNode) { + $comments[] = 'One of '.implode('; ', array_map('json_encode', $node->getValues())); + $default = $node->hasDefaultValue() ? Inline::dump($node->getDefaultValue()) : '~'; + } else { + $default = '~'; + + if ($node->hasDefaultValue()) { + $default = $node->getDefaultValue(); + + if (\is_array($default)) { + if (\count($defaultArray = $node->getDefaultValue())) { + $default = ''; + } elseif (!\is_array($example)) { + $default = '[]'; + } + } else { + $default = Inline::dump($default); + } + } + } + + // required? + if ($node->isRequired()) { + $comments[] = 'Required'; + } + + // deprecated? + if ($node->isDeprecated()) { + $comments[] = sprintf('Deprecated (%s)', $node->getDeprecationMessage($node->getName(), $parentNode ? $parentNode->getPath() : $node->getPath())); + } + + // example + if ($example && !\is_array($example)) { + $comments[] = 'Example: '.$example; + } + + $default = '' != (string) $default ? ' '.$default : ''; + $comments = \count($comments) ? '# '.implode(', ', $comments) : ''; + + $key = $prototypedArray ? '-' : $node->getName().':'; + $text = rtrim(sprintf('%-21s%s %s', $key, $default, $comments), ' '); + + if ($info = $node->getInfo()) { + $this->writeLine(''); + // indenting multi-line info + $info = str_replace("\n", sprintf("\n%".($depth * 4).'s# ', ' '), $info); + $this->writeLine('# '.$info, $depth * 4); + } + + $this->writeLine($text, $depth * 4); + + // output defaults + if ($defaultArray) { + $this->writeLine(''); + + $message = \count($defaultArray) > 1 ? 'Defaults' : 'Default'; + + $this->writeLine('# '.$message.':', $depth * 4 + 4); + + $this->writeArray($defaultArray, $depth + 1); + } + + if (\is_array($example)) { + $this->writeLine(''); + + $message = \count($example) > 1 ? 'Examples' : 'Example'; + + $this->writeLine('# '.$message.':', $depth * 4 + 4); + + $this->writeArray($example, $depth + 1); + } + + if ($children) { + foreach ($children as $childNode) { + $this->writeNode($childNode, $node, $depth + 1, $node instanceof PrototypedArrayNode && !$node->getKeyAttribute()); + } + } + } + + /** + * Outputs a single config reference line. + * + * @param string $text + * @param int $indent + */ + private function writeLine($text, $indent = 0) + { + $indent = \strlen($text) + $indent; + $format = '%'.$indent.'s'; + + $this->reference .= sprintf($format, $text)."\n"; + } + + private function writeArray(array $array, $depth) + { + $isIndexed = array_values($array) === $array; + + foreach ($array as $key => $value) { + if (\is_array($value)) { + $val = ''; + } else { + $val = $value; + } + + if ($isIndexed) { + $this->writeLine('- '.$val, $depth * 4); + } else { + $this->writeLine(sprintf('%-20s %s', $key.':', $val), $depth * 4); + } + + if (\is_array($value)) { + $this->writeArray($value, $depth + 1); + } + } + } + + /** + * @param PrototypedArrayNode $node + * + * @return array + */ + private function getPrototypeChildren(PrototypedArrayNode $node) + { + $prototype = $node->getPrototype(); + $key = $node->getKeyAttribute(); + + // Do not expand prototype if it isn't an array node nor uses attribute as key + if (!$key && !$prototype instanceof ArrayNode) { + return $node->getChildren(); + } + + if ($prototype instanceof ArrayNode) { + $keyNode = new ArrayNode($key, $node); + $children = $prototype->getChildren(); + + if ($prototype instanceof PrototypedArrayNode && $prototype->getKeyAttribute()) { + $children = $this->getPrototypeChildren($prototype); + } + + // add children + foreach ($children as $childNode) { + $keyNode->addChild($childNode); + } + } else { + $keyNode = new ScalarNode($key, $node); + } + + $info = 'Prototype'; + if (null !== $prototype->getInfo()) { + $info .= ': '.$prototype->getInfo(); + } + $keyNode->setInfo($info); + + return [$key => $keyNode]; + } +} diff --git a/lib/symfony/config/Definition/EnumNode.php b/lib/symfony/config/Definition/EnumNode.php new file mode 100644 index 000000000..15c8db3e6 --- /dev/null +++ b/lib/symfony/config/Definition/EnumNode.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition; + +use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; + +/** + * Node which only allows a finite set of values. + * + * @author Johannes M. Schmitt + */ +class EnumNode extends ScalarNode +{ + private $values; + + public function __construct($name, NodeInterface $parent = null, array $values = []) + { + $values = array_unique($values); + if (empty($values)) { + throw new \InvalidArgumentException('$values must contain at least one element.'); + } + + parent::__construct($name, $parent); + $this->values = $values; + } + + public function getValues() + { + return $this->values; + } + + protected function finalizeValue($value) + { + $value = parent::finalizeValue($value); + + if (!\in_array($value, $this->values, true)) { + $ex = new InvalidConfigurationException(sprintf('The value %s is not allowed for path "%s". Permissible values: %s', json_encode($value), $this->getPath(), implode(', ', array_map('json_encode', $this->values)))); + $ex->setPath($this->getPath()); + + throw $ex; + } + + return $value; + } +} diff --git a/lib/symfony/config/Definition/Exception/DuplicateKeyException.php b/lib/symfony/config/Definition/Exception/DuplicateKeyException.php new file mode 100644 index 000000000..48dd9325e --- /dev/null +++ b/lib/symfony/config/Definition/Exception/DuplicateKeyException.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Exception; + +/** + * This exception is thrown whenever the key of an array is not unique. This can + * only be the case if the configuration is coming from an XML file. + * + * @author Johannes M. Schmitt + */ +class DuplicateKeyException extends InvalidConfigurationException +{ +} diff --git a/lib/symfony/config/Definition/Exception/Exception.php b/lib/symfony/config/Definition/Exception/Exception.php new file mode 100644 index 000000000..8933a4923 --- /dev/null +++ b/lib/symfony/config/Definition/Exception/Exception.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Exception; + +/** + * Base exception for all configuration exceptions. + * + * @author Johannes M. Schmitt + */ +class Exception extends \RuntimeException +{ +} diff --git a/lib/symfony/config/Definition/Exception/ForbiddenOverwriteException.php b/lib/symfony/config/Definition/Exception/ForbiddenOverwriteException.php new file mode 100644 index 000000000..726c07fb8 --- /dev/null +++ b/lib/symfony/config/Definition/Exception/ForbiddenOverwriteException.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Exception; + +/** + * This exception is thrown when a configuration path is overwritten from a + * subsequent configuration file, but the entry node specifically forbids this. + * + * @author Johannes M. Schmitt + */ +class ForbiddenOverwriteException extends InvalidConfigurationException +{ +} diff --git a/lib/symfony/config/Definition/Exception/InvalidConfigurationException.php b/lib/symfony/config/Definition/Exception/InvalidConfigurationException.php new file mode 100644 index 000000000..3dbc57b15 --- /dev/null +++ b/lib/symfony/config/Definition/Exception/InvalidConfigurationException.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Exception; + +/** + * A very general exception which can be thrown whenever non of the more specific + * exceptions is suitable. + * + * @author Johannes M. Schmitt + */ +class InvalidConfigurationException extends Exception +{ + private $path; + private $containsHints = false; + + public function setPath($path) + { + $this->path = $path; + } + + public function getPath() + { + return $this->path; + } + + /** + * Adds extra information that is suffixed to the original exception message. + * + * @param string $hint + */ + public function addHint($hint) + { + if (!$this->containsHints) { + $this->message .= "\nHint: ".$hint; + $this->containsHints = true; + } else { + $this->message .= ', '.$hint; + } + } +} diff --git a/lib/symfony/config/Definition/Exception/InvalidDefinitionException.php b/lib/symfony/config/Definition/Exception/InvalidDefinitionException.php new file mode 100644 index 000000000..98310dae0 --- /dev/null +++ b/lib/symfony/config/Definition/Exception/InvalidDefinitionException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Exception; + +/** + * Thrown when an error is detected in a node Definition. + * + * @author Victor Berchet + */ +class InvalidDefinitionException extends Exception +{ +} diff --git a/lib/symfony/config/Definition/Exception/InvalidTypeException.php b/lib/symfony/config/Definition/Exception/InvalidTypeException.php new file mode 100644 index 000000000..d7ca8c9d0 --- /dev/null +++ b/lib/symfony/config/Definition/Exception/InvalidTypeException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Exception; + +/** + * This exception is thrown if an invalid type is encountered. + * + * @author Johannes M. Schmitt + */ +class InvalidTypeException extends InvalidConfigurationException +{ +} diff --git a/lib/symfony/config/Definition/Exception/UnsetKeyException.php b/lib/symfony/config/Definition/Exception/UnsetKeyException.php new file mode 100644 index 000000000..863181a33 --- /dev/null +++ b/lib/symfony/config/Definition/Exception/UnsetKeyException.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Exception; + +/** + * This exception is usually not encountered by the end-user, but only used + * internally to signal the parent scope to unset a key. + * + * @author Johannes M. Schmitt + */ +class UnsetKeyException extends Exception +{ +} diff --git a/lib/symfony/config/Definition/FloatNode.php b/lib/symfony/config/Definition/FloatNode.php new file mode 100644 index 000000000..9eb878995 --- /dev/null +++ b/lib/symfony/config/Definition/FloatNode.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition; + +use Symfony\Component\Config\Definition\Exception\InvalidTypeException; + +/** + * This node represents a float value in the config tree. + * + * @author Jeanmonod David + */ +class FloatNode extends NumericNode +{ + /** + * {@inheritdoc} + */ + protected function validateType($value) + { + // Integers are also accepted, we just cast them + if (\is_int($value)) { + $value = (float) $value; + } + + if (!\is_float($value)) { + $ex = new InvalidTypeException(sprintf('Invalid type for path "%s". Expected float, but got %s.', $this->getPath(), \gettype($value))); + if ($hint = $this->getInfo()) { + $ex->addHint($hint); + } + $ex->setPath($this->getPath()); + + throw $ex; + } + } +} diff --git a/lib/symfony/config/Definition/IntegerNode.php b/lib/symfony/config/Definition/IntegerNode.php new file mode 100644 index 000000000..8ec068a84 --- /dev/null +++ b/lib/symfony/config/Definition/IntegerNode.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition; + +use Symfony\Component\Config\Definition\Exception\InvalidTypeException; + +/** + * This node represents an integer value in the config tree. + * + * @author Jeanmonod David + */ +class IntegerNode extends NumericNode +{ + /** + * {@inheritdoc} + */ + protected function validateType($value) + { + if (!\is_int($value)) { + $ex = new InvalidTypeException(sprintf('Invalid type for path "%s". Expected int, but got %s.', $this->getPath(), \gettype($value))); + if ($hint = $this->getInfo()) { + $ex->addHint($hint); + } + $ex->setPath($this->getPath()); + + throw $ex; + } + } +} diff --git a/lib/symfony/config/Definition/NodeInterface.php b/lib/symfony/config/Definition/NodeInterface.php new file mode 100644 index 000000000..45f1f681c --- /dev/null +++ b/lib/symfony/config/Definition/NodeInterface.php @@ -0,0 +1,100 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition; + +use Symfony\Component\Config\Definition\Exception\ForbiddenOverwriteException; +use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; +use Symfony\Component\Config\Definition\Exception\InvalidTypeException; + +/** + * Common Interface among all nodes. + * + * In most cases, it is better to inherit from BaseNode instead of implementing + * this interface yourself. + * + * @author Johannes M. Schmitt + */ +interface NodeInterface +{ + /** + * Returns the name of the node. + * + * @return string The name of the node + */ + public function getName(); + + /** + * Returns the path of the node. + * + * @return string The node path + */ + public function getPath(); + + /** + * Returns true when the node is required. + * + * @return bool If the node is required + */ + public function isRequired(); + + /** + * Returns true when the node has a default value. + * + * @return bool If the node has a default value + */ + public function hasDefaultValue(); + + /** + * Returns the default value of the node. + * + * @return mixed The default value + * + * @throws \RuntimeException if the node has no default value + */ + public function getDefaultValue(); + + /** + * Normalizes a value. + * + * @param mixed $value The value to normalize + * + * @return mixed The normalized value + * + * @throws InvalidTypeException if the value type is invalid + */ + public function normalize($value); + + /** + * Merges two values together. + * + * @param mixed $leftSide + * @param mixed $rightSide + * + * @return mixed The merged value + * + * @throws ForbiddenOverwriteException if the configuration path cannot be overwritten + * @throws InvalidTypeException if the value type is invalid + */ + public function merge($leftSide, $rightSide); + + /** + * Finalizes a value. + * + * @param mixed $value The value to finalize + * + * @return mixed The finalized value + * + * @throws InvalidTypeException if the value type is invalid + * @throws InvalidConfigurationException if the value is invalid configuration + */ + public function finalize($value); +} diff --git a/lib/symfony/config/Definition/NumericNode.php b/lib/symfony/config/Definition/NumericNode.php new file mode 100644 index 000000000..439935e45 --- /dev/null +++ b/lib/symfony/config/Definition/NumericNode.php @@ -0,0 +1,64 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition; + +use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; + +/** + * This node represents a numeric value in the config tree. + * + * @author David Jeanmonod + */ +class NumericNode extends ScalarNode +{ + protected $min; + protected $max; + + public function __construct($name, NodeInterface $parent = null, $min = null, $max = null) + { + parent::__construct($name, $parent); + $this->min = $min; + $this->max = $max; + } + + /** + * {@inheritdoc} + */ + protected function finalizeValue($value) + { + $value = parent::finalizeValue($value); + + $errorMsg = null; + if (isset($this->min) && $value < $this->min) { + $errorMsg = sprintf('The value %s is too small for path "%s". Should be greater than or equal to %s', $value, $this->getPath(), $this->min); + } + if (isset($this->max) && $value > $this->max) { + $errorMsg = sprintf('The value %s is too big for path "%s". Should be less than or equal to %s', $value, $this->getPath(), $this->max); + } + if (isset($errorMsg)) { + $ex = new InvalidConfigurationException($errorMsg); + $ex->setPath($this->getPath()); + throw $ex; + } + + return $value; + } + + /** + * {@inheritdoc} + */ + protected function isValueEmpty($value) + { + // a numeric value cannot be empty + return false; + } +} diff --git a/lib/symfony/config/Definition/Processor.php b/lib/symfony/config/Definition/Processor.php new file mode 100644 index 000000000..0a935eeb7 --- /dev/null +++ b/lib/symfony/config/Definition/Processor.php @@ -0,0 +1,97 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition; + +/** + * This class is the entry point for config normalization/merging/finalization. + * + * @author Johannes M. Schmitt + */ +class Processor +{ + /** + * Processes an array of configurations. + * + * @param NodeInterface $configTree The node tree describing the configuration + * @param array $configs An array of configuration items to process + * + * @return array The processed configuration + */ + public function process(NodeInterface $configTree, array $configs) + { + $currentConfig = []; + foreach ($configs as $config) { + $config = $configTree->normalize($config); + $currentConfig = $configTree->merge($currentConfig, $config); + } + + return $configTree->finalize($currentConfig); + } + + /** + * Processes an array of configurations. + * + * @param ConfigurationInterface $configuration The configuration class + * @param array $configs An array of configuration items to process + * + * @return array The processed configuration + */ + public function processConfiguration(ConfigurationInterface $configuration, array $configs) + { + return $this->process($configuration->getConfigTreeBuilder()->buildTree(), $configs); + } + + /** + * Normalizes a configuration entry. + * + * This method returns a normalize configuration array for a given key + * to remove the differences due to the original format (YAML and XML mainly). + * + * Here is an example. + * + * The configuration in XML: + * + * twig.extension.foo + * twig.extension.bar + * + * And the same configuration in YAML: + * + * extensions: ['twig.extension.foo', 'twig.extension.bar'] + * + * @param array $config A config array + * @param string $key The key to normalize + * @param string $plural The plural form of the key if it is irregular + * + * @return array + */ + public static function normalizeConfig($config, $key, $plural = null) + { + if (null === $plural) { + $plural = $key.'s'; + } + + if (isset($config[$plural])) { + return $config[$plural]; + } + + if (isset($config[$key])) { + if (\is_string($config[$key]) || !\is_int(key($config[$key]))) { + // only one + return [$config[$key]]; + } + + return $config[$key]; + } + + return []; + } +} diff --git a/lib/symfony/config/Definition/PrototypeNodeInterface.php b/lib/symfony/config/Definition/PrototypeNodeInterface.php new file mode 100644 index 000000000..8bbb84d4d --- /dev/null +++ b/lib/symfony/config/Definition/PrototypeNodeInterface.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition; + +/** + * This interface must be implemented by nodes which can be used as prototypes. + * + * @author Johannes M. Schmitt + */ +interface PrototypeNodeInterface extends NodeInterface +{ + /** + * Sets the name of the node. + * + * @param string $name The name of the node + */ + public function setName($name); +} diff --git a/lib/symfony/config/Definition/PrototypedArrayNode.php b/lib/symfony/config/Definition/PrototypedArrayNode.php new file mode 100644 index 000000000..9a14449c4 --- /dev/null +++ b/lib/symfony/config/Definition/PrototypedArrayNode.php @@ -0,0 +1,377 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition; + +use Symfony\Component\Config\Definition\Exception\DuplicateKeyException; +use Symfony\Component\Config\Definition\Exception\Exception; +use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; +use Symfony\Component\Config\Definition\Exception\UnsetKeyException; + +/** + * Represents a prototyped Array node in the config tree. + * + * @author Johannes M. Schmitt + */ +class PrototypedArrayNode extends ArrayNode +{ + protected $prototype; + protected $keyAttribute; + protected $removeKeyAttribute = false; + protected $minNumberOfElements = 0; + protected $defaultValue = []; + protected $defaultChildren; + /** + * @var NodeInterface[] An array of the prototypes of the simplified value children + */ + private $valuePrototypes = []; + + /** + * Sets the minimum number of elements that a prototype based node must + * contain. By default this is zero, meaning no elements. + * + * @param int $number + */ + public function setMinNumberOfElements($number) + { + $this->minNumberOfElements = $number; + } + + /** + * Sets the attribute which value is to be used as key. + * + * This is useful when you have an indexed array that should be an + * associative array. You can select an item from within the array + * to be the key of the particular item. For example, if "id" is the + * "key", then: + * + * [ + * ['id' => 'my_name', 'foo' => 'bar'], + * ]; + * + * becomes + * + * [ + * 'my_name' => ['foo' => 'bar'], + * ]; + * + * If you'd like "'id' => 'my_name'" to still be present in the resulting + * array, then you can set the second argument of this method to false. + * + * @param string $attribute The name of the attribute which value is to be used as a key + * @param bool $remove Whether or not to remove the key + */ + public function setKeyAttribute($attribute, $remove = true) + { + $this->keyAttribute = $attribute; + $this->removeKeyAttribute = $remove; + } + + /** + * Retrieves the name of the attribute which value should be used as key. + * + * @return string The name of the attribute + */ + public function getKeyAttribute() + { + return $this->keyAttribute; + } + + /** + * Sets the default value of this node. + * + * @param string $value + * + * @throws \InvalidArgumentException if the default value is not an array + */ + public function setDefaultValue($value) + { + if (!\is_array($value)) { + throw new \InvalidArgumentException($this->getPath().': the default value of an array node has to be an array.'); + } + + $this->defaultValue = $value; + } + + /** + * {@inheritdoc} + */ + public function hasDefaultValue() + { + return true; + } + + /** + * Adds default children when none are set. + * + * @param int|string|array|null $children The number of children|The child name|The children names to be added + */ + public function setAddChildrenIfNoneSet($children = ['defaults']) + { + if (null === $children) { + $this->defaultChildren = ['defaults']; + } else { + $this->defaultChildren = \is_int($children) && $children > 0 ? range(1, $children) : (array) $children; + } + } + + /** + * {@inheritdoc} + * + * The default value could be either explicited or derived from the prototype + * default value. + */ + public function getDefaultValue() + { + if (null !== $this->defaultChildren) { + $default = $this->prototype->hasDefaultValue() ? $this->prototype->getDefaultValue() : []; + $defaults = []; + foreach (array_values($this->defaultChildren) as $i => $name) { + $defaults[null === $this->keyAttribute ? $i : $name] = $default; + } + + return $defaults; + } + + return $this->defaultValue; + } + + /** + * Sets the node prototype. + */ + public function setPrototype(PrototypeNodeInterface $node) + { + $this->prototype = $node; + } + + /** + * Retrieves the prototype. + * + * @return PrototypeNodeInterface The prototype + */ + public function getPrototype() + { + return $this->prototype; + } + + /** + * Disable adding concrete children for prototyped nodes. + * + * @throws Exception + */ + public function addChild(NodeInterface $node) + { + throw new Exception('A prototyped array node can not have concrete children.'); + } + + /** + * Finalizes the value of this node. + * + * @param mixed $value + * + * @return mixed The finalized value + * + * @throws UnsetKeyException + * @throws InvalidConfigurationException if the node doesn't have enough children + */ + protected function finalizeValue($value) + { + if (false === $value) { + throw new UnsetKeyException(sprintf('Unsetting key for path "%s", value: %s', $this->getPath(), json_encode($value))); + } + + foreach ($value as $k => $v) { + $prototype = $this->getPrototypeForChild($k); + try { + $value[$k] = $prototype->finalize($v); + } catch (UnsetKeyException $e) { + unset($value[$k]); + } + } + + if (\count($value) < $this->minNumberOfElements) { + $ex = new InvalidConfigurationException(sprintf('The path "%s" should have at least %d element(s) defined.', $this->getPath(), $this->minNumberOfElements)); + $ex->setPath($this->getPath()); + + throw $ex; + } + + return $value; + } + + /** + * Normalizes the value. + * + * @param mixed $value The value to normalize + * + * @return mixed The normalized value + * + * @throws InvalidConfigurationException + * @throws DuplicateKeyException + */ + protected function normalizeValue($value) + { + if (false === $value) { + return $value; + } + + $value = $this->remapXml($value); + + $isAssoc = array_keys($value) !== range(0, \count($value) - 1); + $normalized = []; + foreach ($value as $k => $v) { + if (null !== $this->keyAttribute && \is_array($v)) { + if (!isset($v[$this->keyAttribute]) && \is_int($k) && !$isAssoc) { + $ex = new InvalidConfigurationException(sprintf('The attribute "%s" must be set for path "%s".', $this->keyAttribute, $this->getPath())); + $ex->setPath($this->getPath()); + + throw $ex; + } elseif (isset($v[$this->keyAttribute])) { + $k = $v[$this->keyAttribute]; + + // remove the key attribute when required + if ($this->removeKeyAttribute) { + unset($v[$this->keyAttribute]); + } + + // if only "value" is left + if (array_keys($v) === ['value']) { + $v = $v['value']; + if ($this->prototype instanceof ArrayNode && ($children = $this->prototype->getChildren()) && \array_key_exists('value', $children)) { + $valuePrototype = current($this->valuePrototypes) ?: clone $children['value']; + $valuePrototype->parent = $this; + $originalClosures = $this->prototype->normalizationClosures; + if (\is_array($originalClosures)) { + $valuePrototypeClosures = $valuePrototype->normalizationClosures; + $valuePrototype->normalizationClosures = \is_array($valuePrototypeClosures) ? array_merge($originalClosures, $valuePrototypeClosures) : $originalClosures; + } + $this->valuePrototypes[$k] = $valuePrototype; + } + } + } + + if (\array_key_exists($k, $normalized)) { + $ex = new DuplicateKeyException(sprintf('Duplicate key "%s" for path "%s".', $k, $this->getPath())); + $ex->setPath($this->getPath()); + + throw $ex; + } + } + + $prototype = $this->getPrototypeForChild($k); + if (null !== $this->keyAttribute || $isAssoc) { + $normalized[$k] = $prototype->normalize($v); + } else { + $normalized[] = $prototype->normalize($v); + } + } + + return $normalized; + } + + /** + * Merges values together. + * + * @param mixed $leftSide The left side to merge + * @param mixed $rightSide The right side to merge + * + * @return mixed The merged values + * + * @throws InvalidConfigurationException + * @throws \RuntimeException + */ + protected function mergeValues($leftSide, $rightSide) + { + if (false === $rightSide) { + // if this is still false after the last config has been merged the + // finalization pass will take care of removing this key entirely + return false; + } + + if (false === $leftSide || !$this->performDeepMerging) { + return $rightSide; + } + + foreach ($rightSide as $k => $v) { + // prototype, and key is irrelevant, append the element + if (null === $this->keyAttribute) { + $leftSide[] = $v; + continue; + } + + // no conflict + if (!\array_key_exists($k, $leftSide)) { + if (!$this->allowNewKeys) { + $ex = new InvalidConfigurationException(sprintf('You are not allowed to define new elements for path "%s". Please define all elements for this path in one config file.', $this->getPath())); + $ex->setPath($this->getPath()); + + throw $ex; + } + + $leftSide[$k] = $v; + continue; + } + + $prototype = $this->getPrototypeForChild($k); + $leftSide[$k] = $prototype->merge($leftSide[$k], $v); + } + + return $leftSide; + } + + /** + * Returns a prototype for the child node that is associated to $key in the value array. + * For general child nodes, this will be $this->prototype. + * But if $this->removeKeyAttribute is true and there are only two keys in the child node: + * one is same as this->keyAttribute and the other is 'value', then the prototype will be different. + * + * For example, assume $this->keyAttribute is 'name' and the value array is as follows: + * + * [ + * [ + * 'name' => 'name001', + * 'value' => 'value001' + * ] + * ] + * + * Now, the key is 0 and the child node is: + * + * [ + * 'name' => 'name001', + * 'value' => 'value001' + * ] + * + * When normalizing the value array, the 'name' element will removed from the child node + * and its value becomes the new key of the child node: + * + * [ + * 'name001' => ['value' => 'value001'] + * ] + * + * Now only 'value' element is left in the child node which can be further simplified into a string: + * + * ['name001' => 'value001'] + * + * Now, the key becomes 'name001' and the child node becomes 'value001' and + * the prototype of child node 'name001' should be a ScalarNode instead of an ArrayNode instance. + * + * @param string $key The key of the child node + * + * @return mixed The prototype instance + */ + private function getPrototypeForChild($key) + { + $prototype = isset($this->valuePrototypes[$key]) ? $this->valuePrototypes[$key] : $this->prototype; + $prototype->setName($key); + + return $prototype; + } +} diff --git a/lib/symfony/config/Definition/ScalarNode.php b/lib/symfony/config/Definition/ScalarNode.php new file mode 100644 index 000000000..53c1ed29c --- /dev/null +++ b/lib/symfony/config/Definition/ScalarNode.php @@ -0,0 +1,53 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition; + +use Symfony\Component\Config\Definition\Exception\InvalidTypeException; + +/** + * This node represents a scalar value in the config tree. + * + * The following values are considered scalars: + * * booleans + * * strings + * * null + * * integers + * * floats + * + * @author Johannes M. Schmitt + */ +class ScalarNode extends VariableNode +{ + /** + * {@inheritdoc} + */ + protected function validateType($value) + { + if (!is_scalar($value) && null !== $value) { + $ex = new InvalidTypeException(sprintf('Invalid type for path "%s". Expected scalar, but got %s.', $this->getPath(), \gettype($value))); + if ($hint = $this->getInfo()) { + $ex->addHint($hint); + } + $ex->setPath($this->getPath()); + + throw $ex; + } + } + + /** + * {@inheritdoc} + */ + protected function isValueEmpty($value) + { + return null === $value || '' === $value; + } +} diff --git a/lib/symfony/config/Definition/VariableNode.php b/lib/symfony/config/Definition/VariableNode.php new file mode 100644 index 000000000..1a3442d96 --- /dev/null +++ b/lib/symfony/config/Definition/VariableNode.php @@ -0,0 +1,128 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition; + +use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; + +/** + * This node represents a value of variable type in the config tree. + * + * This node is intended for values of arbitrary type. + * Any PHP type is accepted as a value. + * + * @author Jeremy Mikola + */ +class VariableNode extends BaseNode implements PrototypeNodeInterface +{ + protected $defaultValueSet = false; + protected $defaultValue; + protected $allowEmptyValue = true; + + public function setDefaultValue($value) + { + $this->defaultValueSet = true; + $this->defaultValue = $value; + } + + /** + * {@inheritdoc} + */ + public function hasDefaultValue() + { + return $this->defaultValueSet; + } + + /** + * {@inheritdoc} + */ + public function getDefaultValue() + { + $v = $this->defaultValue; + + return $v instanceof \Closure ? $v() : $v; + } + + /** + * Sets if this node is allowed to have an empty value. + * + * @param bool $boolean True if this entity will accept empty values + */ + public function setAllowEmptyValue($boolean) + { + $this->allowEmptyValue = (bool) $boolean; + } + + /** + * {@inheritdoc} + */ + public function setName($name) + { + $this->name = $name; + } + + /** + * {@inheritdoc} + */ + protected function validateType($value) + { + } + + /** + * {@inheritdoc} + */ + protected function finalizeValue($value) + { + if (!$this->allowEmptyValue && $this->isValueEmpty($value)) { + $ex = new InvalidConfigurationException(sprintf('The path "%s" cannot contain an empty value, but got %s.', $this->getPath(), json_encode($value))); + if ($hint = $this->getInfo()) { + $ex->addHint($hint); + } + $ex->setPath($this->getPath()); + + throw $ex; + } + + return $value; + } + + /** + * {@inheritdoc} + */ + protected function normalizeValue($value) + { + return $value; + } + + /** + * {@inheritdoc} + */ + protected function mergeValues($leftSide, $rightSide) + { + return $rightSide; + } + + /** + * Evaluates if the given value is to be treated as empty. + * + * By default, PHP's empty() function is used to test for emptiness. This + * method may be overridden by subtypes to better match their understanding + * of empty data. + * + * @param mixed $value + * + * @return bool + */ + protected function isValueEmpty($value) + { + return empty($value); + } +} diff --git a/lib/symfony/config/DependencyInjection/ConfigCachePass.php b/lib/symfony/config/DependencyInjection/ConfigCachePass.php new file mode 100644 index 000000000..128bf7e28 --- /dev/null +++ b/lib/symfony/config/DependencyInjection/ConfigCachePass.php @@ -0,0 +1,52 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\DependencyInjection; + +@trigger_error(sprintf('The %s class is deprecated since Symfony 3.4 and will be removed in 4.0. Use tagged iterator arguments instead.', ConfigCachePass::class), E_USER_DEPRECATED); + +use Symfony\Component\DependencyInjection\Argument\IteratorArgument; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\Compiler\PriorityTaggedServiceTrait; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * Adds services tagged config_cache.resource_checker to the config_cache_factory service, ordering them by priority. + * + * @author Matthias Pigulla + * @author Benjamin Klotz + * + * @deprecated since version 3.4, to be removed in 4.0. Use tagged iterator arguments instead. + */ +class ConfigCachePass implements CompilerPassInterface +{ + use PriorityTaggedServiceTrait; + + private $factoryServiceId; + private $resourceCheckerTag; + + public function __construct($factoryServiceId = 'config_cache_factory', $resourceCheckerTag = 'config_cache.resource_checker') + { + $this->factoryServiceId = $factoryServiceId; + $this->resourceCheckerTag = $resourceCheckerTag; + } + + public function process(ContainerBuilder $container) + { + $resourceCheckers = $this->findAndSortTaggedServices($this->resourceCheckerTag, $container); + + if (empty($resourceCheckers)) { + return; + } + + $container->getDefinition($this->factoryServiceId)->replaceArgument(0, new IteratorArgument($resourceCheckers)); + } +} diff --git a/lib/symfony/config/Exception/FileLoaderImportCircularReferenceException.php b/lib/symfony/config/Exception/FileLoaderImportCircularReferenceException.php new file mode 100644 index 000000000..6a3b01cfb --- /dev/null +++ b/lib/symfony/config/Exception/FileLoaderImportCircularReferenceException.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Exception; + +/** + * Exception class for when a circular reference is detected when importing resources. + * + * @author Fabien Potencier + */ +class FileLoaderImportCircularReferenceException extends FileLoaderLoadException +{ + public function __construct(array $resources, $code = null, $previous = null) + { + $message = sprintf('Circular reference detected in "%s" ("%s" > "%s").', $this->varToString($resources[0]), implode('" > "', $resources), $resources[0]); + + \Exception::__construct($message, $code, $previous); + } +} diff --git a/lib/symfony/config/Exception/FileLoaderLoadException.php b/lib/symfony/config/Exception/FileLoaderLoadException.php new file mode 100644 index 000000000..82d90eb39 --- /dev/null +++ b/lib/symfony/config/Exception/FileLoaderLoadException.php @@ -0,0 +1,109 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Exception; + +/** + * Exception class for when a resource cannot be loaded or imported. + * + * @author Ryan Weaver + */ +class FileLoaderLoadException extends \Exception +{ + /** + * @param string $resource The resource that could not be imported + * @param string $sourceResource The original resource importing the new resource + * @param int $code The error code + * @param \Exception $previous A previous exception + * @param string $type The type of resource + */ + public function __construct($resource, $sourceResource = null, $code = null, $previous = null, $type = null) + { + $message = ''; + if ($previous) { + // Include the previous exception, to help the user see what might be the underlying cause + + // Trim the trailing period of the previous message. We only want 1 period remove so no rtrim... + if ('.' === substr($previous->getMessage(), -1)) { + $trimmedMessage = substr($previous->getMessage(), 0, -1); + $message .= sprintf('%s', $trimmedMessage).' in '; + } else { + $message .= sprintf('%s', $previous->getMessage()).' in '; + } + $message .= $resource.' '; + + // show tweaked trace to complete the human readable sentence + if (null === $sourceResource) { + $message .= sprintf('(which is loaded in resource "%s")', $this->varToString($resource)); + } else { + $message .= sprintf('(which is being imported from "%s")', $this->varToString($sourceResource)); + } + $message .= '.'; + + // if there's no previous message, present it the default way + } elseif (null === $sourceResource) { + $message .= sprintf('Cannot load resource "%s".', $this->varToString($resource)); + } else { + $message .= sprintf('Cannot import resource "%s" from "%s".', $this->varToString($resource), $this->varToString($sourceResource)); + } + + // Is the resource located inside a bundle? + if ('@' === $resource[0]) { + $parts = explode(\DIRECTORY_SEPARATOR, $resource); + $bundle = substr($parts[0], 1); + $message .= sprintf(' Make sure the "%s" bundle is correctly registered and loaded in the application kernel class.', $bundle); + $message .= sprintf(' If the bundle is registered, make sure the bundle path "%s" is not empty.', $resource); + } elseif (null !== $type) { + // maybe there is no loader for this specific type + if ('annotation' === $type) { + $message .= ' Make sure annotations are installed and enabled.'; + } else { + $message .= sprintf(' Make sure there is a loader supporting the "%s" type.', $type); + } + } + + parent::__construct($message, $code, $previous); + } + + protected function varToString($var) + { + if (\is_object($var)) { + return sprintf('Object(%s)', \get_class($var)); + } + + if (\is_array($var)) { + $a = []; + foreach ($var as $k => $v) { + $a[] = sprintf('%s => %s', $k, $this->varToString($v)); + } + + return sprintf('Array(%s)', implode(', ', $a)); + } + + if (\is_resource($var)) { + return sprintf('Resource(%s)', get_resource_type($var)); + } + + if (null === $var) { + return 'null'; + } + + if (false === $var) { + return 'false'; + } + + if (true === $var) { + return 'true'; + } + + return (string) $var; + } +} diff --git a/lib/symfony/config/Exception/FileLocatorFileNotFoundException.php b/lib/symfony/config/Exception/FileLocatorFileNotFoundException.php new file mode 100644 index 000000000..648cf0e70 --- /dev/null +++ b/lib/symfony/config/Exception/FileLocatorFileNotFoundException.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Exception; + +/** + * File locator exception if a file does not exist. + * + * @author Leo Feyer + */ +class FileLocatorFileNotFoundException extends \InvalidArgumentException +{ + private $paths; + + public function __construct($message = '', $code = 0, $previous = null, array $paths = []) + { + parent::__construct($message, $code, $previous); + + $this->paths = $paths; + } + + public function getPaths() + { + return $this->paths; + } +} diff --git a/lib/symfony/config/FileLocator.php b/lib/symfony/config/FileLocator.php new file mode 100644 index 000000000..1314cc348 --- /dev/null +++ b/lib/symfony/config/FileLocator.php @@ -0,0 +1,98 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config; + +use Symfony\Component\Config\Exception\FileLocatorFileNotFoundException; + +/** + * FileLocator uses an array of pre-defined paths to find files. + * + * @author Fabien Potencier + */ +class FileLocator implements FileLocatorInterface +{ + protected $paths; + + /** + * @param string|array $paths A path or an array of paths where to look for resources + */ + public function __construct($paths = []) + { + $this->paths = (array) $paths; + } + + /** + * {@inheritdoc} + */ + public function locate($name, $currentPath = null, $first = true) + { + if ('' == $name) { + throw new \InvalidArgumentException('An empty file name is not valid to be located.'); + } + + if ($this->isAbsolutePath($name)) { + if (!file_exists($name)) { + throw new FileLocatorFileNotFoundException(sprintf('The file "%s" does not exist.', $name), 0, null, [$name]); + } + + return $name; + } + + $paths = $this->paths; + + if (null !== $currentPath) { + array_unshift($paths, $currentPath); + } + + $paths = array_unique($paths); + $filepaths = $notfound = []; + + foreach ($paths as $path) { + if (@file_exists($file = $path.\DIRECTORY_SEPARATOR.$name)) { + if (true === $first) { + return $file; + } + $filepaths[] = $file; + } else { + $notfound[] = $file; + } + } + + if (!$filepaths) { + throw new FileLocatorFileNotFoundException(sprintf('The file "%s" does not exist (in: %s).', $name, implode(', ', $paths)), 0, null, $notfound); + } + + return $filepaths; + } + + /** + * Returns whether the file path is an absolute path. + * + * @param string $file A file path + * + * @return bool + */ + private function isAbsolutePath($file) + { + if ('/' === $file[0] || '\\' === $file[0] + || (\strlen($file) > 3 && ctype_alpha($file[0]) + && ':' === $file[1] + && ('\\' === $file[2] || '/' === $file[2]) + ) + || null !== parse_url($file, PHP_URL_SCHEME) + ) { + return true; + } + + return false; + } +} diff --git a/lib/symfony/config/FileLocatorInterface.php b/lib/symfony/config/FileLocatorInterface.php new file mode 100644 index 000000000..cf3c2e594 --- /dev/null +++ b/lib/symfony/config/FileLocatorInterface.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config; + +use Symfony\Component\Config\Exception\FileLocatorFileNotFoundException; + +/** + * @author Fabien Potencier + */ +interface FileLocatorInterface +{ + /** + * Returns a full path for a given file name. + * + * @param string $name The file name to locate + * @param string|null $currentPath The current path + * @param bool $first Whether to return the first occurrence or an array of filenames + * + * @return string|array The full path to the file or an array of file paths + * + * @throws \InvalidArgumentException If $name is empty + * @throws FileLocatorFileNotFoundException If a file is not found + */ + public function locate($name, $currentPath = null, $first = true); +} diff --git a/lib/symfony/config/LICENSE b/lib/symfony/config/LICENSE new file mode 100644 index 000000000..a677f4376 --- /dev/null +++ b/lib/symfony/config/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2019 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. diff --git a/lib/symfony/config/Loader/DelegatingLoader.php b/lib/symfony/config/Loader/DelegatingLoader.php new file mode 100644 index 000000000..452e81c58 --- /dev/null +++ b/lib/symfony/config/Loader/DelegatingLoader.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Loader; + +use Symfony\Component\Config\Exception\FileLoaderLoadException; + +/** + * DelegatingLoader delegates loading to other loaders using a loader resolver. + * + * This loader acts as an array of LoaderInterface objects - each having + * a chance to load a given resource (handled by the resolver) + * + * @author Fabien Potencier + */ +class DelegatingLoader extends Loader +{ + public function __construct(LoaderResolverInterface $resolver) + { + $this->resolver = $resolver; + } + + /** + * {@inheritdoc} + */ + public function load($resource, $type = null) + { + if (false === $loader = $this->resolver->resolve($resource, $type)) { + throw new FileLoaderLoadException($resource, null, null, null, $type); + } + + return $loader->load($resource, $type); + } + + /** + * {@inheritdoc} + */ + public function supports($resource, $type = null) + { + return false !== $this->resolver->resolve($resource, $type); + } +} diff --git a/lib/symfony/config/Loader/FileLoader.php b/lib/symfony/config/Loader/FileLoader.php new file mode 100644 index 000000000..abaf1767d --- /dev/null +++ b/lib/symfony/config/Loader/FileLoader.php @@ -0,0 +1,172 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Loader; + +use Symfony\Component\Config\Exception\FileLoaderImportCircularReferenceException; +use Symfony\Component\Config\Exception\FileLoaderLoadException; +use Symfony\Component\Config\Exception\FileLocatorFileNotFoundException; +use Symfony\Component\Config\FileLocatorInterface; +use Symfony\Component\Config\Resource\FileExistenceResource; +use Symfony\Component\Config\Resource\GlobResource; + +/** + * FileLoader is the abstract class used by all built-in loaders that are file based. + * + * @author Fabien Potencier + */ +abstract class FileLoader extends Loader +{ + protected static $loading = []; + + protected $locator; + + private $currentDir; + + public function __construct(FileLocatorInterface $locator) + { + $this->locator = $locator; + } + + /** + * Sets the current directory. + * + * @param string $dir + */ + public function setCurrentDir($dir) + { + $this->currentDir = $dir; + } + + /** + * Returns the file locator used by this loader. + * + * @return FileLocatorInterface + */ + public function getLocator() + { + return $this->locator; + } + + /** + * Imports a resource. + * + * @param mixed $resource A Resource + * @param string|null $type The resource type or null if unknown + * @param bool $ignoreErrors Whether to ignore import errors or not + * @param string|null $sourceResource The original resource importing the new resource + * + * @return mixed + * + * @throws FileLoaderLoadException + * @throws FileLoaderImportCircularReferenceException + * @throws FileLocatorFileNotFoundException + */ + public function import($resource, $type = null, $ignoreErrors = false, $sourceResource = null) + { + if (\is_string($resource) && \strlen($resource) !== $i = strcspn($resource, '*?{[')) { + $ret = []; + $isSubpath = 0 !== $i && false !== strpos(substr($resource, 0, $i), '/'); + foreach ($this->glob($resource, false, $_, $ignoreErrors || !$isSubpath) as $path => $info) { + if (null !== $res = $this->doImport($path, $type, $ignoreErrors, $sourceResource)) { + $ret[] = $res; + } + $isSubpath = true; + } + + if ($isSubpath) { + return isset($ret[1]) ? $ret : (isset($ret[0]) ? $ret[0] : null); + } + } + + return $this->doImport($resource, $type, $ignoreErrors, $sourceResource); + } + + /** + * @internal + */ + protected function glob($pattern, $recursive, &$resource = null, $ignoreErrors = false) + { + if (\strlen($pattern) === $i = strcspn($pattern, '*?{[')) { + $prefix = $pattern; + $pattern = ''; + } elseif (0 === $i || false === strpos(substr($pattern, 0, $i), '/')) { + $prefix = '.'; + $pattern = '/'.$pattern; + } else { + $prefix = \dirname(substr($pattern, 0, 1 + $i)); + $pattern = substr($pattern, \strlen($prefix)); + } + + try { + $prefix = $this->locator->locate($prefix, $this->currentDir, true); + } catch (FileLocatorFileNotFoundException $e) { + if (!$ignoreErrors) { + throw $e; + } + + $resource = []; + foreach ($e->getPaths() as $path) { + $resource[] = new FileExistenceResource($path); + } + + return; + } + $resource = new GlobResource($prefix, $pattern, $recursive); + + foreach ($resource as $path => $info) { + yield $path => $info; + } + } + + private function doImport($resource, $type = null, $ignoreErrors = false, $sourceResource = null) + { + try { + $loader = $this->resolve($resource, $type); + + if ($loader instanceof self && null !== $this->currentDir) { + $resource = $loader->getLocator()->locate($resource, $this->currentDir, false); + } + + $resources = \is_array($resource) ? $resource : [$resource]; + for ($i = 0; $i < $resourcesCount = \count($resources); ++$i) { + if (isset(self::$loading[$resources[$i]])) { + if ($i == $resourcesCount - 1) { + throw new FileLoaderImportCircularReferenceException(array_keys(self::$loading)); + } + } else { + $resource = $resources[$i]; + break; + } + } + self::$loading[$resource] = true; + + try { + $ret = $loader->load($resource, $type); + } finally { + unset(self::$loading[$resource]); + } + + return $ret; + } catch (FileLoaderImportCircularReferenceException $e) { + throw $e; + } catch (\Exception $e) { + if (!$ignoreErrors) { + // prevent embedded imports from nesting multiple exceptions + if ($e instanceof FileLoaderLoadException) { + throw $e; + } + + throw new FileLoaderLoadException($resource, $sourceResource, null, $e, $type); + } + } + } +} diff --git a/lib/symfony/config/Loader/GlobFileLoader.php b/lib/symfony/config/Loader/GlobFileLoader.php new file mode 100644 index 000000000..f432b45ba --- /dev/null +++ b/lib/symfony/config/Loader/GlobFileLoader.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Loader; + +/** + * GlobFileLoader loads files from a glob pattern. + * + * @author Fabien Potencier + */ +class GlobFileLoader extends FileLoader +{ + /** + * {@inheritdoc} + */ + public function load($resource, $type = null) + { + return $this->import($resource); + } + + /** + * {@inheritdoc} + */ + public function supports($resource, $type = null) + { + return 'glob' === $type; + } +} diff --git a/lib/symfony/config/Loader/Loader.php b/lib/symfony/config/Loader/Loader.php new file mode 100644 index 000000000..d2f2ec90b --- /dev/null +++ b/lib/symfony/config/Loader/Loader.php @@ -0,0 +1,78 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Loader; + +use Symfony\Component\Config\Exception\FileLoaderLoadException; + +/** + * Loader is the abstract class used by all built-in loaders. + * + * @author Fabien Potencier + */ +abstract class Loader implements LoaderInterface +{ + protected $resolver; + + /** + * {@inheritdoc} + */ + public function getResolver() + { + return $this->resolver; + } + + /** + * {@inheritdoc} + */ + public function setResolver(LoaderResolverInterface $resolver) + { + $this->resolver = $resolver; + } + + /** + * Imports a resource. + * + * @param mixed $resource A resource + * @param string|null $type The resource type or null if unknown + * + * @return mixed + */ + public function import($resource, $type = null) + { + return $this->resolve($resource, $type)->load($resource, $type); + } + + /** + * Finds a loader able to load an imported resource. + * + * @param mixed $resource A resource + * @param string|null $type The resource type or null if unknown + * + * @return $this|LoaderInterface + * + * @throws FileLoaderLoadException If no loader is found + */ + public function resolve($resource, $type = null) + { + if ($this->supports($resource, $type)) { + return $this; + } + + $loader = null === $this->resolver ? false : $this->resolver->resolve($resource, $type); + + if (false === $loader) { + throw new FileLoaderLoadException($resource, null, null, null, $type); + } + + return $loader; + } +} diff --git a/lib/symfony/config/Loader/LoaderInterface.php b/lib/symfony/config/Loader/LoaderInterface.php new file mode 100644 index 000000000..dfca9dd27 --- /dev/null +++ b/lib/symfony/config/Loader/LoaderInterface.php @@ -0,0 +1,52 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Loader; + +/** + * LoaderInterface is the interface implemented by all loader classes. + * + * @author Fabien Potencier + */ +interface LoaderInterface +{ + /** + * Loads a resource. + * + * @param mixed $resource The resource + * @param string|null $type The resource type or null if unknown + * + * @throws \Exception If something went wrong + */ + public function load($resource, $type = null); + + /** + * Returns whether this class supports the given resource. + * + * @param mixed $resource A resource + * @param string|null $type The resource type or null if unknown + * + * @return bool True if this class supports the given resource, false otherwise + */ + public function supports($resource, $type = null); + + /** + * Gets the loader resolver. + * + * @return LoaderResolverInterface A LoaderResolverInterface instance + */ + public function getResolver(); + + /** + * Sets the loader resolver. + */ + public function setResolver(LoaderResolverInterface $resolver); +} diff --git a/lib/symfony/config/Loader/LoaderResolver.php b/lib/symfony/config/Loader/LoaderResolver.php new file mode 100644 index 000000000..c99efda4f --- /dev/null +++ b/lib/symfony/config/Loader/LoaderResolver.php @@ -0,0 +1,68 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Loader; + +/** + * LoaderResolver selects a loader for a given resource. + * + * A resource can be anything (e.g. a full path to a config file or a Closure). + * Each loader determines whether it can load a resource and how. + * + * @author Fabien Potencier + */ +class LoaderResolver implements LoaderResolverInterface +{ + /** + * @var LoaderInterface[] An array of LoaderInterface objects + */ + private $loaders = []; + + /** + * @param LoaderInterface[] $loaders An array of loaders + */ + public function __construct(array $loaders = []) + { + foreach ($loaders as $loader) { + $this->addLoader($loader); + } + } + + /** + * {@inheritdoc} + */ + public function resolve($resource, $type = null) + { + foreach ($this->loaders as $loader) { + if ($loader->supports($resource, $type)) { + return $loader; + } + } + + return false; + } + + public function addLoader(LoaderInterface $loader) + { + $this->loaders[] = $loader; + $loader->setResolver($this); + } + + /** + * Returns the registered loaders. + * + * @return LoaderInterface[] An array of LoaderInterface instances + */ + public function getLoaders() + { + return $this->loaders; + } +} diff --git a/lib/symfony/config/Loader/LoaderResolverInterface.php b/lib/symfony/config/Loader/LoaderResolverInterface.php new file mode 100644 index 000000000..40f1a1a63 --- /dev/null +++ b/lib/symfony/config/Loader/LoaderResolverInterface.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Loader; + +/** + * LoaderResolverInterface selects a loader for a given resource. + * + * @author Fabien Potencier + */ +interface LoaderResolverInterface +{ + /** + * Returns a loader able to load the resource. + * + * @param mixed $resource A resource + * @param string|null $type The resource type or null if unknown + * + * @return LoaderInterface|false The loader or false if none is able to load the resource + */ + public function resolve($resource, $type = null); +} diff --git a/lib/symfony/config/README.md b/lib/symfony/config/README.md new file mode 100644 index 000000000..bf400da19 --- /dev/null +++ b/lib/symfony/config/README.md @@ -0,0 +1,15 @@ +Config Component +================ + +The Config component provides several classes to help you find, load, combine, +autofill and validate configuration values of any kind, whatever their source +may be (YAML, XML, INI files, or for instance a database). + +Resources +--------- + + * [Documentation](https://symfony.com/doc/current/components/config/index.html) + * [Contributing](https://symfony.com/doc/current/contributing/index.html) + * [Report issues](https://github.com/symfony/symfony/issues) and + [send Pull Requests](https://github.com/symfony/symfony/pulls) + in the [main Symfony repository](https://github.com/symfony/symfony) diff --git a/lib/symfony/config/Resource/ClassExistenceResource.php b/lib/symfony/config/Resource/ClassExistenceResource.php new file mode 100644 index 000000000..5554f4c67 --- /dev/null +++ b/lib/symfony/config/Resource/ClassExistenceResource.php @@ -0,0 +1,171 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Resource; + +/** + * ClassExistenceResource represents a class existence. + * Freshness is only evaluated against resource existence. + * + * The resource must be a fully-qualified class name. + * + * @author Fabien Potencier + */ +class ClassExistenceResource implements SelfCheckingResourceInterface, \Serializable +{ + private $resource; + private $exists; + + private static $autoloadLevel = 0; + private static $autoloadedClass; + private static $existsCache = []; + + /** + * @param string $resource The fully-qualified class name + * @param bool|null $exists Boolean when the existency check has already been done + */ + public function __construct($resource, $exists = null) + { + $this->resource = $resource; + if (null !== $exists) { + $this->exists = (bool) $exists; + } + } + + /** + * {@inheritdoc} + */ + public function __toString() + { + return $this->resource; + } + + /** + * @return string The file path to the resource + */ + public function getResource() + { + return $this->resource; + } + + /** + * {@inheritdoc} + * + * @throws \ReflectionException when a parent class/interface/trait is not found + */ + public function isFresh($timestamp) + { + $loaded = class_exists($this->resource, false) || interface_exists($this->resource, false) || trait_exists($this->resource, false); + + if (null !== $exists = &self::$existsCache[(int) (0 >= $timestamp)][$this->resource]) { + $exists = $exists || $loaded; + } elseif (!$exists = $loaded) { + if (!self::$autoloadLevel++) { + spl_autoload_register(__CLASS__.'::throwOnRequiredClass'); + } + $autoloadedClass = self::$autoloadedClass; + self::$autoloadedClass = $this->resource; + + try { + $exists = class_exists($this->resource) || interface_exists($this->resource, false) || trait_exists($this->resource, false); + } catch (\ReflectionException $e) { + if (0 >= $timestamp) { + unset(self::$existsCache[1][$this->resource]); + throw $e; + } + } finally { + self::$autoloadedClass = $autoloadedClass; + if (!--self::$autoloadLevel) { + spl_autoload_unregister(__CLASS__.'::throwOnRequiredClass'); + } + } + } + + if (null === $this->exists) { + $this->exists = $exists; + } + + return $this->exists xor !$exists; + } + + /** + * @internal + */ + public function serialize() + { + if (null === $this->exists) { + $this->isFresh(0); + } + + return serialize([$this->resource, $this->exists]); + } + + /** + * @internal + */ + public function unserialize($serialized) + { + list($this->resource, $this->exists) = unserialize($serialized); + } + + /** + * @throws \ReflectionException When $class is not found and is required + * + * @internal + */ + public static function throwOnRequiredClass($class) + { + if (self::$autoloadedClass === $class) { + return; + } + $e = new \ReflectionException("Class $class not found"); + $trace = $e->getTrace(); + $autoloadFrame = [ + 'function' => 'spl_autoload_call', + 'args' => [$class], + ]; + $i = 1 + array_search($autoloadFrame, $trace, true); + + if (isset($trace[$i]['function']) && !isset($trace[$i]['class'])) { + switch ($trace[$i]['function']) { + case 'get_class_methods': + case 'get_class_vars': + case 'get_parent_class': + case 'is_a': + case 'is_subclass_of': + case 'class_exists': + case 'class_implements': + case 'class_parents': + case 'trait_exists': + case 'defined': + case 'interface_exists': + case 'method_exists': + case 'property_exists': + case 'is_callable': + return; + } + + $props = [ + 'file' => $trace[$i]['file'], + 'line' => $trace[$i]['line'], + 'trace' => \array_slice($trace, 1 + $i), + ]; + + foreach ($props as $p => $v) { + $r = new \ReflectionProperty('Exception', $p); + $r->setAccessible(true); + $r->setValue($e, $v); + } + } + + throw $e; + } +} diff --git a/lib/symfony/config/Resource/ComposerResource.php b/lib/symfony/config/Resource/ComposerResource.php new file mode 100644 index 000000000..9fb304bea --- /dev/null +++ b/lib/symfony/config/Resource/ComposerResource.php @@ -0,0 +1,84 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Resource; + +/** + * ComposerResource tracks the PHP version and Composer dependencies. + * + * @author Nicolas Grekas + */ +class ComposerResource implements SelfCheckingResourceInterface, \Serializable +{ + private $vendors; + + private static $runtimeVendors; + + public function __construct() + { + self::refresh(); + $this->vendors = self::$runtimeVendors; + } + + public function getVendors() + { + return array_keys($this->vendors); + } + + /** + * {@inheritdoc} + */ + public function __toString() + { + return __CLASS__; + } + + /** + * {@inheritdoc} + */ + public function isFresh($timestamp) + { + self::refresh(); + + return array_values(self::$runtimeVendors) === array_values($this->vendors); + } + + /** + * @internal + */ + public function serialize() + { + return serialize($this->vendors); + } + + /** + * @internal + */ + public function unserialize($serialized) + { + $this->vendors = unserialize($serialized); + } + + private static function refresh() + { + self::$runtimeVendors = []; + + foreach (get_declared_classes() as $class) { + if ('C' === $class[0] && 0 === strpos($class, 'ComposerAutoloaderInit')) { + $r = new \ReflectionClass($class); + $v = \dirname(\dirname($r->getFileName())); + if (file_exists($v.'/composer/installed.json')) { + self::$runtimeVendors[$v] = @filemtime($v.'/composer/installed.json'); + } + } + } + } +} diff --git a/lib/symfony/config/Resource/DirectoryResource.php b/lib/symfony/config/Resource/DirectoryResource.php new file mode 100644 index 000000000..e79b19ec2 --- /dev/null +++ b/lib/symfony/config/Resource/DirectoryResource.php @@ -0,0 +1,122 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Resource; + +/** + * DirectoryResource represents a resources stored in a subdirectory tree. + * + * @author Fabien Potencier + */ +class DirectoryResource implements SelfCheckingResourceInterface, \Serializable +{ + private $resource; + private $pattern; + + /** + * @param string $resource The file path to the resource + * @param string|null $pattern A pattern to restrict monitored files + * + * @throws \InvalidArgumentException + */ + public function __construct($resource, $pattern = null) + { + $this->resource = realpath($resource) ?: (file_exists($resource) ? $resource : false); + $this->pattern = $pattern; + + if (false === $this->resource || !is_dir($this->resource)) { + throw new \InvalidArgumentException(sprintf('The directory "%s" does not exist.', $resource)); + } + } + + /** + * {@inheritdoc} + */ + public function __toString() + { + return md5(serialize([$this->resource, $this->pattern])); + } + + /** + * @return string The file path to the resource + */ + public function getResource() + { + return $this->resource; + } + + /** + * Returns the pattern to restrict monitored files. + * + * @return string|null + */ + public function getPattern() + { + return $this->pattern; + } + + /** + * {@inheritdoc} + */ + public function isFresh($timestamp) + { + if (!is_dir($this->resource)) { + return false; + } + + if ($timestamp < filemtime($this->resource)) { + return false; + } + + foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->resource), \RecursiveIteratorIterator::SELF_FIRST) as $file) { + // if regex filtering is enabled only check matching files + if ($this->pattern && $file->isFile() && !preg_match($this->pattern, $file->getBasename())) { + continue; + } + + // always monitor directories for changes, except the .. entries + // (otherwise deleted files wouldn't get detected) + if ($file->isDir() && '/..' === substr($file, -3)) { + continue; + } + + // for broken links + try { + $fileMTime = $file->getMTime(); + } catch (\RuntimeException $e) { + continue; + } + + // early return if a file's mtime exceeds the passed timestamp + if ($timestamp < $fileMTime) { + return false; + } + } + + return true; + } + + /** + * @internal + */ + public function serialize() + { + return serialize([$this->resource, $this->pattern]); + } + + /** + * @internal + */ + public function unserialize($serialized) + { + list($this->resource, $this->pattern) = unserialize($serialized); + } +} diff --git a/lib/symfony/config/Resource/FileExistenceResource.php b/lib/symfony/config/Resource/FileExistenceResource.php new file mode 100644 index 000000000..34047651b --- /dev/null +++ b/lib/symfony/config/Resource/FileExistenceResource.php @@ -0,0 +1,76 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Resource; + +/** + * FileExistenceResource represents a resource stored on the filesystem. + * Freshness is only evaluated against resource creation or deletion. + * + * The resource can be a file or a directory. + * + * @author Charles-Henri Bruyand + */ +class FileExistenceResource implements SelfCheckingResourceInterface, \Serializable +{ + private $resource; + + private $exists; + + /** + * @param string $resource The file path to the resource + */ + public function __construct($resource) + { + $this->resource = (string) $resource; + $this->exists = file_exists($resource); + } + + /** + * {@inheritdoc} + */ + public function __toString() + { + return $this->resource; + } + + /** + * @return string The file path to the resource + */ + public function getResource() + { + return $this->resource; + } + + /** + * {@inheritdoc} + */ + public function isFresh($timestamp) + { + return file_exists($this->resource) === $this->exists; + } + + /** + * @internal + */ + public function serialize() + { + return serialize([$this->resource, $this->exists]); + } + + /** + * @internal + */ + public function unserialize($serialized) + { + list($this->resource, $this->exists) = unserialize($serialized); + } +} diff --git a/lib/symfony/config/Resource/FileResource.php b/lib/symfony/config/Resource/FileResource.php new file mode 100644 index 000000000..bee062377 --- /dev/null +++ b/lib/symfony/config/Resource/FileResource.php @@ -0,0 +1,81 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Resource; + +/** + * FileResource represents a resource stored on the filesystem. + * + * The resource can be a file or a directory. + * + * @author Fabien Potencier + */ +class FileResource implements SelfCheckingResourceInterface, \Serializable +{ + /** + * @var string|false + */ + private $resource; + + /** + * @param string $resource The file path to the resource + * + * @throws \InvalidArgumentException + */ + public function __construct($resource) + { + $this->resource = realpath($resource) ?: (file_exists($resource) ? $resource : false); + + if (false === $this->resource) { + throw new \InvalidArgumentException(sprintf('The file "%s" does not exist.', $resource)); + } + } + + /** + * {@inheritdoc} + */ + public function __toString() + { + return $this->resource; + } + + /** + * @return string The canonicalized, absolute path to the resource + */ + public function getResource() + { + return $this->resource; + } + + /** + * {@inheritdoc} + */ + public function isFresh($timestamp) + { + return false !== ($filemtime = @filemtime($this->resource)) && $filemtime <= $timestamp; + } + + /** + * @internal + */ + public function serialize() + { + return serialize($this->resource); + } + + /** + * @internal + */ + public function unserialize($serialized) + { + $this->resource = unserialize($serialized); + } +} diff --git a/lib/symfony/config/Resource/GlobResource.php b/lib/symfony/config/Resource/GlobResource.php new file mode 100644 index 000000000..28ba0a611 --- /dev/null +++ b/lib/symfony/config/Resource/GlobResource.php @@ -0,0 +1,157 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Resource; + +use Symfony\Component\Finder\Finder; +use Symfony\Component\Finder\Glob; + +/** + * GlobResource represents a set of resources stored on the filesystem. + * + * Only existence/removal is tracked (not mtimes.) + * + * @author Nicolas Grekas + */ +class GlobResource implements \IteratorAggregate, SelfCheckingResourceInterface, \Serializable +{ + private $prefix; + private $pattern; + private $recursive; + private $hash; + + /** + * @param string $prefix A directory prefix + * @param string $pattern A glob pattern + * @param bool $recursive Whether directories should be scanned recursively or not + * + * @throws \InvalidArgumentException + */ + public function __construct($prefix, $pattern, $recursive) + { + $this->prefix = realpath($prefix) ?: (file_exists($prefix) ? $prefix : false); + $this->pattern = $pattern; + $this->recursive = $recursive; + + if (false === $this->prefix) { + throw new \InvalidArgumentException(sprintf('The path "%s" does not exist.', $prefix)); + } + } + + public function getPrefix() + { + return $this->prefix; + } + + /** + * {@inheritdoc} + */ + public function __toString() + { + return 'glob.'.$this->prefix.$this->pattern.(int) $this->recursive; + } + + /** + * {@inheritdoc} + */ + public function isFresh($timestamp) + { + $hash = $this->computeHash(); + + if (null === $this->hash) { + $this->hash = $hash; + } + + return $this->hash === $hash; + } + + /** + * @internal + */ + public function serialize() + { + if (null === $this->hash) { + $this->hash = $this->computeHash(); + } + + return serialize([$this->prefix, $this->pattern, $this->recursive, $this->hash]); + } + + /** + * @internal + */ + public function unserialize($serialized) + { + list($this->prefix, $this->pattern, $this->recursive, $this->hash) = unserialize($serialized); + } + + public function getIterator() + { + if (!file_exists($this->prefix) || (!$this->recursive && '' === $this->pattern)) { + return; + } + + if (0 !== strpos($this->prefix, 'phar://') && false === strpos($this->pattern, '/**/') && (\defined('GLOB_BRACE') || false === strpos($this->pattern, '{'))) { + foreach (glob($this->prefix.$this->pattern, \defined('GLOB_BRACE') ? GLOB_BRACE : 0) as $path) { + if ($this->recursive && is_dir($path)) { + $files = iterator_to_array(new \RecursiveIteratorIterator( + new \RecursiveCallbackFilterIterator( + new \RecursiveDirectoryIterator($path, \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS), + function (\SplFileInfo $file) { return '.' !== $file->getBasename()[0]; } + ), + \RecursiveIteratorIterator::LEAVES_ONLY + )); + uasort($files, function (\SplFileInfo $a, \SplFileInfo $b) { + return (string) $a > (string) $b ? 1 : -1; + }); + + foreach ($files as $path => $info) { + if ($info->isFile()) { + yield $path => $info; + } + } + } elseif (is_file($path)) { + yield $path => new \SplFileInfo($path); + } + } + + return; + } + + if (!class_exists(Finder::class)) { + throw new \LogicException(sprintf('Extended glob pattern "%s" cannot be used as the Finder component is not installed.', $this->pattern)); + } + + $finder = new Finder(); + $regex = Glob::toRegex($this->pattern); + if ($this->recursive) { + $regex = substr_replace($regex, '(/|$)', -2, 1); + } + + $prefixLen = \strlen($this->prefix); + foreach ($finder->followLinks()->sortByName()->in($this->prefix) as $path => $info) { + if (preg_match($regex, substr('\\' === \DIRECTORY_SEPARATOR ? str_replace('\\', '/', $path) : $path, $prefixLen)) && $info->isFile()) { + yield $path => $info; + } + } + } + + private function computeHash() + { + $hash = hash_init('md5'); + + foreach ($this->getIterator() as $path => $info) { + hash_update($hash, $path."\n"); + } + + return hash_final($hash); + } +} diff --git a/lib/symfony/config/Resource/ReflectionClassResource.php b/lib/symfony/config/Resource/ReflectionClassResource.php new file mode 100644 index 000000000..f05042f8d --- /dev/null +++ b/lib/symfony/config/Resource/ReflectionClassResource.php @@ -0,0 +1,206 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Resource; + +use Symfony\Component\DependencyInjection\ServiceSubscriberInterface; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +/** + * @author Nicolas Grekas + */ +class ReflectionClassResource implements SelfCheckingResourceInterface, \Serializable +{ + private $files = []; + private $className; + private $classReflector; + private $excludedVendors = []; + private $hash; + + public function __construct(\ReflectionClass $classReflector, $excludedVendors = []) + { + $this->className = $classReflector->name; + $this->classReflector = $classReflector; + $this->excludedVendors = $excludedVendors; + } + + public function isFresh($timestamp) + { + if (null === $this->hash) { + $this->hash = $this->computeHash(); + $this->loadFiles($this->classReflector); + } + + foreach ($this->files as $file => $v) { + if (false === $filemtime = @filemtime($file)) { + return false; + } + + if ($filemtime > $timestamp) { + return $this->hash === $this->computeHash(); + } + } + + return true; + } + + public function __toString() + { + return 'reflection.'.$this->className; + } + + /** + * @internal + */ + public function serialize() + { + if (null === $this->hash) { + $this->hash = $this->computeHash(); + $this->loadFiles($this->classReflector); + } + + return serialize([$this->files, $this->className, $this->hash]); + } + + /** + * @internal + */ + public function unserialize($serialized) + { + list($this->files, $this->className, $this->hash) = unserialize($serialized); + } + + private function loadFiles(\ReflectionClass $class) + { + foreach ($class->getInterfaces() as $v) { + $this->loadFiles($v); + } + do { + $file = $class->getFileName(); + if (false !== $file && file_exists($file)) { + foreach ($this->excludedVendors as $vendor) { + if (0 === strpos($file, $vendor) && false !== strpbrk(substr($file, \strlen($vendor), 1), '/'.\DIRECTORY_SEPARATOR)) { + $file = false; + break; + } + } + if ($file) { + $this->files[$file] = null; + } + } + foreach ($class->getTraits() as $v) { + $this->loadFiles($v); + } + } while ($class = $class->getParentClass()); + } + + private function computeHash() + { + if (null === $this->classReflector) { + try { + $this->classReflector = new \ReflectionClass($this->className); + } catch (\ReflectionException $e) { + // the class does not exist anymore + return false; + } + } + $hash = hash_init('md5'); + + foreach ($this->generateSignature($this->classReflector) as $info) { + hash_update($hash, $info); + } + + return hash_final($hash); + } + + private function generateSignature(\ReflectionClass $class) + { + yield $class->getDocComment(); + yield (int) $class->isFinal(); + yield (int) $class->isAbstract(); + + if ($class->isTrait()) { + yield print_r(class_uses($class->name), true); + } else { + yield print_r(class_parents($class->name), true); + yield print_r(class_implements($class->name), true); + yield print_r($class->getConstants(), true); + } + + if (!$class->isInterface()) { + $defaults = $class->getDefaultProperties(); + + foreach ($class->getProperties(\ReflectionProperty::IS_PUBLIC | \ReflectionProperty::IS_PROTECTED) as $p) { + yield $p->getDocComment().$p; + yield print_r(isset($defaults[$p->name]) ? $defaults[$p->name] : null, true); + } + } + + if (\defined('HHVM_VERSION')) { + foreach ($class->getMethods(\ReflectionMethod::IS_PUBLIC | \ReflectionMethod::IS_PROTECTED) as $m) { + // workaround HHVM bug with variadics, see https://github.com/facebook/hhvm/issues/5762 + yield preg_replace('/^ @@.*/m', '', new ReflectionMethodHhvmWrapper($m->class, $m->name)); + } + } else { + foreach ($class->getMethods(\ReflectionMethod::IS_PUBLIC | \ReflectionMethod::IS_PROTECTED) as $m) { + yield preg_replace('/^ @@.*/m', '', $m); + + $defaults = []; + foreach ($m->getParameters() as $p) { + $defaults[$p->name] = $p->isDefaultValueAvailable() ? $p->getDefaultValue() : null; + } + yield print_r($defaults, true); + } + } + + if ($class->isAbstract() || $class->isInterface() || $class->isTrait()) { + return; + } + + if (interface_exists(EventSubscriberInterface::class, false) && $class->isSubclassOf(EventSubscriberInterface::class)) { + yield EventSubscriberInterface::class; + yield print_r(\call_user_func([$class->name, 'getSubscribedEvents']), true); + } + + if (interface_exists(ServiceSubscriberInterface::class, false) && $class->isSubclassOf(ServiceSubscriberInterface::class)) { + yield ServiceSubscriberInterface::class; + yield print_r(\call_user_func([$class->name, 'getSubscribedServices']), true); + } + } +} + +/** + * @internal + */ +class ReflectionMethodHhvmWrapper extends \ReflectionMethod +{ + public function getParameters() + { + $params = []; + + foreach (parent::getParameters() as $i => $p) { + $params[] = new ReflectionParameterHhvmWrapper([$this->class, $this->name], $i); + } + + return $params; + } +} + +/** + * @internal + */ +class ReflectionParameterHhvmWrapper extends \ReflectionParameter +{ + public function getDefaultValue() + { + return [$this->isVariadic(), $this->isDefaultValueAvailable() ? parent::getDefaultValue() : null]; + } +} diff --git a/lib/symfony/config/Resource/ResourceInterface.php b/lib/symfony/config/Resource/ResourceInterface.php new file mode 100644 index 000000000..d98fd427a --- /dev/null +++ b/lib/symfony/config/Resource/ResourceInterface.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Resource; + +/** + * ResourceInterface is the interface that must be implemented by all Resource classes. + * + * @author Fabien Potencier + */ +interface ResourceInterface +{ + /** + * Returns a string representation of the Resource. + * + * This method is necessary to allow for resource de-duplication, for example by means + * of array_unique(). The string returned need not have a particular meaning, but has + * to be identical for different ResourceInterface instances referring to the same + * resource; and it should be unlikely to collide with that of other, unrelated + * resource instances. + * + * @return string A string representation unique to the underlying Resource + */ + public function __toString(); +} diff --git a/lib/symfony/config/Resource/SelfCheckingResourceChecker.php b/lib/symfony/config/Resource/SelfCheckingResourceChecker.php new file mode 100644 index 000000000..d72203bc1 --- /dev/null +++ b/lib/symfony/config/Resource/SelfCheckingResourceChecker.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Resource; + +use Symfony\Component\Config\ResourceCheckerInterface; + +/** + * Resource checker for instances of SelfCheckingResourceInterface. + * + * As these resources perform the actual check themselves, we can provide + * this class as a standard way of validating them. + * + * @author Matthias Pigulla + */ +class SelfCheckingResourceChecker implements ResourceCheckerInterface +{ + public function supports(ResourceInterface $metadata) + { + return $metadata instanceof SelfCheckingResourceInterface; + } + + public function isFresh(ResourceInterface $resource, $timestamp) + { + /* @var SelfCheckingResourceInterface $resource */ + return $resource->isFresh($timestamp); + } +} diff --git a/lib/symfony/config/Resource/SelfCheckingResourceInterface.php b/lib/symfony/config/Resource/SelfCheckingResourceInterface.php new file mode 100644 index 000000000..b3260f2be --- /dev/null +++ b/lib/symfony/config/Resource/SelfCheckingResourceInterface.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Resource; + +/** + * Interface for Resources that can check for freshness autonomously, + * without special support from external services. + * + * @author Matthias Pigulla + */ +interface SelfCheckingResourceInterface extends ResourceInterface +{ + /** + * Returns true if the resource has not been updated since the given timestamp. + * + * @param int $timestamp The last time the resource was loaded + * + * @return bool True if the resource has not been updated, false otherwise + */ + public function isFresh($timestamp); +} diff --git a/lib/symfony/config/ResourceCheckerConfigCache.php b/lib/symfony/config/ResourceCheckerConfigCache.php new file mode 100644 index 000000000..092b95bf0 --- /dev/null +++ b/lib/symfony/config/ResourceCheckerConfigCache.php @@ -0,0 +1,183 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config; + +use Symfony\Component\Config\Resource\ResourceInterface; +use Symfony\Component\Filesystem\Exception\IOException; +use Symfony\Component\Filesystem\Filesystem; + +/** + * ResourceCheckerConfigCache uses instances of ResourceCheckerInterface + * to check whether cached data is still fresh. + * + * @author Matthias Pigulla + */ +class ResourceCheckerConfigCache implements ConfigCacheInterface +{ + /** + * @var string + */ + private $file; + + /** + * @var iterable|ResourceCheckerInterface[] + */ + private $resourceCheckers; + + /** + * @param string $file The absolute cache path + * @param iterable|ResourceCheckerInterface[] $resourceCheckers The ResourceCheckers to use for the freshness check + */ + public function __construct($file, $resourceCheckers = []) + { + $this->file = $file; + $this->resourceCheckers = $resourceCheckers; + } + + /** + * {@inheritdoc} + */ + public function getPath() + { + return $this->file; + } + + /** + * Checks if the cache is still fresh. + * + * This implementation will make a decision solely based on the ResourceCheckers + * passed in the constructor. + * + * The first ResourceChecker that supports a given resource is considered authoritative. + * Resources with no matching ResourceChecker will silently be ignored and considered fresh. + * + * @return bool true if the cache is fresh, false otherwise + */ + public function isFresh() + { + if (!is_file($this->file)) { + return false; + } + + if ($this->resourceCheckers instanceof \Traversable && !$this->resourceCheckers instanceof \Countable) { + $this->resourceCheckers = iterator_to_array($this->resourceCheckers); + } + + if (!\count($this->resourceCheckers)) { + return true; // shortcut - if we don't have any checkers we don't need to bother with the meta file at all + } + + $metadata = $this->getMetaFile(); + + if (!is_file($metadata)) { + return false; + } + + $meta = $this->safelyUnserialize($metadata); + + if (false === $meta) { + return false; + } + + $time = filemtime($this->file); + + foreach ($meta as $resource) { + /* @var ResourceInterface $resource */ + foreach ($this->resourceCheckers as $checker) { + if (!$checker->supports($resource)) { + continue; // next checker + } + if ($checker->isFresh($resource, $time)) { + break; // no need to further check this resource + } + + return false; // cache is stale + } + // no suitable checker found, ignore this resource + } + + return true; + } + + /** + * Writes cache. + * + * @param string $content The content to write in the cache + * @param ResourceInterface[] $metadata An array of metadata + * + * @throws \RuntimeException When cache file can't be written + */ + public function write($content, array $metadata = null) + { + $mode = 0666; + $umask = umask(); + $filesystem = new Filesystem(); + $filesystem->dumpFile($this->file, $content); + try { + $filesystem->chmod($this->file, $mode, $umask); + } catch (IOException $e) { + // discard chmod failure (some filesystem may not support it) + } + + if (null !== $metadata) { + $filesystem->dumpFile($this->getMetaFile(), serialize($metadata)); + try { + $filesystem->chmod($this->getMetaFile(), $mode, $umask); + } catch (IOException $e) { + // discard chmod failure (some filesystem may not support it) + } + } + + if (\function_exists('opcache_invalidate') && filter_var(ini_get('opcache.enable'), FILTER_VALIDATE_BOOLEAN)) { + @opcache_invalidate($this->file, true); + } + } + + /** + * Gets the meta file path. + * + * @return string The meta file path + */ + private function getMetaFile() + { + return $this->file.'.meta'; + } + + private function safelyUnserialize($file) + { + $e = null; + $meta = false; + $content = file_get_contents($file); + $signalingException = new \UnexpectedValueException(); + $prevUnserializeHandler = ini_set('unserialize_callback_func', ''); + $prevErrorHandler = set_error_handler(function ($type, $msg, $file, $line, $context = []) use (&$prevErrorHandler, $signalingException) { + if (__FILE__ === $file) { + throw $signalingException; + } + + return $prevErrorHandler ? $prevErrorHandler($type, $msg, $file, $line, $context) : false; + }); + + try { + $meta = unserialize($content); + } catch (\Error $e) { + } catch (\Exception $e) { + } + restore_error_handler(); + ini_set('unserialize_callback_func', $prevUnserializeHandler); + if (null !== $e && $e !== $signalingException) { + throw $e; + } + + return $meta; + } +} diff --git a/lib/symfony/config/ResourceCheckerConfigCacheFactory.php b/lib/symfony/config/ResourceCheckerConfigCacheFactory.php new file mode 100644 index 000000000..c00fa7db1 --- /dev/null +++ b/lib/symfony/config/ResourceCheckerConfigCacheFactory.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config; + +/** + * A ConfigCacheFactory implementation that validates the + * cache with an arbitrary set of ResourceCheckers. + * + * @author Matthias Pigulla + */ +class ResourceCheckerConfigCacheFactory implements ConfigCacheFactoryInterface +{ + private $resourceCheckers = []; + + /** + * @param iterable|ResourceCheckerInterface[] $resourceCheckers + */ + public function __construct($resourceCheckers = []) + { + $this->resourceCheckers = $resourceCheckers; + } + + /** + * {@inheritdoc} + */ + public function cache($file, $callback) + { + if (!\is_callable($callback)) { + throw new \InvalidArgumentException(sprintf('Invalid type for callback argument. Expected callable, but got "%s".', \gettype($callback))); + } + + $cache = new ResourceCheckerConfigCache($file, $this->resourceCheckers); + if (!$cache->isFresh()) { + \call_user_func($callback, $cache); + } + + return $cache; + } +} diff --git a/lib/symfony/config/ResourceCheckerInterface.php b/lib/symfony/config/ResourceCheckerInterface.php new file mode 100644 index 000000000..612d77786 --- /dev/null +++ b/lib/symfony/config/ResourceCheckerInterface.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config; + +use Symfony\Component\Config\Resource\ResourceInterface; + +/** + * Interface for ResourceCheckers. + * + * When a ResourceCheckerConfigCache instance is checked for freshness, all its associated + * metadata resources are passed to ResourceCheckers. The ResourceCheckers + * can then inspect the resources and decide whether the cache can be considered + * fresh or not. + * + * @author Matthias Pigulla + * @author Benjamin Klotz + */ +interface ResourceCheckerInterface +{ + /** + * Queries the ResourceChecker whether it can validate a given + * resource or not. + * + * @param ResourceInterface $metadata The resource to be checked for freshness + * + * @return bool True if the ResourceChecker can handle this resource type, false if not + */ + public function supports(ResourceInterface $metadata); + + /** + * Validates the resource. + * + * @param ResourceInterface $resource The resource to be validated + * @param int $timestamp The timestamp at which the cache associated with this resource was created + * + * @return bool True if the resource has not changed since the given timestamp, false otherwise + */ + public function isFresh(ResourceInterface $resource, $timestamp); +} diff --git a/lib/symfony/config/Tests/ConfigCacheFactoryTest.php b/lib/symfony/config/Tests/ConfigCacheFactoryTest.php new file mode 100644 index 000000000..24e3224ce --- /dev/null +++ b/lib/symfony/config/Tests/ConfigCacheFactoryTest.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\ConfigCacheFactory; + +class ConfigCacheFactoryTest extends TestCase +{ + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Invalid type for callback argument. Expected callable, but got "object". + */ + public function testCacheWithInvalidCallback() + { + $cacheFactory = new ConfigCacheFactory(true); + + $cacheFactory->cache('file', new \stdClass()); + } +} diff --git a/lib/symfony/config/Tests/ConfigCacheTest.php b/lib/symfony/config/Tests/ConfigCacheTest.php new file mode 100644 index 000000000..d0b70899b --- /dev/null +++ b/lib/symfony/config/Tests/ConfigCacheTest.php @@ -0,0 +1,99 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\ConfigCache; +use Symfony\Component\Config\Tests\Resource\ResourceStub; + +class ConfigCacheTest extends TestCase +{ + private $cacheFile = null; + + protected function setUp() + { + $this->cacheFile = tempnam(sys_get_temp_dir(), 'config_'); + } + + protected function tearDown() + { + $files = [$this->cacheFile, $this->cacheFile.'.meta']; + + foreach ($files as $file) { + if (file_exists($file)) { + unlink($file); + } + } + } + + /** + * @dataProvider debugModes + */ + public function testCacheIsNotValidIfNothingHasBeenCached($debug) + { + unlink($this->cacheFile); // remove tempnam() side effect + $cache = new ConfigCache($this->cacheFile, $debug); + + $this->assertFalse($cache->isFresh()); + } + + public function testIsAlwaysFreshInProduction() + { + $staleResource = new ResourceStub(); + $staleResource->setFresh(false); + + $cache = new ConfigCache($this->cacheFile, false); + $cache->write('', [$staleResource]); + + $this->assertTrue($cache->isFresh()); + } + + /** + * @dataProvider debugModes + */ + public function testIsFreshWhenNoResourceProvided($debug) + { + $cache = new ConfigCache($this->cacheFile, $debug); + $cache->write('', []); + $this->assertTrue($cache->isFresh()); + } + + public function testFreshResourceInDebug() + { + $freshResource = new ResourceStub(); + $freshResource->setFresh(true); + + $cache = new ConfigCache($this->cacheFile, true); + $cache->write('', [$freshResource]); + + $this->assertTrue($cache->isFresh()); + } + + public function testStaleResourceInDebug() + { + $staleResource = new ResourceStub(); + $staleResource->setFresh(false); + + $cache = new ConfigCache($this->cacheFile, true); + $cache->write('', [$staleResource]); + + $this->assertFalse($cache->isFresh()); + } + + public function debugModes() + { + return [ + [true], + [false], + ]; + } +} diff --git a/lib/symfony/config/Tests/Definition/ArrayNodeTest.php b/lib/symfony/config/Tests/Definition/ArrayNodeTest.php new file mode 100644 index 000000000..8f84cff38 --- /dev/null +++ b/lib/symfony/config/Tests/Definition/ArrayNodeTest.php @@ -0,0 +1,251 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Definition; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Definition\ArrayNode; +use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; +use Symfony\Component\Config\Definition\ScalarNode; + +class ArrayNodeTest extends TestCase +{ + /** + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidTypeException + */ + public function testNormalizeThrowsExceptionWhenFalseIsNotAllowed() + { + $node = new ArrayNode('root'); + $node->normalize(false); + } + + /** + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException + * @expectedExceptionMessage Unrecognized option "foo" under "root" + */ + public function testExceptionThrownOnUnrecognizedChild() + { + $node = new ArrayNode('root'); + $node->normalize(['foo' => 'bar']); + } + + public function ignoreAndRemoveMatrixProvider() + { + $unrecognizedOptionException = new InvalidConfigurationException('Unrecognized option "foo" under "root"'); + + return [ + [true, true, [], 'no exception is thrown for an unrecognized child if the ignoreExtraKeys option is set to true'], + [true, false, ['foo' => 'bar'], 'extra keys are not removed when ignoreExtraKeys second option is set to false'], + [false, true, $unrecognizedOptionException], + [false, false, $unrecognizedOptionException], + ]; + } + + /** + * @dataProvider ignoreAndRemoveMatrixProvider + */ + public function testIgnoreAndRemoveBehaviors($ignore, $remove, $expected, $message = '') + { + if ($expected instanceof \Exception) { + if (method_exists($this, 'expectException')) { + $this->expectException(\get_class($expected)); + $this->expectExceptionMessage($expected->getMessage()); + } else { + $this->setExpectedException(\get_class($expected), $expected->getMessage()); + } + } + $node = new ArrayNode('root'); + $node->setIgnoreExtraKeys($ignore, $remove); + $result = $node->normalize(['foo' => 'bar']); + $this->assertSame($expected, $result, $message); + } + + /** + * @dataProvider getPreNormalizationTests + */ + public function testPreNormalize($denormalized, $normalized) + { + $node = new ArrayNode('foo'); + + $r = new \ReflectionMethod($node, 'preNormalize'); + $r->setAccessible(true); + + $this->assertSame($normalized, $r->invoke($node, $denormalized)); + } + + public function getPreNormalizationTests() + { + return [ + [ + ['foo-bar' => 'foo'], + ['foo_bar' => 'foo'], + ], + [ + ['foo-bar_moo' => 'foo'], + ['foo-bar_moo' => 'foo'], + ], + [ + ['anything-with-dash-and-no-underscore' => 'first', 'no_dash' => 'second'], + ['anything_with_dash_and_no_underscore' => 'first', 'no_dash' => 'second'], + ], + [ + ['foo-bar' => null, 'foo_bar' => 'foo'], + ['foo-bar' => null, 'foo_bar' => 'foo'], + ], + ]; + } + + /** + * @dataProvider getZeroNamedNodeExamplesData + */ + public function testNodeNameCanBeZero($denormalized, $normalized) + { + $zeroNode = new ArrayNode(0); + $zeroNode->addChild(new ScalarNode('name')); + $fiveNode = new ArrayNode(5); + $fiveNode->addChild(new ScalarNode(0)); + $fiveNode->addChild(new ScalarNode('new_key')); + $rootNode = new ArrayNode('root'); + $rootNode->addChild($zeroNode); + $rootNode->addChild($fiveNode); + $rootNode->addChild(new ScalarNode('string_key')); + $r = new \ReflectionMethod($rootNode, 'normalizeValue'); + $r->setAccessible(true); + + $this->assertSame($normalized, $r->invoke($rootNode, $denormalized)); + } + + public function getZeroNamedNodeExamplesData() + { + return [ + [ + [ + 0 => [ + 'name' => 'something', + ], + 5 => [ + 0 => 'this won\'t work too', + 'new_key' => 'some other value', + ], + 'string_key' => 'just value', + ], + [ + 0 => [ + 'name' => 'something', + ], + 5 => [ + 0 => 'this won\'t work too', + 'new_key' => 'some other value', + ], + 'string_key' => 'just value', + ], + ], + ]; + } + + /** + * @dataProvider getPreNormalizedNormalizedOrderedData + */ + public function testChildrenOrderIsMaintainedOnNormalizeValue($prenormalized, $normalized) + { + $scalar1 = new ScalarNode('1'); + $scalar2 = new ScalarNode('2'); + $scalar3 = new ScalarNode('3'); + $node = new ArrayNode('foo'); + $node->addChild($scalar1); + $node->addChild($scalar3); + $node->addChild($scalar2); + + $r = new \ReflectionMethod($node, 'normalizeValue'); + $r->setAccessible(true); + + $this->assertSame($normalized, $r->invoke($node, $prenormalized)); + } + + public function getPreNormalizedNormalizedOrderedData() + { + return [ + [ + ['2' => 'two', '1' => 'one', '3' => 'three'], + ['2' => 'two', '1' => 'one', '3' => 'three'], + ], + ]; + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Child nodes must be named. + */ + public function testAddChildEmptyName() + { + $node = new ArrayNode('root'); + + $childNode = new ArrayNode(''); + $node->addChild($childNode); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage A child node named "foo" already exists. + */ + public function testAddChildNameAlreadyExists() + { + $node = new ArrayNode('root'); + + $childNode = new ArrayNode('foo'); + $node->addChild($childNode); + + $childNodeWithSameName = new ArrayNode('foo'); + $node->addChild($childNodeWithSameName); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage The node at path "foo" has no default value. + */ + public function testGetDefaultValueWithoutDefaultValue() + { + $node = new ArrayNode('foo'); + $node->getDefaultValue(); + } + + public function testSetDeprecated() + { + $childNode = new ArrayNode('foo'); + $childNode->setDeprecated('"%node%" is deprecated'); + + $this->assertTrue($childNode->isDeprecated()); + $this->assertSame('"foo" is deprecated', $childNode->getDeprecationMessage($childNode->getName(), $childNode->getPath())); + + $node = new ArrayNode('root'); + $node->addChild($childNode); + + $deprecationTriggered = false; + $deprecationHandler = function ($level, $message, $file, $line) use (&$prevErrorHandler, &$deprecationTriggered) { + if (E_USER_DEPRECATED === $level) { + return $deprecationTriggered = true; + } + + return $prevErrorHandler ? $prevErrorHandler($level, $message, $file, $line) : false; + }; + + $prevErrorHandler = set_error_handler($deprecationHandler); + $node->finalize([]); + restore_error_handler(); + + $this->assertFalse($deprecationTriggered, '->finalize() should not trigger if the deprecated node is not set'); + + $prevErrorHandler = set_error_handler($deprecationHandler); + $node->finalize(['foo' => []]); + restore_error_handler(); + $this->assertTrue($deprecationTriggered, '->finalize() should trigger if the deprecated node is set'); + } +} diff --git a/lib/symfony/config/Tests/Definition/BooleanNodeTest.php b/lib/symfony/config/Tests/Definition/BooleanNodeTest.php new file mode 100644 index 000000000..bfa2fd3e2 --- /dev/null +++ b/lib/symfony/config/Tests/Definition/BooleanNodeTest.php @@ -0,0 +1,74 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Definition; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Definition\BooleanNode; + +class BooleanNodeTest extends TestCase +{ + /** + * @dataProvider getValidValues + */ + public function testNormalize($value) + { + $node = new BooleanNode('test'); + $this->assertSame($value, $node->normalize($value)); + } + + /** + * @dataProvider getValidValues + * + * @param bool $value + */ + public function testValidNonEmptyValues($value) + { + $node = new BooleanNode('test'); + $node->setAllowEmptyValue(false); + + $this->assertSame($value, $node->finalize($value)); + } + + public function getValidValues() + { + return [ + [false], + [true], + ]; + } + + /** + * @dataProvider getInvalidValues + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidTypeException + */ + public function testNormalizeThrowsExceptionOnInvalidValues($value) + { + $node = new BooleanNode('test'); + $node->normalize($value); + } + + public function getInvalidValues() + { + return [ + [null], + [''], + ['foo'], + [0], + [1], + [0.0], + [0.1], + [[]], + [['foo' => 'bar']], + [new \stdClass()], + ]; + } +} diff --git a/lib/symfony/config/Tests/Definition/Builder/ArrayNodeDefinitionTest.php b/lib/symfony/config/Tests/Definition/Builder/ArrayNodeDefinitionTest.php new file mode 100644 index 000000000..4ad7eabf3 --- /dev/null +++ b/lib/symfony/config/Tests/Definition/Builder/ArrayNodeDefinitionTest.php @@ -0,0 +1,366 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Definition\Builder; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; +use Symfony\Component\Config\Definition\Builder\ScalarNodeDefinition; +use Symfony\Component\Config\Definition\Exception\InvalidDefinitionException; +use Symfony\Component\Config\Definition\Processor; + +class ArrayNodeDefinitionTest extends TestCase +{ + public function testAppendingSomeNode() + { + $parent = new ArrayNodeDefinition('root'); + $child = new ScalarNodeDefinition('child'); + + $parent + ->children() + ->scalarNode('foo')->end() + ->scalarNode('bar')->end() + ->end() + ->append($child); + + $this->assertCount(3, $this->getField($parent, 'children')); + $this->assertContains($child, $this->getField($parent, 'children')); + } + + /** + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidDefinitionException + * @dataProvider providePrototypeNodeSpecificCalls + */ + public function testPrototypeNodeSpecificOption($method, $args) + { + $node = new ArrayNodeDefinition('root'); + + \call_user_func_array([$node, $method], $args); + + $node->getNode(); + } + + public function providePrototypeNodeSpecificCalls() + { + return [ + ['defaultValue', [[]]], + ['addDefaultChildrenIfNoneSet', []], + ['requiresAtLeastOneElement', []], + ['useAttributeAsKey', ['foo']], + ]; + } + + /** + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidDefinitionException + */ + public function testConcreteNodeSpecificOption() + { + $node = new ArrayNodeDefinition('root'); + $node + ->addDefaultsIfNotSet() + ->prototype('array') + ; + $node->getNode(); + } + + /** + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidDefinitionException + */ + public function testPrototypeNodesCantHaveADefaultValueWhenUsingDefaultChildren() + { + $node = new ArrayNodeDefinition('root'); + $node + ->defaultValue([]) + ->addDefaultChildrenIfNoneSet('foo') + ->prototype('array') + ; + $node->getNode(); + } + + public function testPrototypedArrayNodeDefaultWhenUsingDefaultChildren() + { + $node = new ArrayNodeDefinition('root'); + $node + ->addDefaultChildrenIfNoneSet() + ->prototype('array') + ; + $tree = $node->getNode(); + $this->assertEquals([[]], $tree->getDefaultValue()); + } + + /** + * @dataProvider providePrototypedArrayNodeDefaults + */ + public function testPrototypedArrayNodeDefault($args, $shouldThrowWhenUsingAttrAsKey, $shouldThrowWhenNotUsingAttrAsKey, $defaults) + { + $node = new ArrayNodeDefinition('root'); + $node + ->addDefaultChildrenIfNoneSet($args) + ->prototype('array') + ; + + try { + $tree = $node->getNode(); + $this->assertFalse($shouldThrowWhenNotUsingAttrAsKey); + $this->assertEquals($defaults, $tree->getDefaultValue()); + } catch (InvalidDefinitionException $e) { + $this->assertTrue($shouldThrowWhenNotUsingAttrAsKey); + } + + $node = new ArrayNodeDefinition('root'); + $node + ->useAttributeAsKey('attr') + ->addDefaultChildrenIfNoneSet($args) + ->prototype('array') + ; + + try { + $tree = $node->getNode(); + $this->assertFalse($shouldThrowWhenUsingAttrAsKey); + $this->assertEquals($defaults, $tree->getDefaultValue()); + } catch (InvalidDefinitionException $e) { + $this->assertTrue($shouldThrowWhenUsingAttrAsKey); + } + } + + public function providePrototypedArrayNodeDefaults() + { + return [ + [null, true, false, [[]]], + [2, true, false, [[], []]], + ['2', false, true, ['2' => []]], + ['foo', false, true, ['foo' => []]], + [['foo'], false, true, ['foo' => []]], + [['foo', 'bar'], false, true, ['foo' => [], 'bar' => []]], + ]; + } + + public function testNestedPrototypedArrayNodes() + { + $nodeDefinition = new ArrayNodeDefinition('root'); + $nodeDefinition + ->addDefaultChildrenIfNoneSet() + ->prototype('array') + ->prototype('array') + ; + $node = $nodeDefinition->getNode(); + + $this->assertInstanceOf('Symfony\Component\Config\Definition\PrototypedArrayNode', $node); + $this->assertInstanceOf('Symfony\Component\Config\Definition\PrototypedArrayNode', $node->getPrototype()); + } + + public function testEnabledNodeDefaults() + { + $node = new ArrayNodeDefinition('root'); + $node + ->canBeEnabled() + ->children() + ->scalarNode('foo')->defaultValue('bar')->end() + ; + + $this->assertEquals(['enabled' => false, 'foo' => 'bar'], $node->getNode()->getDefaultValue()); + } + + /** + * @dataProvider getEnableableNodeFixtures + */ + public function testTrueEnableEnabledNode($expected, $config, $message) + { + $processor = new Processor(); + $node = new ArrayNodeDefinition('root'); + $node + ->canBeEnabled() + ->children() + ->scalarNode('foo')->defaultValue('bar')->end() + ; + + $this->assertEquals( + $expected, + $processor->process($node->getNode(), $config), + $message + ); + } + + public function testCanBeDisabled() + { + $node = new ArrayNodeDefinition('root'); + $node->canBeDisabled(); + + $this->assertTrue($this->getField($node, 'addDefaults')); + $this->assertEquals(['enabled' => false], $this->getField($node, 'falseEquivalent')); + $this->assertEquals(['enabled' => true], $this->getField($node, 'trueEquivalent')); + $this->assertEquals(['enabled' => true], $this->getField($node, 'nullEquivalent')); + + $nodeChildren = $this->getField($node, 'children'); + $this->assertArrayHasKey('enabled', $nodeChildren); + + $enabledNode = $nodeChildren['enabled']; + $this->assertTrue($this->getField($enabledNode, 'default')); + $this->assertTrue($this->getField($enabledNode, 'defaultValue')); + } + + public function testIgnoreExtraKeys() + { + $node = new ArrayNodeDefinition('root'); + + $this->assertFalse($this->getField($node, 'ignoreExtraKeys')); + + $result = $node->ignoreExtraKeys(); + + $this->assertEquals($node, $result); + $this->assertTrue($this->getField($node, 'ignoreExtraKeys')); + } + + public function testNormalizeKeys() + { + $node = new ArrayNodeDefinition('root'); + + $this->assertTrue($this->getField($node, 'normalizeKeys')); + + $result = $node->normalizeKeys(false); + + $this->assertEquals($node, $result); + $this->assertFalse($this->getField($node, 'normalizeKeys')); + } + + public function testUnsetChild() + { + $node = new ArrayNodeDefinition('root'); + $node + ->children() + ->scalarNode('value') + ->beforeNormalization() + ->ifTrue(function ($value) { + return empty($value); + }) + ->thenUnset() + ->end() + ->end() + ->end() + ; + + $this->assertSame([], $node->getNode()->normalize(['value' => null])); + } + + public function testPrototypeVariable() + { + $node = new ArrayNodeDefinition('root'); + $this->assertEquals($node->prototype('variable'), $node->variablePrototype()); + } + + public function testPrototypeScalar() + { + $node = new ArrayNodeDefinition('root'); + $this->assertEquals($node->prototype('scalar'), $node->scalarPrototype()); + } + + public function testPrototypeBoolean() + { + $node = new ArrayNodeDefinition('root'); + $this->assertEquals($node->prototype('boolean'), $node->booleanPrototype()); + } + + public function testPrototypeInteger() + { + $node = new ArrayNodeDefinition('root'); + $this->assertEquals($node->prototype('integer'), $node->integerPrototype()); + } + + public function testPrototypeFloat() + { + $node = new ArrayNodeDefinition('root'); + $this->assertEquals($node->prototype('float'), $node->floatPrototype()); + } + + public function testPrototypeArray() + { + $node = new ArrayNodeDefinition('root'); + $this->assertEquals($node->prototype('array'), $node->arrayPrototype()); + } + + public function testPrototypeEnum() + { + $node = new ArrayNodeDefinition('root'); + $this->assertEquals($node->prototype('enum'), $node->enumPrototype()); + } + + public function getEnableableNodeFixtures() + { + return [ + [['enabled' => true, 'foo' => 'bar'], [true], 'true enables an enableable node'], + [['enabled' => true, 'foo' => 'bar'], [null], 'null enables an enableable node'], + [['enabled' => true, 'foo' => 'bar'], [['enabled' => true]], 'An enableable node can be enabled'], + [['enabled' => true, 'foo' => 'baz'], [['foo' => 'baz']], 'any configuration enables an enableable node'], + [['enabled' => false, 'foo' => 'baz'], [['foo' => 'baz', 'enabled' => false]], 'An enableable node can be disabled'], + [['enabled' => false, 'foo' => 'bar'], [false], 'false disables an enableable node'], + ]; + } + + public function testRequiresAtLeastOneElement() + { + $node = new ArrayNodeDefinition('root'); + $node + ->requiresAtLeastOneElement() + ->integerPrototype(); + + $node->getNode()->finalize([1]); + + $this->addToAssertionCount(1); + } + + /** + * @group legacy + * @expectedDeprecation Using Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition::cannotBeEmpty() at path "root" has no effect, consider requiresAtLeastOneElement() instead. In 4.0 both methods will behave the same. + */ + public function testCannotBeEmpty() + { + $node = new ArrayNodeDefinition('root'); + $node + ->cannotBeEmpty() + ->integerPrototype(); + + $node->getNode()->finalize([]); + } + + public function testSetDeprecated() + { + $node = new ArrayNodeDefinition('root'); + $node + ->children() + ->arrayNode('foo')->setDeprecated('The "%path%" node is deprecated.')->end() + ->end() + ; + $deprecatedNode = $node->getNode()->getChildren()['foo']; + + $this->assertTrue($deprecatedNode->isDeprecated()); + $this->assertSame('The "root.foo" node is deprecated.', $deprecatedNode->getDeprecationMessage($deprecatedNode->getName(), $deprecatedNode->getPath())); + } + + /** + * @group legacy + * @expectedDeprecation ->cannotBeEmpty() is not applicable to concrete nodes at path "root". In 4.0 it will throw an exception. + */ + public function testCannotBeEmptyOnConcreteNode() + { + $node = new ArrayNodeDefinition('root'); + $node->cannotBeEmpty(); + + $node->getNode()->finalize([]); + } + + protected function getField($object, $field) + { + $reflection = new \ReflectionProperty($object, $field); + $reflection->setAccessible(true); + + return $reflection->getValue($object); + } +} diff --git a/lib/symfony/config/Tests/Definition/Builder/BooleanNodeDefinitionTest.php b/lib/symfony/config/Tests/Definition/Builder/BooleanNodeDefinitionTest.php new file mode 100644 index 000000000..c0d347f3d --- /dev/null +++ b/lib/symfony/config/Tests/Definition/Builder/BooleanNodeDefinitionTest.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Definition\Builder; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Definition\Builder\BooleanNodeDefinition; + +class BooleanNodeDefinitionTest extends TestCase +{ + /** + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidDefinitionException + * @expectedExceptionMessage ->cannotBeEmpty() is not applicable to BooleanNodeDefinition. + */ + public function testCannotBeEmptyThrowsAnException() + { + $def = new BooleanNodeDefinition('foo'); + $def->cannotBeEmpty(); + } + + public function testSetDeprecated() + { + $def = new BooleanNodeDefinition('foo'); + $def->setDeprecated('The "%path%" node is deprecated.'); + + $node = $def->getNode(); + + $this->assertTrue($node->isDeprecated()); + $this->assertSame('The "foo" node is deprecated.', $node->getDeprecationMessage($node->getName(), $node->getPath())); + } +} diff --git a/lib/symfony/config/Tests/Definition/Builder/EnumNodeDefinitionTest.php b/lib/symfony/config/Tests/Definition/Builder/EnumNodeDefinitionTest.php new file mode 100644 index 000000000..26f8586dc --- /dev/null +++ b/lib/symfony/config/Tests/Definition/Builder/EnumNodeDefinitionTest.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Definition\Builder; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Definition\Builder\EnumNodeDefinition; + +class EnumNodeDefinitionTest extends TestCase +{ + public function testWithOneValue() + { + $def = new EnumNodeDefinition('foo'); + $def->values(['foo']); + + $node = $def->getNode(); + $this->assertEquals(['foo'], $node->getValues()); + } + + public function testWithOneDistinctValue() + { + $def = new EnumNodeDefinition('foo'); + $def->values(['foo', 'foo']); + + $node = $def->getNode(); + $this->assertEquals(['foo'], $node->getValues()); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage You must call ->values() on enum nodes. + */ + public function testNoValuesPassed() + { + $def = new EnumNodeDefinition('foo'); + $def->getNode(); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage ->values() must be called with at least one value. + */ + public function testWithNoValues() + { + $def = new EnumNodeDefinition('foo'); + $def->values([]); + } + + public function testGetNode() + { + $def = new EnumNodeDefinition('foo'); + $def->values(['foo', 'bar']); + + $node = $def->getNode(); + $this->assertEquals(['foo', 'bar'], $node->getValues()); + } + + public function testSetDeprecated() + { + $def = new EnumNodeDefinition('foo'); + $def->values(['foo', 'bar']); + $def->setDeprecated('The "%path%" node is deprecated.'); + + $node = $def->getNode(); + + $this->assertTrue($node->isDeprecated()); + $this->assertSame('The "foo" node is deprecated.', $def->getNode()->getDeprecationMessage($node->getName(), $node->getPath())); + } +} diff --git a/lib/symfony/config/Tests/Definition/Builder/ExprBuilderTest.php b/lib/symfony/config/Tests/Definition/Builder/ExprBuilderTest.php new file mode 100644 index 000000000..191bf1c0b --- /dev/null +++ b/lib/symfony/config/Tests/Definition/Builder/ExprBuilderTest.php @@ -0,0 +1,270 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Definition\Builder; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Definition\Builder\TreeBuilder; + +class ExprBuilderTest extends TestCase +{ + public function testAlwaysExpression() + { + $test = $this->getTestBuilder() + ->always($this->returnClosure('new_value')) + ->end(); + + $this->assertFinalizedValueIs('new_value', $test); + } + + public function testIfTrueExpression() + { + $test = $this->getTestBuilder() + ->ifTrue() + ->then($this->returnClosure('new_value')) + ->end(); + $this->assertFinalizedValueIs('new_value', $test, ['key' => true]); + + $test = $this->getTestBuilder() + ->ifTrue(function ($v) { return true; }) + ->then($this->returnClosure('new_value')) + ->end(); + $this->assertFinalizedValueIs('new_value', $test); + + $test = $this->getTestBuilder() + ->ifTrue(function ($v) { return false; }) + ->then($this->returnClosure('new_value')) + ->end(); + $this->assertFinalizedValueIs('value', $test); + } + + public function testIfStringExpression() + { + $test = $this->getTestBuilder() + ->ifString() + ->then($this->returnClosure('new_value')) + ->end(); + $this->assertFinalizedValueIs('new_value', $test); + + $test = $this->getTestBuilder() + ->ifString() + ->then($this->returnClosure('new_value')) + ->end(); + $this->assertFinalizedValueIs(45, $test, ['key' => 45]); + } + + public function testIfNullExpression() + { + $test = $this->getTestBuilder() + ->ifNull() + ->then($this->returnClosure('new_value')) + ->end(); + $this->assertFinalizedValueIs('new_value', $test, ['key' => null]); + + $test = $this->getTestBuilder() + ->ifNull() + ->then($this->returnClosure('new_value')) + ->end(); + $this->assertFinalizedValueIs('value', $test); + } + + public function testIfEmptyExpression() + { + $test = $this->getTestBuilder() + ->ifEmpty() + ->then($this->returnClosure('new_value')) + ->end(); + $this->assertFinalizedValueIs('new_value', $test, ['key' => []]); + + $test = $this->getTestBuilder() + ->ifEmpty() + ->then($this->returnClosure('new_value')) + ->end(); + $this->assertFinalizedValueIs('value', $test); + } + + public function testIfArrayExpression() + { + $test = $this->getTestBuilder() + ->ifArray() + ->then($this->returnClosure('new_value')) + ->end(); + $this->assertFinalizedValueIs('new_value', $test, ['key' => []]); + + $test = $this->getTestBuilder() + ->ifArray() + ->then($this->returnClosure('new_value')) + ->end(); + $this->assertFinalizedValueIs('value', $test); + } + + public function testIfInArrayExpression() + { + $test = $this->getTestBuilder() + ->ifInArray(['foo', 'bar', 'value']) + ->then($this->returnClosure('new_value')) + ->end(); + $this->assertFinalizedValueIs('new_value', $test); + + $test = $this->getTestBuilder() + ->ifInArray(['foo', 'bar']) + ->then($this->returnClosure('new_value')) + ->end(); + $this->assertFinalizedValueIs('value', $test); + } + + public function testIfNotInArrayExpression() + { + $test = $this->getTestBuilder() + ->ifNotInArray(['foo', 'bar']) + ->then($this->returnClosure('new_value')) + ->end(); + $this->assertFinalizedValueIs('new_value', $test); + + $test = $this->getTestBuilder() + ->ifNotInArray(['foo', 'bar', 'value_from_config']) + ->then($this->returnClosure('new_value')) + ->end(); + $this->assertFinalizedValueIs('new_value', $test); + } + + public function testThenEmptyArrayExpression() + { + $test = $this->getTestBuilder() + ->ifString() + ->thenEmptyArray() + ->end(); + $this->assertFinalizedValueIs([], $test); + } + + /** + * @dataProvider castToArrayValues + */ + public function testcastToArrayExpression($configValue, $expectedValue) + { + $test = $this->getTestBuilder() + ->castToArray() + ->end(); + $this->assertFinalizedValueIs($expectedValue, $test, ['key' => $configValue]); + } + + public function castToArrayValues() + { + yield ['value', ['value']]; + yield [-3.14, [-3.14]]; + yield [null, [null]]; + yield [['value'], ['value']]; + } + + /** + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException + */ + public function testThenInvalid() + { + $test = $this->getTestBuilder() + ->ifString() + ->thenInvalid('Invalid value') + ->end(); + $this->finalizeTestBuilder($test); + } + + public function testThenUnsetExpression() + { + $test = $this->getTestBuilder() + ->ifString() + ->thenUnset() + ->end(); + $this->assertEquals([], $this->finalizeTestBuilder($test)); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage You must specify an if part. + */ + public function testEndIfPartNotSpecified() + { + $this->getTestBuilder()->end(); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage You must specify a then part. + */ + public function testEndThenPartNotSpecified() + { + $builder = $this->getTestBuilder(); + $builder->ifPart = 'test'; + $builder->end(); + } + + /** + * Create a test treebuilder with a variable node, and init the validation. + * + * @return TreeBuilder + */ + protected function getTestBuilder() + { + $builder = new TreeBuilder(); + + return $builder + ->root('test') + ->children() + ->variableNode('key') + ->validate() + ; + } + + /** + * Close the validation process and finalize with the given config. + * + * @param TreeBuilder $testBuilder The tree builder to finalize + * @param array $config The config you want to use for the finalization, if nothing provided + * a simple ['key'=>'value'] will be used + * + * @return array The finalized config values + */ + protected function finalizeTestBuilder($testBuilder, $config = null) + { + return $testBuilder + ->end() + ->end() + ->end() + ->buildTree() + ->finalize(null === $config ? ['key' => 'value'] : $config) + ; + } + + /** + * Return a closure that will return the given value. + * + * @param mixed $val The value that the closure must return + * + * @return \Closure + */ + protected function returnClosure($val) + { + return function ($v) use ($val) { + return $val; + }; + } + + /** + * Assert that the given test builder, will return the given value. + * + * @param mixed $value The value to test + * @param TreeBuilder $treeBuilder The tree builder to finalize + * @param mixed $config The config values that new to be finalized + */ + protected function assertFinalizedValueIs($value, $treeBuilder, $config = null) + { + $this->assertEquals(['key' => $value], $this->finalizeTestBuilder($treeBuilder, $config)); + } +} diff --git a/lib/symfony/config/Tests/Definition/Builder/NodeBuilderTest.php b/lib/symfony/config/Tests/Definition/Builder/NodeBuilderTest.php new file mode 100644 index 000000000..cd77dd702 --- /dev/null +++ b/lib/symfony/config/Tests/Definition/Builder/NodeBuilderTest.php @@ -0,0 +1,95 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Definition\Builder; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Definition\Builder\NodeBuilder as BaseNodeBuilder; +use Symfony\Component\Config\Definition\Builder\VariableNodeDefinition as BaseVariableNodeDefinition; + +class NodeBuilderTest extends TestCase +{ + /** + * @expectedException \RuntimeException + */ + public function testThrowsAnExceptionWhenTryingToCreateANonRegisteredNodeType() + { + $builder = new BaseNodeBuilder(); + $builder->node('', 'foobar'); + } + + /** + * @expectedException \RuntimeException + */ + public function testThrowsAnExceptionWhenTheNodeClassIsNotFound() + { + $builder = new BaseNodeBuilder(); + $builder + ->setNodeClass('noclasstype', '\\foo\\bar\\noclass') + ->node('', 'noclasstype'); + } + + public function testAddingANewNodeType() + { + $class = __NAMESPACE__.'\\SomeNodeDefinition'; + + $builder = new BaseNodeBuilder(); + $node = $builder + ->setNodeClass('newtype', $class) + ->node('', 'newtype'); + + $this->assertInstanceOf($class, $node); + } + + public function testOverridingAnExistingNodeType() + { + $class = __NAMESPACE__.'\\SomeNodeDefinition'; + + $builder = new BaseNodeBuilder(); + $node = $builder + ->setNodeClass('variable', $class) + ->node('', 'variable'); + + $this->assertInstanceOf($class, $node); + } + + public function testNodeTypesAreNotCaseSensitive() + { + $builder = new BaseNodeBuilder(); + + $node1 = $builder->node('', 'VaRiAbLe'); + $node2 = $builder->node('', 'variable'); + + $this->assertInstanceOf(\get_class($node1), $node2); + + $builder->setNodeClass('CuStOm', __NAMESPACE__.'\\SomeNodeDefinition'); + + $node1 = $builder->node('', 'CUSTOM'); + $node2 = $builder->node('', 'custom'); + + $this->assertInstanceOf(\get_class($node1), $node2); + } + + public function testNumericNodeCreation() + { + $builder = new BaseNodeBuilder(); + + $node = $builder->integerNode('foo')->min(3)->max(5); + $this->assertInstanceOf('Symfony\Component\Config\Definition\Builder\IntegerNodeDefinition', $node); + + $node = $builder->floatNode('bar')->min(3.0)->max(5.0); + $this->assertInstanceOf('Symfony\Component\Config\Definition\Builder\FloatNodeDefinition', $node); + } +} + +class SomeNodeDefinition extends BaseVariableNodeDefinition +{ +} diff --git a/lib/symfony/config/Tests/Definition/Builder/NumericNodeDefinitionTest.php b/lib/symfony/config/Tests/Definition/Builder/NumericNodeDefinitionTest.php new file mode 100644 index 000000000..31342503d --- /dev/null +++ b/lib/symfony/config/Tests/Definition/Builder/NumericNodeDefinitionTest.php @@ -0,0 +1,104 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Definition\Builder; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Definition\Builder\FloatNodeDefinition; +use Symfony\Component\Config\Definition\Builder\IntegerNodeDefinition; +use Symfony\Component\Config\Definition\Builder\IntegerNodeDefinition as NumericNodeDefinition; + +class NumericNodeDefinitionTest extends TestCase +{ + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage You cannot define a min(4) as you already have a max(3) + */ + public function testIncoherentMinAssertion() + { + $def = new NumericNodeDefinition('foo'); + $def->max(3)->min(4); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage You cannot define a max(2) as you already have a min(3) + */ + public function testIncoherentMaxAssertion() + { + $node = new NumericNodeDefinition('foo'); + $node->min(3)->max(2); + } + + /** + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException + * @expectedExceptionMessage The value 4 is too small for path "foo". Should be greater than or equal to 5 + */ + public function testIntegerMinAssertion() + { + $def = new IntegerNodeDefinition('foo'); + $def->min(5)->getNode()->finalize(4); + } + + /** + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException + * @expectedExceptionMessage The value 4 is too big for path "foo". Should be less than or equal to 3 + */ + public function testIntegerMaxAssertion() + { + $def = new IntegerNodeDefinition('foo'); + $def->max(3)->getNode()->finalize(4); + } + + public function testIntegerValidMinMaxAssertion() + { + $def = new IntegerNodeDefinition('foo'); + $node = $def->min(3)->max(7)->getNode(); + $this->assertEquals(4, $node->finalize(4)); + } + + /** + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException + * @expectedExceptionMessage The value 400 is too small for path "foo". Should be greater than or equal to 500 + */ + public function testFloatMinAssertion() + { + $def = new FloatNodeDefinition('foo'); + $def->min(5E2)->getNode()->finalize(4e2); + } + + /** + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException + * @expectedExceptionMessage The value 4.3 is too big for path "foo". Should be less than or equal to 0.3 + */ + public function testFloatMaxAssertion() + { + $def = new FloatNodeDefinition('foo'); + $def->max(0.3)->getNode()->finalize(4.3); + } + + public function testFloatValidMinMaxAssertion() + { + $def = new FloatNodeDefinition('foo'); + $node = $def->min(3.0)->max(7e2)->getNode(); + $this->assertEquals(4.5, $node->finalize(4.5)); + } + + /** + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidDefinitionException + * @expectedExceptionMessage ->cannotBeEmpty() is not applicable to NumericNodeDefinition. + */ + public function testCannotBeEmptyThrowsAnException() + { + $def = new NumericNodeDefinition('foo'); + $def->cannotBeEmpty(); + } +} diff --git a/lib/symfony/config/Tests/Definition/Builder/TreeBuilderTest.php b/lib/symfony/config/Tests/Definition/Builder/TreeBuilderTest.php new file mode 100644 index 000000000..d94912b78 --- /dev/null +++ b/lib/symfony/config/Tests/Definition/Builder/TreeBuilderTest.php @@ -0,0 +1,134 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Definition\Builder; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Definition\Builder\TreeBuilder; +use Symfony\Component\Config\Tests\Fixtures\Builder\NodeBuilder as CustomNodeBuilder; + +class TreeBuilderTest extends TestCase +{ + public function testUsingACustomNodeBuilder() + { + $builder = new TreeBuilder(); + $root = $builder->root('custom', 'array', new CustomNodeBuilder()); + + $nodeBuilder = $root->children(); + + $this->assertInstanceOf('Symfony\Component\Config\Tests\Fixtures\Builder\NodeBuilder', $nodeBuilder); + + $nodeBuilder = $nodeBuilder->arrayNode('deeper')->children(); + + $this->assertInstanceOf('Symfony\Component\Config\Tests\Fixtures\Builder\NodeBuilder', $nodeBuilder); + } + + public function testOverrideABuiltInNodeType() + { + $builder = new TreeBuilder(); + $root = $builder->root('override', 'array', new CustomNodeBuilder()); + + $definition = $root->children()->variableNode('variable'); + + $this->assertInstanceOf('Symfony\Component\Config\Tests\Fixtures\Builder\VariableNodeDefinition', $definition); + } + + public function testAddANodeType() + { + $builder = new TreeBuilder(); + $root = $builder->root('override', 'array', new CustomNodeBuilder()); + + $definition = $root->children()->barNode('variable'); + + $this->assertInstanceOf('Symfony\Component\Config\Tests\Fixtures\Builder\BarNodeDefinition', $definition); + } + + public function testCreateABuiltInNodeTypeWithACustomNodeBuilder() + { + $builder = new TreeBuilder(); + $root = $builder->root('builtin', 'array', new CustomNodeBuilder()); + + $definition = $root->children()->booleanNode('boolean'); + + $this->assertInstanceOf('Symfony\Component\Config\Definition\Builder\BooleanNodeDefinition', $definition); + } + + public function testPrototypedArrayNodeUseTheCustomNodeBuilder() + { + $builder = new TreeBuilder(); + $root = $builder->root('override', 'array', new CustomNodeBuilder()); + + $root->prototype('bar')->end(); + + $this->assertInstanceOf('Symfony\Component\Config\Tests\Fixtures\BarNode', $root->getNode(true)->getPrototype()); + } + + public function testAnExtendedNodeBuilderGetsPropagatedToTheChildren() + { + $builder = new TreeBuilder(); + + $builder->root('propagation') + ->children() + ->setNodeClass('extended', 'Symfony\Component\Config\Definition\Builder\BooleanNodeDefinition') + ->node('foo', 'extended')->end() + ->arrayNode('child') + ->children() + ->node('foo', 'extended') + ->end() + ->end() + ->end() + ->end(); + + $node = $builder->buildTree(); + $children = $node->getChildren(); + + $this->assertInstanceOf('Symfony\Component\Config\Definition\BooleanNode', $children['foo']); + + $childChildren = $children['child']->getChildren(); + + $this->assertInstanceOf('Symfony\Component\Config\Definition\BooleanNode', $childChildren['foo']); + } + + public function testDefinitionInfoGetsTransferredToNode() + { + $builder = new TreeBuilder(); + + $builder->root('test')->info('root info') + ->children() + ->node('child', 'variable')->info('child info')->defaultValue('default') + ->end() + ->end(); + + $tree = $builder->buildTree(); + $children = $tree->getChildren(); + + $this->assertEquals('root info', $tree->getInfo()); + $this->assertEquals('child info', $children['child']->getInfo()); + } + + public function testDefinitionExampleGetsTransferredToNode() + { + $builder = new TreeBuilder(); + + $builder->root('test') + ->example(['key' => 'value']) + ->children() + ->node('child', 'variable')->info('child info')->defaultValue('default')->example('example') + ->end() + ->end(); + + $tree = $builder->buildTree(); + $children = $tree->getChildren(); + + $this->assertInternalType('array', $tree->getExample()); + $this->assertEquals('example', $children['child']->getExample()); + } +} diff --git a/lib/symfony/config/Tests/Definition/Dumper/XmlReferenceDumperTest.php b/lib/symfony/config/Tests/Definition/Dumper/XmlReferenceDumperTest.php new file mode 100644 index 000000000..5bc961bab --- /dev/null +++ b/lib/symfony/config/Tests/Definition/Dumper/XmlReferenceDumperTest.php @@ -0,0 +1,114 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Definition\Dumper; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Definition\Dumper\XmlReferenceDumper; +use Symfony\Component\Config\Tests\Fixtures\Configuration\ExampleConfiguration; + +class XmlReferenceDumperTest extends TestCase +{ + public function testDumper() + { + $configuration = new ExampleConfiguration(); + + $dumper = new XmlReferenceDumper(); + $this->assertEquals($this->getConfigurationAsString(), $dumper->dump($configuration)); + } + + public function testNamespaceDumper() + { + $configuration = new ExampleConfiguration(); + + $dumper = new XmlReferenceDumper(); + $this->assertEquals(str_replace('http://example.org/schema/dic/acme_root', 'http://symfony.com/schema/dic/symfony', $this->getConfigurationAsString()), $dumper->dump($configuration, 'http://symfony.com/schema/dic/symfony')); + } + + private function getConfigurationAsString() + { + return str_replace("\n", PHP_EOL, <<<'EOL' + + + + + + + + + + + + + + scalar value + + + scalar value + + + + + + + + + + + + + + + + + + + + + + + + +EOL + ); + } +} diff --git a/lib/symfony/config/Tests/Definition/Dumper/YamlReferenceDumperTest.php b/lib/symfony/config/Tests/Definition/Dumper/YamlReferenceDumperTest.php new file mode 100644 index 000000000..3cb9121ba --- /dev/null +++ b/lib/symfony/config/Tests/Definition/Dumper/YamlReferenceDumperTest.php @@ -0,0 +1,143 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Definition\Dumper; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Definition\Dumper\YamlReferenceDumper; +use Symfony\Component\Config\Tests\Fixtures\Configuration\ExampleConfiguration; + +class YamlReferenceDumperTest extends TestCase +{ + public function testDumper() + { + $configuration = new ExampleConfiguration(); + + $dumper = new YamlReferenceDumper(); + + $this->assertEquals($this->getConfigurationAsString(), $dumper->dump($configuration)); + } + + public function provideDumpAtPath() + { + return [ + 'Regular node' => ['scalar_true', << ['array', << ['array.child2', << ['cms_pages.page', << ['cms_pages.page.locale', <<assertSame(trim($expected), trim($dumper->dumpAtPath($configuration, $path))); + } + + private function getConfigurationAsString() + { + return <<<'EOL' +acme_root: + boolean: true + scalar_empty: ~ + scalar_null: null + scalar_true: true + scalar_false: false + scalar_default: default + scalar_array_empty: [] + scalar_array_defaults: + + # Defaults: + - elem1 + - elem2 + scalar_required: ~ # Required + scalar_deprecated: ~ # Deprecated (The child node "scalar_deprecated" at path "acme_root" is deprecated.) + scalar_deprecated_with_message: ~ # Deprecated (Deprecation custom message for "scalar_deprecated_with_message" at "acme_root") + node_with_a_looong_name: ~ + enum_with_default: this # One of "this"; "that" + enum: ~ # One of "this"; "that" + + # some info + array: + child1: ~ + child2: ~ + + # this is a long + # multi-line info text + # which should be indented + child3: ~ # Example: example setting + scalar_prototyped: [] + parameters: + + # Prototype: Parameter name + name: ~ + connections: + + # Prototype + - + user: ~ + pass: ~ + cms_pages: + + # Prototype + page: + + # Prototype + locale: + title: ~ # Required + path: ~ # Required + pipou: + + # Prototype + name: [] + +EOL; + } +} diff --git a/lib/symfony/config/Tests/Definition/EnumNodeTest.php b/lib/symfony/config/Tests/Definition/EnumNodeTest.php new file mode 100644 index 000000000..206bfdd5b --- /dev/null +++ b/lib/symfony/config/Tests/Definition/EnumNodeTest.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Definition; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Definition\EnumNode; + +class EnumNodeTest extends TestCase +{ + public function testFinalizeValue() + { + $node = new EnumNode('foo', null, ['foo', 'bar']); + $this->assertSame('foo', $node->finalize('foo')); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage $values must contain at least one element. + */ + public function testConstructionWithNoValues() + { + new EnumNode('foo', null, []); + } + + public function testConstructionWithOneValue() + { + $node = new EnumNode('foo', null, ['foo']); + $this->assertSame('foo', $node->finalize('foo')); + } + + public function testConstructionWithOneDistinctValue() + { + $node = new EnumNode('foo', null, ['foo', 'foo']); + $this->assertSame('foo', $node->finalize('foo')); + } + + /** + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException + * @expectedExceptionMessage The value "foobar" is not allowed for path "foo". Permissible values: "foo", "bar" + */ + public function testFinalizeWithInvalidValue() + { + $node = new EnumNode('foo', null, ['foo', 'bar']); + $node->finalize('foobar'); + } +} diff --git a/lib/symfony/config/Tests/Definition/FinalizationTest.php b/lib/symfony/config/Tests/Definition/FinalizationTest.php new file mode 100644 index 000000000..be68a27c6 --- /dev/null +++ b/lib/symfony/config/Tests/Definition/FinalizationTest.php @@ -0,0 +1,74 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Definition; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Definition\Builder\TreeBuilder; +use Symfony\Component\Config\Definition\NodeInterface; +use Symfony\Component\Config\Definition\Processor; + +class FinalizationTest extends TestCase +{ + public function testUnsetKeyWithDeepHierarchy() + { + $tb = new TreeBuilder(); + $tree = $tb + ->root('config', 'array') + ->children() + ->node('level1', 'array') + ->canBeUnset() + ->children() + ->node('level2', 'array') + ->canBeUnset() + ->children() + ->node('somevalue', 'scalar')->end() + ->node('anothervalue', 'scalar')->end() + ->end() + ->end() + ->node('level1_scalar', 'scalar')->end() + ->end() + ->end() + ->end() + ->end() + ->buildTree() + ; + + $a = [ + 'level1' => [ + 'level2' => [ + 'somevalue' => 'foo', + 'anothervalue' => 'bar', + ], + 'level1_scalar' => 'foo', + ], + ]; + + $b = [ + 'level1' => [ + 'level2' => false, + ], + ]; + + $this->assertEquals([ + 'level1' => [ + 'level1_scalar' => 'foo', + ], + ], $this->process($tree, [$a, $b])); + } + + protected function process(NodeInterface $tree, array $configs) + { + $processor = new Processor(); + + return $processor->process($tree, $configs); + } +} diff --git a/lib/symfony/config/Tests/Definition/FloatNodeTest.php b/lib/symfony/config/Tests/Definition/FloatNodeTest.php new file mode 100644 index 000000000..8268fe83b --- /dev/null +++ b/lib/symfony/config/Tests/Definition/FloatNodeTest.php @@ -0,0 +1,78 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Definition; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Definition\FloatNode; + +class FloatNodeTest extends TestCase +{ + /** + * @dataProvider getValidValues + */ + public function testNormalize($value) + { + $node = new FloatNode('test'); + $this->assertSame($value, $node->normalize($value)); + } + + /** + * @dataProvider getValidValues + * + * @param int $value + */ + public function testValidNonEmptyValues($value) + { + $node = new FloatNode('test'); + $node->setAllowEmptyValue(false); + + $this->assertSame($value, $node->finalize($value)); + } + + public function getValidValues() + { + return [ + [1798.0], + [-678.987], + [12.56E45], + [0.0], + // Integer are accepted too, they will be cast + [17], + [-10], + [0], + ]; + } + + /** + * @dataProvider getInvalidValues + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidTypeException + */ + public function testNormalizeThrowsExceptionOnInvalidValues($value) + { + $node = new FloatNode('test'); + $node->normalize($value); + } + + public function getInvalidValues() + { + return [ + [null], + [''], + ['foo'], + [true], + [false], + [[]], + [['foo' => 'bar']], + [new \stdClass()], + ]; + } +} diff --git a/lib/symfony/config/Tests/Definition/IntegerNodeTest.php b/lib/symfony/config/Tests/Definition/IntegerNodeTest.php new file mode 100644 index 000000000..b4c17e1cb --- /dev/null +++ b/lib/symfony/config/Tests/Definition/IntegerNodeTest.php @@ -0,0 +1,75 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Definition; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Definition\IntegerNode; + +class IntegerNodeTest extends TestCase +{ + /** + * @dataProvider getValidValues + */ + public function testNormalize($value) + { + $node = new IntegerNode('test'); + $this->assertSame($value, $node->normalize($value)); + } + + /** + * @dataProvider getValidValues + * + * @param int $value + */ + public function testValidNonEmptyValues($value) + { + $node = new IntegerNode('test'); + $node->setAllowEmptyValue(false); + + $this->assertSame($value, $node->finalize($value)); + } + + public function getValidValues() + { + return [ + [1798], + [-678], + [0], + ]; + } + + /** + * @dataProvider getInvalidValues + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidTypeException + */ + public function testNormalizeThrowsExceptionOnInvalidValues($value) + { + $node = new IntegerNode('test'); + $node->normalize($value); + } + + public function getInvalidValues() + { + return [ + [null], + [''], + ['foo'], + [true], + [false], + [0.0], + [0.1], + [[]], + [['foo' => 'bar']], + [new \stdClass()], + ]; + } +} diff --git a/lib/symfony/config/Tests/Definition/MergeTest.php b/lib/symfony/config/Tests/Definition/MergeTest.php new file mode 100644 index 000000000..5d37e137b --- /dev/null +++ b/lib/symfony/config/Tests/Definition/MergeTest.php @@ -0,0 +1,196 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Definition; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Definition\Builder\TreeBuilder; + +class MergeTest extends TestCase +{ + /** + * @expectedException \Symfony\Component\Config\Definition\Exception\ForbiddenOverwriteException + */ + public function testForbiddenOverwrite() + { + $tb = new TreeBuilder(); + $tree = $tb + ->root('root', 'array') + ->children() + ->node('foo', 'scalar') + ->cannotBeOverwritten() + ->end() + ->end() + ->end() + ->buildTree() + ; + + $a = [ + 'foo' => 'bar', + ]; + + $b = [ + 'foo' => 'moo', + ]; + + $tree->merge($a, $b); + } + + public function testUnsetKey() + { + $tb = new TreeBuilder(); + $tree = $tb + ->root('root', 'array') + ->children() + ->node('foo', 'scalar')->end() + ->node('bar', 'scalar')->end() + ->node('unsettable', 'array') + ->canBeUnset() + ->children() + ->node('foo', 'scalar')->end() + ->node('bar', 'scalar')->end() + ->end() + ->end() + ->node('unsetted', 'array') + ->canBeUnset() + ->prototype('scalar')->end() + ->end() + ->end() + ->end() + ->buildTree() + ; + + $a = [ + 'foo' => 'bar', + 'unsettable' => [ + 'foo' => 'a', + 'bar' => 'b', + ], + 'unsetted' => false, + ]; + + $b = [ + 'foo' => 'moo', + 'bar' => 'b', + 'unsettable' => false, + 'unsetted' => ['a', 'b'], + ]; + + $this->assertEquals([ + 'foo' => 'moo', + 'bar' => 'b', + 'unsettable' => false, + 'unsetted' => ['a', 'b'], + ], $tree->merge($a, $b)); + } + + /** + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException + */ + public function testDoesNotAllowNewKeysInSubsequentConfigs() + { + $tb = new TreeBuilder(); + $tree = $tb + ->root('config', 'array') + ->children() + ->node('test', 'array') + ->disallowNewKeysInSubsequentConfigs() + ->useAttributeAsKey('key') + ->prototype('array') + ->children() + ->node('value', 'scalar')->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ->buildTree(); + + $a = [ + 'test' => [ + 'a' => ['value' => 'foo'], + ], + ]; + + $b = [ + 'test' => [ + 'b' => ['value' => 'foo'], + ], + ]; + + $tree->merge($a, $b); + } + + public function testPerformsNoDeepMerging() + { + $tb = new TreeBuilder(); + + $tree = $tb + ->root('config', 'array') + ->children() + ->node('no_deep_merging', 'array') + ->performNoDeepMerging() + ->children() + ->node('foo', 'scalar')->end() + ->node('bar', 'scalar')->end() + ->end() + ->end() + ->end() + ->end() + ->buildTree() + ; + + $a = [ + 'no_deep_merging' => [ + 'foo' => 'a', + 'bar' => 'b', + ], + ]; + + $b = [ + 'no_deep_merging' => [ + 'c' => 'd', + ], + ]; + + $this->assertEquals([ + 'no_deep_merging' => [ + 'c' => 'd', + ], + ], $tree->merge($a, $b)); + } + + public function testPrototypeWithoutAKeyAttribute() + { + $tb = new TreeBuilder(); + + $tree = $tb + ->root('config', 'array') + ->children() + ->arrayNode('append_elements') + ->prototype('scalar')->end() + ->end() + ->end() + ->end() + ->buildTree() + ; + + $a = [ + 'append_elements' => ['a', 'b'], + ]; + + $b = [ + 'append_elements' => ['c', 'd'], + ]; + + $this->assertEquals(['append_elements' => ['a', 'b', 'c', 'd']], $tree->merge($a, $b)); + } +} diff --git a/lib/symfony/config/Tests/Definition/NormalizationTest.php b/lib/symfony/config/Tests/Definition/NormalizationTest.php new file mode 100644 index 000000000..d6544ccc8 --- /dev/null +++ b/lib/symfony/config/Tests/Definition/NormalizationTest.php @@ -0,0 +1,230 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Definition; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Definition\Builder\TreeBuilder; +use Symfony\Component\Config\Definition\NodeInterface; + +class NormalizationTest extends TestCase +{ + /** + * @dataProvider getEncoderTests + */ + public function testNormalizeEncoders($denormalized) + { + $tb = new TreeBuilder(); + $tree = $tb + ->root('root_name', 'array') + ->fixXmlConfig('encoder') + ->children() + ->node('encoders', 'array') + ->useAttributeAsKey('class') + ->prototype('array') + ->beforeNormalization()->ifString()->then(function ($v) { return ['algorithm' => $v]; })->end() + ->children() + ->node('algorithm', 'scalar')->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ->buildTree() + ; + + $normalized = [ + 'encoders' => [ + 'foo' => ['algorithm' => 'plaintext'], + ], + ]; + + $this->assertNormalized($tree, $denormalized, $normalized); + } + + public function getEncoderTests() + { + $configs = []; + + // XML + $configs[] = [ + 'encoder' => [ + ['class' => 'foo', 'algorithm' => 'plaintext'], + ], + ]; + + // XML when only one element of this type + $configs[] = [ + 'encoder' => ['class' => 'foo', 'algorithm' => 'plaintext'], + ]; + + // YAML/PHP + $configs[] = [ + 'encoders' => [ + ['class' => 'foo', 'algorithm' => 'plaintext'], + ], + ]; + + // YAML/PHP + $configs[] = [ + 'encoders' => [ + 'foo' => 'plaintext', + ], + ]; + + // YAML/PHP + $configs[] = [ + 'encoders' => [ + 'foo' => ['algorithm' => 'plaintext'], + ], + ]; + + return array_map(function ($v) { + return [$v]; + }, $configs); + } + + /** + * @dataProvider getAnonymousKeysTests + */ + public function testAnonymousKeysArray($denormalized) + { + $tb = new TreeBuilder(); + $tree = $tb + ->root('root', 'array') + ->children() + ->node('logout', 'array') + ->fixXmlConfig('handler') + ->children() + ->node('handlers', 'array') + ->prototype('scalar')->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ->buildTree() + ; + + $normalized = ['logout' => ['handlers' => ['a', 'b', 'c']]]; + + $this->assertNormalized($tree, $denormalized, $normalized); + } + + public function getAnonymousKeysTests() + { + $configs = []; + + $configs[] = [ + 'logout' => [ + 'handlers' => ['a', 'b', 'c'], + ], + ]; + + $configs[] = [ + 'logout' => [ + 'handler' => ['a', 'b', 'c'], + ], + ]; + + return array_map(function ($v) { return [$v]; }, $configs); + } + + /** + * @dataProvider getNumericKeysTests + */ + public function testNumericKeysAsAttributes($denormalized) + { + $normalized = [ + 'thing' => [42 => ['foo', 'bar'], 1337 => ['baz', 'qux']], + ]; + + $this->assertNormalized($this->getNumericKeysTestTree(), $denormalized, $normalized); + } + + public function getNumericKeysTests() + { + $configs = []; + + $configs[] = [ + 'thing' => [ + 42 => ['foo', 'bar'], 1337 => ['baz', 'qux'], + ], + ]; + + $configs[] = [ + 'thing' => [ + ['foo', 'bar', 'id' => 42], ['baz', 'qux', 'id' => 1337], + ], + ]; + + return array_map(function ($v) { return [$v]; }, $configs); + } + + /** + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException + * @expectedExceptionMessage The attribute "id" must be set for path "root.thing". + */ + public function testNonAssociativeArrayThrowsExceptionIfAttributeNotSet() + { + $denormalized = [ + 'thing' => [ + ['foo', 'bar'], ['baz', 'qux'], + ], + ]; + + $this->assertNormalized($this->getNumericKeysTestTree(), $denormalized, []); + } + + public function testAssociativeArrayPreserveKeys() + { + $tb = new TreeBuilder(); + $tree = $tb + ->root('root', 'array') + ->prototype('array') + ->children() + ->node('foo', 'scalar')->end() + ->end() + ->end() + ->end() + ->buildTree() + ; + + $data = ['first' => ['foo' => 'bar']]; + + $this->assertNormalized($tree, $data, $data); + } + + public static function assertNormalized(NodeInterface $tree, $denormalized, $normalized) + { + self::assertSame($normalized, $tree->normalize($denormalized)); + } + + private function getNumericKeysTestTree() + { + $tb = new TreeBuilder(); + $tree = $tb + ->root('root', 'array') + ->children() + ->node('thing', 'array') + ->useAttributeAsKey('id') + ->prototype('array') + ->prototype('scalar')->end() + ->end() + ->end() + ->end() + ->end() + ->buildTree() + ; + + return $tree; + } +} diff --git a/lib/symfony/config/Tests/Definition/PrototypedArrayNodeTest.php b/lib/symfony/config/Tests/Definition/PrototypedArrayNodeTest.php new file mode 100644 index 000000000..6478bd12d --- /dev/null +++ b/lib/symfony/config/Tests/Definition/PrototypedArrayNodeTest.php @@ -0,0 +1,342 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Definition; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Definition\ArrayNode; +use Symfony\Component\Config\Definition\PrototypedArrayNode; +use Symfony\Component\Config\Definition\ScalarNode; +use Symfony\Component\Config\Definition\VariableNode; + +class PrototypedArrayNodeTest extends TestCase +{ + public function testGetDefaultValueReturnsAnEmptyArrayForPrototypes() + { + $node = new PrototypedArrayNode('root'); + $prototype = new ArrayNode(null, $node); + $node->setPrototype($prototype); + $this->assertEmpty($node->getDefaultValue()); + } + + public function testGetDefaultValueReturnsDefaultValueForPrototypes() + { + $node = new PrototypedArrayNode('root'); + $prototype = new ArrayNode(null, $node); + $node->setPrototype($prototype); + $node->setDefaultValue(['test']); + $this->assertEquals(['test'], $node->getDefaultValue()); + } + + // a remapped key (e.g. "mapping" -> "mappings") should be unset after being used + public function testRemappedKeysAreUnset() + { + $node = new ArrayNode('root'); + $mappingsNode = new PrototypedArrayNode('mappings'); + $node->addChild($mappingsNode); + + // each item under mappings is just a scalar + $prototype = new ScalarNode(null, $mappingsNode); + $mappingsNode->setPrototype($prototype); + + $remappings = []; + $remappings[] = ['mapping', 'mappings']; + $node->setXmlRemappings($remappings); + + $normalized = $node->normalize(['mapping' => ['foo', 'bar']]); + $this->assertEquals(['mappings' => ['foo', 'bar']], $normalized); + } + + /** + * Tests that when a key attribute is mapped, that key is removed from the array. + * + * + * + * + * The above should finally be mapped to an array that looks like this + * (because "id" is the key attribute). + * + * [ + * 'things' => [ + * 'option1' => 'foo', + * 'option2' => 'bar', + * ] + * ] + */ + public function testMappedAttributeKeyIsRemoved() + { + $node = new PrototypedArrayNode('root'); + $node->setKeyAttribute('id', true); + + // each item under the root is an array, with one scalar item + $prototype = new ArrayNode(null, $node); + $prototype->addChild(new ScalarNode('foo')); + $node->setPrototype($prototype); + + $children = []; + $children[] = ['id' => 'item_name', 'foo' => 'bar']; + $normalized = $node->normalize($children); + + $expected = []; + $expected['item_name'] = ['foo' => 'bar']; + $this->assertEquals($expected, $normalized); + } + + /** + * Tests the opposite of the testMappedAttributeKeyIsRemoved because + * the removal can be toggled with an option. + */ + public function testMappedAttributeKeyNotRemoved() + { + $node = new PrototypedArrayNode('root'); + $node->setKeyAttribute('id', false); + + // each item under the root is an array, with two scalar items + $prototype = new ArrayNode(null, $node); + $prototype->addChild(new ScalarNode('foo')); + $prototype->addChild(new ScalarNode('id')); // the key attribute will remain + $node->setPrototype($prototype); + + $children = []; + $children[] = ['id' => 'item_name', 'foo' => 'bar']; + $normalized = $node->normalize($children); + + $expected = []; + $expected['item_name'] = ['id' => 'item_name', 'foo' => 'bar']; + $this->assertEquals($expected, $normalized); + } + + public function testAddDefaultChildren() + { + $node = $this->getPrototypeNodeWithDefaultChildren(); + $node->setAddChildrenIfNoneSet(); + $this->assertTrue($node->hasDefaultValue()); + $this->assertEquals([['foo' => 'bar']], $node->getDefaultValue()); + + $node = $this->getPrototypeNodeWithDefaultChildren(); + $node->setKeyAttribute('foobar'); + $node->setAddChildrenIfNoneSet(); + $this->assertTrue($node->hasDefaultValue()); + $this->assertEquals(['defaults' => ['foo' => 'bar']], $node->getDefaultValue()); + + $node = $this->getPrototypeNodeWithDefaultChildren(); + $node->setKeyAttribute('foobar'); + $node->setAddChildrenIfNoneSet('defaultkey'); + $this->assertTrue($node->hasDefaultValue()); + $this->assertEquals(['defaultkey' => ['foo' => 'bar']], $node->getDefaultValue()); + + $node = $this->getPrototypeNodeWithDefaultChildren(); + $node->setKeyAttribute('foobar'); + $node->setAddChildrenIfNoneSet(['defaultkey']); + $this->assertTrue($node->hasDefaultValue()); + $this->assertEquals(['defaultkey' => ['foo' => 'bar']], $node->getDefaultValue()); + + $node = $this->getPrototypeNodeWithDefaultChildren(); + $node->setKeyAttribute('foobar'); + $node->setAddChildrenIfNoneSet(['dk1', 'dk2']); + $this->assertTrue($node->hasDefaultValue()); + $this->assertEquals(['dk1' => ['foo' => 'bar'], 'dk2' => ['foo' => 'bar']], $node->getDefaultValue()); + + $node = $this->getPrototypeNodeWithDefaultChildren(); + $node->setAddChildrenIfNoneSet([5, 6]); + $this->assertTrue($node->hasDefaultValue()); + $this->assertEquals([0 => ['foo' => 'bar'], 1 => ['foo' => 'bar']], $node->getDefaultValue()); + + $node = $this->getPrototypeNodeWithDefaultChildren(); + $node->setAddChildrenIfNoneSet(2); + $this->assertTrue($node->hasDefaultValue()); + $this->assertEquals([['foo' => 'bar'], ['foo' => 'bar']], $node->getDefaultValue()); + } + + public function testDefaultChildrenWinsOverDefaultValue() + { + $node = $this->getPrototypeNodeWithDefaultChildren(); + $node->setAddChildrenIfNoneSet(); + $node->setDefaultValue(['bar' => 'foo']); + $this->assertTrue($node->hasDefaultValue()); + $this->assertEquals([['foo' => 'bar']], $node->getDefaultValue()); + } + + protected function getPrototypeNodeWithDefaultChildren() + { + $node = new PrototypedArrayNode('root'); + $prototype = new ArrayNode(null, $node); + $child = new ScalarNode('foo'); + $child->setDefaultValue('bar'); + $prototype->addChild($child); + $prototype->setAddIfNotSet(true); + $node->setPrototype($prototype); + + return $node; + } + + /** + * Tests that when a key attribute is mapped, that key is removed from the array. + * And if only 'value' element is left in the array, it will replace its wrapper array. + * + * + * + * + * The above should finally be mapped to an array that looks like this + * (because "id" is the key attribute). + * + * [ + * 'things' => [ + * 'option1' => 'value1' + * ] + * ] + * + * It's also possible to mix 'value-only' and 'non-value-only' elements in the array. + * + * + * + * + * The above should finally be mapped to an array as follows + * + * [ + * 'things' => [ + * 'option1' => 'value1', + * 'option2' => [ + * 'value' => 'value2', + * 'foo' => 'foo2' + * ] + * ] + * ] + * + * The 'value' element can also be ArrayNode: + * + * + * + * + * + * The above should be finally be mapped to an array as follows + * + * [ + * 'things' => [ + * 'option1' => [ + * 'foo' => 'foo1', + * 'bar' => 'bar1' + * ] + * ] + * ] + * + * If using VariableNode for value node, it's also possible to mix different types of value nodes: + * + * + * + * + * + * The above should be finally mapped to an array as follows + * + * [ + * 'things' => [ + * 'option1' => [ + * 'foo' => 'foo1', + * 'bar' => 'bar1' + * ], + * 'option2' => 'value2' + * ] + * ] + * + * + * @dataProvider getDataForKeyRemovedLeftValueOnly + */ + public function testMappedAttributeKeyIsRemovedLeftValueOnly($value, $children, $expected) + { + $node = new PrototypedArrayNode('root'); + $node->setKeyAttribute('id', true); + + // each item under the root is an array, with one scalar item + $prototype = new ArrayNode(null, $node); + $prototype->addChild(new ScalarNode('id')); + $prototype->addChild(new ScalarNode('foo')); + $prototype->addChild($value); + $node->setPrototype($prototype); + + $normalized = $node->normalize($children); + $this->assertEquals($expected, $normalized); + } + + public function getDataForKeyRemovedLeftValueOnly() + { + $scalarValue = new ScalarNode('value'); + + $arrayValue = new ArrayNode('value'); + $arrayValue->addChild(new ScalarNode('foo')); + $arrayValue->addChild(new ScalarNode('bar')); + + $variableValue = new VariableNode('value'); + + return [ + [ + $scalarValue, + [ + ['id' => 'option1', 'value' => 'value1'], + ], + ['option1' => 'value1'], + ], + + [ + $scalarValue, + [ + ['id' => 'option1', 'value' => 'value1'], + ['id' => 'option2', 'value' => 'value2', 'foo' => 'foo2'], + ], + [ + 'option1' => 'value1', + 'option2' => ['value' => 'value2', 'foo' => 'foo2'], + ], + ], + + [ + $arrayValue, + [ + [ + 'id' => 'option1', + 'value' => ['foo' => 'foo1', 'bar' => 'bar1'], + ], + ], + [ + 'option1' => ['foo' => 'foo1', 'bar' => 'bar1'], + ], + ], + + [$variableValue, + [ + [ + 'id' => 'option1', 'value' => ['foo' => 'foo1', 'bar' => 'bar1'], + ], + ['id' => 'option2', 'value' => 'value2'], + ], + [ + 'option1' => ['foo' => 'foo1', 'bar' => 'bar1'], + 'option2' => 'value2', + ], + ], + ]; + } +} diff --git a/lib/symfony/config/Tests/Definition/ScalarNodeTest.php b/lib/symfony/config/Tests/Definition/ScalarNodeTest.php new file mode 100644 index 000000000..ada5b04be --- /dev/null +++ b/lib/symfony/config/Tests/Definition/ScalarNodeTest.php @@ -0,0 +1,169 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Definition; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Definition\ArrayNode; +use Symfony\Component\Config\Definition\ScalarNode; + +class ScalarNodeTest extends TestCase +{ + /** + * @dataProvider getValidValues + */ + public function testNormalize($value) + { + $node = new ScalarNode('test'); + $this->assertSame($value, $node->normalize($value)); + } + + public function getValidValues() + { + return [ + [false], + [true], + [null], + [''], + ['foo'], + [0], + [1], + [0.0], + [0.1], + ]; + } + + public function testSetDeprecated() + { + $childNode = new ScalarNode('foo'); + $childNode->setDeprecated('"%node%" is deprecated'); + + $this->assertTrue($childNode->isDeprecated()); + $this->assertSame('"foo" is deprecated', $childNode->getDeprecationMessage($childNode->getName(), $childNode->getPath())); + + $node = new ArrayNode('root'); + $node->addChild($childNode); + + $deprecationTriggered = 0; + $deprecationHandler = function ($level, $message, $file, $line) use (&$prevErrorHandler, &$deprecationTriggered) { + if (E_USER_DEPRECATED === $level) { + return ++$deprecationTriggered; + } + + return $prevErrorHandler ? $prevErrorHandler($level, $message, $file, $line) : false; + }; + + $prevErrorHandler = set_error_handler($deprecationHandler); + $node->finalize([]); + restore_error_handler(); + $this->assertSame(0, $deprecationTriggered, '->finalize() should not trigger if the deprecated node is not set'); + + $prevErrorHandler = set_error_handler($deprecationHandler); + $node->finalize(['foo' => '']); + restore_error_handler(); + $this->assertSame(1, $deprecationTriggered, '->finalize() should trigger if the deprecated node is set'); + } + + /** + * @dataProvider getInvalidValues + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidTypeException + */ + public function testNormalizeThrowsExceptionOnInvalidValues($value) + { + $node = new ScalarNode('test'); + $node->normalize($value); + } + + public function getInvalidValues() + { + return [ + [[]], + [['foo' => 'bar']], + [new \stdClass()], + ]; + } + + public function testNormalizeThrowsExceptionWithoutHint() + { + $node = new ScalarNode('test'); + + if (method_exists($this, 'expectException')) { + $this->expectException('Symfony\Component\Config\Definition\Exception\InvalidTypeException'); + $this->expectExceptionMessage('Invalid type for path "test". Expected scalar, but got array.'); + } else { + $this->setExpectedException('Symfony\Component\Config\Definition\Exception\InvalidTypeException', 'Invalid type for path "test". Expected scalar, but got array.'); + } + + $node->normalize([]); + } + + public function testNormalizeThrowsExceptionWithErrorMessage() + { + $node = new ScalarNode('test'); + $node->setInfo('"the test value"'); + + if (method_exists($this, 'expectException')) { + $this->expectException('Symfony\Component\Config\Definition\Exception\InvalidTypeException'); + $this->expectExceptionMessage("Invalid type for path \"test\". Expected scalar, but got array.\nHint: \"the test value\""); + } else { + $this->setExpectedException('Symfony\Component\Config\Definition\Exception\InvalidTypeException', "Invalid type for path \"test\". Expected scalar, but got array.\nHint: \"the test value\""); + } + + $node->normalize([]); + } + + /** + * @dataProvider getValidNonEmptyValues + * + * @param mixed $value + */ + public function testValidNonEmptyValues($value) + { + $node = new ScalarNode('test'); + $node->setAllowEmptyValue(false); + + $this->assertSame($value, $node->finalize($value)); + } + + public function getValidNonEmptyValues() + { + return [ + [false], + [true], + ['foo'], + [0], + [1], + [0.0], + [0.1], + ]; + } + + /** + * @dataProvider getEmptyValues + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException + * + * @param mixed $value + */ + public function testNotAllowedEmptyValuesThrowException($value) + { + $node = new ScalarNode('test'); + $node->setAllowEmptyValue(false); + $node->finalize($value); + } + + public function getEmptyValues() + { + return [ + [null], + [''], + ]; + } +} diff --git a/lib/symfony/config/Tests/DependencyInjection/ConfigCachePassTest.php b/lib/symfony/config/Tests/DependencyInjection/ConfigCachePassTest.php new file mode 100644 index 000000000..c2b95195d --- /dev/null +++ b/lib/symfony/config/Tests/DependencyInjection/ConfigCachePassTest.php @@ -0,0 +1,59 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\DependencyInjection; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\DependencyInjection\ConfigCachePass; +use Symfony\Component\DependencyInjection\Argument\IteratorArgument; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +/** + * @group legacy + */ +class ConfigCachePassTest extends TestCase +{ + public function testThatCheckersAreProcessedInPriorityOrder() + { + $container = new ContainerBuilder(); + + $definition = $container->register('config_cache_factory')->addArgument(null); + $container->register('checker_2')->addTag('config_cache.resource_checker', ['priority' => 100]); + $container->register('checker_1')->addTag('config_cache.resource_checker', ['priority' => 200]); + $container->register('checker_3')->addTag('config_cache.resource_checker'); + + $pass = new ConfigCachePass(); + $pass->process($container); + + $expected = new IteratorArgument([ + new Reference('checker_1'), + new Reference('checker_2'), + new Reference('checker_3'), + ]); + $this->assertEquals($expected, $definition->getArgument(0)); + } + + public function testThatCheckersCanBeMissing() + { + $container = new ContainerBuilder(); + + $definitionsBefore = \count($container->getDefinitions()); + $aliasesBefore = \count($container->getAliases()); + + $pass = new ConfigCachePass(); + $pass->process($container); + + // the container is untouched (i.e. no new definitions or aliases) + $this->assertCount($definitionsBefore, $container->getDefinitions()); + $this->assertCount($aliasesBefore, $container->getAliases()); + } +} diff --git a/lib/symfony/config/Tests/Exception/FileLoaderLoadExceptionTest.php b/lib/symfony/config/Tests/Exception/FileLoaderLoadExceptionTest.php new file mode 100644 index 000000000..8363084c1 --- /dev/null +++ b/lib/symfony/config/Tests/Exception/FileLoaderLoadExceptionTest.php @@ -0,0 +1,98 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Exception; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Exception\FileLoaderLoadException; + +class FileLoaderLoadExceptionTest extends TestCase +{ + public function testMessageCannotLoadResource() + { + $exception = new FileLoaderLoadException('resource', null); + $this->assertEquals('Cannot load resource "resource".', $exception->getMessage()); + } + + public function testMessageCannotLoadResourceWithType() + { + $exception = new FileLoaderLoadException('resource', null, null, null, 'foobar'); + $this->assertEquals('Cannot load resource "resource". Make sure there is a loader supporting the "foobar" type.', $exception->getMessage()); + } + + public function testMessageCannotLoadResourceWithAnnotationType() + { + $exception = new FileLoaderLoadException('resource', null, null, null, 'annotation'); + $this->assertEquals('Cannot load resource "resource". Make sure annotations are installed and enabled.', $exception->getMessage()); + } + + public function testMessageCannotImportResourceFromSource() + { + $exception = new FileLoaderLoadException('resource', 'sourceResource'); + $this->assertEquals('Cannot import resource "resource" from "sourceResource".', $exception->getMessage()); + } + + public function testMessageCannotImportBundleResource() + { + $exception = new FileLoaderLoadException('@resource', 'sourceResource'); + $this->assertEquals( + 'Cannot import resource "@resource" from "sourceResource". '. + 'Make sure the "resource" bundle is correctly registered and loaded in the application kernel class. '. + 'If the bundle is registered, make sure the bundle path "@resource" is not empty.', + $exception->getMessage() + ); + } + + public function testMessageHasPreviousErrorWithDotAndUnableToLoad() + { + $exception = new FileLoaderLoadException( + 'resource', + null, + null, + new \Exception('There was a previous error with an ending dot.') + ); + $this->assertEquals( + 'There was a previous error with an ending dot in resource (which is loaded in resource "resource").', + $exception->getMessage() + ); + } + + public function testMessageHasPreviousErrorWithoutDotAndUnableToLoad() + { + $exception = new FileLoaderLoadException( + 'resource', + null, + null, + new \Exception('There was a previous error with no ending dot') + ); + $this->assertEquals( + 'There was a previous error with no ending dot in resource (which is loaded in resource "resource").', + $exception->getMessage() + ); + } + + public function testMessageHasPreviousErrorAndUnableToLoadBundle() + { + $exception = new FileLoaderLoadException( + '@resource', + null, + null, + new \Exception('There was a previous error with an ending dot.') + ); + $this->assertEquals( + 'There was a previous error with an ending dot in @resource '. + '(which is loaded in resource "@resource"). '. + 'Make sure the "resource" bundle is correctly registered and loaded in the application kernel class. '. + 'If the bundle is registered, make sure the bundle path "@resource" is not empty.', + $exception->getMessage() + ); + } +} diff --git a/lib/symfony/config/Tests/FileLocatorTest.php b/lib/symfony/config/Tests/FileLocatorTest.php new file mode 100644 index 000000000..0bd97f7d8 --- /dev/null +++ b/lib/symfony/config/Tests/FileLocatorTest.php @@ -0,0 +1,120 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\FileLocator; + +class FileLocatorTest extends TestCase +{ + /** + * @dataProvider getIsAbsolutePathTests + */ + public function testIsAbsolutePath($path) + { + $loader = new FileLocator([]); + $r = new \ReflectionObject($loader); + $m = $r->getMethod('isAbsolutePath'); + $m->setAccessible(true); + + $this->assertTrue($m->invoke($loader, $path), '->isAbsolutePath() returns true for an absolute path'); + } + + public function getIsAbsolutePathTests() + { + return [ + ['/foo.xml'], + ['c:\\\\foo.xml'], + ['c:/foo.xml'], + ['\\server\\foo.xml'], + ['https://server/foo.xml'], + ['phar://server/foo.xml'], + ]; + } + + public function testLocate() + { + $loader = new FileLocator(__DIR__.'/Fixtures'); + + $this->assertEquals( + __DIR__.\DIRECTORY_SEPARATOR.'FileLocatorTest.php', + $loader->locate('FileLocatorTest.php', __DIR__), + '->locate() returns the absolute filename if the file exists in the given path' + ); + + $this->assertEquals( + __DIR__.'/Fixtures'.\DIRECTORY_SEPARATOR.'foo.xml', + $loader->locate('foo.xml', __DIR__), + '->locate() returns the absolute filename if the file exists in one of the paths given in the constructor' + ); + + $this->assertEquals( + __DIR__.'/Fixtures'.\DIRECTORY_SEPARATOR.'foo.xml', + $loader->locate(__DIR__.'/Fixtures'.\DIRECTORY_SEPARATOR.'foo.xml', __DIR__), + '->locate() returns the absolute filename if the file exists in one of the paths given in the constructor' + ); + + $loader = new FileLocator([__DIR__.'/Fixtures', __DIR__.'/Fixtures/Again']); + + $this->assertEquals( + [__DIR__.'/Fixtures'.\DIRECTORY_SEPARATOR.'foo.xml', __DIR__.'/Fixtures/Again'.\DIRECTORY_SEPARATOR.'foo.xml'], + $loader->locate('foo.xml', __DIR__, false), + '->locate() returns an array of absolute filenames' + ); + + $this->assertEquals( + [__DIR__.'/Fixtures'.\DIRECTORY_SEPARATOR.'foo.xml', __DIR__.'/Fixtures/Again'.\DIRECTORY_SEPARATOR.'foo.xml'], + $loader->locate('foo.xml', __DIR__.'/Fixtures', false), + '->locate() returns an array of absolute filenames' + ); + + $loader = new FileLocator(__DIR__.'/Fixtures/Again'); + + $this->assertEquals( + [__DIR__.'/Fixtures'.\DIRECTORY_SEPARATOR.'foo.xml', __DIR__.'/Fixtures/Again'.\DIRECTORY_SEPARATOR.'foo.xml'], + $loader->locate('foo.xml', __DIR__.'/Fixtures', false), + '->locate() returns an array of absolute filenames' + ); + } + + /** + * @expectedException \Symfony\Component\Config\Exception\FileLocatorFileNotFoundException + * @expectedExceptionMessage The file "foobar.xml" does not exist + */ + public function testLocateThrowsAnExceptionIfTheFileDoesNotExists() + { + $loader = new FileLocator([__DIR__.'/Fixtures']); + + $loader->locate('foobar.xml', __DIR__); + } + + /** + * @expectedException \Symfony\Component\Config\Exception\FileLocatorFileNotFoundException + */ + public function testLocateThrowsAnExceptionIfTheFileDoesNotExistsInAbsolutePath() + { + $loader = new FileLocator([__DIR__.'/Fixtures']); + + $loader->locate(__DIR__.'/Fixtures/foobar.xml', __DIR__); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage An empty file name is not valid to be located. + */ + public function testLocateEmpty() + { + $loader = new FileLocator([__DIR__.'/Fixtures']); + + $loader->locate(null, __DIR__); + } +} diff --git a/lib/symfony/config/Tests/Fixtures/Again/foo.xml b/lib/symfony/config/Tests/Fixtures/Again/foo.xml new file mode 100644 index 000000000..e69de29bb diff --git a/lib/symfony/config/Tests/Fixtures/BadParent.php b/lib/symfony/config/Tests/Fixtures/BadParent.php new file mode 100644 index 000000000..68d7296ed --- /dev/null +++ b/lib/symfony/config/Tests/Fixtures/BadParent.php @@ -0,0 +1,7 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Fixtures; + +use Symfony\Component\Config\Definition\ArrayNode; + +class BarNode extends ArrayNode +{ +} diff --git a/lib/symfony/config/Tests/Fixtures/Builder/BarNodeDefinition.php b/lib/symfony/config/Tests/Fixtures/Builder/BarNodeDefinition.php new file mode 100644 index 000000000..b9c62e537 --- /dev/null +++ b/lib/symfony/config/Tests/Fixtures/Builder/BarNodeDefinition.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Fixtures\Builder; + +use Symfony\Component\Config\Definition\Builder\NodeDefinition; +use Symfony\Component\Config\Tests\Fixtures\BarNode; + +class BarNodeDefinition extends NodeDefinition +{ + protected function createNode() + { + return new BarNode($this->name); + } +} diff --git a/lib/symfony/config/Tests/Fixtures/Builder/NodeBuilder.php b/lib/symfony/config/Tests/Fixtures/Builder/NodeBuilder.php new file mode 100644 index 000000000..22b8b32fb --- /dev/null +++ b/lib/symfony/config/Tests/Fixtures/Builder/NodeBuilder.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Fixtures\Builder; + +use Symfony\Component\Config\Definition\Builder\NodeBuilder as BaseNodeBuilder; + +class NodeBuilder extends BaseNodeBuilder +{ + public function barNode($name) + { + return $this->node($name, 'bar'); + } + + protected function getNodeClass($type) + { + switch ($type) { + case 'variable': + return __NAMESPACE__.'\\'.ucfirst($type).'NodeDefinition'; + case 'bar': + return __NAMESPACE__.'\\'.ucfirst($type).'NodeDefinition'; + default: + return parent::getNodeClass($type); + } + } +} diff --git a/lib/symfony/config/Tests/Fixtures/Builder/VariableNodeDefinition.php b/lib/symfony/config/Tests/Fixtures/Builder/VariableNodeDefinition.php new file mode 100644 index 000000000..6126ed434 --- /dev/null +++ b/lib/symfony/config/Tests/Fixtures/Builder/VariableNodeDefinition.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Fixtures\Builder; + +use Symfony\Component\Config\Definition\Builder\VariableNodeDefinition as BaseVariableNodeDefinition; + +class VariableNodeDefinition extends BaseVariableNodeDefinition +{ +} diff --git a/lib/symfony/config/Tests/Fixtures/Configuration/ExampleConfiguration.php b/lib/symfony/config/Tests/Fixtures/Configuration/ExampleConfiguration.php new file mode 100644 index 000000000..3f02700a1 --- /dev/null +++ b/lib/symfony/config/Tests/Fixtures/Configuration/ExampleConfiguration.php @@ -0,0 +1,102 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Fixtures\Configuration; + +use Symfony\Component\Config\Definition\Builder\TreeBuilder; +use Symfony\Component\Config\Definition\ConfigurationInterface; + +class ExampleConfiguration implements ConfigurationInterface +{ + public function getConfigTreeBuilder() + { + $treeBuilder = new TreeBuilder(); + $rootNode = $treeBuilder->root('acme_root'); + + $rootNode + ->fixXmlConfig('parameter') + ->fixXmlConfig('connection') + ->fixXmlConfig('cms_page') + ->children() + ->booleanNode('boolean')->defaultTrue()->end() + ->scalarNode('scalar_empty')->end() + ->scalarNode('scalar_null')->defaultNull()->end() + ->scalarNode('scalar_true')->defaultTrue()->end() + ->scalarNode('scalar_false')->defaultFalse()->end() + ->scalarNode('scalar_default')->defaultValue('default')->end() + ->scalarNode('scalar_array_empty')->defaultValue([])->end() + ->scalarNode('scalar_array_defaults')->defaultValue(['elem1', 'elem2'])->end() + ->scalarNode('scalar_required')->isRequired()->end() + ->scalarNode('scalar_deprecated')->setDeprecated()->end() + ->scalarNode('scalar_deprecated_with_message')->setDeprecated('Deprecation custom message for "%node%" at "%path%"')->end() + ->scalarNode('node_with_a_looong_name')->end() + ->enumNode('enum_with_default')->values(['this', 'that'])->defaultValue('this')->end() + ->enumNode('enum')->values(['this', 'that'])->end() + ->arrayNode('array') + ->info('some info') + ->canBeUnset() + ->children() + ->scalarNode('child1')->end() + ->scalarNode('child2')->end() + ->scalarNode('child3') + ->info( + "this is a long\n". + "multi-line info text\n". + 'which should be indented' + ) + ->example('example setting') + ->end() + ->end() + ->end() + ->arrayNode('scalar_prototyped') + ->prototype('scalar')->end() + ->end() + ->arrayNode('parameters') + ->useAttributeAsKey('name') + ->prototype('scalar')->info('Parameter name')->end() + ->end() + ->arrayNode('connections') + ->prototype('array') + ->children() + ->scalarNode('user')->end() + ->scalarNode('pass')->end() + ->end() + ->end() + ->end() + ->arrayNode('cms_pages') + ->useAttributeAsKey('page') + ->prototype('array') + ->useAttributeAsKey('locale') + ->prototype('array') + ->children() + ->scalarNode('title')->isRequired()->end() + ->scalarNode('path')->isRequired()->end() + ->end() + ->end() + ->end() + ->end() + ->arrayNode('pipou') + ->useAttributeAsKey('name') + ->prototype('array') + ->prototype('array') + ->children() + ->scalarNode('didou') + ->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ; + + return $treeBuilder; + } +} diff --git a/lib/symfony/config/Tests/Fixtures/Resource/.hiddenFile b/lib/symfony/config/Tests/Fixtures/Resource/.hiddenFile new file mode 100644 index 000000000..e69de29bb diff --git a/lib/symfony/config/Tests/Fixtures/Resource/ConditionalClass.php b/lib/symfony/config/Tests/Fixtures/Resource/ConditionalClass.php new file mode 100644 index 000000000..2ba48c5b0 --- /dev/null +++ b/lib/symfony/config/Tests/Fixtures/Resource/ConditionalClass.php @@ -0,0 +1,9 @@ + +]> + diff --git a/lib/symfony/config/Tests/Fixtures/Util/invalid.xml b/lib/symfony/config/Tests/Fixtures/Util/invalid.xml new file mode 100644 index 000000000..a07af9fd8 --- /dev/null +++ b/lib/symfony/config/Tests/Fixtures/Util/invalid.xml @@ -0,0 +1,2 @@ + + diff --git a/lib/symfony/config/Tests/Fixtures/Util/invalid_schema.xml b/lib/symfony/config/Tests/Fixtures/Util/invalid_schema.xml new file mode 100644 index 000000000..e2725a2c2 --- /dev/null +++ b/lib/symfony/config/Tests/Fixtures/Util/invalid_schema.xml @@ -0,0 +1,2 @@ + + diff --git a/lib/symfony/config/Tests/Fixtures/Util/schema.xsd b/lib/symfony/config/Tests/Fixtures/Util/schema.xsd new file mode 100644 index 000000000..e56820f69 --- /dev/null +++ b/lib/symfony/config/Tests/Fixtures/Util/schema.xsd @@ -0,0 +1,9 @@ + + + + + + diff --git a/lib/symfony/config/Tests/Fixtures/Util/valid.xml b/lib/symfony/config/Tests/Fixtures/Util/valid.xml new file mode 100644 index 000000000..a96bb3826 --- /dev/null +++ b/lib/symfony/config/Tests/Fixtures/Util/valid.xml @@ -0,0 +1,3 @@ + + + diff --git a/lib/symfony/config/Tests/Fixtures/foo.xml b/lib/symfony/config/Tests/Fixtures/foo.xml new file mode 100644 index 000000000..e69de29bb diff --git a/lib/symfony/config/Tests/Loader/DelegatingLoaderTest.php b/lib/symfony/config/Tests/Loader/DelegatingLoaderTest.php new file mode 100644 index 000000000..6556962d1 --- /dev/null +++ b/lib/symfony/config/Tests/Loader/DelegatingLoaderTest.php @@ -0,0 +1,71 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Loader; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Loader\DelegatingLoader; +use Symfony\Component\Config\Loader\LoaderResolver; + +class DelegatingLoaderTest extends TestCase +{ + public function testConstructor() + { + $loader = new DelegatingLoader($resolver = new LoaderResolver()); + $this->assertTrue(true, '__construct() takes a loader resolver as its first argument'); + } + + public function testGetSetResolver() + { + $resolver = new LoaderResolver(); + $loader = new DelegatingLoader($resolver); + $this->assertSame($resolver, $loader->getResolver(), '->getResolver() gets the resolver loader'); + $loader->setResolver($resolver = new LoaderResolver()); + $this->assertSame($resolver, $loader->getResolver(), '->setResolver() sets the resolver loader'); + } + + public function testSupports() + { + $loader1 = $this->getMockBuilder('Symfony\Component\Config\Loader\LoaderInterface')->getMock(); + $loader1->expects($this->once())->method('supports')->willReturn(true); + $loader = new DelegatingLoader(new LoaderResolver([$loader1])); + $this->assertTrue($loader->supports('foo.xml'), '->supports() returns true if the resource is loadable'); + + $loader1 = $this->getMockBuilder('Symfony\Component\Config\Loader\LoaderInterface')->getMock(); + $loader1->expects($this->once())->method('supports')->willReturn(false); + $loader = new DelegatingLoader(new LoaderResolver([$loader1])); + $this->assertFalse($loader->supports('foo.foo'), '->supports() returns false if the resource is not loadable'); + } + + public function testLoad() + { + $loader = $this->getMockBuilder('Symfony\Component\Config\Loader\LoaderInterface')->getMock(); + $loader->expects($this->once())->method('supports')->willReturn(true); + $loader->expects($this->once())->method('load'); + $resolver = new LoaderResolver([$loader]); + $loader = new DelegatingLoader($resolver); + + $loader->load('foo'); + } + + /** + * @expectedException \Symfony\Component\Config\Exception\FileLoaderLoadException + */ + public function testLoadThrowsAnExceptionIfTheResourceCannotBeLoaded() + { + $loader = $this->getMockBuilder('Symfony\Component\Config\Loader\LoaderInterface')->getMock(); + $loader->expects($this->once())->method('supports')->willReturn(false); + $resolver = new LoaderResolver([$loader]); + $loader = new DelegatingLoader($resolver); + + $loader->load('foo'); + } +} diff --git a/lib/symfony/config/Tests/Loader/FileLoaderTest.php b/lib/symfony/config/Tests/Loader/FileLoaderTest.php new file mode 100644 index 000000000..b59ace46f --- /dev/null +++ b/lib/symfony/config/Tests/Loader/FileLoaderTest.php @@ -0,0 +1,128 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Loader; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\FileLocator; +use Symfony\Component\Config\Loader\FileLoader; +use Symfony\Component\Config\Loader\LoaderResolver; + +class FileLoaderTest extends TestCase +{ + public function testImportWithFileLocatorDelegation() + { + $locatorMock = $this->getMockBuilder('Symfony\Component\Config\FileLocatorInterface')->getMock(); + + $locatorMockForAdditionalLoader = $this->getMockBuilder('Symfony\Component\Config\FileLocatorInterface')->getMock(); + $locatorMockForAdditionalLoader->expects($this->any())->method('locate')->will($this->onConsecutiveCalls( + ['path/to/file1'], // Default + ['path/to/file1', 'path/to/file2'], // First is imported + ['path/to/file1', 'path/to/file2'], // Second is imported + ['path/to/file1'], // Exception + ['path/to/file1', 'path/to/file2'] // Exception + )); + + $fileLoader = new TestFileLoader($locatorMock); + $fileLoader->setSupports(false); + $fileLoader->setCurrentDir('.'); + + $additionalLoader = new TestFileLoader($locatorMockForAdditionalLoader); + $additionalLoader->setCurrentDir('.'); + + $fileLoader->setResolver($loaderResolver = new LoaderResolver([$fileLoader, $additionalLoader])); + + // Default case + $this->assertSame('path/to/file1', $fileLoader->import('my_resource')); + + // Check first file is imported if not already loading + $this->assertSame('path/to/file1', $fileLoader->import('my_resource')); + + // Check second file is imported if first is already loading + $fileLoader->addLoading('path/to/file1'); + $this->assertSame('path/to/file2', $fileLoader->import('my_resource')); + + // Check exception throws if first (and only available) file is already loading + try { + $fileLoader->import('my_resource'); + $this->fail('->import() throws a FileLoaderImportCircularReferenceException if the resource is already loading'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\Component\Config\Exception\FileLoaderImportCircularReferenceException', $e, '->import() throws a FileLoaderImportCircularReferenceException if the resource is already loading'); + } + + // Check exception throws if all files are already loading + try { + $fileLoader->addLoading('path/to/file2'); + $fileLoader->import('my_resource'); + $this->fail('->import() throws a FileLoaderImportCircularReferenceException if the resource is already loading'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\Component\Config\Exception\FileLoaderImportCircularReferenceException', $e, '->import() throws a FileLoaderImportCircularReferenceException if the resource is already loading'); + } + } + + public function testImportWithGlobLikeResource() + { + $locatorMock = $this->getMockBuilder('Symfony\Component\Config\FileLocatorInterface')->getMock(); + $loader = new TestFileLoader($locatorMock); + + $this->assertSame('[foo]', $loader->import('[foo]')); + } + + public function testImportWithNoGlobMatch() + { + $locatorMock = $this->getMockBuilder('Symfony\Component\Config\FileLocatorInterface')->getMock(); + $loader = new TestFileLoader($locatorMock); + + $this->assertNull($loader->import('./*.abc')); + } + + public function testImportWithSimpleGlob() + { + $loader = new TestFileLoader(new FileLocator(__DIR__)); + + $this->assertSame(__FILE__, strtr($loader->import('FileLoaderTest.*'), '/', \DIRECTORY_SEPARATOR)); + } +} + +class TestFileLoader extends FileLoader +{ + private $supports = true; + + public function load($resource, $type = null) + { + return $resource; + } + + public function supports($resource, $type = null) + { + return $this->supports; + } + + public function addLoading($resource) + { + self::$loading[$resource] = true; + } + + public function removeLoading($resource) + { + unset(self::$loading[$resource]); + } + + public function clearLoading() + { + self::$loading = []; + } + + public function setSupports($supports) + { + $this->supports = $supports; + } +} diff --git a/lib/symfony/config/Tests/Loader/LoaderResolverTest.php b/lib/symfony/config/Tests/Loader/LoaderResolverTest.php new file mode 100644 index 000000000..aabc2a600 --- /dev/null +++ b/lib/symfony/config/Tests/Loader/LoaderResolverTest.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Loader; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Loader\LoaderResolver; + +class LoaderResolverTest extends TestCase +{ + public function testConstructor() + { + $resolver = new LoaderResolver([ + $loader = $this->getMockBuilder('Symfony\Component\Config\Loader\LoaderInterface')->getMock(), + ]); + + $this->assertEquals([$loader], $resolver->getLoaders(), '__construct() takes an array of loaders as its first argument'); + } + + public function testResolve() + { + $loader = $this->getMockBuilder('Symfony\Component\Config\Loader\LoaderInterface')->getMock(); + $resolver = new LoaderResolver([$loader]); + $this->assertFalse($resolver->resolve('foo.foo'), '->resolve() returns false if no loader is able to load the resource'); + + $loader = $this->getMockBuilder('Symfony\Component\Config\Loader\LoaderInterface')->getMock(); + $loader->expects($this->once())->method('supports')->willReturn(true); + $resolver = new LoaderResolver([$loader]); + $this->assertEquals($loader, $resolver->resolve(function () {}), '->resolve() returns the loader for the given resource'); + } + + public function testLoaders() + { + $resolver = new LoaderResolver(); + $resolver->addLoader($loader = $this->getMockBuilder('Symfony\Component\Config\Loader\LoaderInterface')->getMock()); + + $this->assertEquals([$loader], $resolver->getLoaders(), 'addLoader() adds a loader'); + } +} diff --git a/lib/symfony/config/Tests/Loader/LoaderTest.php b/lib/symfony/config/Tests/Loader/LoaderTest.php new file mode 100644 index 000000000..0c6e3fc02 --- /dev/null +++ b/lib/symfony/config/Tests/Loader/LoaderTest.php @@ -0,0 +1,118 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Loader; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Loader\Loader; + +class LoaderTest extends TestCase +{ + public function testGetSetResolver() + { + $resolver = $this->getMockBuilder('Symfony\Component\Config\Loader\LoaderResolverInterface')->getMock(); + + $loader = new ProjectLoader1(); + $loader->setResolver($resolver); + + $this->assertSame($resolver, $loader->getResolver(), '->setResolver() sets the resolver loader'); + } + + public function testResolve() + { + $resolvedLoader = $this->getMockBuilder('Symfony\Component\Config\Loader\LoaderInterface')->getMock(); + + $resolver = $this->getMockBuilder('Symfony\Component\Config\Loader\LoaderResolverInterface')->getMock(); + $resolver->expects($this->once()) + ->method('resolve') + ->with('foo.xml') + ->willReturn($resolvedLoader); + + $loader = new ProjectLoader1(); + $loader->setResolver($resolver); + + $this->assertSame($loader, $loader->resolve('foo.foo'), '->resolve() finds a loader'); + $this->assertSame($resolvedLoader, $loader->resolve('foo.xml'), '->resolve() finds a loader'); + } + + /** + * @expectedException \Symfony\Component\Config\Exception\FileLoaderLoadException + */ + public function testResolveWhenResolverCannotFindLoader() + { + $resolver = $this->getMockBuilder('Symfony\Component\Config\Loader\LoaderResolverInterface')->getMock(); + $resolver->expects($this->once()) + ->method('resolve') + ->with('FOOBAR') + ->willReturn(false); + + $loader = new ProjectLoader1(); + $loader->setResolver($resolver); + + $loader->resolve('FOOBAR'); + } + + public function testImport() + { + $resolvedLoader = $this->getMockBuilder('Symfony\Component\Config\Loader\LoaderInterface')->getMock(); + $resolvedLoader->expects($this->once()) + ->method('load') + ->with('foo') + ->willReturn('yes'); + + $resolver = $this->getMockBuilder('Symfony\Component\Config\Loader\LoaderResolverInterface')->getMock(); + $resolver->expects($this->once()) + ->method('resolve') + ->with('foo') + ->willReturn($resolvedLoader); + + $loader = new ProjectLoader1(); + $loader->setResolver($resolver); + + $this->assertEquals('yes', $loader->import('foo')); + } + + public function testImportWithType() + { + $resolvedLoader = $this->getMockBuilder('Symfony\Component\Config\Loader\LoaderInterface')->getMock(); + $resolvedLoader->expects($this->once()) + ->method('load') + ->with('foo', 'bar') + ->willReturn('yes'); + + $resolver = $this->getMockBuilder('Symfony\Component\Config\Loader\LoaderResolverInterface')->getMock(); + $resolver->expects($this->once()) + ->method('resolve') + ->with('foo', 'bar') + ->willReturn($resolvedLoader); + + $loader = new ProjectLoader1(); + $loader->setResolver($resolver); + + $this->assertEquals('yes', $loader->import('foo', 'bar')); + } +} + +class ProjectLoader1 extends Loader +{ + public function load($resource, $type = null) + { + } + + public function supports($resource, $type = null) + { + return \is_string($resource) && 'foo' === pathinfo($resource, PATHINFO_EXTENSION); + } + + public function getType() + { + } +} diff --git a/lib/symfony/config/Tests/Resource/ClassExistenceResourceTest.php b/lib/symfony/config/Tests/Resource/ClassExistenceResourceTest.php new file mode 100644 index 000000000..79bc64d69 --- /dev/null +++ b/lib/symfony/config/Tests/Resource/ClassExistenceResourceTest.php @@ -0,0 +1,100 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Resource; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Resource\ClassExistenceResource; +use Symfony\Component\Config\Tests\Fixtures\BadParent; +use Symfony\Component\Config\Tests\Fixtures\Resource\ConditionalClass; + +class ClassExistenceResourceTest extends TestCase +{ + public function testToString() + { + $res = new ClassExistenceResource('BarClass'); + $this->assertSame('BarClass', (string) $res); + } + + public function testGetResource() + { + $res = new ClassExistenceResource('BarClass'); + $this->assertSame('BarClass', $res->getResource()); + } + + public function testIsFreshWhenClassDoesNotExist() + { + $res = new ClassExistenceResource('Symfony\Component\Config\Tests\Fixtures\BarClass'); + + $this->assertTrue($res->isFresh(time())); + + eval(<<assertFalse($res->isFresh(time())); + } + + public function testIsFreshWhenClassExists() + { + $res = new ClassExistenceResource('Symfony\Component\Config\Tests\Resource\ClassExistenceResourceTest'); + + $this->assertTrue($res->isFresh(time())); + } + + public function testExistsKo() + { + spl_autoload_register($autoloader = function ($class) use (&$loadedClass) { $loadedClass = $class; }); + + try { + $res = new ClassExistenceResource('MissingFooClass'); + $this->assertTrue($res->isFresh(0)); + + $this->assertSame('MissingFooClass', $loadedClass); + + $loadedClass = 123; + + $res = new ClassExistenceResource('MissingFooClass', false); + + $this->assertSame(123, $loadedClass); + } finally { + spl_autoload_unregister($autoloader); + } + } + + public function testBadParentWithTimestamp() + { + $res = new ClassExistenceResource(BadParent::class, false); + $this->assertTrue($res->isFresh(time())); + } + + /** + * @expectedException \ReflectionException + * @expectedExceptionMessage Class Symfony\Component\Config\Tests\Fixtures\MissingParent not found + */ + public function testBadParentWithNoTimestamp() + { + $res = new ClassExistenceResource(BadParent::class, false); + $res->isFresh(0); + } + + public function testConditionalClass() + { + $res = new ClassExistenceResource(ConditionalClass::class, false); + + $this->assertFalse($res->isFresh(0)); + } +} diff --git a/lib/symfony/config/Tests/Resource/ComposerResourceTest.php b/lib/symfony/config/Tests/Resource/ComposerResourceTest.php new file mode 100644 index 000000000..6857c766d --- /dev/null +++ b/lib/symfony/config/Tests/Resource/ComposerResourceTest.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Resource; + +use Composer\Autoload\ClassLoader; +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Resource\ComposerResource; + +class ComposerResourceTest extends TestCase +{ + public function testGetVendor() + { + $res = new ComposerResource(); + + $r = new \ReflectionClass(ClassLoader::class); + $found = false; + + foreach ($res->getVendors() as $vendor) { + if ($vendor && 0 === strpos($r->getFileName(), $vendor)) { + $found = true; + break; + } + } + + $this->assertTrue($found); + } + + public function testSerializeUnserialize() + { + $res = new ComposerResource(); + $ser = unserialize(serialize($res)); + + $this->assertTrue($res->isFresh(0)); + $this->assertTrue($ser->isFresh(0)); + + $this->assertEquals($res, $ser); + } +} diff --git a/lib/symfony/config/Tests/Resource/DirectoryResourceTest.php b/lib/symfony/config/Tests/Resource/DirectoryResourceTest.php new file mode 100644 index 000000000..85f6c02ee --- /dev/null +++ b/lib/symfony/config/Tests/Resource/DirectoryResourceTest.php @@ -0,0 +1,183 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Resource; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Resource\DirectoryResource; + +class DirectoryResourceTest extends TestCase +{ + protected $directory; + + protected function setUp() + { + $this->directory = sys_get_temp_dir().\DIRECTORY_SEPARATOR.'symfonyDirectoryIterator'; + if (!file_exists($this->directory)) { + mkdir($this->directory); + } + touch($this->directory.'/tmp.xml'); + } + + protected function tearDown() + { + if (!is_dir($this->directory)) { + return; + } + $this->removeDirectory($this->directory); + } + + protected function removeDirectory($directory) + { + $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($directory), \RecursiveIteratorIterator::CHILD_FIRST); + foreach ($iterator as $path) { + if (preg_match('#[/\\\\]\.\.?$#', $path->__toString())) { + continue; + } + if ($path->isDir()) { + rmdir($path->__toString()); + } else { + unlink($path->__toString()); + } + } + rmdir($directory); + } + + public function testGetResource() + { + $resource = new DirectoryResource($this->directory); + $this->assertSame(realpath($this->directory), $resource->getResource(), '->getResource() returns the path to the resource'); + } + + public function testGetPattern() + { + $resource = new DirectoryResource($this->directory, 'bar'); + $this->assertEquals('bar', $resource->getPattern()); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessageRegExp /The directory ".*" does not exist./ + */ + public function testResourceDoesNotExist() + { + $resource = new DirectoryResource('/____foo/foobar'.mt_rand(1, 999999)); + } + + public function testIsFresh() + { + $resource = new DirectoryResource($this->directory); + $this->assertTrue($resource->isFresh(time() + 10), '->isFresh() returns true if the resource has not changed'); + $this->assertFalse($resource->isFresh(time() - 86400), '->isFresh() returns false if the resource has been updated'); + } + + public function testIsFreshForDeletedResources() + { + $resource = new DirectoryResource($this->directory); + $this->removeDirectory($this->directory); + + $this->assertFalse($resource->isFresh(time()), '->isFresh() returns false if the resource does not exist'); + } + + public function testIsFreshUpdateFile() + { + $resource = new DirectoryResource($this->directory); + touch($this->directory.'/tmp.xml', time() + 20); + $this->assertFalse($resource->isFresh(time() + 10), '->isFresh() returns false if an existing file is modified'); + } + + public function testIsFreshNewFile() + { + $resource = new DirectoryResource($this->directory); + touch($this->directory.'/new.xml', time() + 20); + $this->assertFalse($resource->isFresh(time() + 10), '->isFresh() returns false if a new file is added'); + } + + public function testIsFreshNewFileWithDifferentPattern() + { + $resource = new DirectoryResource($this->directory, '/.xml$/'); + touch($this->directory.'/new.yaml', time() + 20); + $this->assertTrue($resource->isFresh(time() + 10), '->isFresh() returns true if a new file with a non-matching pattern is added'); + } + + public function testIsFreshDeleteFile() + { + $resource = new DirectoryResource($this->directory); + $time = time(); + sleep(1); + unlink($this->directory.'/tmp.xml'); + $this->assertFalse($resource->isFresh($time), '->isFresh() returns false if an existing file is removed'); + } + + public function testIsFreshDeleteDirectory() + { + $resource = new DirectoryResource($this->directory); + $this->removeDirectory($this->directory); + $this->assertFalse($resource->isFresh(time()), '->isFresh() returns false if the whole resource is removed'); + } + + public function testIsFreshCreateFileInSubdirectory() + { + $subdirectory = $this->directory.'/subdirectory'; + mkdir($subdirectory); + + $resource = new DirectoryResource($this->directory); + $this->assertTrue($resource->isFresh(time() + 10), '->isFresh() returns true if an unmodified subdirectory exists'); + + touch($subdirectory.'/newfile.xml', time() + 20); + $this->assertFalse($resource->isFresh(time() + 10), '->isFresh() returns false if a new file in a subdirectory is added'); + } + + public function testIsFreshModifySubdirectory() + { + $resource = new DirectoryResource($this->directory); + + $subdirectory = $this->directory.'/subdirectory'; + mkdir($subdirectory); + touch($subdirectory, time() + 20); + + $this->assertFalse($resource->isFresh(time() + 10), '->isFresh() returns false if a subdirectory is modified (e.g. a file gets deleted)'); + } + + public function testFilterRegexListNoMatch() + { + $resource = new DirectoryResource($this->directory, '/\.(foo|xml)$/'); + + touch($this->directory.'/new.bar', time() + 20); + $this->assertTrue($resource->isFresh(time() + 10), '->isFresh() returns true if a new file not matching the filter regex is created'); + } + + public function testFilterRegexListMatch() + { + $resource = new DirectoryResource($this->directory, '/\.(foo|xml)$/'); + + touch($this->directory.'/new.xml', time() + 20); + $this->assertFalse($resource->isFresh(time() + 10), '->isFresh() returns false if an new file matching the filter regex is created '); + } + + public function testSerializeUnserialize() + { + $resource = new DirectoryResource($this->directory, '/\.(foo|xml)$/'); + + $unserialized = unserialize(serialize($resource)); + + $this->assertSame(realpath($this->directory), $resource->getResource()); + $this->assertSame('/\.(foo|xml)$/', $resource->getPattern()); + } + + public function testResourcesWithDifferentPatternsAreDifferent() + { + $resourceA = new DirectoryResource($this->directory, '/.xml$/'); + $resourceB = new DirectoryResource($this->directory, '/.yaml$/'); + + $this->assertCount(2, array_unique([$resourceA, $resourceB])); + } +} diff --git a/lib/symfony/config/Tests/Resource/FileExistenceResourceTest.php b/lib/symfony/config/Tests/Resource/FileExistenceResourceTest.php new file mode 100644 index 000000000..433f65e82 --- /dev/null +++ b/lib/symfony/config/Tests/Resource/FileExistenceResourceTest.php @@ -0,0 +1,71 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Resource; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Resource\FileExistenceResource; + +class FileExistenceResourceTest extends TestCase +{ + protected $resource; + protected $file; + protected $time; + + protected function setUp() + { + $this->file = realpath(sys_get_temp_dir()).'/tmp.xml'; + $this->time = time(); + $this->resource = new FileExistenceResource($this->file); + } + + protected function tearDown() + { + if (file_exists($this->file)) { + unlink($this->file); + } + } + + public function testToString() + { + $this->assertSame($this->file, (string) $this->resource); + } + + public function testGetResource() + { + $this->assertSame($this->file, $this->resource->getResource(), '->getResource() returns the path to the resource'); + } + + public function testIsFreshWithExistingResource() + { + touch($this->file, $this->time); + $serialized = serialize(new FileExistenceResource($this->file)); + + $resource = unserialize($serialized); + $this->assertTrue($resource->isFresh($this->time), '->isFresh() returns true if the resource is still present'); + + unlink($this->file); + $resource = unserialize($serialized); + $this->assertFalse($resource->isFresh($this->time), '->isFresh() returns false if the resource has been deleted'); + } + + public function testIsFreshWithAbsentResource() + { + $serialized = serialize(new FileExistenceResource($this->file)); + + $resource = unserialize($serialized); + $this->assertTrue($resource->isFresh($this->time), '->isFresh() returns true if the resource is still absent'); + + touch($this->file, $this->time); + $resource = unserialize($serialized); + $this->assertFalse($resource->isFresh($this->time), '->isFresh() returns false if the resource has been created'); + } +} diff --git a/lib/symfony/config/Tests/Resource/FileResourceTest.php b/lib/symfony/config/Tests/Resource/FileResourceTest.php new file mode 100644 index 000000000..97781ffab --- /dev/null +++ b/lib/symfony/config/Tests/Resource/FileResourceTest.php @@ -0,0 +1,85 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Resource; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Resource\FileResource; + +class FileResourceTest extends TestCase +{ + protected $resource; + protected $file; + protected $time; + + protected function setUp() + { + $this->file = sys_get_temp_dir().'/tmp.xml'; + $this->time = time(); + touch($this->file, $this->time); + $this->resource = new FileResource($this->file); + } + + protected function tearDown() + { + if (!file_exists($this->file)) { + return; + } + + unlink($this->file); + } + + public function testGetResource() + { + $this->assertSame(realpath($this->file), $this->resource->getResource(), '->getResource() returns the path to the resource'); + } + + public function testGetResourceWithScheme() + { + $resource = new FileResource('file://'.$this->file); + $this->assertSame('file://'.$this->file, $resource->getResource(), '->getResource() returns the path to the schemed resource'); + } + + public function testToString() + { + $this->assertSame(realpath($this->file), (string) $this->resource); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessageRegExp /The file ".*" does not exist./ + */ + public function testResourceDoesNotExist() + { + $resource = new FileResource('/____foo/foobar'.mt_rand(1, 999999)); + } + + public function testIsFresh() + { + $this->assertTrue($this->resource->isFresh($this->time), '->isFresh() returns true if the resource has not changed in same second'); + $this->assertTrue($this->resource->isFresh($this->time + 10), '->isFresh() returns true if the resource has not changed'); + $this->assertFalse($this->resource->isFresh($this->time - 86400), '->isFresh() returns false if the resource has been updated'); + } + + public function testIsFreshForDeletedResources() + { + unlink($this->file); + + $this->assertFalse($this->resource->isFresh($this->time), '->isFresh() returns false if the resource does not exist'); + } + + public function testSerializeUnserialize() + { + $unserialized = unserialize(serialize($this->resource)); + + $this->assertSame(realpath($this->file), $this->resource->getResource()); + } +} diff --git a/lib/symfony/config/Tests/Resource/GlobResourceTest.php b/lib/symfony/config/Tests/Resource/GlobResourceTest.php new file mode 100644 index 000000000..cfbfd2b45 --- /dev/null +++ b/lib/symfony/config/Tests/Resource/GlobResourceTest.php @@ -0,0 +1,114 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Resource; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Resource\GlobResource; + +class GlobResourceTest extends TestCase +{ + protected function tearDown() + { + $dir = \dirname(__DIR__).'/Fixtures'; + @rmdir($dir.'/TmpGlob'); + @unlink($dir.'/TmpGlob'); + @unlink($dir.'/Resource/TmpGlob'); + touch($dir.'/Resource/.hiddenFile'); + } + + public function testIterator() + { + $dir = \dirname(__DIR__).\DIRECTORY_SEPARATOR.'Fixtures'; + $resource = new GlobResource($dir, '/Resource', true); + + $paths = iterator_to_array($resource); + + $file = $dir.'/Resource'.\DIRECTORY_SEPARATOR.'ConditionalClass.php'; + $this->assertEquals([$file => new \SplFileInfo($file)], $paths); + $this->assertInstanceOf('SplFileInfo', current($paths)); + $this->assertSame($dir, $resource->getPrefix()); + + $resource = new GlobResource($dir, '/**/Resource', true); + + $paths = iterator_to_array($resource); + + $file = $dir.\DIRECTORY_SEPARATOR.'Resource'.\DIRECTORY_SEPARATOR.'ConditionalClass.php'; + $this->assertEquals([$file => $file], $paths); + $this->assertInstanceOf('SplFileInfo', current($paths)); + $this->assertSame($dir, $resource->getPrefix()); + } + + public function testIsFreshNonRecursiveDetectsNewFile() + { + $dir = \dirname(__DIR__).'/Fixtures'; + $resource = new GlobResource($dir, '/*', false); + + $this->assertTrue($resource->isFresh(0)); + + mkdir($dir.'/TmpGlob'); + $this->assertTrue($resource->isFresh(0)); + + rmdir($dir.'/TmpGlob'); + $this->assertTrue($resource->isFresh(0)); + + touch($dir.'/TmpGlob'); + $this->assertFalse($resource->isFresh(0)); + + unlink($dir.'/TmpGlob'); + $this->assertTrue($resource->isFresh(0)); + } + + public function testIsFreshNonRecursiveDetectsRemovedFile() + { + $dir = \dirname(__DIR__).'/Fixtures'; + $resource = new GlobResource($dir, '/*', false); + + touch($dir.'/TmpGlob'); + touch($dir.'/.TmpGlob'); + $this->assertTrue($resource->isFresh(0)); + + unlink($dir.'/.TmpGlob'); + $this->assertTrue($resource->isFresh(0)); + + unlink($dir.'/TmpGlob'); + $this->assertFalse($resource->isFresh(0)); + } + + public function testIsFreshRecursiveDetectsRemovedFile() + { + $dir = \dirname(__DIR__).'/Fixtures'; + $resource = new GlobResource($dir, '/*', true); + + touch($dir.'/Resource/TmpGlob'); + $this->assertTrue($resource->isFresh(0)); + + unlink($dir.'/Resource/TmpGlob'); + $this->assertFalse($resource->isFresh(0)); + + touch($dir.'/Resource/TmpGlob'); + $this->assertTrue($resource->isFresh(0)); + + unlink($dir.'/Resource/.hiddenFile'); + $this->assertTrue($resource->isFresh(0)); + } + + public function testIsFreshRecursiveDetectsNewFile() + { + $dir = \dirname(__DIR__).'/Fixtures'; + $resource = new GlobResource($dir, '/*', true); + + $this->assertTrue($resource->isFresh(0)); + + touch($dir.'/Resource/TmpGlob'); + $this->assertFalse($resource->isFresh(0)); + } +} diff --git a/lib/symfony/config/Tests/Resource/ReflectionClassResourceTest.php b/lib/symfony/config/Tests/Resource/ReflectionClassResourceTest.php new file mode 100644 index 000000000..1f0bcb17b --- /dev/null +++ b/lib/symfony/config/Tests/Resource/ReflectionClassResourceTest.php @@ -0,0 +1,197 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Resource; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Resource\ReflectionClassResource; +use Symfony\Component\DependencyInjection\ServiceSubscriberInterface; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +class ReflectionClassResourceTest extends TestCase +{ + public function testToString() + { + $res = new ReflectionClassResource(new \ReflectionClass('ErrorException')); + + $this->assertSame('reflection.ErrorException', (string) $res); + } + + public function testSerializeUnserialize() + { + $res = new ReflectionClassResource(new \ReflectionClass(DummyInterface::class)); + $ser = unserialize(serialize($res)); + + $this->assertTrue($res->isFresh(0)); + $this->assertTrue($ser->isFresh(0)); + + $this->assertSame((string) $res, (string) $ser); + } + + public function testIsFresh() + { + $res = new ReflectionClassResource(new \ReflectionClass(__CLASS__)); + $mtime = filemtime(__FILE__); + + $this->assertTrue($res->isFresh($mtime), '->isFresh() returns true if the resource has not changed in same second'); + $this->assertTrue($res->isFresh($mtime + 10), '->isFresh() returns true if the resource has not changed'); + $this->assertTrue($res->isFresh($mtime - 86400), '->isFresh() returns true if the resource has not changed'); + } + + public function testIsFreshForDeletedResources() + { + $now = time(); + $tmp = sys_get_temp_dir().'/tmp.php'; + file_put_contents($tmp, 'assertTrue($res->isFresh($now)); + + unlink($tmp); + $this->assertFalse($res->isFresh($now), '->isFresh() returns false if the resource does not exist'); + } + + /** + * @dataProvider provideHashedSignature + */ + public function testHashedSignature($changeExpected, $changedLine, $changedCode) + { + $code = <<<'EOPHP' +/* 0*/ +/* 1*/ class %s extends ErrorException +/* 2*/ { +/* 3*/ const FOO = 123; +/* 4*/ +/* 5*/ public $pub = []; +/* 6*/ +/* 7*/ protected $prot; +/* 8*/ +/* 9*/ private $priv; +/*10*/ +/*11*/ public function pub($arg = null) {} +/*12*/ +/*13*/ protected function prot($a = []) {} +/*14*/ +/*15*/ private function priv() {} +/*16*/ } +EOPHP; + + static $expectedSignature, $generateSignature; + + if (null === $expectedSignature) { + eval(sprintf($code, $class = 'Foo'.str_replace('.', '_', uniqid('', true)))); + $r = new \ReflectionClass(ReflectionClassResource::class); + $generateSignature = $r->getMethod('generateSignature'); + $generateSignature->setAccessible(true); + $generateSignature = $generateSignature->getClosure($r->newInstanceWithoutConstructor()); + $expectedSignature = implode("\n", iterator_to_array($generateSignature(new \ReflectionClass($class)))); + } + + $code = explode("\n", $code); + $code[$changedLine] = $changedCode; + eval(sprintf(implode("\n", $code), $class = 'Foo'.str_replace('.', '_', uniqid('', true)))); + $signature = implode("\n", iterator_to_array($generateSignature(new \ReflectionClass($class)))); + + if ($changeExpected) { + $this->assertNotSame($expectedSignature, $signature); + } else { + $this->assertSame($expectedSignature, $signature); + } + } + + public function provideHashedSignature() + { + yield [0, 0, "// line change\n\n"]; + yield [1, 0, '/** class docblock */']; + yield [1, 1, 'abstract class %s']; + yield [1, 1, 'final class %s']; + yield [1, 1, 'class %s extends Exception']; + yield [1, 1, 'class %s implements '.DummyInterface::class]; + yield [1, 3, 'const FOO = 456;']; + yield [1, 3, 'const BAR = 123;']; + yield [1, 4, '/** pub docblock */']; + yield [1, 5, 'protected $pub = [];']; + yield [1, 5, 'public $pub = [123];']; + yield [1, 6, '/** prot docblock */']; + yield [1, 7, 'private $prot;']; + yield [0, 8, '/** priv docblock */']; + yield [0, 9, 'private $priv = 123;']; + yield [1, 10, '/** pub docblock */']; + if (\PHP_VERSION_ID >= 50600) { + yield [1, 11, 'public function pub(...$arg) {}']; + } + if (\PHP_VERSION_ID >= 70000) { + yield [1, 11, 'public function pub($arg = null): Foo {}']; + } + yield [0, 11, "public function pub(\$arg = null) {\nreturn 123;\n}"]; + yield [1, 12, '/** prot docblock */']; + yield [1, 13, 'protected function prot($a = [123]) {}']; + yield [0, 14, '/** priv docblock */']; + yield [0, 15, '']; + + if (\PHP_VERSION_ID >= 70400) { + // PHP7.4 typed properties without default value are + // undefined, make sure this doesn't throw an error + yield [1, 5, 'public array $pub;']; + yield [0, 7, 'protected int $prot;']; + yield [0, 9, 'private string $priv;']; + } + } + + public function testEventSubscriber() + { + $res = new ReflectionClassResource(new \ReflectionClass(TestEventSubscriber::class)); + $this->assertTrue($res->isFresh(0)); + + TestEventSubscriber::$subscribedEvents = [123]; + $this->assertFalse($res->isFresh(0)); + + $res = new ReflectionClassResource(new \ReflectionClass(TestEventSubscriber::class)); + $this->assertTrue($res->isFresh(0)); + } + + public function testServiceSubscriber() + { + $res = new ReflectionClassResource(new \ReflectionClass(TestServiceSubscriber::class)); + $this->assertTrue($res->isFresh(0)); + + TestServiceSubscriber::$subscribedServices = [123]; + $this->assertFalse($res->isFresh(0)); + + $res = new ReflectionClassResource(new \ReflectionClass(TestServiceSubscriber::class)); + $this->assertTrue($res->isFresh(0)); + } +} + +interface DummyInterface +{ +} + +class TestEventSubscriber implements EventSubscriberInterface +{ + public static $subscribedEvents = []; + + public static function getSubscribedEvents() + { + return self::$subscribedEvents; + } +} + +class TestServiceSubscriber implements ServiceSubscriberInterface +{ + public static $subscribedServices = []; + + public static function getSubscribedServices() + { + return self::$subscribedServices; + } +} diff --git a/lib/symfony/config/Tests/Resource/ResourceStub.php b/lib/symfony/config/Tests/Resource/ResourceStub.php new file mode 100644 index 000000000..b01729cbf --- /dev/null +++ b/lib/symfony/config/Tests/Resource/ResourceStub.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Resource; + +use Symfony\Component\Config\Resource\SelfCheckingResourceInterface; + +class ResourceStub implements SelfCheckingResourceInterface +{ + private $fresh = true; + + public function setFresh($isFresh) + { + $this->fresh = $isFresh; + } + + public function __toString() + { + return 'stub'; + } + + public function isFresh($timestamp) + { + return $this->fresh; + } +} diff --git a/lib/symfony/config/Tests/ResourceCheckerConfigCacheTest.php b/lib/symfony/config/Tests/ResourceCheckerConfigCacheTest.php new file mode 100644 index 000000000..a2c2eeb81 --- /dev/null +++ b/lib/symfony/config/Tests/ResourceCheckerConfigCacheTest.php @@ -0,0 +1,150 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Resource\FileResource; +use Symfony\Component\Config\ResourceCheckerConfigCache; +use Symfony\Component\Config\Tests\Resource\ResourceStub; + +class ResourceCheckerConfigCacheTest extends TestCase +{ + private $cacheFile = null; + + protected function setUp() + { + $this->cacheFile = tempnam(sys_get_temp_dir(), 'config_'); + } + + protected function tearDown() + { + $files = [$this->cacheFile, "{$this->cacheFile}.meta"]; + + foreach ($files as $file) { + if (file_exists($file)) { + unlink($file); + } + } + } + + public function testGetPath() + { + $cache = new ResourceCheckerConfigCache($this->cacheFile); + + $this->assertSame($this->cacheFile, $cache->getPath()); + } + + public function testCacheIsNotFreshIfEmpty() + { + $checker = $this->getMockBuilder('\Symfony\Component\Config\ResourceCheckerInterface')->getMock() + ->expects($this->never())->method('supports'); + + /* If there is nothing in the cache, it needs to be filled (and thus it's not fresh). + It does not matter if you provide checkers or not. */ + + unlink($this->cacheFile); // remove tempnam() side effect + $cache = new ResourceCheckerConfigCache($this->cacheFile, [$checker]); + + $this->assertFalse($cache->isFresh()); + } + + public function testCacheIsFreshIfNoCheckerProvided() + { + /* For example in prod mode, you may choose not to run any checkers + at all. In that case, the cache should always be considered fresh. */ + $cache = new ResourceCheckerConfigCache($this->cacheFile); + $this->assertTrue($cache->isFresh()); + } + + public function testCacheIsFreshIfEmptyCheckerIteratorProvided() + { + $cache = new ResourceCheckerConfigCache($this->cacheFile, new \ArrayIterator([])); + $this->assertTrue($cache->isFresh()); + } + + public function testResourcesWithoutcheckersAreIgnoredAndConsideredFresh() + { + /* As in the previous test, but this time we have a resource. */ + $cache = new ResourceCheckerConfigCache($this->cacheFile); + $cache->write('', [new ResourceStub()]); + + $this->assertTrue($cache->isFresh()); // no (matching) ResourceChecker passed + } + + public function testIsFreshWithchecker() + { + $checker = $this->getMockBuilder('\Symfony\Component\Config\ResourceCheckerInterface')->getMock(); + + $checker->expects($this->once()) + ->method('supports') + ->willReturn(true); + + $checker->expects($this->once()) + ->method('isFresh') + ->willReturn(true); + + $cache = new ResourceCheckerConfigCache($this->cacheFile, [$checker]); + $cache->write('', [new ResourceStub()]); + + $this->assertTrue($cache->isFresh()); + } + + public function testIsNotFreshWithchecker() + { + $checker = $this->getMockBuilder('\Symfony\Component\Config\ResourceCheckerInterface')->getMock(); + + $checker->expects($this->once()) + ->method('supports') + ->willReturn(true); + + $checker->expects($this->once()) + ->method('isFresh') + ->willReturn(false); + + $cache = new ResourceCheckerConfigCache($this->cacheFile, [$checker]); + $cache->write('', [new ResourceStub()]); + + $this->assertFalse($cache->isFresh()); + } + + public function testCacheIsNotFreshWhenUnserializeFails() + { + $checker = $this->getMockBuilder('\Symfony\Component\Config\ResourceCheckerInterface')->getMock(); + $cache = new ResourceCheckerConfigCache($this->cacheFile, [$checker]); + $cache->write('foo', [new FileResource(__FILE__)]); + + $metaFile = "{$this->cacheFile}.meta"; + file_put_contents($metaFile, str_replace('FileResource', 'ClassNotHere', file_get_contents($metaFile))); + + $this->assertFalse($cache->isFresh()); + } + + public function testCacheKeepsContent() + { + $cache = new ResourceCheckerConfigCache($this->cacheFile); + $cache->write('FOOBAR'); + + $this->assertSame('FOOBAR', file_get_contents($cache->getPath())); + } + + public function testCacheIsNotFreshIfNotExistsMetaFile() + { + $checker = $this->getMockBuilder('\Symfony\Component\Config\ResourceCheckerInterface')->getMock(); + $cache = new ResourceCheckerConfigCache($this->cacheFile, [$checker]); + $cache->write('foo', [new FileResource(__FILE__)]); + + $metaFile = "{$this->cacheFile}.meta"; + unlink($metaFile); + + $this->assertFalse($cache->isFresh()); + } +} diff --git a/lib/symfony/config/Tests/Util/XmlUtilsTest.php b/lib/symfony/config/Tests/Util/XmlUtilsTest.php new file mode 100644 index 000000000..0a6637f78 --- /dev/null +++ b/lib/symfony/config/Tests/Util/XmlUtilsTest.php @@ -0,0 +1,217 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Util; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Util\XmlUtils; + +class XmlUtilsTest extends TestCase +{ + public function testLoadFile() + { + $fixtures = __DIR__.'/../Fixtures/Util/'; + + try { + XmlUtils::loadFile($fixtures.'invalid.xml'); + $this->fail(); + } catch (\InvalidArgumentException $e) { + $this->assertContains('ERROR 77', $e->getMessage()); + } + + try { + XmlUtils::loadFile($fixtures.'document_type.xml'); + $this->fail(); + } catch (\InvalidArgumentException $e) { + $this->assertContains('Document types are not allowed', $e->getMessage()); + } + + try { + XmlUtils::loadFile($fixtures.'invalid_schema.xml', $fixtures.'schema.xsd'); + $this->fail(); + } catch (\InvalidArgumentException $e) { + $this->assertContains('ERROR 1845', $e->getMessage()); + } + + try { + XmlUtils::loadFile($fixtures.'invalid_schema.xml', 'invalid_callback_or_file'); + $this->fail(); + } catch (\InvalidArgumentException $e) { + $this->assertContains('XSD file or callable', $e->getMessage()); + } + + $mock = $this->getMockBuilder(__NAMESPACE__.'\Validator')->getMock(); + $mock->expects($this->exactly(2))->method('validate')->will($this->onConsecutiveCalls(false, true)); + + try { + XmlUtils::loadFile($fixtures.'valid.xml', [$mock, 'validate']); + $this->fail(); + } catch (\InvalidArgumentException $e) { + $this->assertRegExp('/The XML file ".+" is not valid\./', $e->getMessage()); + } + + $this->assertInstanceOf('DOMDocument', XmlUtils::loadFile($fixtures.'valid.xml', [$mock, 'validate'])); + $this->assertSame([], libxml_get_errors()); + } + + /** + * @expectedException \Symfony\Component\Config\Util\Exception\InvalidXmlException + * @expectedExceptionMessage The XML is not valid + */ + public function testParseWithInvalidValidatorCallable() + { + $fixtures = __DIR__.'/../Fixtures/Util/'; + + $mock = $this->getMockBuilder(__NAMESPACE__.'\Validator')->getMock(); + $mock->expects($this->once())->method('validate')->willReturn(false); + + XmlUtils::parse(file_get_contents($fixtures.'valid.xml'), [$mock, 'validate']); + } + + public function testLoadFileWithInternalErrorsEnabled() + { + $internalErrors = libxml_use_internal_errors(true); + + $this->assertSame([], libxml_get_errors()); + $this->assertInstanceOf('DOMDocument', XmlUtils::loadFile(__DIR__.'/../Fixtures/Util/invalid_schema.xml')); + $this->assertSame([], libxml_get_errors()); + + libxml_clear_errors(); + libxml_use_internal_errors($internalErrors); + } + + /** + * @dataProvider getDataForConvertDomToArray + */ + public function testConvertDomToArray($expected, $xml, $root = false, $checkPrefix = true) + { + $dom = new \DOMDocument(); + $dom->loadXML($root ? $xml : ''.$xml.''); + + $this->assertSame($expected, XmlUtils::convertDomElementToArray($dom->documentElement, $checkPrefix)); + } + + public function getDataForConvertDomToArray() + { + return [ + [null, ''], + ['bar', 'bar'], + [['bar' => 'foobar'], '', true], + [['foo' => null], ''], + [['foo' => 'bar'], 'bar'], + [['foo' => ['foo' => 'bar']], ''], + [['foo' => ['foo' => 0]], '0'], + [['foo' => ['foo' => 'bar']], 'bar'], + [['foo' => ['foo' => 'bar', 'value' => 'text']], 'text'], + [['foo' => ['attr' => 'bar', 'foo' => 'text']], 'text'], + [['foo' => ['bar', 'text']], 'bartext'], + [['foo' => [['foo' => 'bar'], ['foo' => 'text']]], ''], + [['foo' => ['foo' => ['bar', 'text']]], 'text'], + [['foo' => 'bar'], 'bar'], + [['foo' => 'text'], 'text'], + [['foo' => ['bar' => 'bar', 'value' => 'text']], 'text', false, false], + [['attr' => 1, 'b' => 'hello'], 'hello2', true], + ]; + } + + /** + * @dataProvider getDataForPhpize + */ + public function testPhpize($expected, $value) + { + $this->assertSame($expected, XmlUtils::phpize($value)); + } + + public function getDataForPhpize() + { + return [ + ['', ''], + [null, 'null'], + [true, 'true'], + [false, 'false'], + [null, 'Null'], + [true, 'True'], + [false, 'False'], + [0, '0'], + [1, '1'], + [-1, '-1'], + [0777, '0777'], + [255, '0xFF'], + [100.0, '1e2'], + [-120.0, '-1.2E2'], + [-10100.1, '-10100.1'], + ['-10,100.1', '-10,100.1'], + ['1234 5678 9101 1121 3141', '1234 5678 9101 1121 3141'], + ['1,2,3,4', '1,2,3,4'], + ['11,22,33,44', '11,22,33,44'], + ['11,222,333,4', '11,222,333,4'], + ['1,222,333,444', '1,222,333,444'], + ['11,222,333,444', '11,222,333,444'], + ['111,222,333,444', '111,222,333,444'], + ['1111,2222,3333,4444,5555', '1111,2222,3333,4444,5555'], + ['foo', 'foo'], + [6, '0b0110'], + ]; + } + + public function testLoadEmptyXmlFile() + { + $file = __DIR__.'/../Fixtures/foo.xml'; + + if (method_exists($this, 'expectException')) { + $this->expectException('InvalidArgumentException'); + $this->expectExceptionMessage(sprintf('File %s does not contain valid XML, it is empty.', $file)); + } else { + $this->setExpectedException('InvalidArgumentException', sprintf('File %s does not contain valid XML, it is empty.', $file)); + } + + XmlUtils::loadFile($file); + } + + // test for issue https://github.com/symfony/symfony/issues/9731 + public function testLoadWrongEmptyXMLWithErrorHandler() + { + $originalDisableEntities = libxml_disable_entity_loader(false); + $errorReporting = error_reporting(-1); + + set_error_handler(function ($errno, $errstr) { + throw new \Exception($errstr, $errno); + }); + + $file = __DIR__.'/../Fixtures/foo.xml'; + try { + try { + XmlUtils::loadFile($file); + $this->fail('An exception should have been raised'); + } catch (\InvalidArgumentException $e) { + $this->assertEquals(sprintf('File %s does not contain valid XML, it is empty.', $file), $e->getMessage()); + } + } finally { + restore_error_handler(); + error_reporting($errorReporting); + } + + $disableEntities = libxml_disable_entity_loader(true); + libxml_disable_entity_loader($disableEntities); + + libxml_disable_entity_loader($originalDisableEntities); + + $this->assertFalse($disableEntities); + + // should not throw an exception + XmlUtils::loadFile(__DIR__.'/../Fixtures/Util/valid.xml', __DIR__.'/../Fixtures/Util/schema.xsd'); + } +} + +interface Validator +{ + public function validate(); +} diff --git a/lib/symfony/config/Util/Exception/InvalidXmlException.php b/lib/symfony/config/Util/Exception/InvalidXmlException.php new file mode 100644 index 000000000..a335bbd2e --- /dev/null +++ b/lib/symfony/config/Util/Exception/InvalidXmlException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Util\Exception; + +/** + * Exception class for when XML parsing with an XSD schema file path or a callable validator produces errors unrelated + * to the actual XML parsing. + * + * @author Ole Rößner + */ +class InvalidXmlException extends XmlParsingException +{ +} diff --git a/lib/symfony/config/Util/Exception/XmlParsingException.php b/lib/symfony/config/Util/Exception/XmlParsingException.php new file mode 100644 index 000000000..9bceed660 --- /dev/null +++ b/lib/symfony/config/Util/Exception/XmlParsingException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Util\Exception; + +/** + * Exception class for when XML cannot be parsed properly. + * + * @author Ole Rößner + */ +class XmlParsingException extends \InvalidArgumentException +{ +} diff --git a/lib/symfony/config/Util/XmlUtils.php b/lib/symfony/config/Util/XmlUtils.php new file mode 100644 index 000000000..df1edd68b --- /dev/null +++ b/lib/symfony/config/Util/XmlUtils.php @@ -0,0 +1,269 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Util; + +use Symfony\Component\Config\Util\Exception\InvalidXmlException; +use Symfony\Component\Config\Util\Exception\XmlParsingException; + +/** + * XMLUtils is a bunch of utility methods to XML operations. + * + * This class contains static methods only and is not meant to be instantiated. + * + * @author Fabien Potencier + * @author Martin Hasoň + * @author Ole Rößner + */ +class XmlUtils +{ + /** + * This class should not be instantiated. + */ + private function __construct() + { + } + + /** + * Parses an XML string. + * + * @param string $content An XML string + * @param string|callable|null $schemaOrCallable An XSD schema file path, a callable, or null to disable validation + * + * @return \DOMDocument + * + * @throws XmlParsingException When parsing of XML file returns error + * @throws InvalidXmlException When parsing of XML with schema or callable produces any errors unrelated to the XML parsing itself + * @throws \RuntimeException When DOM extension is missing + */ + public static function parse($content, $schemaOrCallable = null) + { + if (!\extension_loaded('dom')) { + throw new \RuntimeException('Extension DOM is required.'); + } + + $internalErrors = libxml_use_internal_errors(true); + $disableEntities = libxml_disable_entity_loader(true); + libxml_clear_errors(); + + $dom = new \DOMDocument(); + $dom->validateOnParse = true; + if (!$dom->loadXML($content, LIBXML_NONET | (\defined('LIBXML_COMPACT') ? LIBXML_COMPACT : 0))) { + libxml_disable_entity_loader($disableEntities); + + throw new XmlParsingException(implode("\n", static::getXmlErrors($internalErrors))); + } + + $dom->normalizeDocument(); + + libxml_use_internal_errors($internalErrors); + libxml_disable_entity_loader($disableEntities); + + foreach ($dom->childNodes as $child) { + if (XML_DOCUMENT_TYPE_NODE === $child->nodeType) { + throw new XmlParsingException('Document types are not allowed.'); + } + } + + if (null !== $schemaOrCallable) { + $internalErrors = libxml_use_internal_errors(true); + libxml_clear_errors(); + + $e = null; + if (\is_callable($schemaOrCallable)) { + try { + $valid = \call_user_func($schemaOrCallable, $dom, $internalErrors); + } catch (\Exception $e) { + $valid = false; + } + } elseif (!\is_array($schemaOrCallable) && is_file((string) $schemaOrCallable)) { + $schemaSource = file_get_contents((string) $schemaOrCallable); + $valid = @$dom->schemaValidateSource($schemaSource); + } else { + libxml_use_internal_errors($internalErrors); + + throw new XmlParsingException('The schemaOrCallable argument has to be a valid path to XSD file or callable.'); + } + + if (!$valid) { + $messages = static::getXmlErrors($internalErrors); + if (empty($messages)) { + throw new InvalidXmlException('The XML is not valid.', 0, $e); + } + throw new XmlParsingException(implode("\n", $messages), 0, $e); + } + } + + libxml_clear_errors(); + libxml_use_internal_errors($internalErrors); + + return $dom; + } + + /** + * Loads an XML file. + * + * @param string $file An XML file path + * @param string|callable|null $schemaOrCallable An XSD schema file path, a callable, or null to disable validation + * + * @return \DOMDocument + * + * @throws \InvalidArgumentException When loading of XML file returns error + * @throws XmlParsingException When XML parsing returns any errors + * @throws \RuntimeException When DOM extension is missing + */ + public static function loadFile($file, $schemaOrCallable = null) + { + $content = @file_get_contents($file); + if ('' === trim($content)) { + throw new \InvalidArgumentException(sprintf('File %s does not contain valid XML, it is empty.', $file)); + } + + try { + return static::parse($content, $schemaOrCallable); + } catch (InvalidXmlException $e) { + throw new XmlParsingException(sprintf('The XML file "%s" is not valid.', $file), 0, $e->getPrevious()); + } + } + + /** + * Converts a \DOMElement object to a PHP array. + * + * The following rules applies during the conversion: + * + * * Each tag is converted to a key value or an array + * if there is more than one "value" + * + * * The content of a tag is set under a "value" key (bar) + * if the tag also has some nested tags + * + * * The attributes are converted to keys () + * + * * The nested-tags are converted to keys (bar) + * + * @param \DOMElement $element A \DOMElement instance + * @param bool $checkPrefix Check prefix in an element or an attribute name + * + * @return array A PHP array + */ + public static function convertDomElementToArray(\DOMElement $element, $checkPrefix = true) + { + $prefix = (string) $element->prefix; + $empty = true; + $config = []; + foreach ($element->attributes as $name => $node) { + if ($checkPrefix && !\in_array((string) $node->prefix, ['', $prefix], true)) { + continue; + } + $config[$name] = static::phpize($node->value); + $empty = false; + } + + $nodeValue = false; + foreach ($element->childNodes as $node) { + if ($node instanceof \DOMText) { + if ('' !== trim($node->nodeValue)) { + $nodeValue = trim($node->nodeValue); + $empty = false; + } + } elseif ($checkPrefix && $prefix != (string) $node->prefix) { + continue; + } elseif (!$node instanceof \DOMComment) { + $value = static::convertDomElementToArray($node, $checkPrefix); + + $key = $node->localName; + if (isset($config[$key])) { + if (!\is_array($config[$key]) || !\is_int(key($config[$key]))) { + $config[$key] = [$config[$key]]; + } + $config[$key][] = $value; + } else { + $config[$key] = $value; + } + + $empty = false; + } + } + + if (false !== $nodeValue) { + $value = static::phpize($nodeValue); + if (\count($config)) { + $config['value'] = $value; + } else { + $config = $value; + } + } + + return !$empty ? $config : null; + } + + /** + * Converts an xml value to a PHP type. + * + * @param mixed $value + * + * @return mixed + */ + public static function phpize($value) + { + $value = (string) $value; + $lowercaseValue = strtolower($value); + + switch (true) { + case 'null' === $lowercaseValue: + return; + case ctype_digit($value): + $raw = $value; + $cast = (int) $value; + + return '0' == $value[0] ? octdec($value) : (((string) $raw === (string) $cast) ? $cast : $raw); + case isset($value[1]) && '-' === $value[0] && ctype_digit(substr($value, 1)): + $raw = $value; + $cast = (int) $value; + + return '0' == $value[1] ? octdec($value) : (((string) $raw === (string) $cast) ? $cast : $raw); + case 'true' === $lowercaseValue: + return true; + case 'false' === $lowercaseValue: + return false; + case isset($value[1]) && '0b' == $value[0].$value[1]: + return bindec($value); + case is_numeric($value): + return '0x' === $value[0].$value[1] ? hexdec($value) : (float) $value; + case preg_match('/^0x[0-9a-f]++$/i', $value): + return hexdec($value); + case preg_match('/^[+-]?[0-9]+(\.[0-9]+)?$/', $value): + return (float) $value; + default: + return $value; + } + } + + protected static function getXmlErrors($internalErrors) + { + $errors = []; + foreach (libxml_get_errors() as $error) { + $errors[] = sprintf('[%s %s] %s (in %s - line %d, column %d)', + LIBXML_ERR_WARNING == $error->level ? 'WARNING' : 'ERROR', + $error->code, + trim($error->message), + $error->file ?: 'n/a', + $error->line, + $error->column + ); + } + + libxml_clear_errors(); + libxml_use_internal_errors($internalErrors); + + return $errors; + } +} diff --git a/lib/symfony/config/composer.json b/lib/symfony/config/composer.json new file mode 100644 index 000000000..c04294dd4 --- /dev/null +++ b/lib/symfony/config/composer.json @@ -0,0 +1,48 @@ +{ + "name": "symfony/config", + "type": "library", + "description": "Symfony Config Component", + "keywords": [], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": "^5.5.9|>=7.0.8", + "symfony/filesystem": "~2.8|~3.0|~4.0", + "symfony/polyfill-ctype": "~1.8" + }, + "require-dev": { + "symfony/finder": "~3.3|~4.0", + "symfony/yaml": "~3.0|~4.0", + "symfony/dependency-injection": "~3.3|~4.0", + "symfony/event-dispatcher": "~3.3|~4.0" + }, + "conflict": { + "symfony/finder": "<3.3", + "symfony/dependency-injection": "<3.3" + }, + "suggest": { + "symfony/yaml": "To use the yaml reference dumper" + }, + "autoload": { + "psr-4": { "Symfony\\Component\\Config\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + } +} diff --git a/lib/symfony/config/phpunit.xml.dist b/lib/symfony/config/phpunit.xml.dist new file mode 100644 index 000000000..1cfdb3cdc --- /dev/null +++ b/lib/symfony/config/phpunit.xml.dist @@ -0,0 +1,31 @@ + + + + + + + + + + ./Tests/ + + + + + + ./ + + ./Resources + ./Tests + ./vendor + + + + diff --git a/lib/symfony/console/.gitignore b/lib/symfony/console/.gitignore new file mode 100644 index 000000000..c49a5d8df --- /dev/null +++ b/lib/symfony/console/.gitignore @@ -0,0 +1,3 @@ +vendor/ +composer.lock +phpunit.xml diff --git a/lib/symfony/console/Application.php b/lib/symfony/console/Application.php new file mode 100644 index 000000000..4d10c3169 --- /dev/null +++ b/lib/symfony/console/Application.php @@ -0,0 +1,1254 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Command\HelpCommand; +use Symfony\Component\Console\Command\ListCommand; +use Symfony\Component\Console\CommandLoader\CommandLoaderInterface; +use Symfony\Component\Console\Event\ConsoleCommandEvent; +use Symfony\Component\Console\Event\ConsoleErrorEvent; +use Symfony\Component\Console\Event\ConsoleExceptionEvent; +use Symfony\Component\Console\Event\ConsoleTerminateEvent; +use Symfony\Component\Console\Exception\CommandNotFoundException; +use Symfony\Component\Console\Exception\ExceptionInterface; +use Symfony\Component\Console\Exception\LogicException; +use Symfony\Component\Console\Formatter\OutputFormatter; +use Symfony\Component\Console\Helper\DebugFormatterHelper; +use Symfony\Component\Console\Helper\FormatterHelper; +use Symfony\Component\Console\Helper\Helper; +use Symfony\Component\Console\Helper\HelperSet; +use Symfony\Component\Console\Helper\ProcessHelper; +use Symfony\Component\Console\Helper\QuestionHelper; +use Symfony\Component\Console\Input\ArgvInput; +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputAwareInterface; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\StreamableInputInterface; +use Symfony\Component\Console\Output\ConsoleOutput; +use Symfony\Component\Console\Output\ConsoleOutputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Debug\ErrorHandler; +use Symfony\Component\Debug\Exception\FatalThrowableError; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; + +/** + * An Application is the container for a collection of commands. + * + * It is the main entry point of a Console application. + * + * This class is optimized for a standard CLI environment. + * + * Usage: + * + * $app = new Application('myapp', '1.0 (stable)'); + * $app->add(new SimpleCommand()); + * $app->run(); + * + * @author Fabien Potencier + */ +class Application +{ + private $commands = []; + private $wantHelps = false; + private $runningCommand; + private $name; + private $version; + private $commandLoader; + private $catchExceptions = true; + private $autoExit = true; + private $definition; + private $helperSet; + private $dispatcher; + private $terminal; + private $defaultCommand; + private $singleCommand = false; + private $initialized; + + /** + * @param string $name The name of the application + * @param string $version The version of the application + */ + public function __construct($name = 'UNKNOWN', $version = 'UNKNOWN') + { + $this->name = $name; + $this->version = $version; + $this->terminal = new Terminal(); + $this->defaultCommand = 'list'; + } + + public function setDispatcher(EventDispatcherInterface $dispatcher) + { + $this->dispatcher = $dispatcher; + } + + public function setCommandLoader(CommandLoaderInterface $commandLoader) + { + $this->commandLoader = $commandLoader; + } + + /** + * Runs the current application. + * + * @return int 0 if everything went fine, or an error code + * + * @throws \Exception When running fails. Bypass this when {@link setCatchExceptions()}. + */ + public function run(InputInterface $input = null, OutputInterface $output = null) + { + putenv('LINES='.$this->terminal->getHeight()); + putenv('COLUMNS='.$this->terminal->getWidth()); + + if (null === $input) { + $input = new ArgvInput(); + } + + if (null === $output) { + $output = new ConsoleOutput(); + } + + $renderException = function ($e) use ($output) { + if (!$e instanceof \Exception) { + $e = class_exists(FatalThrowableError::class) ? new FatalThrowableError($e) : new \ErrorException($e->getMessage(), $e->getCode(), E_ERROR, $e->getFile(), $e->getLine()); + } + if ($output instanceof ConsoleOutputInterface) { + $this->renderException($e, $output->getErrorOutput()); + } else { + $this->renderException($e, $output); + } + }; + if ($phpHandler = set_exception_handler($renderException)) { + restore_exception_handler(); + if (!\is_array($phpHandler) || !$phpHandler[0] instanceof ErrorHandler) { + $debugHandler = true; + } elseif ($debugHandler = $phpHandler[0]->setExceptionHandler($renderException)) { + $phpHandler[0]->setExceptionHandler($debugHandler); + } + } + + if (null !== $this->dispatcher && $this->dispatcher->hasListeners(ConsoleEvents::EXCEPTION)) { + @trigger_error(sprintf('The "ConsoleEvents::EXCEPTION" event is deprecated since Symfony 3.3 and will be removed in 4.0. Listen to the "ConsoleEvents::ERROR" event instead.'), E_USER_DEPRECATED); + } + + $this->configureIO($input, $output); + + try { + $exitCode = $this->doRun($input, $output); + } catch (\Exception $e) { + if (!$this->catchExceptions) { + throw $e; + } + + $renderException($e); + + $exitCode = $e->getCode(); + if (is_numeric($exitCode)) { + $exitCode = (int) $exitCode; + if (0 === $exitCode) { + $exitCode = 1; + } + } else { + $exitCode = 1; + } + } finally { + // if the exception handler changed, keep it + // otherwise, unregister $renderException + if (!$phpHandler) { + if (set_exception_handler($renderException) === $renderException) { + restore_exception_handler(); + } + restore_exception_handler(); + } elseif (!$debugHandler) { + $finalHandler = $phpHandler[0]->setExceptionHandler(null); + if ($finalHandler !== $renderException) { + $phpHandler[0]->setExceptionHandler($finalHandler); + } + } + } + + if ($this->autoExit) { + if ($exitCode > 255) { + $exitCode = 255; + } + + exit($exitCode); + } + + return $exitCode; + } + + /** + * Runs the current application. + * + * @return int 0 if everything went fine, or an error code + */ + public function doRun(InputInterface $input, OutputInterface $output) + { + if (true === $input->hasParameterOption(['--version', '-V'], true)) { + $output->writeln($this->getLongVersion()); + + return 0; + } + + try { + // Makes ArgvInput::getFirstArgument() able to distinguish an option from an argument. + $input->bind($this->getDefinition()); + } catch (ExceptionInterface $e) { + // Errors must be ignored, full binding/validation happens later when the command is known. + } + + $name = $this->getCommandName($input); + if (true === $input->hasParameterOption(['--help', '-h'], true)) { + if (!$name) { + $name = 'help'; + $input = new ArrayInput(['command_name' => $this->defaultCommand]); + } else { + $this->wantHelps = true; + } + } + + if (!$name) { + $name = $this->defaultCommand; + $definition = $this->getDefinition(); + $definition->setArguments(array_merge( + $definition->getArguments(), + [ + 'command' => new InputArgument('command', InputArgument::OPTIONAL, $definition->getArgument('command')->getDescription(), $name), + ] + )); + } + + try { + $e = $this->runningCommand = null; + // the command name MUST be the first element of the input + $command = $this->find($name); + } catch (\Exception $e) { + } catch (\Throwable $e) { + } + if (null !== $e) { + if (null !== $this->dispatcher) { + $event = new ConsoleErrorEvent($input, $output, $e); + $this->dispatcher->dispatch(ConsoleEvents::ERROR, $event); + $e = $event->getError(); + + if (0 === $event->getExitCode()) { + return 0; + } + } + + throw $e; + } + + $this->runningCommand = $command; + $exitCode = $this->doRunCommand($command, $input, $output); + $this->runningCommand = null; + + return $exitCode; + } + + public function setHelperSet(HelperSet $helperSet) + { + $this->helperSet = $helperSet; + } + + /** + * Get the helper set associated with the command. + * + * @return HelperSet The HelperSet instance associated with this command + */ + public function getHelperSet() + { + if (!$this->helperSet) { + $this->helperSet = $this->getDefaultHelperSet(); + } + + return $this->helperSet; + } + + public function setDefinition(InputDefinition $definition) + { + $this->definition = $definition; + } + + /** + * Gets the InputDefinition related to this Application. + * + * @return InputDefinition The InputDefinition instance + */ + public function getDefinition() + { + if (!$this->definition) { + $this->definition = $this->getDefaultInputDefinition(); + } + + if ($this->singleCommand) { + $inputDefinition = $this->definition; + $inputDefinition->setArguments(); + + return $inputDefinition; + } + + return $this->definition; + } + + /** + * Gets the help message. + * + * @return string A help message + */ + public function getHelp() + { + return $this->getLongVersion(); + } + + /** + * Gets whether to catch exceptions or not during commands execution. + * + * @return bool Whether to catch exceptions or not during commands execution + */ + public function areExceptionsCaught() + { + return $this->catchExceptions; + } + + /** + * Sets whether to catch exceptions or not during commands execution. + * + * @param bool $boolean Whether to catch exceptions or not during commands execution + */ + public function setCatchExceptions($boolean) + { + $this->catchExceptions = (bool) $boolean; + } + + /** + * Gets whether to automatically exit after a command execution or not. + * + * @return bool Whether to automatically exit after a command execution or not + */ + public function isAutoExitEnabled() + { + return $this->autoExit; + } + + /** + * Sets whether to automatically exit after a command execution or not. + * + * @param bool $boolean Whether to automatically exit after a command execution or not + */ + public function setAutoExit($boolean) + { + $this->autoExit = (bool) $boolean; + } + + /** + * Gets the name of the application. + * + * @return string The application name + */ + public function getName() + { + return $this->name; + } + + /** + * Sets the application name. + * + * @param string $name The application name + */ + public function setName($name) + { + $this->name = $name; + } + + /** + * Gets the application version. + * + * @return string The application version + */ + public function getVersion() + { + return $this->version; + } + + /** + * Sets the application version. + * + * @param string $version The application version + */ + public function setVersion($version) + { + $this->version = $version; + } + + /** + * Returns the long version of the application. + * + * @return string The long application version + */ + public function getLongVersion() + { + if ('UNKNOWN' !== $this->getName()) { + if ('UNKNOWN' !== $this->getVersion()) { + return sprintf('%s %s', $this->getName(), $this->getVersion()); + } + + return $this->getName(); + } + + return 'Console Tool'; + } + + /** + * Registers a new command. + * + * @param string $name The command name + * + * @return Command The newly created command + */ + public function register($name) + { + return $this->add(new Command($name)); + } + + /** + * Adds an array of command objects. + * + * If a Command is not enabled it will not be added. + * + * @param Command[] $commands An array of commands + */ + public function addCommands(array $commands) + { + foreach ($commands as $command) { + $this->add($command); + } + } + + /** + * Adds a command object. + * + * If a command with the same name already exists, it will be overridden. + * If the command is not enabled it will not be added. + * + * @return Command|null The registered command if enabled or null + */ + public function add(Command $command) + { + $this->init(); + + $command->setApplication($this); + + if (!$command->isEnabled()) { + $command->setApplication(null); + + return; + } + + if (null === $command->getDefinition()) { + throw new LogicException(sprintf('Command class "%s" is not correctly initialized. You probably forgot to call the parent constructor.', \get_class($command))); + } + + if (!$command->getName()) { + throw new LogicException(sprintf('The command defined in "%s" cannot have an empty name.', \get_class($command))); + } + + $this->commands[$command->getName()] = $command; + + foreach ($command->getAliases() as $alias) { + $this->commands[$alias] = $command; + } + + return $command; + } + + /** + * Returns a registered command by name or alias. + * + * @param string $name The command name or alias + * + * @return Command A Command object + * + * @throws CommandNotFoundException When given command name does not exist + */ + public function get($name) + { + $this->init(); + + if (!$this->has($name)) { + throw new CommandNotFoundException(sprintf('The command "%s" does not exist.', $name)); + } + + $command = $this->commands[$name]; + + if ($this->wantHelps) { + $this->wantHelps = false; + + $helpCommand = $this->get('help'); + $helpCommand->setCommand($command); + + return $helpCommand; + } + + return $command; + } + + /** + * Returns true if the command exists, false otherwise. + * + * @param string $name The command name or alias + * + * @return bool true if the command exists, false otherwise + */ + public function has($name) + { + $this->init(); + + return isset($this->commands[$name]) || ($this->commandLoader && $this->commandLoader->has($name) && $this->add($this->commandLoader->get($name))); + } + + /** + * Returns an array of all unique namespaces used by currently registered commands. + * + * It does not return the global namespace which always exists. + * + * @return string[] An array of namespaces + */ + public function getNamespaces() + { + $namespaces = []; + foreach ($this->all() as $command) { + $namespaces = array_merge($namespaces, $this->extractAllNamespaces($command->getName())); + + foreach ($command->getAliases() as $alias) { + $namespaces = array_merge($namespaces, $this->extractAllNamespaces($alias)); + } + } + + return array_values(array_unique(array_filter($namespaces))); + } + + /** + * Finds a registered namespace by a name or an abbreviation. + * + * @param string $namespace A namespace or abbreviation to search for + * + * @return string A registered namespace + * + * @throws CommandNotFoundException When namespace is incorrect or ambiguous + */ + public function findNamespace($namespace) + { + $allNamespaces = $this->getNamespaces(); + $expr = preg_replace_callback('{([^:]+|)}', function ($matches) { return preg_quote($matches[1]).'[^:]*'; }, $namespace); + $namespaces = preg_grep('{^'.$expr.'}', $allNamespaces); + + if (empty($namespaces)) { + $message = sprintf('There are no commands defined in the "%s" namespace.', $namespace); + + if ($alternatives = $this->findAlternatives($namespace, $allNamespaces)) { + if (1 == \count($alternatives)) { + $message .= "\n\nDid you mean this?\n "; + } else { + $message .= "\n\nDid you mean one of these?\n "; + } + + $message .= implode("\n ", $alternatives); + } + + throw new CommandNotFoundException($message, $alternatives); + } + + $exact = \in_array($namespace, $namespaces, true); + if (\count($namespaces) > 1 && !$exact) { + throw new CommandNotFoundException(sprintf("The namespace \"%s\" is ambiguous.\nDid you mean one of these?\n%s", $namespace, $this->getAbbreviationSuggestions(array_values($namespaces))), array_values($namespaces)); + } + + return $exact ? $namespace : reset($namespaces); + } + + /** + * Finds a command by name or alias. + * + * Contrary to get, this command tries to find the best + * match if you give it an abbreviation of a name or alias. + * + * @param string $name A command name or a command alias + * + * @return Command A Command instance + * + * @throws CommandNotFoundException When command name is incorrect or ambiguous + */ + public function find($name) + { + $this->init(); + + $aliases = []; + + foreach ($this->commands as $command) { + foreach ($command->getAliases() as $alias) { + if (!$this->has($alias)) { + $this->commands[$alias] = $command; + } + } + } + + $allCommands = $this->commandLoader ? array_merge($this->commandLoader->getNames(), array_keys($this->commands)) : array_keys($this->commands); + $expr = preg_replace_callback('{([^:]+|)}', function ($matches) { return preg_quote($matches[1]).'[^:]*'; }, $name); + $commands = preg_grep('{^'.$expr.'}', $allCommands); + + if (empty($commands)) { + $commands = preg_grep('{^'.$expr.'}i', $allCommands); + } + + // if no commands matched or we just matched namespaces + if (empty($commands) || \count(preg_grep('{^'.$expr.'$}i', $commands)) < 1) { + if (false !== $pos = strrpos($name, ':')) { + // check if a namespace exists and contains commands + $this->findNamespace(substr($name, 0, $pos)); + } + + $message = sprintf('Command "%s" is not defined.', $name); + + if ($alternatives = $this->findAlternatives($name, $allCommands)) { + if (1 == \count($alternatives)) { + $message .= "\n\nDid you mean this?\n "; + } else { + $message .= "\n\nDid you mean one of these?\n "; + } + $message .= implode("\n ", $alternatives); + } + + throw new CommandNotFoundException($message, $alternatives); + } + + // filter out aliases for commands which are already on the list + if (\count($commands) > 1) { + $commandList = $this->commandLoader ? array_merge(array_flip($this->commandLoader->getNames()), $this->commands) : $this->commands; + $commands = array_unique(array_filter($commands, function ($nameOrAlias) use ($commandList, $commands, &$aliases) { + $commandName = $commandList[$nameOrAlias] instanceof Command ? $commandList[$nameOrAlias]->getName() : $nameOrAlias; + $aliases[$nameOrAlias] = $commandName; + + return $commandName === $nameOrAlias || !\in_array($commandName, $commands); + })); + } + + $exact = \in_array($name, $commands, true) || isset($aliases[$name]); + if (\count($commands) > 1 && !$exact) { + $usableWidth = $this->terminal->getWidth() - 10; + $abbrevs = array_values($commands); + $maxLen = 0; + foreach ($abbrevs as $abbrev) { + $maxLen = max(Helper::strlen($abbrev), $maxLen); + } + $abbrevs = array_map(function ($cmd) use ($commandList, $usableWidth, $maxLen) { + if (!$commandList[$cmd] instanceof Command) { + return $cmd; + } + $abbrev = str_pad($cmd, $maxLen, ' ').' '.$commandList[$cmd]->getDescription(); + + return Helper::strlen($abbrev) > $usableWidth ? Helper::substr($abbrev, 0, $usableWidth - 3).'...' : $abbrev; + }, array_values($commands)); + $suggestions = $this->getAbbreviationSuggestions($abbrevs); + + throw new CommandNotFoundException(sprintf("Command \"%s\" is ambiguous.\nDid you mean one of these?\n%s", $name, $suggestions), array_values($commands)); + } + + return $this->get($exact ? $name : reset($commands)); + } + + /** + * Gets the commands (registered in the given namespace if provided). + * + * The array keys are the full names and the values the command instances. + * + * @param string $namespace A namespace name + * + * @return Command[] An array of Command instances + */ + public function all($namespace = null) + { + $this->init(); + + if (null === $namespace) { + if (!$this->commandLoader) { + return $this->commands; + } + + $commands = $this->commands; + foreach ($this->commandLoader->getNames() as $name) { + if (!isset($commands[$name]) && $this->has($name)) { + $commands[$name] = $this->get($name); + } + } + + return $commands; + } + + $commands = []; + foreach ($this->commands as $name => $command) { + if ($namespace === $this->extractNamespace($name, substr_count($namespace, ':') + 1)) { + $commands[$name] = $command; + } + } + + if ($this->commandLoader) { + foreach ($this->commandLoader->getNames() as $name) { + if (!isset($commands[$name]) && $namespace === $this->extractNamespace($name, substr_count($namespace, ':') + 1) && $this->has($name)) { + $commands[$name] = $this->get($name); + } + } + } + + return $commands; + } + + /** + * Returns an array of possible abbreviations given a set of names. + * + * @param array $names An array of names + * + * @return array An array of abbreviations + */ + public static function getAbbreviations($names) + { + $abbrevs = []; + foreach ($names as $name) { + for ($len = \strlen($name); $len > 0; --$len) { + $abbrev = substr($name, 0, $len); + $abbrevs[$abbrev][] = $name; + } + } + + return $abbrevs; + } + + /** + * Renders a caught exception. + */ + public function renderException(\Exception $e, OutputInterface $output) + { + $output->writeln('', OutputInterface::VERBOSITY_QUIET); + + $this->doRenderException($e, $output); + + if (null !== $this->runningCommand) { + $output->writeln(sprintf('%s', sprintf($this->runningCommand->getSynopsis(), $this->getName())), OutputInterface::VERBOSITY_QUIET); + $output->writeln('', OutputInterface::VERBOSITY_QUIET); + } + } + + protected function doRenderException(\Exception $e, OutputInterface $output) + { + do { + $message = trim($e->getMessage()); + if ('' === $message || OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) { + $title = sprintf(' [%s%s] ', \get_class($e), 0 !== ($code = $e->getCode()) ? ' ('.$code.')' : ''); + $len = Helper::strlen($title); + } else { + $len = 0; + } + + $width = $this->terminal->getWidth() ? $this->terminal->getWidth() - 1 : PHP_INT_MAX; + // HHVM only accepts 32 bits integer in str_split, even when PHP_INT_MAX is a 64 bit integer: https://github.com/facebook/hhvm/issues/1327 + if (\defined('HHVM_VERSION') && $width > 1 << 31) { + $width = 1 << 31; + } + $lines = []; + foreach ('' !== $message ? preg_split('/\r?\n/', $message) : [] as $line) { + foreach ($this->splitStringByWidth($line, $width - 4) as $line) { + // pre-format lines to get the right string length + $lineLength = Helper::strlen($line) + 4; + $lines[] = [$line, $lineLength]; + + $len = max($lineLength, $len); + } + } + + $messages = []; + if (!$e instanceof ExceptionInterface || OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) { + $messages[] = sprintf('%s', OutputFormatter::escape(sprintf('In %s line %s:', basename($e->getFile()) ?: 'n/a', $e->getLine() ?: 'n/a'))); + } + $messages[] = $emptyLine = sprintf('%s', str_repeat(' ', $len)); + if ('' === $message || OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) { + $messages[] = sprintf('%s%s', $title, str_repeat(' ', max(0, $len - Helper::strlen($title)))); + } + foreach ($lines as $line) { + $messages[] = sprintf(' %s %s', OutputFormatter::escape($line[0]), str_repeat(' ', $len - $line[1])); + } + $messages[] = $emptyLine; + $messages[] = ''; + + $output->writeln($messages, OutputInterface::VERBOSITY_QUIET); + + if (OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) { + $output->writeln('Exception trace:', OutputInterface::VERBOSITY_QUIET); + + // exception related properties + $trace = $e->getTrace(); + + array_unshift($trace, [ + 'function' => '', + 'file' => $e->getFile() ?: 'n/a', + 'line' => $e->getLine() ?: 'n/a', + 'args' => [], + ]); + + for ($i = 0, $count = \count($trace); $i < $count; ++$i) { + $class = isset($trace[$i]['class']) ? $trace[$i]['class'] : ''; + $type = isset($trace[$i]['type']) ? $trace[$i]['type'] : ''; + $function = $trace[$i]['function']; + $file = isset($trace[$i]['file']) ? $trace[$i]['file'] : 'n/a'; + $line = isset($trace[$i]['line']) ? $trace[$i]['line'] : 'n/a'; + + $output->writeln(sprintf(' %s%s%s() at %s:%s', $class, $type, $function, $file, $line), OutputInterface::VERBOSITY_QUIET); + } + + $output->writeln('', OutputInterface::VERBOSITY_QUIET); + } + } while ($e = $e->getPrevious()); + } + + /** + * Tries to figure out the terminal width in which this application runs. + * + * @return int|null + * + * @deprecated since version 3.2, to be removed in 4.0. Create a Terminal instance instead. + */ + protected function getTerminalWidth() + { + @trigger_error(sprintf('The "%s()" method is deprecated as of 3.2 and will be removed in 4.0. Create a Terminal instance instead.', __METHOD__), E_USER_DEPRECATED); + + return $this->terminal->getWidth(); + } + + /** + * Tries to figure out the terminal height in which this application runs. + * + * @return int|null + * + * @deprecated since version 3.2, to be removed in 4.0. Create a Terminal instance instead. + */ + protected function getTerminalHeight() + { + @trigger_error(sprintf('The "%s()" method is deprecated as of 3.2 and will be removed in 4.0. Create a Terminal instance instead.', __METHOD__), E_USER_DEPRECATED); + + return $this->terminal->getHeight(); + } + + /** + * Tries to figure out the terminal dimensions based on the current environment. + * + * @return array Array containing width and height + * + * @deprecated since version 3.2, to be removed in 4.0. Create a Terminal instance instead. + */ + public function getTerminalDimensions() + { + @trigger_error(sprintf('The "%s()" method is deprecated as of 3.2 and will be removed in 4.0. Create a Terminal instance instead.', __METHOD__), E_USER_DEPRECATED); + + return [$this->terminal->getWidth(), $this->terminal->getHeight()]; + } + + /** + * Sets terminal dimensions. + * + * Can be useful to force terminal dimensions for functional tests. + * + * @param int $width The width + * @param int $height The height + * + * @return $this + * + * @deprecated since version 3.2, to be removed in 4.0. Set the COLUMNS and LINES env vars instead. + */ + public function setTerminalDimensions($width, $height) + { + @trigger_error(sprintf('The "%s()" method is deprecated as of 3.2 and will be removed in 4.0. Set the COLUMNS and LINES env vars instead.', __METHOD__), E_USER_DEPRECATED); + + putenv('COLUMNS='.$width); + putenv('LINES='.$height); + + return $this; + } + + /** + * Configures the input and output instances based on the user arguments and options. + */ + protected function configureIO(InputInterface $input, OutputInterface $output) + { + if (true === $input->hasParameterOption(['--ansi'], true)) { + $output->setDecorated(true); + } elseif (true === $input->hasParameterOption(['--no-ansi'], true)) { + $output->setDecorated(false); + } + + if (true === $input->hasParameterOption(['--no-interaction', '-n'], true)) { + $input->setInteractive(false); + } elseif (\function_exists('posix_isatty')) { + $inputStream = null; + + if ($input instanceof StreamableInputInterface) { + $inputStream = $input->getStream(); + } + + // This check ensures that calling QuestionHelper::setInputStream() works + // To be removed in 4.0 (in the same time as QuestionHelper::setInputStream) + if (!$inputStream && $this->getHelperSet()->has('question')) { + $inputStream = $this->getHelperSet()->get('question')->getInputStream(false); + } + + if (!@posix_isatty($inputStream) && false === getenv('SHELL_INTERACTIVE')) { + $input->setInteractive(false); + } + } + + switch ($shellVerbosity = (int) getenv('SHELL_VERBOSITY')) { + case -1: $output->setVerbosity(OutputInterface::VERBOSITY_QUIET); break; + case 1: $output->setVerbosity(OutputInterface::VERBOSITY_VERBOSE); break; + case 2: $output->setVerbosity(OutputInterface::VERBOSITY_VERY_VERBOSE); break; + case 3: $output->setVerbosity(OutputInterface::VERBOSITY_DEBUG); break; + default: $shellVerbosity = 0; break; + } + + if (true === $input->hasParameterOption(['--quiet', '-q'], true)) { + $output->setVerbosity(OutputInterface::VERBOSITY_QUIET); + $shellVerbosity = -1; + } else { + if ($input->hasParameterOption('-vvv', true) || $input->hasParameterOption('--verbose=3', true) || 3 === $input->getParameterOption('--verbose', false, true)) { + $output->setVerbosity(OutputInterface::VERBOSITY_DEBUG); + $shellVerbosity = 3; + } elseif ($input->hasParameterOption('-vv', true) || $input->hasParameterOption('--verbose=2', true) || 2 === $input->getParameterOption('--verbose', false, true)) { + $output->setVerbosity(OutputInterface::VERBOSITY_VERY_VERBOSE); + $shellVerbosity = 2; + } elseif ($input->hasParameterOption('-v', true) || $input->hasParameterOption('--verbose=1', true) || $input->hasParameterOption('--verbose', true) || $input->getParameterOption('--verbose', false, true)) { + $output->setVerbosity(OutputInterface::VERBOSITY_VERBOSE); + $shellVerbosity = 1; + } + } + + if (-1 === $shellVerbosity) { + $input->setInteractive(false); + } + + putenv('SHELL_VERBOSITY='.$shellVerbosity); + $_ENV['SHELL_VERBOSITY'] = $shellVerbosity; + $_SERVER['SHELL_VERBOSITY'] = $shellVerbosity; + } + + /** + * Runs the current command. + * + * If an event dispatcher has been attached to the application, + * events are also dispatched during the life-cycle of the command. + * + * @return int 0 if everything went fine, or an error code + */ + protected function doRunCommand(Command $command, InputInterface $input, OutputInterface $output) + { + foreach ($command->getHelperSet() as $helper) { + if ($helper instanceof InputAwareInterface) { + $helper->setInput($input); + } + } + + if (null === $this->dispatcher) { + return $command->run($input, $output); + } + + // bind before the console.command event, so the listeners have access to input options/arguments + try { + $command->mergeApplicationDefinition(); + $input->bind($command->getDefinition()); + } catch (ExceptionInterface $e) { + // ignore invalid options/arguments for now, to allow the event listeners to customize the InputDefinition + } + + $event = new ConsoleCommandEvent($command, $input, $output); + $e = null; + + try { + $this->dispatcher->dispatch(ConsoleEvents::COMMAND, $event); + + if ($event->commandShouldRun()) { + $exitCode = $command->run($input, $output); + } else { + $exitCode = ConsoleCommandEvent::RETURN_CODE_DISABLED; + } + } catch (\Exception $e) { + } catch (\Throwable $e) { + } + if (null !== $e) { + if ($this->dispatcher->hasListeners(ConsoleEvents::EXCEPTION)) { + $x = $e instanceof \Exception ? $e : new FatalThrowableError($e); + $event = new ConsoleExceptionEvent($command, $input, $output, $x, $x->getCode()); + $this->dispatcher->dispatch(ConsoleEvents::EXCEPTION, $event); + + if ($x !== $event->getException()) { + $e = $event->getException(); + } + } + $event = new ConsoleErrorEvent($input, $output, $e, $command); + $this->dispatcher->dispatch(ConsoleEvents::ERROR, $event); + $e = $event->getError(); + + if (0 === $exitCode = $event->getExitCode()) { + $e = null; + } + } + + $event = new ConsoleTerminateEvent($command, $input, $output, $exitCode); + $this->dispatcher->dispatch(ConsoleEvents::TERMINATE, $event); + + if (null !== $e) { + throw $e; + } + + return $event->getExitCode(); + } + + /** + * Gets the name of the command based on input. + * + * @return string The command name + */ + protected function getCommandName(InputInterface $input) + { + return $this->singleCommand ? $this->defaultCommand : $input->getFirstArgument(); + } + + /** + * Gets the default input definition. + * + * @return InputDefinition An InputDefinition instance + */ + protected function getDefaultInputDefinition() + { + return new InputDefinition([ + new InputArgument('command', InputArgument::REQUIRED, 'The command to execute'), + + new InputOption('--help', '-h', InputOption::VALUE_NONE, 'Display this help message'), + new InputOption('--quiet', '-q', InputOption::VALUE_NONE, 'Do not output any message'), + new InputOption('--verbose', '-v|vv|vvv', InputOption::VALUE_NONE, 'Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug'), + new InputOption('--version', '-V', InputOption::VALUE_NONE, 'Display this application version'), + new InputOption('--ansi', '', InputOption::VALUE_NONE, 'Force ANSI output'), + new InputOption('--no-ansi', '', InputOption::VALUE_NONE, 'Disable ANSI output'), + new InputOption('--no-interaction', '-n', InputOption::VALUE_NONE, 'Do not ask any interactive question'), + ]); + } + + /** + * Gets the default commands that should always be available. + * + * @return Command[] An array of default Command instances + */ + protected function getDefaultCommands() + { + return [new HelpCommand(), new ListCommand()]; + } + + /** + * Gets the default helper set with the helpers that should always be available. + * + * @return HelperSet A HelperSet instance + */ + protected function getDefaultHelperSet() + { + return new HelperSet([ + new FormatterHelper(), + new DebugFormatterHelper(), + new ProcessHelper(), + new QuestionHelper(), + ]); + } + + /** + * Returns abbreviated suggestions in string format. + * + * @param array $abbrevs Abbreviated suggestions to convert + * + * @return string A formatted string of abbreviated suggestions + */ + private function getAbbreviationSuggestions($abbrevs) + { + return ' '.implode("\n ", $abbrevs); + } + + /** + * Returns the namespace part of the command name. + * + * This method is not part of public API and should not be used directly. + * + * @param string $name The full name of the command + * @param string $limit The maximum number of parts of the namespace + * + * @return string The namespace of the command + */ + public function extractNamespace($name, $limit = null) + { + $parts = explode(':', $name); + array_pop($parts); + + return implode(':', null === $limit ? $parts : \array_slice($parts, 0, $limit)); + } + + /** + * Finds alternative of $name among $collection, + * if nothing is found in $collection, try in $abbrevs. + * + * @param string $name The string + * @param iterable $collection The collection + * + * @return string[] A sorted array of similar string + */ + private function findAlternatives($name, $collection) + { + $threshold = 1e3; + $alternatives = []; + + $collectionParts = []; + foreach ($collection as $item) { + $collectionParts[$item] = explode(':', $item); + } + + foreach (explode(':', $name) as $i => $subname) { + foreach ($collectionParts as $collectionName => $parts) { + $exists = isset($alternatives[$collectionName]); + if (!isset($parts[$i]) && $exists) { + $alternatives[$collectionName] += $threshold; + continue; + } elseif (!isset($parts[$i])) { + continue; + } + + $lev = levenshtein($subname, $parts[$i]); + if ($lev <= \strlen($subname) / 3 || '' !== $subname && false !== strpos($parts[$i], $subname)) { + $alternatives[$collectionName] = $exists ? $alternatives[$collectionName] + $lev : $lev; + } elseif ($exists) { + $alternatives[$collectionName] += $threshold; + } + } + } + + foreach ($collection as $item) { + $lev = levenshtein($name, $item); + if ($lev <= \strlen($name) / 3 || false !== strpos($item, $name)) { + $alternatives[$item] = isset($alternatives[$item]) ? $alternatives[$item] - $lev : $lev; + } + } + + $alternatives = array_filter($alternatives, function ($lev) use ($threshold) { return $lev < 2 * $threshold; }); + ksort($alternatives, SORT_NATURAL | SORT_FLAG_CASE); + + return array_keys($alternatives); + } + + /** + * Sets the default Command name. + * + * @param string $commandName The Command name + * @param bool $isSingleCommand Set to true if there is only one command in this application + * + * @return self + */ + public function setDefaultCommand($commandName, $isSingleCommand = false) + { + $this->defaultCommand = $commandName; + + if ($isSingleCommand) { + // Ensure the command exist + $this->find($commandName); + + $this->singleCommand = true; + } + + return $this; + } + + /** + * @internal + */ + public function isSingleCommand() + { + return $this->singleCommand; + } + + private function splitStringByWidth($string, $width) + { + // str_split is not suitable for multi-byte characters, we should use preg_split to get char array properly. + // additionally, array_slice() is not enough as some character has doubled width. + // we need a function to split string not by character count but by string width + if (false === $encoding = mb_detect_encoding($string, null, true)) { + return str_split($string, $width); + } + + $utf8String = mb_convert_encoding($string, 'utf8', $encoding); + $lines = []; + $line = ''; + foreach (preg_split('//u', $utf8String) as $char) { + // test if $char could be appended to current line + if (mb_strwidth($line.$char, 'utf8') <= $width) { + $line .= $char; + continue; + } + // if not, push current line to array and make new line + $lines[] = str_pad($line, $width); + $line = $char; + } + + $lines[] = \count($lines) ? str_pad($line, $width) : $line; + + mb_convert_variables($encoding, 'utf8', $lines); + + return $lines; + } + + /** + * Returns all namespaces of the command name. + * + * @param string $name The full name of the command + * + * @return string[] The namespaces of the command + */ + private function extractAllNamespaces($name) + { + // -1 as third argument is needed to skip the command short name when exploding + $parts = explode(':', $name, -1); + $namespaces = []; + + foreach ($parts as $part) { + if (\count($namespaces)) { + $namespaces[] = end($namespaces).':'.$part; + } else { + $namespaces[] = $part; + } + } + + return $namespaces; + } + + private function init() + { + if ($this->initialized) { + return; + } + $this->initialized = true; + + foreach ($this->getDefaultCommands() as $command) { + $this->add($command); + } + } +} diff --git a/lib/symfony/console/CHANGELOG.md b/lib/symfony/console/CHANGELOG.md new file mode 100644 index 000000000..946ff1e0b --- /dev/null +++ b/lib/symfony/console/CHANGELOG.md @@ -0,0 +1,112 @@ +CHANGELOG +========= + +3.4.0 +----- + + * added `SHELL_VERBOSITY` env var to control verbosity + * added `CommandLoaderInterface`, `FactoryCommandLoader` and PSR-11 + `ContainerCommandLoader` for commands lazy-loading + * added a case-insensitive command name matching fallback + * added static `Command::$defaultName/getDefaultName()`, allowing for + commands to be registered at compile time in the application command loader. + Setting the `$defaultName` property avoids the need for filling the `command` + attribute on the `console.command` tag when using `AddConsoleCommandPass`. + +3.3.0 +----- + +* added `ExceptionListener` +* added `AddConsoleCommandPass` (originally in FrameworkBundle) +* [BC BREAK] `Input::getOption()` no longer returns the default value for options + with value optional explicitly passed empty +* added console.error event to catch exceptions thrown by other listeners +* deprecated console.exception event in favor of console.error +* added ability to handle `CommandNotFoundException` through the + `console.error` event +* deprecated default validation in `SymfonyQuestionHelper::ask` + +3.2.0 +------ + +* added `setInputs()` method to CommandTester for ease testing of commands expecting inputs +* added `setStream()` and `getStream()` methods to Input (implement StreamableInputInterface) +* added StreamableInputInterface +* added LockableTrait + +3.1.0 +----- + + * added truncate method to FormatterHelper + * added setColumnWidth(s) method to Table + +2.8.3 +----- + + * remove readline support from the question helper as it caused issues + +2.8.0 +----- + + * use readline for user input in the question helper when available to allow + the use of arrow keys + +2.6.0 +----- + + * added a Process helper + * added a DebugFormatter helper + +2.5.0 +----- + + * deprecated the dialog helper (use the question helper instead) + * deprecated TableHelper in favor of Table + * deprecated ProgressHelper in favor of ProgressBar + * added ConsoleLogger + * added a question helper + * added a way to set the process name of a command + * added a way to set a default command instead of `ListCommand` + +2.4.0 +----- + + * added a way to force terminal dimensions + * added a convenient method to detect verbosity level + * [BC BREAK] made descriptors use output instead of returning a string + +2.3.0 +----- + + * added multiselect support to the select dialog helper + * added Table Helper for tabular data rendering + * added support for events in `Application` + * added a way to normalize EOLs in `ApplicationTester::getDisplay()` and `CommandTester::getDisplay()` + * added a way to set the progress bar progress via the `setCurrent` method + * added support for multiple InputOption shortcuts, written as `'-a|-b|-c'` + * added two additional verbosity levels, VERBOSITY_VERY_VERBOSE and VERBOSITY_DEBUG + +2.2.0 +----- + + * added support for colorization on Windows via ConEmu + * add a method to Dialog Helper to ask for a question and hide the response + * added support for interactive selections in console (DialogHelper::select()) + * added support for autocompletion as you type in Dialog Helper + +2.1.0 +----- + + * added ConsoleOutputInterface + * added the possibility to disable a command (Command::isEnabled()) + * added suggestions when a command does not exist + * added a --raw option to the list command + * added support for STDERR in the console output class (errors are now sent + to STDERR) + * made the defaults (helper set, commands, input definition) in Application + more easily customizable + * added support for the shell even if readline is not available + * added support for process isolation in Symfony shell via + `--process-isolation` switch + * added support for `--`, which disables options parsing after that point + (tokens will be parsed as arguments) diff --git a/lib/symfony/console/Command/Command.php b/lib/symfony/console/Command/Command.php new file mode 100644 index 000000000..5f30da7a2 --- /dev/null +++ b/lib/symfony/console/Command/Command.php @@ -0,0 +1,664 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Command; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Exception\ExceptionInterface; +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\LogicException; +use Symfony\Component\Console\Helper\HelperSet; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Base class for all commands. + * + * @author Fabien Potencier + */ +class Command +{ + /** + * @var string|null The default command name + */ + protected static $defaultName; + + private $application; + private $name; + private $processTitle; + private $aliases = []; + private $definition; + private $hidden = false; + private $help; + private $description; + private $ignoreValidationErrors = false; + private $applicationDefinitionMerged = false; + private $applicationDefinitionMergedWithArgs = false; + private $code; + private $synopsis = []; + private $usages = []; + private $helperSet; + + /** + * @return string|null The default command name or null when no default name is set + */ + public static function getDefaultName() + { + $class = \get_called_class(); + $r = new \ReflectionProperty($class, 'defaultName'); + + return $class === $r->class ? static::$defaultName : null; + } + + /** + * @param string|null $name The name of the command; passing null means it must be set in configure() + * + * @throws LogicException When the command name is empty + */ + public function __construct($name = null) + { + $this->definition = new InputDefinition(); + + if (null !== $name || null !== $name = static::getDefaultName()) { + $this->setName($name); + } + + $this->configure(); + } + + /** + * Ignores validation errors. + * + * This is mainly useful for the help command. + */ + public function ignoreValidationErrors() + { + $this->ignoreValidationErrors = true; + } + + public function setApplication(Application $application = null) + { + $this->application = $application; + if ($application) { + $this->setHelperSet($application->getHelperSet()); + } else { + $this->helperSet = null; + } + } + + public function setHelperSet(HelperSet $helperSet) + { + $this->helperSet = $helperSet; + } + + /** + * Gets the helper set. + * + * @return HelperSet A HelperSet instance + */ + public function getHelperSet() + { + return $this->helperSet; + } + + /** + * Gets the application instance for this command. + * + * @return Application An Application instance + */ + public function getApplication() + { + return $this->application; + } + + /** + * Checks whether the command is enabled or not in the current environment. + * + * Override this to check for x or y and return false if the command can not + * run properly under the current conditions. + * + * @return bool + */ + public function isEnabled() + { + return true; + } + + /** + * Configures the current command. + */ + protected function configure() + { + } + + /** + * Executes the current command. + * + * This method is not abstract because you can use this class + * as a concrete class. In this case, instead of defining the + * execute() method, you set the code to execute by passing + * a Closure to the setCode() method. + * + * @return int|null null or 0 if everything went fine, or an error code + * + * @throws LogicException When this abstract method is not implemented + * + * @see setCode() + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + throw new LogicException('You must override the execute() method in the concrete command class.'); + } + + /** + * Interacts with the user. + * + * This method is executed before the InputDefinition is validated. + * This means that this is the only place where the command can + * interactively ask for values of missing required arguments. + */ + protected function interact(InputInterface $input, OutputInterface $output) + { + } + + /** + * Initializes the command after the input has been bound and before the input + * is validated. + * + * This is mainly useful when a lot of commands extends one main command + * where some things need to be initialized based on the input arguments and options. + * + * @see InputInterface::bind() + * @see InputInterface::validate() + */ + protected function initialize(InputInterface $input, OutputInterface $output) + { + } + + /** + * Runs the command. + * + * The code to execute is either defined directly with the + * setCode() method or by overriding the execute() method + * in a sub-class. + * + * @return int The command exit code + * + * @throws \Exception When binding input fails. Bypass this by calling {@link ignoreValidationErrors()}. + * + * @see setCode() + * @see execute() + */ + public function run(InputInterface $input, OutputInterface $output) + { + // force the creation of the synopsis before the merge with the app definition + $this->getSynopsis(true); + $this->getSynopsis(false); + + // add the application arguments and options + $this->mergeApplicationDefinition(); + + // bind the input against the command specific arguments/options + try { + $input->bind($this->definition); + } catch (ExceptionInterface $e) { + if (!$this->ignoreValidationErrors) { + throw $e; + } + } + + $this->initialize($input, $output); + + if (null !== $this->processTitle) { + if (\function_exists('cli_set_process_title')) { + if (!@cli_set_process_title($this->processTitle)) { + if ('Darwin' === PHP_OS) { + $output->writeln('Running "cli_set_process_title" as an unprivileged user is not supported on MacOS.', OutputInterface::VERBOSITY_VERY_VERBOSE); + } else { + cli_set_process_title($this->processTitle); + } + } + } elseif (\function_exists('setproctitle')) { + setproctitle($this->processTitle); + } elseif (OutputInterface::VERBOSITY_VERY_VERBOSE === $output->getVerbosity()) { + $output->writeln('Install the proctitle PECL to be able to change the process title.'); + } + } + + if ($input->isInteractive()) { + $this->interact($input, $output); + } + + // The command name argument is often omitted when a command is executed directly with its run() method. + // It would fail the validation if we didn't make sure the command argument is present, + // since it's required by the application. + if ($input->hasArgument('command') && null === $input->getArgument('command')) { + $input->setArgument('command', $this->getName()); + } + + $input->validate(); + + if ($this->code) { + $statusCode = \call_user_func($this->code, $input, $output); + } else { + $statusCode = $this->execute($input, $output); + } + + return is_numeric($statusCode) ? (int) $statusCode : 0; + } + + /** + * Sets the code to execute when running this command. + * + * If this method is used, it overrides the code defined + * in the execute() method. + * + * @param callable $code A callable(InputInterface $input, OutputInterface $output) + * + * @return $this + * + * @throws InvalidArgumentException + * + * @see execute() + */ + public function setCode(callable $code) + { + if ($code instanceof \Closure) { + $r = new \ReflectionFunction($code); + if (null === $r->getClosureThis()) { + if (\PHP_VERSION_ID < 70000) { + // Bug in PHP5: https://bugs.php.net/bug.php?id=64761 + // This means that we cannot bind static closures and therefore we must + // ignore any errors here. There is no way to test if the closure is + // bindable. + $code = @\Closure::bind($code, $this); + } else { + $code = \Closure::bind($code, $this); + } + } + } + + $this->code = $code; + + return $this; + } + + /** + * Merges the application definition with the command definition. + * + * This method is not part of public API and should not be used directly. + * + * @param bool $mergeArgs Whether to merge or not the Application definition arguments to Command definition arguments + */ + public function mergeApplicationDefinition($mergeArgs = true) + { + if (null === $this->application || (true === $this->applicationDefinitionMerged && ($this->applicationDefinitionMergedWithArgs || !$mergeArgs))) { + return; + } + + $this->definition->addOptions($this->application->getDefinition()->getOptions()); + + $this->applicationDefinitionMerged = true; + + if ($mergeArgs) { + $currentArguments = $this->definition->getArguments(); + $this->definition->setArguments($this->application->getDefinition()->getArguments()); + $this->definition->addArguments($currentArguments); + + $this->applicationDefinitionMergedWithArgs = true; + } + } + + /** + * Sets an array of argument and option instances. + * + * @param array|InputDefinition $definition An array of argument and option instances or a definition instance + * + * @return $this + */ + public function setDefinition($definition) + { + if ($definition instanceof InputDefinition) { + $this->definition = $definition; + } else { + $this->definition->setDefinition($definition); + } + + $this->applicationDefinitionMerged = false; + + return $this; + } + + /** + * Gets the InputDefinition attached to this Command. + * + * @return InputDefinition An InputDefinition instance + */ + public function getDefinition() + { + return $this->definition; + } + + /** + * Gets the InputDefinition to be used to create representations of this Command. + * + * Can be overridden to provide the original command representation when it would otherwise + * be changed by merging with the application InputDefinition. + * + * This method is not part of public API and should not be used directly. + * + * @return InputDefinition An InputDefinition instance + */ + public function getNativeDefinition() + { + return $this->getDefinition(); + } + + /** + * Adds an argument. + * + * @param string $name The argument name + * @param int|null $mode The argument mode: InputArgument::REQUIRED or InputArgument::OPTIONAL + * @param string $description A description text + * @param string|string[]|null $default The default value (for InputArgument::OPTIONAL mode only) + * + * @throws InvalidArgumentException When argument mode is not valid + * + * @return $this + */ + public function addArgument($name, $mode = null, $description = '', $default = null) + { + $this->definition->addArgument(new InputArgument($name, $mode, $description, $default)); + + return $this; + } + + /** + * Adds an option. + * + * @param string $name The option name + * @param string|array|null $shortcut The shortcuts, can be null, a string of shortcuts delimited by | or an array of shortcuts + * @param int|null $mode The option mode: One of the InputOption::VALUE_* constants + * @param string $description A description text + * @param string|string[]|int|bool|null $default The default value (must be null for InputOption::VALUE_NONE) + * + * @throws InvalidArgumentException If option mode is invalid or incompatible + * + * @return $this + */ + public function addOption($name, $shortcut = null, $mode = null, $description = '', $default = null) + { + $this->definition->addOption(new InputOption($name, $shortcut, $mode, $description, $default)); + + return $this; + } + + /** + * Sets the name of the command. + * + * This method can set both the namespace and the name if + * you separate them by a colon (:) + * + * $command->setName('foo:bar'); + * + * @param string $name The command name + * + * @return $this + * + * @throws InvalidArgumentException When the name is invalid + */ + public function setName($name) + { + $this->validateName($name); + + $this->name = $name; + + return $this; + } + + /** + * Sets the process title of the command. + * + * This feature should be used only when creating a long process command, + * like a daemon. + * + * PHP 5.5+ or the proctitle PECL library is required + * + * @param string $title The process title + * + * @return $this + */ + public function setProcessTitle($title) + { + $this->processTitle = $title; + + return $this; + } + + /** + * Returns the command name. + * + * @return string The command name + */ + public function getName() + { + return $this->name; + } + + /** + * @param bool $hidden Whether or not the command should be hidden from the list of commands + * + * @return Command The current instance + */ + public function setHidden($hidden) + { + $this->hidden = (bool) $hidden; + + return $this; + } + + /** + * @return bool whether the command should be publicly shown or not + */ + public function isHidden() + { + return $this->hidden; + } + + /** + * Sets the description for the command. + * + * @param string $description The description for the command + * + * @return $this + */ + public function setDescription($description) + { + $this->description = $description; + + return $this; + } + + /** + * Returns the description for the command. + * + * @return string The description for the command + */ + public function getDescription() + { + return $this->description; + } + + /** + * Sets the help for the command. + * + * @param string $help The help for the command + * + * @return $this + */ + public function setHelp($help) + { + $this->help = $help; + + return $this; + } + + /** + * Returns the help for the command. + * + * @return string The help for the command + */ + public function getHelp() + { + return $this->help; + } + + /** + * Returns the processed help for the command replacing the %command.name% and + * %command.full_name% patterns with the real values dynamically. + * + * @return string The processed help for the command + */ + public function getProcessedHelp() + { + $name = $this->name; + $isSingleCommand = $this->application && $this->application->isSingleCommand(); + + $placeholders = [ + '%command.name%', + '%command.full_name%', + ]; + $replacements = [ + $name, + $isSingleCommand ? $_SERVER['PHP_SELF'] : $_SERVER['PHP_SELF'].' '.$name, + ]; + + return str_replace($placeholders, $replacements, $this->getHelp() ?: $this->getDescription()); + } + + /** + * Sets the aliases for the command. + * + * @param string[] $aliases An array of aliases for the command + * + * @return $this + * + * @throws InvalidArgumentException When an alias is invalid + */ + public function setAliases($aliases) + { + if (!\is_array($aliases) && !$aliases instanceof \Traversable) { + throw new InvalidArgumentException('$aliases must be an array or an instance of \Traversable'); + } + + foreach ($aliases as $alias) { + $this->validateName($alias); + } + + $this->aliases = $aliases; + + return $this; + } + + /** + * Returns the aliases for the command. + * + * @return array An array of aliases for the command + */ + public function getAliases() + { + return $this->aliases; + } + + /** + * Returns the synopsis for the command. + * + * @param bool $short Whether to show the short version of the synopsis (with options folded) or not + * + * @return string The synopsis + */ + public function getSynopsis($short = false) + { + $key = $short ? 'short' : 'long'; + + if (!isset($this->synopsis[$key])) { + $this->synopsis[$key] = trim(sprintf('%s %s', $this->name, $this->definition->getSynopsis($short))); + } + + return $this->synopsis[$key]; + } + + /** + * Add a command usage example. + * + * @param string $usage The usage, it'll be prefixed with the command name + * + * @return $this + */ + public function addUsage($usage) + { + if (0 !== strpos($usage, $this->name)) { + $usage = sprintf('%s %s', $this->name, $usage); + } + + $this->usages[] = $usage; + + return $this; + } + + /** + * Returns alternative usages of the command. + * + * @return array + */ + public function getUsages() + { + return $this->usages; + } + + /** + * Gets a helper instance by name. + * + * @param string $name The helper name + * + * @return mixed The helper value + * + * @throws LogicException if no HelperSet is defined + * @throws InvalidArgumentException if the helper is not defined + */ + public function getHelper($name) + { + if (null === $this->helperSet) { + throw new LogicException(sprintf('Cannot retrieve helper "%s" because there is no HelperSet defined. Did you forget to add your command to the application or to set the application on the command using the setApplication() method? You can also set the HelperSet directly using the setHelperSet() method.', $name)); + } + + return $this->helperSet->get($name); + } + + /** + * Validates a command name. + * + * It must be non-empty and parts can optionally be separated by ":". + * + * @param string $name + * + * @throws InvalidArgumentException When the name is invalid + */ + private function validateName($name) + { + if (!preg_match('/^[^\:]++(\:[^\:]++)*$/', $name)) { + throw new InvalidArgumentException(sprintf('Command name "%s" is invalid.', $name)); + } + } +} diff --git a/lib/symfony/console/Command/HelpCommand.php b/lib/symfony/console/Command/HelpCommand.php new file mode 100644 index 000000000..23847766b --- /dev/null +++ b/lib/symfony/console/Command/HelpCommand.php @@ -0,0 +1,81 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Command; + +use Symfony\Component\Console\Helper\DescriptorHelper; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * HelpCommand displays the help for a given command. + * + * @author Fabien Potencier + */ +class HelpCommand extends Command +{ + private $command; + + /** + * {@inheritdoc} + */ + protected function configure() + { + $this->ignoreValidationErrors(); + + $this + ->setName('help') + ->setDefinition([ + new InputArgument('command_name', InputArgument::OPTIONAL, 'The command name', 'help'), + new InputOption('format', null, InputOption::VALUE_REQUIRED, 'The output format (txt, xml, json, or md)', 'txt'), + new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw command help'), + ]) + ->setDescription('Displays help for a command') + ->setHelp(<<<'EOF' +The %command.name% command displays help for a given command: + + php %command.full_name% list + +You can also output the help in other formats by using the --format option: + + php %command.full_name% --format=xml list + +To display the list of available commands, please use the list command. +EOF + ) + ; + } + + public function setCommand(Command $command) + { + $this->command = $command; + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + if (null === $this->command) { + $this->command = $this->getApplication()->find($input->getArgument('command_name')); + } + + $helper = new DescriptorHelper(); + $helper->describe($output, $this->command, [ + 'format' => $input->getOption('format'), + 'raw_text' => $input->getOption('raw'), + ]); + + $this->command = null; + } +} diff --git a/lib/symfony/console/Command/ListCommand.php b/lib/symfony/console/Command/ListCommand.php new file mode 100644 index 000000000..7259b1263 --- /dev/null +++ b/lib/symfony/console/Command/ListCommand.php @@ -0,0 +1,90 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Command; + +use Symfony\Component\Console\Helper\DescriptorHelper; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * ListCommand displays the list of all available commands for the application. + * + * @author Fabien Potencier + */ +class ListCommand extends Command +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('list') + ->setDefinition($this->createDefinition()) + ->setDescription('Lists commands') + ->setHelp(<<<'EOF' +The %command.name% command lists all commands: + + php %command.full_name% + +You can also display the commands for a specific namespace: + + php %command.full_name% test + +You can also output the information in other formats by using the --format option: + + php %command.full_name% --format=xml + +It's also possible to get raw list of commands (useful for embedding command runner): + + php %command.full_name% --raw +EOF + ) + ; + } + + /** + * {@inheritdoc} + */ + public function getNativeDefinition() + { + return $this->createDefinition(); + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $helper = new DescriptorHelper(); + $helper->describe($output, $this->getApplication(), [ + 'format' => $input->getOption('format'), + 'raw_text' => $input->getOption('raw'), + 'namespace' => $input->getArgument('namespace'), + ]); + } + + /** + * {@inheritdoc} + */ + private function createDefinition() + { + return new InputDefinition([ + new InputArgument('namespace', InputArgument::OPTIONAL, 'The namespace name'), + new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw command list'), + new InputOption('format', null, InputOption::VALUE_REQUIRED, 'The output format (txt, xml, json, or md)', 'txt'), + ]); + } +} diff --git a/lib/symfony/console/Command/LockableTrait.php b/lib/symfony/console/Command/LockableTrait.php new file mode 100644 index 000000000..308ebf28c --- /dev/null +++ b/lib/symfony/console/Command/LockableTrait.php @@ -0,0 +1,72 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Command; + +use Symfony\Component\Console\Exception\LogicException; +use Symfony\Component\Console\Exception\RuntimeException; +use Symfony\Component\Lock\Factory; +use Symfony\Component\Lock\Lock; +use Symfony\Component\Lock\Store\FlockStore; +use Symfony\Component\Lock\Store\SemaphoreStore; + +/** + * Basic lock feature for commands. + * + * @author Geoffrey Brier + */ +trait LockableTrait +{ + /** @var Lock */ + private $lock; + + /** + * Locks a command. + * + * @return bool + */ + private function lock($name = null, $blocking = false) + { + if (!class_exists(SemaphoreStore::class)) { + throw new RuntimeException('To enable the locking feature you must install the symfony/lock component.'); + } + + if (null !== $this->lock) { + throw new LogicException('A lock is already in place.'); + } + + if (SemaphoreStore::isSupported($blocking)) { + $store = new SemaphoreStore(); + } else { + $store = new FlockStore(); + } + + $this->lock = (new Factory($store))->createLock($name ?: $this->getName()); + if (!$this->lock->acquire($blocking)) { + $this->lock = null; + + return false; + } + + return true; + } + + /** + * Releases the command lock if there is one. + */ + private function release() + { + if ($this->lock) { + $this->lock->release(); + $this->lock = null; + } + } +} diff --git a/lib/symfony/console/CommandLoader/CommandLoaderInterface.php b/lib/symfony/console/CommandLoader/CommandLoaderInterface.php new file mode 100644 index 000000000..ca1029cb6 --- /dev/null +++ b/lib/symfony/console/CommandLoader/CommandLoaderInterface.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\CommandLoader; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Exception\CommandNotFoundException; + +/** + * @author Robin Chalas + */ +interface CommandLoaderInterface +{ + /** + * Loads a command. + * + * @param string $name + * + * @return Command + * + * @throws CommandNotFoundException + */ + public function get($name); + + /** + * Checks if a command exists. + * + * @param string $name + * + * @return bool + */ + public function has($name); + + /** + * @return string[] All registered command names + */ + public function getNames(); +} diff --git a/lib/symfony/console/CommandLoader/ContainerCommandLoader.php b/lib/symfony/console/CommandLoader/ContainerCommandLoader.php new file mode 100644 index 000000000..8000c7d5e --- /dev/null +++ b/lib/symfony/console/CommandLoader/ContainerCommandLoader.php @@ -0,0 +1,64 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\CommandLoader; + +use Psr\Container\ContainerInterface; +use Symfony\Component\Console\Exception\CommandNotFoundException; + +/** + * Loads commands from a PSR-11 container. + * + * @author Robin Chalas + */ +class ContainerCommandLoader implements CommandLoaderInterface +{ + private $container; + private $commandMap; + + /** + * @param ContainerInterface $container A container from which to load command services + * @param array $commandMap An array with command names as keys and service ids as values + */ + public function __construct(ContainerInterface $container, array $commandMap) + { + $this->container = $container; + $this->commandMap = $commandMap; + } + + /** + * {@inheritdoc} + */ + public function get($name) + { + if (!$this->has($name)) { + throw new CommandNotFoundException(sprintf('Command "%s" does not exist.', $name)); + } + + return $this->container->get($this->commandMap[$name]); + } + + /** + * {@inheritdoc} + */ + public function has($name) + { + return isset($this->commandMap[$name]) && $this->container->has($this->commandMap[$name]); + } + + /** + * {@inheritdoc} + */ + public function getNames() + { + return array_keys($this->commandMap); + } +} diff --git a/lib/symfony/console/CommandLoader/FactoryCommandLoader.php b/lib/symfony/console/CommandLoader/FactoryCommandLoader.php new file mode 100644 index 000000000..d9c205571 --- /dev/null +++ b/lib/symfony/console/CommandLoader/FactoryCommandLoader.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\CommandLoader; + +use Symfony\Component\Console\Exception\CommandNotFoundException; + +/** + * A simple command loader using factories to instantiate commands lazily. + * + * @author Maxime Steinhausser + */ +class FactoryCommandLoader implements CommandLoaderInterface +{ + private $factories; + + /** + * @param callable[] $factories Indexed by command names + */ + public function __construct(array $factories) + { + $this->factories = $factories; + } + + /** + * {@inheritdoc} + */ + public function has($name) + { + return isset($this->factories[$name]); + } + + /** + * {@inheritdoc} + */ + public function get($name) + { + if (!isset($this->factories[$name])) { + throw new CommandNotFoundException(sprintf('Command "%s" does not exist.', $name)); + } + + $factory = $this->factories[$name]; + + return $factory(); + } + + /** + * {@inheritdoc} + */ + public function getNames() + { + return array_keys($this->factories); + } +} diff --git a/lib/symfony/console/ConsoleEvents.php b/lib/symfony/console/ConsoleEvents.php new file mode 100644 index 000000000..bf6cab9a1 --- /dev/null +++ b/lib/symfony/console/ConsoleEvents.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console; + +/** + * Contains all events dispatched by an Application. + * + * @author Francesco Levorato + */ +final class ConsoleEvents +{ + /** + * The COMMAND event allows you to attach listeners before any command is + * executed by the console. It also allows you to modify the command, input and output + * before they are handled to the command. + * + * @Event("Symfony\Component\Console\Event\ConsoleCommandEvent") + */ + const COMMAND = 'console.command'; + + /** + * The TERMINATE event allows you to attach listeners after a command is + * executed by the console. + * + * @Event("Symfony\Component\Console\Event\ConsoleTerminateEvent") + */ + const TERMINATE = 'console.terminate'; + + /** + * The EXCEPTION event occurs when an uncaught exception appears + * while executing Command#run(). + * + * This event allows you to deal with the exception or + * to modify the thrown exception. + * + * @Event("Symfony\Component\Console\Event\ConsoleExceptionEvent") + * + * @deprecated The console.exception event is deprecated since version 3.3 and will be removed in 4.0. Use the console.error event instead. + */ + const EXCEPTION = 'console.exception'; + + /** + * The ERROR event occurs when an uncaught exception or error appears. + * + * This event allows you to deal with the exception/error or + * to modify the thrown exception. + * + * @Event("Symfony\Component\Console\Event\ConsoleErrorEvent") + */ + const ERROR = 'console.error'; +} diff --git a/lib/symfony/console/DependencyInjection/AddConsoleCommandPass.php b/lib/symfony/console/DependencyInjection/AddConsoleCommandPass.php new file mode 100644 index 000000000..f656d6a86 --- /dev/null +++ b/lib/symfony/console/DependencyInjection/AddConsoleCommandPass.php @@ -0,0 +1,106 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\DependencyInjection; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\CommandLoader\ContainerCommandLoader; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\TypedReference; + +/** + * Registers console commands. + * + * @author Grégoire Pineau + */ +class AddConsoleCommandPass implements CompilerPassInterface +{ + private $commandLoaderServiceId; + private $commandTag; + + public function __construct($commandLoaderServiceId = 'console.command_loader', $commandTag = 'console.command') + { + $this->commandLoaderServiceId = $commandLoaderServiceId; + $this->commandTag = $commandTag; + } + + public function process(ContainerBuilder $container) + { + $commandServices = $container->findTaggedServiceIds($this->commandTag, true); + $lazyCommandMap = []; + $lazyCommandRefs = []; + $serviceIds = []; + $lazyServiceIds = []; + + foreach ($commandServices as $id => $tags) { + $definition = $container->getDefinition($id); + $class = $container->getParameterBag()->resolveValue($definition->getClass()); + + $commandId = 'console.command.'.strtolower(str_replace('\\', '_', $class)); + + if (isset($tags[0]['command'])) { + $commandName = $tags[0]['command']; + } else { + if (!$r = $container->getReflectionClass($class)) { + throw new InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.', $class, $id)); + } + if (!$r->isSubclassOf(Command::class)) { + throw new InvalidArgumentException(sprintf('The service "%s" tagged "%s" must be a subclass of "%s".', $id, $this->commandTag, Command::class)); + } + $commandName = $class::getDefaultName(); + } + + if (null === $commandName) { + if (isset($serviceIds[$commandId]) || $container->hasAlias($commandId)) { + $commandId = $commandId.'_'.$id; + } + if (!$definition->isPublic() || $definition->isPrivate()) { + $container->setAlias($commandId, $id)->setPublic(true); + $id = $commandId; + } + $serviceIds[$commandId] = $id; + + continue; + } + + $serviceIds[$commandId] = $id; + $lazyServiceIds[$id] = true; + unset($tags[0]); + $lazyCommandMap[$commandName] = $id; + $lazyCommandRefs[$id] = new TypedReference($id, $class); + $aliases = []; + + foreach ($tags as $tag) { + if (isset($tag['command'])) { + $aliases[] = $tag['command']; + $lazyCommandMap[$tag['command']] = $id; + } + } + + $definition->addMethodCall('setName', [$commandName]); + + if ($aliases) { + $definition->addMethodCall('setAliases', [$aliases]); + } + } + + $container + ->register($this->commandLoaderServiceId, ContainerCommandLoader::class) + ->setPublic(true) + ->setArguments([ServiceLocatorTagPass::register($container, $lazyCommandRefs), $lazyCommandMap]); + + $container->setParameter('console.command.ids', $serviceIds); + $container->setParameter('console.lazy_command.ids', $lazyServiceIds); + } +} diff --git a/lib/symfony/console/Descriptor/ApplicationDescription.php b/lib/symfony/console/Descriptor/ApplicationDescription.php new file mode 100644 index 000000000..79a358fb3 --- /dev/null +++ b/lib/symfony/console/Descriptor/ApplicationDescription.php @@ -0,0 +1,152 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Descriptor; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Exception\CommandNotFoundException; + +/** + * @author Jean-François Simon + * + * @internal + */ +class ApplicationDescription +{ + const GLOBAL_NAMESPACE = '_global'; + + private $application; + private $namespace; + private $showHidden; + + /** + * @var array + */ + private $namespaces; + + /** + * @var Command[] + */ + private $commands; + + /** + * @var Command[] + */ + private $aliases; + + /** + * @param Application $application + * @param string|null $namespace + * @param bool $showHidden + */ + public function __construct(Application $application, $namespace = null, $showHidden = false) + { + $this->application = $application; + $this->namespace = $namespace; + $this->showHidden = $showHidden; + } + + /** + * @return array + */ + public function getNamespaces() + { + if (null === $this->namespaces) { + $this->inspectApplication(); + } + + return $this->namespaces; + } + + /** + * @return Command[] + */ + public function getCommands() + { + if (null === $this->commands) { + $this->inspectApplication(); + } + + return $this->commands; + } + + /** + * @param string $name + * + * @return Command + * + * @throws CommandNotFoundException + */ + public function getCommand($name) + { + if (!isset($this->commands[$name]) && !isset($this->aliases[$name])) { + throw new CommandNotFoundException(sprintf('Command %s does not exist.', $name)); + } + + return isset($this->commands[$name]) ? $this->commands[$name] : $this->aliases[$name]; + } + + private function inspectApplication() + { + $this->commands = []; + $this->namespaces = []; + + $all = $this->application->all($this->namespace ? $this->application->findNamespace($this->namespace) : null); + foreach ($this->sortCommands($all) as $namespace => $commands) { + $names = []; + + /** @var Command $command */ + foreach ($commands as $name => $command) { + if (!$command->getName() || (!$this->showHidden && $command->isHidden())) { + continue; + } + + if ($command->getName() === $name) { + $this->commands[$name] = $command; + } else { + $this->aliases[$name] = $command; + } + + $names[] = $name; + } + + $this->namespaces[$namespace] = ['id' => $namespace, 'commands' => $names]; + } + } + + /** + * @return array + */ + private function sortCommands(array $commands) + { + $namespacedCommands = []; + $globalCommands = []; + foreach ($commands as $name => $command) { + $key = $this->application->extractNamespace($name, 1); + if (!$key) { + $globalCommands['_global'][$name] = $command; + } else { + $namespacedCommands[$key][$name] = $command; + } + } + ksort($namespacedCommands); + $namespacedCommands = array_merge($globalCommands, $namespacedCommands); + + foreach ($namespacedCommands as &$commandsSet) { + ksort($commandsSet); + } + // unset reference to keep scope clear + unset($commandsSet); + + return $namespacedCommands; + } +} diff --git a/lib/symfony/console/Descriptor/Descriptor.php b/lib/symfony/console/Descriptor/Descriptor.php new file mode 100644 index 000000000..d25a708e4 --- /dev/null +++ b/lib/symfony/console/Descriptor/Descriptor.php @@ -0,0 +1,107 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Descriptor; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * @author Jean-François Simon + * + * @internal + */ +abstract class Descriptor implements DescriptorInterface +{ + /** + * @var OutputInterface + */ + protected $output; + + /** + * {@inheritdoc} + */ + public function describe(OutputInterface $output, $object, array $options = []) + { + $this->output = $output; + + switch (true) { + case $object instanceof InputArgument: + $this->describeInputArgument($object, $options); + break; + case $object instanceof InputOption: + $this->describeInputOption($object, $options); + break; + case $object instanceof InputDefinition: + $this->describeInputDefinition($object, $options); + break; + case $object instanceof Command: + $this->describeCommand($object, $options); + break; + case $object instanceof Application: + $this->describeApplication($object, $options); + break; + default: + throw new InvalidArgumentException(sprintf('Object of type "%s" is not describable.', \get_class($object))); + } + } + + /** + * Writes content to output. + * + * @param string $content + * @param bool $decorated + */ + protected function write($content, $decorated = false) + { + $this->output->write($content, false, $decorated ? OutputInterface::OUTPUT_NORMAL : OutputInterface::OUTPUT_RAW); + } + + /** + * Describes an InputArgument instance. + * + * @return string|mixed + */ + abstract protected function describeInputArgument(InputArgument $argument, array $options = []); + + /** + * Describes an InputOption instance. + * + * @return string|mixed + */ + abstract protected function describeInputOption(InputOption $option, array $options = []); + + /** + * Describes an InputDefinition instance. + * + * @return string|mixed + */ + abstract protected function describeInputDefinition(InputDefinition $definition, array $options = []); + + /** + * Describes a Command instance. + * + * @return string|mixed + */ + abstract protected function describeCommand(Command $command, array $options = []); + + /** + * Describes an Application instance. + * + * @return string|mixed + */ + abstract protected function describeApplication(Application $application, array $options = []); +} diff --git a/lib/symfony/console/Descriptor/DescriptorInterface.php b/lib/symfony/console/Descriptor/DescriptorInterface.php new file mode 100644 index 000000000..fbc07df87 --- /dev/null +++ b/lib/symfony/console/Descriptor/DescriptorInterface.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Descriptor; + +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Descriptor interface. + * + * @author Jean-François Simon + */ +interface DescriptorInterface +{ + /** + * Describes an object if supported. + * + * @param OutputInterface $output + * @param object $object + * @param array $options + */ + public function describe(OutputInterface $output, $object, array $options = []); +} diff --git a/lib/symfony/console/Descriptor/JsonDescriptor.php b/lib/symfony/console/Descriptor/JsonDescriptor.php new file mode 100644 index 000000000..f5a143800 --- /dev/null +++ b/lib/symfony/console/Descriptor/JsonDescriptor.php @@ -0,0 +1,170 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Descriptor; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; + +/** + * JSON descriptor. + * + * @author Jean-François Simon + * + * @internal + */ +class JsonDescriptor extends Descriptor +{ + /** + * {@inheritdoc} + */ + protected function describeInputArgument(InputArgument $argument, array $options = []) + { + $this->writeData($this->getInputArgumentData($argument), $options); + } + + /** + * {@inheritdoc} + */ + protected function describeInputOption(InputOption $option, array $options = []) + { + $this->writeData($this->getInputOptionData($option), $options); + } + + /** + * {@inheritdoc} + */ + protected function describeInputDefinition(InputDefinition $definition, array $options = []) + { + $this->writeData($this->getInputDefinitionData($definition), $options); + } + + /** + * {@inheritdoc} + */ + protected function describeCommand(Command $command, array $options = []) + { + $this->writeData($this->getCommandData($command), $options); + } + + /** + * {@inheritdoc} + */ + protected function describeApplication(Application $application, array $options = []) + { + $describedNamespace = isset($options['namespace']) ? $options['namespace'] : null; + $description = new ApplicationDescription($application, $describedNamespace, true); + $commands = []; + + foreach ($description->getCommands() as $command) { + $commands[] = $this->getCommandData($command); + } + + $data = []; + if ('UNKNOWN' !== $application->getName()) { + $data['application']['name'] = $application->getName(); + if ('UNKNOWN' !== $application->getVersion()) { + $data['application']['version'] = $application->getVersion(); + } + } + + $data['commands'] = $commands; + + if ($describedNamespace) { + $data['namespace'] = $describedNamespace; + } else { + $data['namespaces'] = array_values($description->getNamespaces()); + } + + $this->writeData($data, $options); + } + + /** + * Writes data as json. + * + * @return array|string + */ + private function writeData(array $data, array $options) + { + $flags = isset($options['json_encoding']) ? $options['json_encoding'] : 0; + + $this->write(json_encode($data, $flags)); + } + + /** + * @return array + */ + private function getInputArgumentData(InputArgument $argument) + { + return [ + 'name' => $argument->getName(), + 'is_required' => $argument->isRequired(), + 'is_array' => $argument->isArray(), + 'description' => preg_replace('/\s*[\r\n]\s*/', ' ', $argument->getDescription()), + 'default' => INF === $argument->getDefault() ? 'INF' : $argument->getDefault(), + ]; + } + + /** + * @return array + */ + private function getInputOptionData(InputOption $option) + { + return [ + 'name' => '--'.$option->getName(), + 'shortcut' => $option->getShortcut() ? '-'.str_replace('|', '|-', $option->getShortcut()) : '', + 'accept_value' => $option->acceptValue(), + 'is_value_required' => $option->isValueRequired(), + 'is_multiple' => $option->isArray(), + 'description' => preg_replace('/\s*[\r\n]\s*/', ' ', $option->getDescription()), + 'default' => INF === $option->getDefault() ? 'INF' : $option->getDefault(), + ]; + } + + /** + * @return array + */ + private function getInputDefinitionData(InputDefinition $definition) + { + $inputArguments = []; + foreach ($definition->getArguments() as $name => $argument) { + $inputArguments[$name] = $this->getInputArgumentData($argument); + } + + $inputOptions = []; + foreach ($definition->getOptions() as $name => $option) { + $inputOptions[$name] = $this->getInputOptionData($option); + } + + return ['arguments' => $inputArguments, 'options' => $inputOptions]; + } + + /** + * @return array + */ + private function getCommandData(Command $command) + { + $command->getSynopsis(); + $command->mergeApplicationDefinition(false); + + return [ + 'name' => $command->getName(), + 'usage' => array_merge([$command->getSynopsis()], $command->getUsages(), $command->getAliases()), + 'description' => $command->getDescription(), + 'help' => $command->getProcessedHelp(), + 'definition' => $this->getInputDefinitionData($command->getNativeDefinition()), + 'hidden' => $command->isHidden(), + ]; + } +} diff --git a/lib/symfony/console/Descriptor/MarkdownDescriptor.php b/lib/symfony/console/Descriptor/MarkdownDescriptor.php new file mode 100644 index 000000000..e6245778f --- /dev/null +++ b/lib/symfony/console/Descriptor/MarkdownDescriptor.php @@ -0,0 +1,182 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Descriptor; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Helper\Helper; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Markdown descriptor. + * + * @author Jean-François Simon + * + * @internal + */ +class MarkdownDescriptor extends Descriptor +{ + /** + * {@inheritdoc} + */ + public function describe(OutputInterface $output, $object, array $options = []) + { + $decorated = $output->isDecorated(); + $output->setDecorated(false); + + parent::describe($output, $object, $options); + + $output->setDecorated($decorated); + } + + /** + * {@inheritdoc} + */ + protected function write($content, $decorated = true) + { + parent::write($content, $decorated); + } + + /** + * {@inheritdoc} + */ + protected function describeInputArgument(InputArgument $argument, array $options = []) + { + $this->write( + '#### `'.($argument->getName() ?: '')."`\n\n" + .($argument->getDescription() ? preg_replace('/\s*[\r\n]\s*/', "\n", $argument->getDescription())."\n\n" : '') + .'* Is required: '.($argument->isRequired() ? 'yes' : 'no')."\n" + .'* Is array: '.($argument->isArray() ? 'yes' : 'no')."\n" + .'* Default: `'.str_replace("\n", '', var_export($argument->getDefault(), true)).'`' + ); + } + + /** + * {@inheritdoc} + */ + protected function describeInputOption(InputOption $option, array $options = []) + { + $name = '--'.$option->getName(); + if ($option->getShortcut()) { + $name .= '|-'.str_replace('|', '|-', $option->getShortcut()).''; + } + + $this->write( + '#### `'.$name.'`'."\n\n" + .($option->getDescription() ? preg_replace('/\s*[\r\n]\s*/', "\n", $option->getDescription())."\n\n" : '') + .'* Accept value: '.($option->acceptValue() ? 'yes' : 'no')."\n" + .'* Is value required: '.($option->isValueRequired() ? 'yes' : 'no')."\n" + .'* Is multiple: '.($option->isArray() ? 'yes' : 'no')."\n" + .'* Default: `'.str_replace("\n", '', var_export($option->getDefault(), true)).'`' + ); + } + + /** + * {@inheritdoc} + */ + protected function describeInputDefinition(InputDefinition $definition, array $options = []) + { + if ($showArguments = \count($definition->getArguments()) > 0) { + $this->write('### Arguments'); + foreach ($definition->getArguments() as $argument) { + $this->write("\n\n"); + $this->write($this->describeInputArgument($argument)); + } + } + + if (\count($definition->getOptions()) > 0) { + if ($showArguments) { + $this->write("\n\n"); + } + + $this->write('### Options'); + foreach ($definition->getOptions() as $option) { + $this->write("\n\n"); + $this->write($this->describeInputOption($option)); + } + } + } + + /** + * {@inheritdoc} + */ + protected function describeCommand(Command $command, array $options = []) + { + $command->getSynopsis(); + $command->mergeApplicationDefinition(false); + + $this->write( + '`'.$command->getName()."`\n" + .str_repeat('-', Helper::strlen($command->getName()) + 2)."\n\n" + .($command->getDescription() ? $command->getDescription()."\n\n" : '') + .'### Usage'."\n\n" + .array_reduce(array_merge([$command->getSynopsis()], $command->getAliases(), $command->getUsages()), function ($carry, $usage) { + return $carry.'* `'.$usage.'`'."\n"; + }) + ); + + if ($help = $command->getProcessedHelp()) { + $this->write("\n"); + $this->write($help); + } + + if ($command->getNativeDefinition()) { + $this->write("\n\n"); + $this->describeInputDefinition($command->getNativeDefinition()); + } + } + + /** + * {@inheritdoc} + */ + protected function describeApplication(Application $application, array $options = []) + { + $describedNamespace = isset($options['namespace']) ? $options['namespace'] : null; + $description = new ApplicationDescription($application, $describedNamespace); + $title = $this->getApplicationTitle($application); + + $this->write($title."\n".str_repeat('=', Helper::strlen($title))); + + foreach ($description->getNamespaces() as $namespace) { + if (ApplicationDescription::GLOBAL_NAMESPACE !== $namespace['id']) { + $this->write("\n\n"); + $this->write('**'.$namespace['id'].':**'); + } + + $this->write("\n\n"); + $this->write(implode("\n", array_map(function ($commandName) use ($description) { + return sprintf('* [`%s`](#%s)', $commandName, str_replace(':', '', $description->getCommand($commandName)->getName())); + }, $namespace['commands']))); + } + + foreach ($description->getCommands() as $command) { + $this->write("\n\n"); + $this->write($this->describeCommand($command)); + } + } + + private function getApplicationTitle(Application $application) + { + if ('UNKNOWN' !== $application->getName()) { + if ('UNKNOWN' !== $application->getVersion()) { + return sprintf('%s %s', $application->getName(), $application->getVersion()); + } + + return $application->getName(); + } + + return 'Console Tool'; + } +} diff --git a/lib/symfony/console/Descriptor/TextDescriptor.php b/lib/symfony/console/Descriptor/TextDescriptor.php new file mode 100644 index 000000000..af9088c7c --- /dev/null +++ b/lib/symfony/console/Descriptor/TextDescriptor.php @@ -0,0 +1,342 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Descriptor; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Formatter\OutputFormatter; +use Symfony\Component\Console\Helper\Helper; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; + +/** + * Text descriptor. + * + * @author Jean-François Simon + * + * @internal + */ +class TextDescriptor extends Descriptor +{ + /** + * {@inheritdoc} + */ + protected function describeInputArgument(InputArgument $argument, array $options = []) + { + if (null !== $argument->getDefault() && (!\is_array($argument->getDefault()) || \count($argument->getDefault()))) { + $default = sprintf(' [default: %s]', $this->formatDefaultValue($argument->getDefault())); + } else { + $default = ''; + } + + $totalWidth = isset($options['total_width']) ? $options['total_width'] : Helper::strlen($argument->getName()); + $spacingWidth = $totalWidth - \strlen($argument->getName()); + + $this->writeText(sprintf(' %s %s%s%s', + $argument->getName(), + str_repeat(' ', $spacingWidth), + // + 4 = 2 spaces before , 2 spaces after + preg_replace('/\s*[\r\n]\s*/', "\n".str_repeat(' ', $totalWidth + 4), $argument->getDescription()), + $default + ), $options); + } + + /** + * {@inheritdoc} + */ + protected function describeInputOption(InputOption $option, array $options = []) + { + if ($option->acceptValue() && null !== $option->getDefault() && (!\is_array($option->getDefault()) || \count($option->getDefault()))) { + $default = sprintf(' [default: %s]', $this->formatDefaultValue($option->getDefault())); + } else { + $default = ''; + } + + $value = ''; + if ($option->acceptValue()) { + $value = '='.strtoupper($option->getName()); + + if ($option->isValueOptional()) { + $value = '['.$value.']'; + } + } + + $totalWidth = isset($options['total_width']) ? $options['total_width'] : $this->calculateTotalWidthForOptions([$option]); + $synopsis = sprintf('%s%s', + $option->getShortcut() ? sprintf('-%s, ', $option->getShortcut()) : ' ', + sprintf('--%s%s', $option->getName(), $value) + ); + + $spacingWidth = $totalWidth - Helper::strlen($synopsis); + + $this->writeText(sprintf(' %s %s%s%s%s', + $synopsis, + str_repeat(' ', $spacingWidth), + // + 4 = 2 spaces before , 2 spaces after + preg_replace('/\s*[\r\n]\s*/', "\n".str_repeat(' ', $totalWidth + 4), $option->getDescription()), + $default, + $option->isArray() ? ' (multiple values allowed)' : '' + ), $options); + } + + /** + * {@inheritdoc} + */ + protected function describeInputDefinition(InputDefinition $definition, array $options = []) + { + $totalWidth = $this->calculateTotalWidthForOptions($definition->getOptions()); + foreach ($definition->getArguments() as $argument) { + $totalWidth = max($totalWidth, Helper::strlen($argument->getName())); + } + + if ($definition->getArguments()) { + $this->writeText('Arguments:', $options); + $this->writeText("\n"); + foreach ($definition->getArguments() as $argument) { + $this->describeInputArgument($argument, array_merge($options, ['total_width' => $totalWidth])); + $this->writeText("\n"); + } + } + + if ($definition->getArguments() && $definition->getOptions()) { + $this->writeText("\n"); + } + + if ($definition->getOptions()) { + $laterOptions = []; + + $this->writeText('Options:', $options); + foreach ($definition->getOptions() as $option) { + if (\strlen($option->getShortcut()) > 1) { + $laterOptions[] = $option; + continue; + } + $this->writeText("\n"); + $this->describeInputOption($option, array_merge($options, ['total_width' => $totalWidth])); + } + foreach ($laterOptions as $option) { + $this->writeText("\n"); + $this->describeInputOption($option, array_merge($options, ['total_width' => $totalWidth])); + } + } + } + + /** + * {@inheritdoc} + */ + protected function describeCommand(Command $command, array $options = []) + { + $command->getSynopsis(true); + $command->getSynopsis(false); + $command->mergeApplicationDefinition(false); + + $this->writeText('Usage:', $options); + foreach (array_merge([$command->getSynopsis(true)], $command->getAliases(), $command->getUsages()) as $usage) { + $this->writeText("\n"); + $this->writeText(' '.OutputFormatter::escape($usage), $options); + } + $this->writeText("\n"); + + $definition = $command->getNativeDefinition(); + if ($definition->getOptions() || $definition->getArguments()) { + $this->writeText("\n"); + $this->describeInputDefinition($definition, $options); + $this->writeText("\n"); + } + + if ($help = $command->getProcessedHelp()) { + $this->writeText("\n"); + $this->writeText('Help:', $options); + $this->writeText("\n"); + $this->writeText(' '.str_replace("\n", "\n ", $help), $options); + $this->writeText("\n"); + } + } + + /** + * {@inheritdoc} + */ + protected function describeApplication(Application $application, array $options = []) + { + $describedNamespace = isset($options['namespace']) ? $options['namespace'] : null; + $description = new ApplicationDescription($application, $describedNamespace); + + if (isset($options['raw_text']) && $options['raw_text']) { + $width = $this->getColumnWidth($description->getCommands()); + + foreach ($description->getCommands() as $command) { + $this->writeText(sprintf("%-{$width}s %s", $command->getName(), $command->getDescription()), $options); + $this->writeText("\n"); + } + } else { + if ('' != $help = $application->getHelp()) { + $this->writeText("$help\n\n", $options); + } + + $this->writeText("Usage:\n", $options); + $this->writeText(" command [options] [arguments]\n\n", $options); + + $this->describeInputDefinition(new InputDefinition($application->getDefinition()->getOptions()), $options); + + $this->writeText("\n"); + $this->writeText("\n"); + + $commands = $description->getCommands(); + $namespaces = $description->getNamespaces(); + if ($describedNamespace && $namespaces) { + // make sure all alias commands are included when describing a specific namespace + $describedNamespaceInfo = reset($namespaces); + foreach ($describedNamespaceInfo['commands'] as $name) { + $commands[$name] = $description->getCommand($name); + } + } + + // calculate max. width based on available commands per namespace + $width = $this->getColumnWidth(\call_user_func_array('array_merge', array_map(function ($namespace) use ($commands) { + return array_intersect($namespace['commands'], array_keys($commands)); + }, $namespaces))); + + if ($describedNamespace) { + $this->writeText(sprintf('Available commands for the "%s" namespace:', $describedNamespace), $options); + } else { + $this->writeText('Available commands:', $options); + } + + foreach ($namespaces as $namespace) { + $namespace['commands'] = array_filter($namespace['commands'], function ($name) use ($commands) { + return isset($commands[$name]); + }); + + if (!$namespace['commands']) { + continue; + } + + if (!$describedNamespace && ApplicationDescription::GLOBAL_NAMESPACE !== $namespace['id']) { + $this->writeText("\n"); + $this->writeText(' '.$namespace['id'].'', $options); + } + + foreach ($namespace['commands'] as $name) { + $this->writeText("\n"); + $spacingWidth = $width - Helper::strlen($name); + $command = $commands[$name]; + $commandAliases = $name === $command->getName() ? $this->getCommandAliasesText($command) : ''; + $this->writeText(sprintf(' %s%s%s', $name, str_repeat(' ', $spacingWidth), $commandAliases.$command->getDescription()), $options); + } + } + + $this->writeText("\n"); + } + } + + /** + * {@inheritdoc} + */ + private function writeText($content, array $options = []) + { + $this->write( + isset($options['raw_text']) && $options['raw_text'] ? strip_tags($content) : $content, + isset($options['raw_output']) ? !$options['raw_output'] : true + ); + } + + /** + * Formats command aliases to show them in the command description. + * + * @return string + */ + private function getCommandAliasesText(Command $command) + { + $text = ''; + $aliases = $command->getAliases(); + + if ($aliases) { + $text = '['.implode('|', $aliases).'] '; + } + + return $text; + } + + /** + * Formats input option/argument default value. + * + * @param mixed $default + * + * @return string + */ + private function formatDefaultValue($default) + { + if (INF === $default) { + return 'INF'; + } + + if (\is_string($default)) { + $default = OutputFormatter::escape($default); + } elseif (\is_array($default)) { + foreach ($default as $key => $value) { + if (\is_string($value)) { + $default[$key] = OutputFormatter::escape($value); + } + } + } + + return str_replace('\\\\', '\\', json_encode($default, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)); + } + + /** + * @param (Command|string)[] $commands + * + * @return int + */ + private function getColumnWidth(array $commands) + { + $widths = []; + + foreach ($commands as $command) { + if ($command instanceof Command) { + $widths[] = Helper::strlen($command->getName()); + foreach ($command->getAliases() as $alias) { + $widths[] = Helper::strlen($alias); + } + } else { + $widths[] = Helper::strlen($command); + } + } + + return $widths ? max($widths) + 2 : 0; + } + + /** + * @param InputOption[] $options + * + * @return int + */ + private function calculateTotalWidthForOptions(array $options) + { + $totalWidth = 0; + foreach ($options as $option) { + // "-" + shortcut + ", --" + name + $nameLength = 1 + max(Helper::strlen($option->getShortcut()), 1) + 4 + Helper::strlen($option->getName()); + + if ($option->acceptValue()) { + $valueLength = 1 + Helper::strlen($option->getName()); // = + value + $valueLength += $option->isValueOptional() ? 2 : 0; // [ + ] + + $nameLength += $valueLength; + } + $totalWidth = max($totalWidth, $nameLength); + } + + return $totalWidth; + } +} diff --git a/lib/symfony/console/Descriptor/XmlDescriptor.php b/lib/symfony/console/Descriptor/XmlDescriptor.php new file mode 100644 index 000000000..0cc0c9901 --- /dev/null +++ b/lib/symfony/console/Descriptor/XmlDescriptor.php @@ -0,0 +1,251 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Descriptor; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; + +/** + * XML descriptor. + * + * @author Jean-François Simon + * + * @internal + */ +class XmlDescriptor extends Descriptor +{ + /** + * @return \DOMDocument + */ + public function getInputDefinitionDocument(InputDefinition $definition) + { + $dom = new \DOMDocument('1.0', 'UTF-8'); + $dom->appendChild($definitionXML = $dom->createElement('definition')); + + $definitionXML->appendChild($argumentsXML = $dom->createElement('arguments')); + foreach ($definition->getArguments() as $argument) { + $this->appendDocument($argumentsXML, $this->getInputArgumentDocument($argument)); + } + + $definitionXML->appendChild($optionsXML = $dom->createElement('options')); + foreach ($definition->getOptions() as $option) { + $this->appendDocument($optionsXML, $this->getInputOptionDocument($option)); + } + + return $dom; + } + + /** + * @return \DOMDocument + */ + public function getCommandDocument(Command $command) + { + $dom = new \DOMDocument('1.0', 'UTF-8'); + $dom->appendChild($commandXML = $dom->createElement('command')); + + $command->getSynopsis(); + $command->mergeApplicationDefinition(false); + + $commandXML->setAttribute('id', $command->getName()); + $commandXML->setAttribute('name', $command->getName()); + $commandXML->setAttribute('hidden', $command->isHidden() ? 1 : 0); + + $commandXML->appendChild($usagesXML = $dom->createElement('usages')); + + foreach (array_merge([$command->getSynopsis()], $command->getAliases(), $command->getUsages()) as $usage) { + $usagesXML->appendChild($dom->createElement('usage', $usage)); + } + + $commandXML->appendChild($descriptionXML = $dom->createElement('description')); + $descriptionXML->appendChild($dom->createTextNode(str_replace("\n", "\n ", $command->getDescription()))); + + $commandXML->appendChild($helpXML = $dom->createElement('help')); + $helpXML->appendChild($dom->createTextNode(str_replace("\n", "\n ", $command->getProcessedHelp()))); + + $definitionXML = $this->getInputDefinitionDocument($command->getNativeDefinition()); + $this->appendDocument($commandXML, $definitionXML->getElementsByTagName('definition')->item(0)); + + return $dom; + } + + /** + * @param Application $application + * @param string|null $namespace + * + * @return \DOMDocument + */ + public function getApplicationDocument(Application $application, $namespace = null) + { + $dom = new \DOMDocument('1.0', 'UTF-8'); + $dom->appendChild($rootXml = $dom->createElement('symfony')); + + if ('UNKNOWN' !== $application->getName()) { + $rootXml->setAttribute('name', $application->getName()); + if ('UNKNOWN' !== $application->getVersion()) { + $rootXml->setAttribute('version', $application->getVersion()); + } + } + + $rootXml->appendChild($commandsXML = $dom->createElement('commands')); + + $description = new ApplicationDescription($application, $namespace, true); + + if ($namespace) { + $commandsXML->setAttribute('namespace', $namespace); + } + + foreach ($description->getCommands() as $command) { + $this->appendDocument($commandsXML, $this->getCommandDocument($command)); + } + + if (!$namespace) { + $rootXml->appendChild($namespacesXML = $dom->createElement('namespaces')); + + foreach ($description->getNamespaces() as $namespaceDescription) { + $namespacesXML->appendChild($namespaceArrayXML = $dom->createElement('namespace')); + $namespaceArrayXML->setAttribute('id', $namespaceDescription['id']); + + foreach ($namespaceDescription['commands'] as $name) { + $namespaceArrayXML->appendChild($commandXML = $dom->createElement('command')); + $commandXML->appendChild($dom->createTextNode($name)); + } + } + } + + return $dom; + } + + /** + * {@inheritdoc} + */ + protected function describeInputArgument(InputArgument $argument, array $options = []) + { + $this->writeDocument($this->getInputArgumentDocument($argument)); + } + + /** + * {@inheritdoc} + */ + protected function describeInputOption(InputOption $option, array $options = []) + { + $this->writeDocument($this->getInputOptionDocument($option)); + } + + /** + * {@inheritdoc} + */ + protected function describeInputDefinition(InputDefinition $definition, array $options = []) + { + $this->writeDocument($this->getInputDefinitionDocument($definition)); + } + + /** + * {@inheritdoc} + */ + protected function describeCommand(Command $command, array $options = []) + { + $this->writeDocument($this->getCommandDocument($command)); + } + + /** + * {@inheritdoc} + */ + protected function describeApplication(Application $application, array $options = []) + { + $this->writeDocument($this->getApplicationDocument($application, isset($options['namespace']) ? $options['namespace'] : null)); + } + + /** + * Appends document children to parent node. + */ + private function appendDocument(\DOMNode $parentNode, \DOMNode $importedParent) + { + foreach ($importedParent->childNodes as $childNode) { + $parentNode->appendChild($parentNode->ownerDocument->importNode($childNode, true)); + } + } + + /** + * Writes DOM document. + * + * @return \DOMDocument|string + */ + private function writeDocument(\DOMDocument $dom) + { + $dom->formatOutput = true; + $this->write($dom->saveXML()); + } + + /** + * @return \DOMDocument + */ + private function getInputArgumentDocument(InputArgument $argument) + { + $dom = new \DOMDocument('1.0', 'UTF-8'); + + $dom->appendChild($objectXML = $dom->createElement('argument')); + $objectXML->setAttribute('name', $argument->getName()); + $objectXML->setAttribute('is_required', $argument->isRequired() ? 1 : 0); + $objectXML->setAttribute('is_array', $argument->isArray() ? 1 : 0); + $objectXML->appendChild($descriptionXML = $dom->createElement('description')); + $descriptionXML->appendChild($dom->createTextNode($argument->getDescription())); + + $objectXML->appendChild($defaultsXML = $dom->createElement('defaults')); + $defaults = \is_array($argument->getDefault()) ? $argument->getDefault() : (\is_bool($argument->getDefault()) ? [var_export($argument->getDefault(), true)] : ($argument->getDefault() ? [$argument->getDefault()] : [])); + foreach ($defaults as $default) { + $defaultsXML->appendChild($defaultXML = $dom->createElement('default')); + $defaultXML->appendChild($dom->createTextNode($default)); + } + + return $dom; + } + + /** + * @return \DOMDocument + */ + private function getInputOptionDocument(InputOption $option) + { + $dom = new \DOMDocument('1.0', 'UTF-8'); + + $dom->appendChild($objectXML = $dom->createElement('option')); + $objectXML->setAttribute('name', '--'.$option->getName()); + $pos = strpos($option->getShortcut(), '|'); + if (false !== $pos) { + $objectXML->setAttribute('shortcut', '-'.substr($option->getShortcut(), 0, $pos)); + $objectXML->setAttribute('shortcuts', '-'.str_replace('|', '|-', $option->getShortcut())); + } else { + $objectXML->setAttribute('shortcut', $option->getShortcut() ? '-'.$option->getShortcut() : ''); + } + $objectXML->setAttribute('accept_value', $option->acceptValue() ? 1 : 0); + $objectXML->setAttribute('is_value_required', $option->isValueRequired() ? 1 : 0); + $objectXML->setAttribute('is_multiple', $option->isArray() ? 1 : 0); + $objectXML->appendChild($descriptionXML = $dom->createElement('description')); + $descriptionXML->appendChild($dom->createTextNode($option->getDescription())); + + if ($option->acceptValue()) { + $defaults = \is_array($option->getDefault()) ? $option->getDefault() : (\is_bool($option->getDefault()) ? [var_export($option->getDefault(), true)] : ($option->getDefault() ? [$option->getDefault()] : [])); + $objectXML->appendChild($defaultsXML = $dom->createElement('defaults')); + + if (!empty($defaults)) { + foreach ($defaults as $default) { + $defaultsXML->appendChild($defaultXML = $dom->createElement('default')); + $defaultXML->appendChild($dom->createTextNode($default)); + } + } + } + + return $dom; + } +} diff --git a/lib/symfony/console/Event/ConsoleCommandEvent.php b/lib/symfony/console/Event/ConsoleCommandEvent.php new file mode 100644 index 000000000..2f517c1db --- /dev/null +++ b/lib/symfony/console/Event/ConsoleCommandEvent.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Event; + +/** + * Allows to do things before the command is executed, like skipping the command or changing the input. + * + * @author Fabien Potencier + */ +class ConsoleCommandEvent extends ConsoleEvent +{ + /** + * The return code for skipped commands, this will also be passed into the terminate event. + */ + const RETURN_CODE_DISABLED = 113; + + /** + * Indicates if the command should be run or skipped. + */ + private $commandShouldRun = true; + + /** + * Disables the command, so it won't be run. + * + * @return bool + */ + public function disableCommand() + { + return $this->commandShouldRun = false; + } + + /** + * Enables the command. + * + * @return bool + */ + public function enableCommand() + { + return $this->commandShouldRun = true; + } + + /** + * Returns true if the command is runnable, false otherwise. + * + * @return bool + */ + public function commandShouldRun() + { + return $this->commandShouldRun; + } +} diff --git a/lib/symfony/console/Event/ConsoleErrorEvent.php b/lib/symfony/console/Event/ConsoleErrorEvent.php new file mode 100644 index 000000000..51a5f56d5 --- /dev/null +++ b/lib/symfony/console/Event/ConsoleErrorEvent.php @@ -0,0 +1,83 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Event; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Allows to handle throwables thrown while running a command. + * + * @author Wouter de Jong + */ +final class ConsoleErrorEvent extends ConsoleEvent +{ + private $error; + private $exitCode; + + public function __construct(InputInterface $input, OutputInterface $output, $error, Command $command = null) + { + parent::__construct($command, $input, $output); + + $this->setError($error); + } + + /** + * Returns the thrown error/exception. + * + * @return \Throwable + */ + public function getError() + { + return $this->error; + } + + /** + * Replaces the thrown error/exception. + * + * @param \Throwable $error + */ + public function setError($error) + { + if (!$error instanceof \Throwable && !$error instanceof \Exception) { + throw new InvalidArgumentException(sprintf('The error passed to ConsoleErrorEvent must be an instance of \Throwable or \Exception, "%s" was passed instead.', \is_object($error) ? \get_class($error) : \gettype($error))); + } + + $this->error = $error; + } + + /** + * Sets the exit code. + * + * @param int $exitCode The command exit code + */ + public function setExitCode($exitCode) + { + $this->exitCode = (int) $exitCode; + + $r = new \ReflectionProperty($this->error, 'code'); + $r->setAccessible(true); + $r->setValue($this->error, $this->exitCode); + } + + /** + * Gets the exit code. + * + * @return int The command exit code + */ + public function getExitCode() + { + return null !== $this->exitCode ? $this->exitCode : (\is_int($this->error->getCode()) && 0 !== $this->error->getCode() ? $this->error->getCode() : 1); + } +} diff --git a/lib/symfony/console/Event/ConsoleEvent.php b/lib/symfony/console/Event/ConsoleEvent.php new file mode 100644 index 000000000..5440da216 --- /dev/null +++ b/lib/symfony/console/Event/ConsoleEvent.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Event; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\EventDispatcher\Event; + +/** + * Allows to inspect input and output of a command. + * + * @author Francesco Levorato + */ +class ConsoleEvent extends Event +{ + protected $command; + + private $input; + private $output; + + public function __construct(Command $command = null, InputInterface $input, OutputInterface $output) + { + $this->command = $command; + $this->input = $input; + $this->output = $output; + } + + /** + * Gets the command that is executed. + * + * @return Command|null A Command instance + */ + public function getCommand() + { + return $this->command; + } + + /** + * Gets the input instance. + * + * @return InputInterface An InputInterface instance + */ + public function getInput() + { + return $this->input; + } + + /** + * Gets the output instance. + * + * @return OutputInterface An OutputInterface instance + */ + public function getOutput() + { + return $this->output; + } +} diff --git a/lib/symfony/console/Event/ConsoleExceptionEvent.php b/lib/symfony/console/Event/ConsoleExceptionEvent.php new file mode 100644 index 000000000..0adfaf9ac --- /dev/null +++ b/lib/symfony/console/Event/ConsoleExceptionEvent.php @@ -0,0 +1,71 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Event; + +@trigger_error(sprintf('The "%s" class is deprecated since Symfony 3.3 and will be removed in 4.0. Use the ConsoleErrorEvent instead.', ConsoleExceptionEvent::class), E_USER_DEPRECATED); + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Allows to handle exception thrown in a command. + * + * @author Fabien Potencier + * + * @deprecated since version 3.3, to be removed in 4.0. Use ConsoleErrorEvent instead. + */ +class ConsoleExceptionEvent extends ConsoleEvent +{ + private $exception; + private $exitCode; + + public function __construct(Command $command, InputInterface $input, OutputInterface $output, \Exception $exception, $exitCode) + { + parent::__construct($command, $input, $output); + + $this->setException($exception); + $this->exitCode = (int) $exitCode; + } + + /** + * Returns the thrown exception. + * + * @return \Exception The thrown exception + */ + public function getException() + { + return $this->exception; + } + + /** + * Replaces the thrown exception. + * + * This exception will be thrown if no response is set in the event. + * + * @param \Exception $exception The thrown exception + */ + public function setException(\Exception $exception) + { + $this->exception = $exception; + } + + /** + * Gets the exit code. + * + * @return int The command exit code + */ + public function getExitCode() + { + return $this->exitCode; + } +} diff --git a/lib/symfony/console/Event/ConsoleTerminateEvent.php b/lib/symfony/console/Event/ConsoleTerminateEvent.php new file mode 100644 index 000000000..b6a5d7c0d --- /dev/null +++ b/lib/symfony/console/Event/ConsoleTerminateEvent.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Event; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Allows to manipulate the exit code of a command after its execution. + * + * @author Francesco Levorato + */ +class ConsoleTerminateEvent extends ConsoleEvent +{ + /** + * The exit code of the command. + * + * @var int + */ + private $exitCode; + + public function __construct(Command $command, InputInterface $input, OutputInterface $output, $exitCode) + { + parent::__construct($command, $input, $output); + + $this->setExitCode($exitCode); + } + + /** + * Sets the exit code. + * + * @param int $exitCode The command exit code + */ + public function setExitCode($exitCode) + { + $this->exitCode = (int) $exitCode; + } + + /** + * Gets the exit code. + * + * @return int The command exit code + */ + public function getExitCode() + { + return $this->exitCode; + } +} diff --git a/lib/symfony/console/EventListener/ErrorListener.php b/lib/symfony/console/EventListener/ErrorListener.php new file mode 100644 index 000000000..212ad1d96 --- /dev/null +++ b/lib/symfony/console/EventListener/ErrorListener.php @@ -0,0 +1,91 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\EventListener; + +use Psr\Log\LoggerInterface; +use Symfony\Component\Console\ConsoleEvents; +use Symfony\Component\Console\Event\ConsoleErrorEvent; +use Symfony\Component\Console\Event\ConsoleEvent; +use Symfony\Component\Console\Event\ConsoleTerminateEvent; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +/** + * @author James Halsall + * @author Robin Chalas + */ +class ErrorListener implements EventSubscriberInterface +{ + private $logger; + + public function __construct(LoggerInterface $logger = null) + { + $this->logger = $logger; + } + + public function onConsoleError(ConsoleErrorEvent $event) + { + if (null === $this->logger) { + return; + } + + $error = $event->getError(); + + if (!$inputString = $this->getInputString($event)) { + return $this->logger->error('An error occurred while using the console. Message: "{message}"', ['exception' => $error, 'message' => $error->getMessage()]); + } + + $this->logger->error('Error thrown while running command "{command}". Message: "{message}"', ['exception' => $error, 'command' => $inputString, 'message' => $error->getMessage()]); + } + + public function onConsoleTerminate(ConsoleTerminateEvent $event) + { + if (null === $this->logger) { + return; + } + + $exitCode = $event->getExitCode(); + + if (0 === $exitCode) { + return; + } + + if (!$inputString = $this->getInputString($event)) { + return $this->logger->debug('The console exited with code "{code}"', ['code' => $exitCode]); + } + + $this->logger->debug('Command "{command}" exited with code "{code}"', ['command' => $inputString, 'code' => $exitCode]); + } + + public static function getSubscribedEvents() + { + return [ + ConsoleEvents::ERROR => ['onConsoleError', -128], + ConsoleEvents::TERMINATE => ['onConsoleTerminate', -128], + ]; + } + + private static function getInputString(ConsoleEvent $event) + { + $commandName = $event->getCommand() ? $event->getCommand()->getName() : null; + $input = $event->getInput(); + + if (method_exists($input, '__toString')) { + if ($commandName) { + return str_replace(["'$commandName'", "\"$commandName\""], $commandName, (string) $input); + } + + return (string) $input; + } + + return $commandName; + } +} diff --git a/lib/symfony/console/Exception/CommandNotFoundException.php b/lib/symfony/console/Exception/CommandNotFoundException.php new file mode 100644 index 000000000..d28fceb12 --- /dev/null +++ b/lib/symfony/console/Exception/CommandNotFoundException.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Exception; + +/** + * Represents an incorrect command name typed in the console. + * + * @author Jérôme Tamarelle + */ +class CommandNotFoundException extends \InvalidArgumentException implements ExceptionInterface +{ + private $alternatives; + + /** + * @param string $message Exception message to throw + * @param array $alternatives List of similar defined names + * @param int $code Exception code + * @param \Exception $previous Previous exception used for the exception chaining + */ + public function __construct($message, array $alternatives = [], $code = 0, \Exception $previous = null) + { + parent::__construct($message, $code, $previous); + + $this->alternatives = $alternatives; + } + + /** + * @return array A list of similar defined names + */ + public function getAlternatives() + { + return $this->alternatives; + } +} diff --git a/lib/symfony/console/Exception/ExceptionInterface.php b/lib/symfony/console/Exception/ExceptionInterface.php new file mode 100644 index 000000000..491cc4c64 --- /dev/null +++ b/lib/symfony/console/Exception/ExceptionInterface.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Exception; + +/** + * ExceptionInterface. + * + * @author Jérôme Tamarelle + */ +interface ExceptionInterface +{ +} diff --git a/lib/symfony/console/Exception/InvalidArgumentException.php b/lib/symfony/console/Exception/InvalidArgumentException.php new file mode 100644 index 000000000..07cc0b61d --- /dev/null +++ b/lib/symfony/console/Exception/InvalidArgumentException.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Exception; + +/** + * @author Jérôme Tamarelle + */ +class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface +{ +} diff --git a/lib/symfony/console/Exception/InvalidOptionException.php b/lib/symfony/console/Exception/InvalidOptionException.php new file mode 100644 index 000000000..b2eec6165 --- /dev/null +++ b/lib/symfony/console/Exception/InvalidOptionException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Exception; + +/** + * Represents an incorrect option name typed in the console. + * + * @author Jérôme Tamarelle + */ +class InvalidOptionException extends \InvalidArgumentException implements ExceptionInterface +{ +} diff --git a/lib/symfony/console/Exception/LogicException.php b/lib/symfony/console/Exception/LogicException.php new file mode 100644 index 000000000..fc37b8d8a --- /dev/null +++ b/lib/symfony/console/Exception/LogicException.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Exception; + +/** + * @author Jérôme Tamarelle + */ +class LogicException extends \LogicException implements ExceptionInterface +{ +} diff --git a/lib/symfony/console/Exception/RuntimeException.php b/lib/symfony/console/Exception/RuntimeException.php new file mode 100644 index 000000000..51d7d80ac --- /dev/null +++ b/lib/symfony/console/Exception/RuntimeException.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Exception; + +/** + * @author Jérôme Tamarelle + */ +class RuntimeException extends \RuntimeException implements ExceptionInterface +{ +} diff --git a/lib/symfony/console/Formatter/OutputFormatter.php b/lib/symfony/console/Formatter/OutputFormatter.php new file mode 100644 index 000000000..adfcdc875 --- /dev/null +++ b/lib/symfony/console/Formatter/OutputFormatter.php @@ -0,0 +1,243 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Formatter; + +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * Formatter class for console output. + * + * @author Konstantin Kudryashov + */ +class OutputFormatter implements OutputFormatterInterface +{ + private $decorated; + private $styles = []; + private $styleStack; + + /** + * Escapes "<" special char in given text. + * + * @param string $text Text to escape + * + * @return string Escaped text + */ + public static function escape($text) + { + $text = preg_replace('/([^\\\\]?) FormatterStyle" instances + */ + public function __construct($decorated = false, array $styles = []) + { + $this->decorated = (bool) $decorated; + + $this->setStyle('error', new OutputFormatterStyle('white', 'red')); + $this->setStyle('info', new OutputFormatterStyle('green')); + $this->setStyle('comment', new OutputFormatterStyle('yellow')); + $this->setStyle('question', new OutputFormatterStyle('black', 'cyan')); + + foreach ($styles as $name => $style) { + $this->setStyle($name, $style); + } + + $this->styleStack = new OutputFormatterStyleStack(); + } + + /** + * {@inheritdoc} + */ + public function setDecorated($decorated) + { + $this->decorated = (bool) $decorated; + } + + /** + * {@inheritdoc} + */ + public function isDecorated() + { + return $this->decorated; + } + + /** + * {@inheritdoc} + */ + public function setStyle($name, OutputFormatterStyleInterface $style) + { + $this->styles[strtolower($name)] = $style; + } + + /** + * {@inheritdoc} + */ + public function hasStyle($name) + { + return isset($this->styles[strtolower($name)]); + } + + /** + * {@inheritdoc} + */ + public function getStyle($name) + { + if (!$this->hasStyle($name)) { + throw new InvalidArgumentException(sprintf('Undefined style: %s', $name)); + } + + return $this->styles[strtolower($name)]; + } + + /** + * {@inheritdoc} + */ + public function format($message) + { + $message = (string) $message; + $offset = 0; + $output = ''; + $tagRegex = '[a-z][a-z0-9,_=;-]*+'; + preg_match_all("#<(($tagRegex) | /($tagRegex)?)>#ix", $message, $matches, PREG_OFFSET_CAPTURE); + foreach ($matches[0] as $i => $match) { + $pos = $match[1]; + $text = $match[0]; + + if (0 != $pos && '\\' == $message[$pos - 1]) { + continue; + } + + // add the text up to the next tag + $output .= $this->applyCurrentStyle(substr($message, $offset, $pos - $offset)); + $offset = $pos + \strlen($text); + + // opening tag? + if ($open = '/' != $text[1]) { + $tag = $matches[1][$i][0]; + } else { + $tag = isset($matches[3][$i][0]) ? $matches[3][$i][0] : ''; + } + + if (!$open && !$tag) { + // + $this->styleStack->pop(); + } elseif (false === $style = $this->createStyleFromString($tag)) { + $output .= $this->applyCurrentStyle($text); + } elseif ($open) { + $this->styleStack->push($style); + } else { + $this->styleStack->pop($style); + } + } + + $output .= $this->applyCurrentStyle(substr($message, $offset)); + + if (false !== strpos($output, "\0")) { + return strtr($output, ["\0" => '\\', '\\<' => '<']); + } + + return str_replace('\\<', '<', $output); + } + + /** + * @return OutputFormatterStyleStack + */ + public function getStyleStack() + { + return $this->styleStack; + } + + /** + * Tries to create new style instance from string. + * + * @param string $string + * + * @return OutputFormatterStyle|false false if string is not format string + */ + private function createStyleFromString($string) + { + if (isset($this->styles[$string])) { + return $this->styles[$string]; + } + + if (!preg_match_all('/([^=]+)=([^;]+)(;|$)/', $string, $matches, PREG_SET_ORDER)) { + return false; + } + + $style = new OutputFormatterStyle(); + foreach ($matches as $match) { + array_shift($match); + $match[0] = strtolower($match[0]); + + if ('fg' == $match[0]) { + $style->setForeground(strtolower($match[1])); + } elseif ('bg' == $match[0]) { + $style->setBackground(strtolower($match[1])); + } elseif ('options' === $match[0]) { + preg_match_all('([^,;]+)', strtolower($match[1]), $options); + $options = array_shift($options); + foreach ($options as $option) { + try { + $style->setOption($option); + } catch (\InvalidArgumentException $e) { + @trigger_error(sprintf('Unknown style options are deprecated since Symfony 3.2 and will be removed in 4.0. Exception "%s".', $e->getMessage()), E_USER_DEPRECATED); + + return false; + } + } + } else { + return false; + } + } + + return $style; + } + + /** + * Applies current style from stack to text, if must be applied. + * + * @param string $text Input text + * + * @return string Styled text + */ + private function applyCurrentStyle($text) + { + return $this->isDecorated() && \strlen($text) > 0 ? $this->styleStack->getCurrent()->apply($text) : $text; + } +} diff --git a/lib/symfony/console/Formatter/OutputFormatterInterface.php b/lib/symfony/console/Formatter/OutputFormatterInterface.php new file mode 100644 index 000000000..281e240c5 --- /dev/null +++ b/lib/symfony/console/Formatter/OutputFormatterInterface.php @@ -0,0 +1,71 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Formatter; + +/** + * Formatter interface for console output. + * + * @author Konstantin Kudryashov + */ +interface OutputFormatterInterface +{ + /** + * Sets the decorated flag. + * + * @param bool $decorated Whether to decorate the messages or not + */ + public function setDecorated($decorated); + + /** + * Gets the decorated flag. + * + * @return bool true if the output will decorate messages, false otherwise + */ + public function isDecorated(); + + /** + * Sets a new style. + * + * @param string $name The style name + * @param OutputFormatterStyleInterface $style The style instance + */ + public function setStyle($name, OutputFormatterStyleInterface $style); + + /** + * Checks if output formatter has style with specified name. + * + * @param string $name + * + * @return bool + */ + public function hasStyle($name); + + /** + * Gets style options from style with specified name. + * + * @param string $name + * + * @return OutputFormatterStyleInterface + * + * @throws \InvalidArgumentException When style isn't defined + */ + public function getStyle($name); + + /** + * Formats a message according to the given styles. + * + * @param string $message The message to style + * + * @return string The styled message + */ + public function format($message); +} diff --git a/lib/symfony/console/Formatter/OutputFormatterStyle.php b/lib/symfony/console/Formatter/OutputFormatterStyle.php new file mode 100644 index 000000000..477bd87f0 --- /dev/null +++ b/lib/symfony/console/Formatter/OutputFormatterStyle.php @@ -0,0 +1,183 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Formatter; + +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * Formatter style class for defining styles. + * + * @author Konstantin Kudryashov + */ +class OutputFormatterStyle implements OutputFormatterStyleInterface +{ + private static $availableForegroundColors = [ + 'black' => ['set' => 30, 'unset' => 39], + 'red' => ['set' => 31, 'unset' => 39], + 'green' => ['set' => 32, 'unset' => 39], + 'yellow' => ['set' => 33, 'unset' => 39], + 'blue' => ['set' => 34, 'unset' => 39], + 'magenta' => ['set' => 35, 'unset' => 39], + 'cyan' => ['set' => 36, 'unset' => 39], + 'white' => ['set' => 37, 'unset' => 39], + 'default' => ['set' => 39, 'unset' => 39], + ]; + private static $availableBackgroundColors = [ + 'black' => ['set' => 40, 'unset' => 49], + 'red' => ['set' => 41, 'unset' => 49], + 'green' => ['set' => 42, 'unset' => 49], + 'yellow' => ['set' => 43, 'unset' => 49], + 'blue' => ['set' => 44, 'unset' => 49], + 'magenta' => ['set' => 45, 'unset' => 49], + 'cyan' => ['set' => 46, 'unset' => 49], + 'white' => ['set' => 47, 'unset' => 49], + 'default' => ['set' => 49, 'unset' => 49], + ]; + private static $availableOptions = [ + 'bold' => ['set' => 1, 'unset' => 22], + 'underscore' => ['set' => 4, 'unset' => 24], + 'blink' => ['set' => 5, 'unset' => 25], + 'reverse' => ['set' => 7, 'unset' => 27], + 'conceal' => ['set' => 8, 'unset' => 28], + ]; + + private $foreground; + private $background; + private $options = []; + + /** + * Initializes output formatter style. + * + * @param string|null $foreground The style foreground color name + * @param string|null $background The style background color name + * @param array $options The style options + */ + public function __construct($foreground = null, $background = null, array $options = []) + { + if (null !== $foreground) { + $this->setForeground($foreground); + } + if (null !== $background) { + $this->setBackground($background); + } + if (\count($options)) { + $this->setOptions($options); + } + } + + /** + * {@inheritdoc} + */ + public function setForeground($color = null) + { + if (null === $color) { + $this->foreground = null; + + return; + } + + if (!isset(static::$availableForegroundColors[$color])) { + throw new InvalidArgumentException(sprintf('Invalid foreground color specified: "%s". Expected one of (%s)', $color, implode(', ', array_keys(static::$availableForegroundColors)))); + } + + $this->foreground = static::$availableForegroundColors[$color]; + } + + /** + * {@inheritdoc} + */ + public function setBackground($color = null) + { + if (null === $color) { + $this->background = null; + + return; + } + + if (!isset(static::$availableBackgroundColors[$color])) { + throw new InvalidArgumentException(sprintf('Invalid background color specified: "%s". Expected one of (%s)', $color, implode(', ', array_keys(static::$availableBackgroundColors)))); + } + + $this->background = static::$availableBackgroundColors[$color]; + } + + /** + * {@inheritdoc} + */ + public function setOption($option) + { + if (!isset(static::$availableOptions[$option])) { + throw new InvalidArgumentException(sprintf('Invalid option specified: "%s". Expected one of (%s)', $option, implode(', ', array_keys(static::$availableOptions)))); + } + + if (!\in_array(static::$availableOptions[$option], $this->options)) { + $this->options[] = static::$availableOptions[$option]; + } + } + + /** + * {@inheritdoc} + */ + public function unsetOption($option) + { + if (!isset(static::$availableOptions[$option])) { + throw new InvalidArgumentException(sprintf('Invalid option specified: "%s". Expected one of (%s)', $option, implode(', ', array_keys(static::$availableOptions)))); + } + + $pos = array_search(static::$availableOptions[$option], $this->options); + if (false !== $pos) { + unset($this->options[$pos]); + } + } + + /** + * {@inheritdoc} + */ + public function setOptions(array $options) + { + $this->options = []; + + foreach ($options as $option) { + $this->setOption($option); + } + } + + /** + * {@inheritdoc} + */ + public function apply($text) + { + $setCodes = []; + $unsetCodes = []; + + if (null !== $this->foreground) { + $setCodes[] = $this->foreground['set']; + $unsetCodes[] = $this->foreground['unset']; + } + if (null !== $this->background) { + $setCodes[] = $this->background['set']; + $unsetCodes[] = $this->background['unset']; + } + if (\count($this->options)) { + foreach ($this->options as $option) { + $setCodes[] = $option['set']; + $unsetCodes[] = $option['unset']; + } + } + + if (0 === \count($setCodes)) { + return $text; + } + + return sprintf("\033[%sm%s\033[%sm", implode(';', $setCodes), $text, implode(';', $unsetCodes)); + } +} diff --git a/lib/symfony/console/Formatter/OutputFormatterStyleInterface.php b/lib/symfony/console/Formatter/OutputFormatterStyleInterface.php new file mode 100644 index 000000000..af171c270 --- /dev/null +++ b/lib/symfony/console/Formatter/OutputFormatterStyleInterface.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Formatter; + +/** + * Formatter style interface for defining styles. + * + * @author Konstantin Kudryashov + */ +interface OutputFormatterStyleInterface +{ + /** + * Sets style foreground color. + * + * @param string|null $color The color name + */ + public function setForeground($color = null); + + /** + * Sets style background color. + * + * @param string $color The color name + */ + public function setBackground($color = null); + + /** + * Sets some specific style option. + * + * @param string $option The option name + */ + public function setOption($option); + + /** + * Unsets some specific style option. + * + * @param string $option The option name + */ + public function unsetOption($option); + + /** + * Sets multiple style options at once. + */ + public function setOptions(array $options); + + /** + * Applies the style to a given text. + * + * @param string $text The text to style + * + * @return string + */ + public function apply($text); +} diff --git a/lib/symfony/console/Formatter/OutputFormatterStyleStack.php b/lib/symfony/console/Formatter/OutputFormatterStyleStack.php new file mode 100644 index 000000000..506664991 --- /dev/null +++ b/lib/symfony/console/Formatter/OutputFormatterStyleStack.php @@ -0,0 +1,109 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Formatter; + +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * @author Jean-François Simon + */ +class OutputFormatterStyleStack +{ + /** + * @var OutputFormatterStyleInterface[] + */ + private $styles; + + private $emptyStyle; + + public function __construct(OutputFormatterStyleInterface $emptyStyle = null) + { + $this->emptyStyle = $emptyStyle ?: new OutputFormatterStyle(); + $this->reset(); + } + + /** + * Resets stack (ie. empty internal arrays). + */ + public function reset() + { + $this->styles = []; + } + + /** + * Pushes a style in the stack. + */ + public function push(OutputFormatterStyleInterface $style) + { + $this->styles[] = $style; + } + + /** + * Pops a style from the stack. + * + * @return OutputFormatterStyleInterface + * + * @throws InvalidArgumentException When style tags incorrectly nested + */ + public function pop(OutputFormatterStyleInterface $style = null) + { + if (empty($this->styles)) { + return $this->emptyStyle; + } + + if (null === $style) { + return array_pop($this->styles); + } + + foreach (array_reverse($this->styles, true) as $index => $stackedStyle) { + if ($style->apply('') === $stackedStyle->apply('')) { + $this->styles = \array_slice($this->styles, 0, $index); + + return $stackedStyle; + } + } + + throw new InvalidArgumentException('Incorrectly nested style tag found.'); + } + + /** + * Computes current style with stacks top codes. + * + * @return OutputFormatterStyle + */ + public function getCurrent() + { + if (empty($this->styles)) { + return $this->emptyStyle; + } + + return $this->styles[\count($this->styles) - 1]; + } + + /** + * @return $this + */ + public function setEmptyStyle(OutputFormatterStyleInterface $emptyStyle) + { + $this->emptyStyle = $emptyStyle; + + return $this; + } + + /** + * @return OutputFormatterStyleInterface + */ + public function getEmptyStyle() + { + return $this->emptyStyle; + } +} diff --git a/lib/symfony/console/Helper/DebugFormatterHelper.php b/lib/symfony/console/Helper/DebugFormatterHelper.php new file mode 100644 index 000000000..16d117553 --- /dev/null +++ b/lib/symfony/console/Helper/DebugFormatterHelper.php @@ -0,0 +1,127 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +/** + * Helps outputting debug information when running an external program from a command. + * + * An external program can be a Process, an HTTP request, or anything else. + * + * @author Fabien Potencier + */ +class DebugFormatterHelper extends Helper +{ + private $colors = ['black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white', 'default']; + private $started = []; + private $count = -1; + + /** + * Starts a debug formatting session. + * + * @param string $id The id of the formatting session + * @param string $message The message to display + * @param string $prefix The prefix to use + * + * @return string + */ + public function start($id, $message, $prefix = 'RUN') + { + $this->started[$id] = ['border' => ++$this->count % \count($this->colors)]; + + return sprintf("%s %s %s\n", $this->getBorder($id), $prefix, $message); + } + + /** + * Adds progress to a formatting session. + * + * @param string $id The id of the formatting session + * @param string $buffer The message to display + * @param bool $error Whether to consider the buffer as error + * @param string $prefix The prefix for output + * @param string $errorPrefix The prefix for error output + * + * @return string + */ + public function progress($id, $buffer, $error = false, $prefix = 'OUT', $errorPrefix = 'ERR') + { + $message = ''; + + if ($error) { + if (isset($this->started[$id]['out'])) { + $message .= "\n"; + unset($this->started[$id]['out']); + } + if (!isset($this->started[$id]['err'])) { + $message .= sprintf('%s %s ', $this->getBorder($id), $errorPrefix); + $this->started[$id]['err'] = true; + } + + $message .= str_replace("\n", sprintf("\n%s %s ", $this->getBorder($id), $errorPrefix), $buffer); + } else { + if (isset($this->started[$id]['err'])) { + $message .= "\n"; + unset($this->started[$id]['err']); + } + if (!isset($this->started[$id]['out'])) { + $message .= sprintf('%s %s ', $this->getBorder($id), $prefix); + $this->started[$id]['out'] = true; + } + + $message .= str_replace("\n", sprintf("\n%s %s ", $this->getBorder($id), $prefix), $buffer); + } + + return $message; + } + + /** + * Stops a formatting session. + * + * @param string $id The id of the formatting session + * @param string $message The message to display + * @param bool $successful Whether to consider the result as success + * @param string $prefix The prefix for the end output + * + * @return string + */ + public function stop($id, $message, $successful, $prefix = 'RES') + { + $trailingEOL = isset($this->started[$id]['out']) || isset($this->started[$id]['err']) ? "\n" : ''; + + if ($successful) { + return sprintf("%s%s %s %s\n", $trailingEOL, $this->getBorder($id), $prefix, $message); + } + + $message = sprintf("%s%s %s %s\n", $trailingEOL, $this->getBorder($id), $prefix, $message); + + unset($this->started[$id]['out'], $this->started[$id]['err']); + + return $message; + } + + /** + * @param string $id The id of the formatting session + * + * @return string + */ + private function getBorder($id) + { + return sprintf(' ', $this->colors[$this->started[$id]['border']]); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'debug_formatter'; + } +} diff --git a/lib/symfony/console/Helper/DescriptorHelper.php b/lib/symfony/console/Helper/DescriptorHelper.php new file mode 100644 index 000000000..f8a3847b4 --- /dev/null +++ b/lib/symfony/console/Helper/DescriptorHelper.php @@ -0,0 +1,94 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Descriptor\DescriptorInterface; +use Symfony\Component\Console\Descriptor\JsonDescriptor; +use Symfony\Component\Console\Descriptor\MarkdownDescriptor; +use Symfony\Component\Console\Descriptor\TextDescriptor; +use Symfony\Component\Console\Descriptor\XmlDescriptor; +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * This class adds helper method to describe objects in various formats. + * + * @author Jean-François Simon + */ +class DescriptorHelper extends Helper +{ + /** + * @var DescriptorInterface[] + */ + private $descriptors = []; + + public function __construct() + { + $this + ->register('txt', new TextDescriptor()) + ->register('xml', new XmlDescriptor()) + ->register('json', new JsonDescriptor()) + ->register('md', new MarkdownDescriptor()) + ; + } + + /** + * Describes an object if supported. + * + * Available options are: + * * format: string, the output format name + * * raw_text: boolean, sets output type as raw + * + * @param OutputInterface $output + * @param object $object + * @param array $options + * + * @throws InvalidArgumentException when the given format is not supported + */ + public function describe(OutputInterface $output, $object, array $options = []) + { + $options = array_merge([ + 'raw_text' => false, + 'format' => 'txt', + ], $options); + + if (!isset($this->descriptors[$options['format']])) { + throw new InvalidArgumentException(sprintf('Unsupported format "%s".', $options['format'])); + } + + $descriptor = $this->descriptors[$options['format']]; + $descriptor->describe($output, $object, $options); + } + + /** + * Registers a descriptor. + * + * @param string $format + * @param DescriptorInterface $descriptor + * + * @return $this + */ + public function register($format, DescriptorInterface $descriptor) + { + $this->descriptors[$format] = $descriptor; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'descriptor'; + } +} diff --git a/lib/symfony/console/Helper/FormatterHelper.php b/lib/symfony/console/Helper/FormatterHelper.php new file mode 100644 index 000000000..4ad63856d --- /dev/null +++ b/lib/symfony/console/Helper/FormatterHelper.php @@ -0,0 +1,106 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Formatter\OutputFormatter; + +/** + * The Formatter class provides helpers to format messages. + * + * @author Fabien Potencier + */ +class FormatterHelper extends Helper +{ + /** + * Formats a message within a section. + * + * @param string $section The section name + * @param string $message The message + * @param string $style The style to apply to the section + * + * @return string The format section + */ + public function formatSection($section, $message, $style = 'info') + { + return sprintf('<%s>[%s] %s', $style, $section, $style, $message); + } + + /** + * Formats a message as a block of text. + * + * @param string|array $messages The message to write in the block + * @param string $style The style to apply to the whole block + * @param bool $large Whether to return a large block + * + * @return string The formatter message + */ + public function formatBlock($messages, $style, $large = false) + { + if (!\is_array($messages)) { + $messages = [$messages]; + } + + $len = 0; + $lines = []; + foreach ($messages as $message) { + $message = OutputFormatter::escape($message); + $lines[] = sprintf($large ? ' %s ' : ' %s ', $message); + $len = max($this->strlen($message) + ($large ? 4 : 2), $len); + } + + $messages = $large ? [str_repeat(' ', $len)] : []; + for ($i = 0; isset($lines[$i]); ++$i) { + $messages[] = $lines[$i].str_repeat(' ', $len - $this->strlen($lines[$i])); + } + if ($large) { + $messages[] = str_repeat(' ', $len); + } + + for ($i = 0; isset($messages[$i]); ++$i) { + $messages[$i] = sprintf('<%s>%s', $style, $messages[$i], $style); + } + + return implode("\n", $messages); + } + + /** + * Truncates a message to the given length. + * + * @param string $message + * @param int $length + * @param string $suffix + * + * @return string + */ + public function truncate($message, $length, $suffix = '...') + { + $computedLength = $length - $this->strlen($suffix); + + if ($computedLength > $this->strlen($message)) { + return $message; + } + + if (false === $encoding = mb_detect_encoding($message, null, true)) { + return substr($message, 0, $length).$suffix; + } + + return mb_substr($message, 0, $length, $encoding).$suffix; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'formatter'; + } +} diff --git a/lib/symfony/console/Helper/Helper.php b/lib/symfony/console/Helper/Helper.php new file mode 100644 index 000000000..0ddddf6bc --- /dev/null +++ b/lib/symfony/console/Helper/Helper.php @@ -0,0 +1,138 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Formatter\OutputFormatterInterface; + +/** + * Helper is the base class for all helper classes. + * + * @author Fabien Potencier + */ +abstract class Helper implements HelperInterface +{ + protected $helperSet = null; + + /** + * {@inheritdoc} + */ + public function setHelperSet(HelperSet $helperSet = null) + { + $this->helperSet = $helperSet; + } + + /** + * {@inheritdoc} + */ + public function getHelperSet() + { + return $this->helperSet; + } + + /** + * Returns the length of a string, using mb_strwidth if it is available. + * + * @param string $string The string to check its length + * + * @return int The length of the string + */ + public static function strlen($string) + { + if (false === $encoding = mb_detect_encoding($string, null, true)) { + return \strlen($string); + } + + return mb_strwidth($string, $encoding); + } + + /** + * Returns the subset of a string, using mb_substr if it is available. + * + * @param string $string String to subset + * @param int $from Start offset + * @param int|null $length Length to read + * + * @return string The string subset + */ + public static function substr($string, $from, $length = null) + { + if (false === $encoding = mb_detect_encoding($string, null, true)) { + return substr($string, $from, $length); + } + + return mb_substr($string, $from, $length, $encoding); + } + + public static function formatTime($secs) + { + static $timeFormats = [ + [0, '< 1 sec'], + [1, '1 sec'], + [2, 'secs', 1], + [60, '1 min'], + [120, 'mins', 60], + [3600, '1 hr'], + [7200, 'hrs', 3600], + [86400, '1 day'], + [172800, 'days', 86400], + ]; + + foreach ($timeFormats as $index => $format) { + if ($secs >= $format[0]) { + if ((isset($timeFormats[$index + 1]) && $secs < $timeFormats[$index + 1][0]) + || $index == \count($timeFormats) - 1 + ) { + if (2 == \count($format)) { + return $format[1]; + } + + return floor($secs / $format[2]).' '.$format[1]; + } + } + } + } + + public static function formatMemory($memory) + { + if ($memory >= 1024 * 1024 * 1024) { + return sprintf('%.1f GiB', $memory / 1024 / 1024 / 1024); + } + + if ($memory >= 1024 * 1024) { + return sprintf('%.1f MiB', $memory / 1024 / 1024); + } + + if ($memory >= 1024) { + return sprintf('%d KiB', $memory / 1024); + } + + return sprintf('%d B', $memory); + } + + public static function strlenWithoutDecoration(OutputFormatterInterface $formatter, $string) + { + return self::strlen(self::removeDecoration($formatter, $string)); + } + + public static function removeDecoration(OutputFormatterInterface $formatter, $string) + { + $isDecorated = $formatter->isDecorated(); + $formatter->setDecorated(false); + // remove <...> formatting + $string = $formatter->format($string); + // remove already formatted characters + $string = preg_replace("/\033\[[^m]*m/", '', $string); + $formatter->setDecorated($isDecorated); + + return $string; + } +} diff --git a/lib/symfony/console/Helper/HelperInterface.php b/lib/symfony/console/Helper/HelperInterface.php new file mode 100644 index 000000000..1ce823587 --- /dev/null +++ b/lib/symfony/console/Helper/HelperInterface.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +/** + * HelperInterface is the interface all helpers must implement. + * + * @author Fabien Potencier + */ +interface HelperInterface +{ + /** + * Sets the helper set associated with this helper. + */ + public function setHelperSet(HelperSet $helperSet = null); + + /** + * Gets the helper set associated with this helper. + * + * @return HelperSet A HelperSet instance + */ + public function getHelperSet(); + + /** + * Returns the canonical name of this helper. + * + * @return string The canonical name + */ + public function getName(); +} diff --git a/lib/symfony/console/Helper/HelperSet.php b/lib/symfony/console/Helper/HelperSet.php new file mode 100644 index 000000000..c73fecd47 --- /dev/null +++ b/lib/symfony/console/Helper/HelperSet.php @@ -0,0 +1,108 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * HelperSet represents a set of helpers to be used with a command. + * + * @author Fabien Potencier + */ +class HelperSet implements \IteratorAggregate +{ + /** + * @var Helper[] + */ + private $helpers = []; + private $command; + + /** + * @param Helper[] $helpers An array of helper + */ + public function __construct(array $helpers = []) + { + foreach ($helpers as $alias => $helper) { + $this->set($helper, \is_int($alias) ? null : $alias); + } + } + + /** + * Sets a helper. + * + * @param HelperInterface $helper The helper instance + * @param string $alias An alias + */ + public function set(HelperInterface $helper, $alias = null) + { + $this->helpers[$helper->getName()] = $helper; + if (null !== $alias) { + $this->helpers[$alias] = $helper; + } + + $helper->setHelperSet($this); + } + + /** + * Returns true if the helper if defined. + * + * @param string $name The helper name + * + * @return bool true if the helper is defined, false otherwise + */ + public function has($name) + { + return isset($this->helpers[$name]); + } + + /** + * Gets a helper value. + * + * @param string $name The helper name + * + * @return HelperInterface The helper instance + * + * @throws InvalidArgumentException if the helper is not defined + */ + public function get($name) + { + if (!$this->has($name)) { + throw new InvalidArgumentException(sprintf('The helper "%s" is not defined.', $name)); + } + + return $this->helpers[$name]; + } + + public function setCommand(Command $command = null) + { + $this->command = $command; + } + + /** + * Gets the command associated with this helper set. + * + * @return Command A Command instance + */ + public function getCommand() + { + return $this->command; + } + + /** + * @return Helper[] + */ + public function getIterator() + { + return new \ArrayIterator($this->helpers); + } +} diff --git a/lib/symfony/console/Helper/InputAwareHelper.php b/lib/symfony/console/Helper/InputAwareHelper.php new file mode 100644 index 000000000..0d0dba23e --- /dev/null +++ b/lib/symfony/console/Helper/InputAwareHelper.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Input\InputAwareInterface; +use Symfony\Component\Console\Input\InputInterface; + +/** + * An implementation of InputAwareInterface for Helpers. + * + * @author Wouter J + */ +abstract class InputAwareHelper extends Helper implements InputAwareInterface +{ + protected $input; + + /** + * {@inheritdoc} + */ + public function setInput(InputInterface $input) + { + $this->input = $input; + } +} diff --git a/lib/symfony/console/Helper/ProcessHelper.php b/lib/symfony/console/Helper/ProcessHelper.php new file mode 100644 index 000000000..666f114a2 --- /dev/null +++ b/lib/symfony/console/Helper/ProcessHelper.php @@ -0,0 +1,141 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Output\ConsoleOutputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Process\Exception\ProcessFailedException; +use Symfony\Component\Process\Process; + +/** + * The ProcessHelper class provides helpers to run external processes. + * + * @author Fabien Potencier + */ +class ProcessHelper extends Helper +{ + /** + * Runs an external process. + * + * @param OutputInterface $output An OutputInterface instance + * @param string|array|Process $cmd An instance of Process or an array of arguments to escape and run or a command to run + * @param string|null $error An error message that must be displayed if something went wrong + * @param callable|null $callback A PHP callback to run whenever there is some + * output available on STDOUT or STDERR + * @param int $verbosity The threshold for verbosity + * + * @return Process The process that ran + */ + public function run(OutputInterface $output, $cmd, $error = null, callable $callback = null, $verbosity = OutputInterface::VERBOSITY_VERY_VERBOSE) + { + if ($output instanceof ConsoleOutputInterface) { + $output = $output->getErrorOutput(); + } + + $formatter = $this->getHelperSet()->get('debug_formatter'); + + if ($cmd instanceof Process) { + $process = $cmd; + } else { + $process = new Process($cmd); + } + + if ($verbosity <= $output->getVerbosity()) { + $output->write($formatter->start(spl_object_hash($process), $this->escapeString($process->getCommandLine()))); + } + + if ($output->isDebug()) { + $callback = $this->wrapCallback($output, $process, $callback); + } + + $process->run($callback); + + if ($verbosity <= $output->getVerbosity()) { + $message = $process->isSuccessful() ? 'Command ran successfully' : sprintf('%s Command did not run successfully', $process->getExitCode()); + $output->write($formatter->stop(spl_object_hash($process), $message, $process->isSuccessful())); + } + + if (!$process->isSuccessful() && null !== $error) { + $output->writeln(sprintf('%s', $this->escapeString($error))); + } + + return $process; + } + + /** + * Runs the process. + * + * This is identical to run() except that an exception is thrown if the process + * exits with a non-zero exit code. + * + * @param OutputInterface $output An OutputInterface instance + * @param string|Process $cmd An instance of Process or a command to run + * @param string|null $error An error message that must be displayed if something went wrong + * @param callable|null $callback A PHP callback to run whenever there is some + * output available on STDOUT or STDERR + * + * @return Process The process that ran + * + * @throws ProcessFailedException + * + * @see run() + */ + public function mustRun(OutputInterface $output, $cmd, $error = null, callable $callback = null) + { + $process = $this->run($output, $cmd, $error, $callback); + + if (!$process->isSuccessful()) { + throw new ProcessFailedException($process); + } + + return $process; + } + + /** + * Wraps a Process callback to add debugging output. + * + * @param OutputInterface $output An OutputInterface interface + * @param Process $process The Process + * @param callable|null $callback A PHP callable + * + * @return callable + */ + public function wrapCallback(OutputInterface $output, Process $process, callable $callback = null) + { + if ($output instanceof ConsoleOutputInterface) { + $output = $output->getErrorOutput(); + } + + $formatter = $this->getHelperSet()->get('debug_formatter'); + + return function ($type, $buffer) use ($output, $process, $callback, $formatter) { + $output->write($formatter->progress(spl_object_hash($process), $this->escapeString($buffer), Process::ERR === $type)); + + if (null !== $callback) { + \call_user_func($callback, $type, $buffer); + } + }; + } + + private function escapeString($str) + { + return str_replace('<', '\\<', $str); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'process'; + } +} diff --git a/lib/symfony/console/Helper/ProgressBar.php b/lib/symfony/console/Helper/ProgressBar.php new file mode 100644 index 000000000..172565514 --- /dev/null +++ b/lib/symfony/console/Helper/ProgressBar.php @@ -0,0 +1,614 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Exception\LogicException; +use Symfony\Component\Console\Output\ConsoleOutputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Terminal; + +/** + * The ProgressBar provides helpers to display progress output. + * + * @author Fabien Potencier + * @author Chris Jones + */ +final class ProgressBar +{ + private $barWidth = 28; + private $barChar; + private $emptyBarChar = '-'; + private $progressChar = '>'; + private $format; + private $internalFormat; + private $redrawFreq = 1; + private $output; + private $step = 0; + private $max; + private $startTime; + private $stepWidth; + private $percent = 0.0; + private $formatLineCount; + private $messages = []; + private $overwrite = true; + private $terminal; + private $firstRun = true; + + private static $formatters; + private static $formats; + + /** + * @param OutputInterface $output An OutputInterface instance + * @param int $max Maximum steps (0 if unknown) + */ + public function __construct(OutputInterface $output, $max = 0) + { + if ($output instanceof ConsoleOutputInterface) { + $output = $output->getErrorOutput(); + } + + $this->output = $output; + $this->setMaxSteps($max); + $this->terminal = new Terminal(); + + if (!$this->output->isDecorated()) { + // disable overwrite when output does not support ANSI codes. + $this->overwrite = false; + + // set a reasonable redraw frequency so output isn't flooded + $this->setRedrawFrequency($max / 10); + } + + $this->startTime = time(); + } + + /** + * Sets a placeholder formatter for a given name. + * + * This method also allow you to override an existing placeholder. + * + * @param string $name The placeholder name (including the delimiter char like %) + * @param callable $callable A PHP callable + */ + public static function setPlaceholderFormatterDefinition($name, callable $callable) + { + if (!self::$formatters) { + self::$formatters = self::initPlaceholderFormatters(); + } + + self::$formatters[$name] = $callable; + } + + /** + * Gets the placeholder formatter for a given name. + * + * @param string $name The placeholder name (including the delimiter char like %) + * + * @return callable|null A PHP callable + */ + public static function getPlaceholderFormatterDefinition($name) + { + if (!self::$formatters) { + self::$formatters = self::initPlaceholderFormatters(); + } + + return isset(self::$formatters[$name]) ? self::$formatters[$name] : null; + } + + /** + * Sets a format for a given name. + * + * This method also allow you to override an existing format. + * + * @param string $name The format name + * @param string $format A format string + */ + public static function setFormatDefinition($name, $format) + { + if (!self::$formats) { + self::$formats = self::initFormats(); + } + + self::$formats[$name] = $format; + } + + /** + * Gets the format for a given name. + * + * @param string $name The format name + * + * @return string|null A format string + */ + public static function getFormatDefinition($name) + { + if (!self::$formats) { + self::$formats = self::initFormats(); + } + + return isset(self::$formats[$name]) ? self::$formats[$name] : null; + } + + /** + * Associates a text with a named placeholder. + * + * The text is displayed when the progress bar is rendered but only + * when the corresponding placeholder is part of the custom format line + * (by wrapping the name with %). + * + * @param string $message The text to associate with the placeholder + * @param string $name The name of the placeholder + */ + public function setMessage($message, $name = 'message') + { + $this->messages[$name] = $message; + } + + public function getMessage($name = 'message') + { + return $this->messages[$name]; + } + + /** + * Gets the progress bar start time. + * + * @return int The progress bar start time + */ + public function getStartTime() + { + return $this->startTime; + } + + /** + * Gets the progress bar maximal steps. + * + * @return int The progress bar max steps + */ + public function getMaxSteps() + { + return $this->max; + } + + /** + * Gets the current step position. + * + * @return int The progress bar step + */ + public function getProgress() + { + return $this->step; + } + + /** + * Gets the progress bar step width. + * + * @return int The progress bar step width + */ + private function getStepWidth() + { + return $this->stepWidth; + } + + /** + * Gets the current progress bar percent. + * + * @return float The current progress bar percent + */ + public function getProgressPercent() + { + return $this->percent; + } + + /** + * Sets the progress bar width. + * + * @param int $size The progress bar size + */ + public function setBarWidth($size) + { + $this->barWidth = max(1, (int) $size); + } + + /** + * Gets the progress bar width. + * + * @return int The progress bar size + */ + public function getBarWidth() + { + return $this->barWidth; + } + + /** + * Sets the bar character. + * + * @param string $char A character + */ + public function setBarCharacter($char) + { + $this->barChar = $char; + } + + /** + * Gets the bar character. + * + * @return string A character + */ + public function getBarCharacter() + { + if (null === $this->barChar) { + return $this->max ? '=' : $this->emptyBarChar; + } + + return $this->barChar; + } + + /** + * Sets the empty bar character. + * + * @param string $char A character + */ + public function setEmptyBarCharacter($char) + { + $this->emptyBarChar = $char; + } + + /** + * Gets the empty bar character. + * + * @return string A character + */ + public function getEmptyBarCharacter() + { + return $this->emptyBarChar; + } + + /** + * Sets the progress bar character. + * + * @param string $char A character + */ + public function setProgressCharacter($char) + { + $this->progressChar = $char; + } + + /** + * Gets the progress bar character. + * + * @return string A character + */ + public function getProgressCharacter() + { + return $this->progressChar; + } + + /** + * Sets the progress bar format. + * + * @param string $format The format + */ + public function setFormat($format) + { + $this->format = null; + $this->internalFormat = $format; + } + + /** + * Sets the redraw frequency. + * + * @param int|float $freq The frequency in steps + */ + public function setRedrawFrequency($freq) + { + $this->redrawFreq = max((int) $freq, 1); + } + + /** + * Starts the progress output. + * + * @param int|null $max Number of steps to complete the bar (0 if indeterminate), null to leave unchanged + */ + public function start($max = null) + { + $this->startTime = time(); + $this->step = 0; + $this->percent = 0.0; + + if (null !== $max) { + $this->setMaxSteps($max); + } + + $this->display(); + } + + /** + * Advances the progress output X steps. + * + * @param int $step Number of steps to advance + */ + public function advance($step = 1) + { + $this->setProgress($this->step + $step); + } + + /** + * Sets whether to overwrite the progressbar, false for new line. + * + * @param bool $overwrite + */ + public function setOverwrite($overwrite) + { + $this->overwrite = (bool) $overwrite; + } + + /** + * Sets the current progress. + * + * @param int $step The current progress + */ + public function setProgress($step) + { + $step = (int) $step; + + if ($this->max && $step > $this->max) { + $this->max = $step; + } elseif ($step < 0) { + $step = 0; + } + + $prevPeriod = (int) ($this->step / $this->redrawFreq); + $currPeriod = (int) ($step / $this->redrawFreq); + $this->step = $step; + $this->percent = $this->max ? (float) $this->step / $this->max : 0; + if ($prevPeriod !== $currPeriod || $this->max === $step) { + $this->display(); + } + } + + /** + * Finishes the progress output. + */ + public function finish() + { + if (!$this->max) { + $this->max = $this->step; + } + + if ($this->step === $this->max && !$this->overwrite) { + // prevent double 100% output + return; + } + + $this->setProgress($this->max); + } + + /** + * Outputs the current progress string. + */ + public function display() + { + if (OutputInterface::VERBOSITY_QUIET === $this->output->getVerbosity()) { + return; + } + + if (null === $this->format) { + $this->setRealFormat($this->internalFormat ?: $this->determineBestFormat()); + } + + $this->overwrite($this->buildLine()); + } + + /** + * Removes the progress bar from the current line. + * + * This is useful if you wish to write some output + * while a progress bar is running. + * Call display() to show the progress bar again. + */ + public function clear() + { + if (!$this->overwrite) { + return; + } + + if (null === $this->format) { + $this->setRealFormat($this->internalFormat ?: $this->determineBestFormat()); + } + + $this->overwrite(''); + } + + /** + * Sets the progress bar format. + * + * @param string $format The format + */ + private function setRealFormat($format) + { + // try to use the _nomax variant if available + if (!$this->max && null !== self::getFormatDefinition($format.'_nomax')) { + $this->format = self::getFormatDefinition($format.'_nomax'); + } elseif (null !== self::getFormatDefinition($format)) { + $this->format = self::getFormatDefinition($format); + } else { + $this->format = $format; + } + + $this->formatLineCount = substr_count($this->format, "\n"); + } + + /** + * Sets the progress bar maximal steps. + * + * @param int $max The progress bar max steps + */ + private function setMaxSteps($max) + { + $this->max = max(0, (int) $max); + $this->stepWidth = $this->max ? Helper::strlen($this->max) : 4; + } + + /** + * Overwrites a previous message to the output. + * + * @param string $message The message + */ + private function overwrite($message) + { + if ($this->overwrite) { + if (!$this->firstRun) { + // Erase previous lines + if ($this->formatLineCount > 0) { + $message = str_repeat("\x1B[1A\x1B[2K", $this->formatLineCount).$message; + } + + // Move the cursor to the beginning of the line and erase the line + $message = "\x0D\x1B[2K$message"; + } + } elseif ($this->step > 0) { + $message = PHP_EOL.$message; + } + + $this->firstRun = false; + + $this->output->write($message); + } + + private function determineBestFormat() + { + switch ($this->output->getVerbosity()) { + // OutputInterface::VERBOSITY_QUIET: display is disabled anyway + case OutputInterface::VERBOSITY_VERBOSE: + return $this->max ? 'verbose' : 'verbose_nomax'; + case OutputInterface::VERBOSITY_VERY_VERBOSE: + return $this->max ? 'very_verbose' : 'very_verbose_nomax'; + case OutputInterface::VERBOSITY_DEBUG: + return $this->max ? 'debug' : 'debug_nomax'; + default: + return $this->max ? 'normal' : 'normal_nomax'; + } + } + + private static function initPlaceholderFormatters() + { + return [ + 'bar' => function (self $bar, OutputInterface $output) { + $completeBars = floor($bar->getMaxSteps() > 0 ? $bar->getProgressPercent() * $bar->getBarWidth() : $bar->getProgress() % $bar->getBarWidth()); + $display = str_repeat($bar->getBarCharacter(), $completeBars); + if ($completeBars < $bar->getBarWidth()) { + $emptyBars = $bar->getBarWidth() - $completeBars - Helper::strlenWithoutDecoration($output->getFormatter(), $bar->getProgressCharacter()); + $display .= $bar->getProgressCharacter().str_repeat($bar->getEmptyBarCharacter(), $emptyBars); + } + + return $display; + }, + 'elapsed' => function (self $bar) { + return Helper::formatTime(time() - $bar->getStartTime()); + }, + 'remaining' => function (self $bar) { + if (!$bar->getMaxSteps()) { + throw new LogicException('Unable to display the remaining time if the maximum number of steps is not set.'); + } + + if (!$bar->getProgress()) { + $remaining = 0; + } else { + $remaining = round((time() - $bar->getStartTime()) / $bar->getProgress() * ($bar->getMaxSteps() - $bar->getProgress())); + } + + return Helper::formatTime($remaining); + }, + 'estimated' => function (self $bar) { + if (!$bar->getMaxSteps()) { + throw new LogicException('Unable to display the estimated time if the maximum number of steps is not set.'); + } + + if (!$bar->getProgress()) { + $estimated = 0; + } else { + $estimated = round((time() - $bar->getStartTime()) / $bar->getProgress() * $bar->getMaxSteps()); + } + + return Helper::formatTime($estimated); + }, + 'memory' => function (self $bar) { + return Helper::formatMemory(memory_get_usage(true)); + }, + 'current' => function (self $bar) { + return str_pad($bar->getProgress(), $bar->getStepWidth(), ' ', STR_PAD_LEFT); + }, + 'max' => function (self $bar) { + return $bar->getMaxSteps(); + }, + 'percent' => function (self $bar) { + return floor($bar->getProgressPercent() * 100); + }, + ]; + } + + private static function initFormats() + { + return [ + 'normal' => ' %current%/%max% [%bar%] %percent:3s%%', + 'normal_nomax' => ' %current% [%bar%]', + + 'verbose' => ' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%', + 'verbose_nomax' => ' %current% [%bar%] %elapsed:6s%', + + 'very_verbose' => ' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%/%estimated:-6s%', + 'very_verbose_nomax' => ' %current% [%bar%] %elapsed:6s%', + + 'debug' => ' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%/%estimated:-6s% %memory:6s%', + 'debug_nomax' => ' %current% [%bar%] %elapsed:6s% %memory:6s%', + ]; + } + + /** + * @return string + */ + private function buildLine() + { + $regex = "{%([a-z\-_]+)(?:\:([^%]+))?%}i"; + $callback = function ($matches) { + if ($formatter = $this::getPlaceholderFormatterDefinition($matches[1])) { + $text = \call_user_func($formatter, $this, $this->output); + } elseif (isset($this->messages[$matches[1]])) { + $text = $this->messages[$matches[1]]; + } else { + return $matches[0]; + } + + if (isset($matches[2])) { + $text = sprintf('%'.$matches[2], $text); + } + + return $text; + }; + $line = preg_replace_callback($regex, $callback, $this->format); + + // gets string length for each sub line with multiline format + $linesLength = array_map(function ($subLine) { + return Helper::strlenWithoutDecoration($this->output->getFormatter(), rtrim($subLine, "\r")); + }, explode("\n", $line)); + + $linesWidth = max($linesLength); + + $terminalWidth = $this->terminal->getWidth(); + if ($linesWidth <= $terminalWidth) { + return $line; + } + + $this->setBarWidth($this->barWidth - $linesWidth + $terminalWidth); + + return preg_replace_callback($regex, $callback, $this->format); + } +} diff --git a/lib/symfony/console/Helper/ProgressIndicator.php b/lib/symfony/console/Helper/ProgressIndicator.php new file mode 100644 index 000000000..3f5751fae --- /dev/null +++ b/lib/symfony/console/Helper/ProgressIndicator.php @@ -0,0 +1,271 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\LogicException; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * @author Kevin Bond + */ +class ProgressIndicator +{ + private $output; + private $startTime; + private $format; + private $message; + private $indicatorValues; + private $indicatorCurrent; + private $indicatorChangeInterval; + private $indicatorUpdateTime; + private $started = false; + + private static $formatters; + private static $formats; + + /** + * @param OutputInterface $output + * @param string|null $format Indicator format + * @param int $indicatorChangeInterval Change interval in milliseconds + * @param array|null $indicatorValues Animated indicator characters + */ + public function __construct(OutputInterface $output, $format = null, $indicatorChangeInterval = 100, $indicatorValues = null) + { + $this->output = $output; + + if (null === $format) { + $format = $this->determineBestFormat(); + } + + if (null === $indicatorValues) { + $indicatorValues = ['-', '\\', '|', '/']; + } + + $indicatorValues = array_values($indicatorValues); + + if (2 > \count($indicatorValues)) { + throw new InvalidArgumentException('Must have at least 2 indicator value characters.'); + } + + $this->format = self::getFormatDefinition($format); + $this->indicatorChangeInterval = $indicatorChangeInterval; + $this->indicatorValues = $indicatorValues; + $this->startTime = time(); + } + + /** + * Sets the current indicator message. + * + * @param string|null $message + */ + public function setMessage($message) + { + $this->message = $message; + + $this->display(); + } + + /** + * Starts the indicator output. + * + * @param $message + */ + public function start($message) + { + if ($this->started) { + throw new LogicException('Progress indicator already started.'); + } + + $this->message = $message; + $this->started = true; + $this->startTime = time(); + $this->indicatorUpdateTime = $this->getCurrentTimeInMilliseconds() + $this->indicatorChangeInterval; + $this->indicatorCurrent = 0; + + $this->display(); + } + + /** + * Advances the indicator. + */ + public function advance() + { + if (!$this->started) { + throw new LogicException('Progress indicator has not yet been started.'); + } + + if (!$this->output->isDecorated()) { + return; + } + + $currentTime = $this->getCurrentTimeInMilliseconds(); + + if ($currentTime < $this->indicatorUpdateTime) { + return; + } + + $this->indicatorUpdateTime = $currentTime + $this->indicatorChangeInterval; + ++$this->indicatorCurrent; + + $this->display(); + } + + /** + * Finish the indicator with message. + * + * @param $message + */ + public function finish($message) + { + if (!$this->started) { + throw new LogicException('Progress indicator has not yet been started.'); + } + + $this->message = $message; + $this->display(); + $this->output->writeln(''); + $this->started = false; + } + + /** + * Gets the format for a given name. + * + * @param string $name The format name + * + * @return string|null A format string + */ + public static function getFormatDefinition($name) + { + if (!self::$formats) { + self::$formats = self::initFormats(); + } + + return isset(self::$formats[$name]) ? self::$formats[$name] : null; + } + + /** + * Sets a placeholder formatter for a given name. + * + * This method also allow you to override an existing placeholder. + * + * @param string $name The placeholder name (including the delimiter char like %) + * @param callable $callable A PHP callable + */ + public static function setPlaceholderFormatterDefinition($name, $callable) + { + if (!self::$formatters) { + self::$formatters = self::initPlaceholderFormatters(); + } + + self::$formatters[$name] = $callable; + } + + /** + * Gets the placeholder formatter for a given name. + * + * @param string $name The placeholder name (including the delimiter char like %) + * + * @return callable|null A PHP callable + */ + public static function getPlaceholderFormatterDefinition($name) + { + if (!self::$formatters) { + self::$formatters = self::initPlaceholderFormatters(); + } + + return isset(self::$formatters[$name]) ? self::$formatters[$name] : null; + } + + private function display() + { + if (OutputInterface::VERBOSITY_QUIET === $this->output->getVerbosity()) { + return; + } + + $self = $this; + + $this->overwrite(preg_replace_callback("{%([a-z\-_]+)(?:\:([^%]+))?%}i", function ($matches) use ($self) { + if ($formatter = $self::getPlaceholderFormatterDefinition($matches[1])) { + return \call_user_func($formatter, $self); + } + + return $matches[0]; + }, $this->format)); + } + + private function determineBestFormat() + { + switch ($this->output->getVerbosity()) { + // OutputInterface::VERBOSITY_QUIET: display is disabled anyway + case OutputInterface::VERBOSITY_VERBOSE: + return $this->output->isDecorated() ? 'verbose' : 'verbose_no_ansi'; + case OutputInterface::VERBOSITY_VERY_VERBOSE: + case OutputInterface::VERBOSITY_DEBUG: + return $this->output->isDecorated() ? 'very_verbose' : 'very_verbose_no_ansi'; + default: + return $this->output->isDecorated() ? 'normal' : 'normal_no_ansi'; + } + } + + /** + * Overwrites a previous message to the output. + * + * @param string $message The message + */ + private function overwrite($message) + { + if ($this->output->isDecorated()) { + $this->output->write("\x0D\x1B[2K"); + $this->output->write($message); + } else { + $this->output->writeln($message); + } + } + + private function getCurrentTimeInMilliseconds() + { + return round(microtime(true) * 1000); + } + + private static function initPlaceholderFormatters() + { + return [ + 'indicator' => function (self $indicator) { + return $indicator->indicatorValues[$indicator->indicatorCurrent % \count($indicator->indicatorValues)]; + }, + 'message' => function (self $indicator) { + return $indicator->message; + }, + 'elapsed' => function (self $indicator) { + return Helper::formatTime(time() - $indicator->startTime); + }, + 'memory' => function () { + return Helper::formatMemory(memory_get_usage(true)); + }, + ]; + } + + private static function initFormats() + { + return [ + 'normal' => ' %indicator% %message%', + 'normal_no_ansi' => ' %message%', + + 'verbose' => ' %indicator% %message% (%elapsed:6s%)', + 'verbose_no_ansi' => ' %message% (%elapsed:6s%)', + + 'very_verbose' => ' %indicator% %message% (%elapsed:6s%, %memory:6s%)', + 'very_verbose_no_ansi' => ' %message% (%elapsed:6s%, %memory:6s%)', + ]; + } +} diff --git a/lib/symfony/console/Helper/QuestionHelper.php b/lib/symfony/console/Helper/QuestionHelper.php new file mode 100644 index 000000000..2d910f435 --- /dev/null +++ b/lib/symfony/console/Helper/QuestionHelper.php @@ -0,0 +1,517 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\RuntimeException; +use Symfony\Component\Console\Formatter\OutputFormatter; +use Symfony\Component\Console\Formatter\OutputFormatterStyle; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\StreamableInputInterface; +use Symfony\Component\Console\Output\ConsoleOutputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Question\ChoiceQuestion; +use Symfony\Component\Console\Question\Question; + +/** + * The QuestionHelper class provides helpers to interact with the user. + * + * @author Fabien Potencier + */ +class QuestionHelper extends Helper +{ + private $inputStream; + private static $shell; + private static $stty; + + /** + * Asks a question to the user. + * + * @return mixed The user answer + * + * @throws RuntimeException If there is no data to read in the input stream + */ + public function ask(InputInterface $input, OutputInterface $output, Question $question) + { + if ($output instanceof ConsoleOutputInterface) { + $output = $output->getErrorOutput(); + } + + if (!$input->isInteractive()) { + $default = $question->getDefault(); + + if (null === $default) { + return $default; + } + + if ($validator = $question->getValidator()) { + return \call_user_func($question->getValidator(), $default); + } elseif ($question instanceof ChoiceQuestion) { + $choices = $question->getChoices(); + + if (!$question->isMultiselect()) { + return isset($choices[$default]) ? $choices[$default] : $default; + } + + $default = explode(',', $default); + foreach ($default as $k => $v) { + $v = trim($v); + $default[$k] = isset($choices[$v]) ? $choices[$v] : $v; + } + } + + return $default; + } + + if ($input instanceof StreamableInputInterface && $stream = $input->getStream()) { + $this->inputStream = $stream; + } + + if (!$question->getValidator()) { + return $this->doAsk($output, $question); + } + + $interviewer = function () use ($output, $question) { + return $this->doAsk($output, $question); + }; + + return $this->validateAttempts($interviewer, $output, $question); + } + + /** + * Sets the input stream to read from when interacting with the user. + * + * This is mainly useful for testing purpose. + * + * @deprecated since version 3.2, to be removed in 4.0. Use + * StreamableInputInterface::setStream() instead. + * + * @param resource $stream The input stream + * + * @throws InvalidArgumentException In case the stream is not a resource + */ + public function setInputStream($stream) + { + @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.2 and will be removed in 4.0. Use %s::setStream() instead.', __METHOD__, StreamableInputInterface::class), E_USER_DEPRECATED); + + if (!\is_resource($stream)) { + throw new InvalidArgumentException('Input stream must be a valid resource.'); + } + + $this->inputStream = $stream; + } + + /** + * Returns the helper's input stream. + * + * @deprecated since version 3.2, to be removed in 4.0. Use + * StreamableInputInterface::getStream() instead. + * + * @return resource + */ + public function getInputStream() + { + if (0 === \func_num_args() || func_get_arg(0)) { + @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.2 and will be removed in 4.0. Use %s::getStream() instead.', __METHOD__, StreamableInputInterface::class), E_USER_DEPRECATED); + } + + return $this->inputStream; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'question'; + } + + /** + * Prevents usage of stty. + */ + public static function disableStty() + { + self::$stty = false; + } + + /** + * Asks the question to the user. + * + * @return bool|mixed|string|null + * + * @throws RuntimeException In case the fallback is deactivated and the response cannot be hidden + */ + private function doAsk(OutputInterface $output, Question $question) + { + $this->writePrompt($output, $question); + + $inputStream = $this->inputStream ?: STDIN; + $autocomplete = $question->getAutocompleterValues(); + + if (null === $autocomplete || !$this->hasSttyAvailable()) { + $ret = false; + if ($question->isHidden()) { + try { + $ret = trim($this->getHiddenResponse($output, $inputStream)); + } catch (RuntimeException $e) { + if (!$question->isHiddenFallback()) { + throw $e; + } + } + } + + if (false === $ret) { + $ret = fgets($inputStream, 4096); + if (false === $ret) { + throw new RuntimeException('Aborted.'); + } + $ret = trim($ret); + } + } else { + $ret = trim($this->autocomplete($output, $question, $inputStream, \is_array($autocomplete) ? $autocomplete : iterator_to_array($autocomplete, false))); + } + + $ret = \strlen($ret) > 0 ? $ret : $question->getDefault(); + + if ($normalizer = $question->getNormalizer()) { + return $normalizer($ret); + } + + return $ret; + } + + /** + * Outputs the question prompt. + */ + protected function writePrompt(OutputInterface $output, Question $question) + { + $message = $question->getQuestion(); + + if ($question instanceof ChoiceQuestion) { + $maxWidth = max(array_map([$this, 'strlen'], array_keys($question->getChoices()))); + + $messages = (array) $question->getQuestion(); + foreach ($question->getChoices() as $key => $value) { + $width = $maxWidth - $this->strlen($key); + $messages[] = ' ['.$key.str_repeat(' ', $width).'] '.$value; + } + + $output->writeln($messages); + + $message = $question->getPrompt(); + } + + $output->write($message); + } + + /** + * Outputs an error message. + */ + protected function writeError(OutputInterface $output, \Exception $error) + { + if (null !== $this->getHelperSet() && $this->getHelperSet()->has('formatter')) { + $message = $this->getHelperSet()->get('formatter')->formatBlock($error->getMessage(), 'error'); + } else { + $message = ''.$error->getMessage().''; + } + + $output->writeln($message); + } + + /** + * Autocompletes a question. + * + * @param OutputInterface $output + * @param Question $question + * @param resource $inputStream + * @param array $autocomplete + * + * @return string + */ + private function autocomplete(OutputInterface $output, Question $question, $inputStream, array $autocomplete) + { + $fullChoice = ''; + $ret = ''; + + $i = 0; + $ofs = -1; + $matches = $autocomplete; + $numMatches = \count($matches); + + $sttyMode = shell_exec('stty -g'); + + // Disable icanon (so we can fread each keypress) and echo (we'll do echoing here instead) + shell_exec('stty -icanon -echo'); + + // Add highlighted text style + $output->getFormatter()->setStyle('hl', new OutputFormatterStyle('black', 'white')); + + // Read a keypress + while (!feof($inputStream)) { + $c = fread($inputStream, 1); + + // as opposed to fgets(), fread() returns an empty string when the stream content is empty, not false. + if (false === $c || ('' === $ret && '' === $c && null === $question->getDefault())) { + shell_exec(sprintf('stty %s', $sttyMode)); + throw new RuntimeException('Aborted.'); + } elseif ("\177" === $c) { // Backspace Character + if (0 === $numMatches && 0 !== $i) { + --$i; + $fullChoice = substr($fullChoice, 0, -1); + // Move cursor backwards + $output->write("\033[1D"); + } + + if (0 === $i) { + $ofs = -1; + $matches = $autocomplete; + $numMatches = \count($matches); + } else { + $numMatches = 0; + } + + // Pop the last character off the end of our string + $ret = substr($ret, 0, $i); + } elseif ("\033" === $c) { + // Did we read an escape sequence? + $c .= fread($inputStream, 2); + + // A = Up Arrow. B = Down Arrow + if (isset($c[2]) && ('A' === $c[2] || 'B' === $c[2])) { + if ('A' === $c[2] && -1 === $ofs) { + $ofs = 0; + } + + if (0 === $numMatches) { + continue; + } + + $ofs += ('A' === $c[2]) ? -1 : 1; + $ofs = ($numMatches + $ofs) % $numMatches; + } + } elseif (\ord($c) < 32) { + if ("\t" === $c || "\n" === $c) { + if ($numMatches > 0 && -1 !== $ofs) { + $ret = $matches[$ofs]; + // Echo out remaining chars for current match + $remainingCharacters = substr($ret, \strlen(trim($this->mostRecentlyEnteredValue($fullChoice)))); + $output->write($remainingCharacters); + $fullChoice .= $remainingCharacters; + $i = \strlen($fullChoice); + } + + if ("\n" === $c) { + $output->write($c); + break; + } + + $numMatches = 0; + } + + continue; + } else { + if ("\x80" <= $c) { + $c .= fread($inputStream, ["\xC0" => 1, "\xD0" => 1, "\xE0" => 2, "\xF0" => 3][$c & "\xF0"]); + } + + $output->write($c); + $ret .= $c; + $fullChoice .= $c; + ++$i; + + $tempRet = $ret; + + if ($question instanceof ChoiceQuestion && $question->isMultiselect()) { + $tempRet = $this->mostRecentlyEnteredValue($fullChoice); + } + + $numMatches = 0; + $ofs = 0; + + foreach ($autocomplete as $value) { + // If typed characters match the beginning chunk of value (e.g. [AcmeDe]moBundle) + if (0 === strpos($value, $tempRet)) { + $matches[$numMatches++] = $value; + } + } + } + + // Erase characters from cursor to end of line + $output->write("\033[K"); + + if ($numMatches > 0 && -1 !== $ofs) { + // Save cursor position + $output->write("\0337"); + // Write highlighted text, complete the partially entered response + $charactersEntered = \strlen(trim($this->mostRecentlyEnteredValue($fullChoice))); + $output->write(''.OutputFormatter::escapeTrailingBackslash(substr($matches[$ofs], $charactersEntered)).''); + // Restore cursor position + $output->write("\0338"); + } + } + + // Reset stty so it behaves normally again + shell_exec(sprintf('stty %s', $sttyMode)); + + return $fullChoice; + } + + private function mostRecentlyEnteredValue($entered) + { + // Determine the most recent value that the user entered + if (false === strpos($entered, ',')) { + return $entered; + } + + $choices = explode(',', $entered); + if (\strlen($lastChoice = trim($choices[\count($choices) - 1])) > 0) { + return $lastChoice; + } + + return $entered; + } + + /** + * Gets a hidden response from user. + * + * @param OutputInterface $output An Output instance + * @param resource $inputStream The handler resource + * + * @return string The answer + * + * @throws RuntimeException In case the fallback is deactivated and the response cannot be hidden + */ + private function getHiddenResponse(OutputInterface $output, $inputStream) + { + if ('\\' === \DIRECTORY_SEPARATOR) { + $exe = __DIR__.'/../Resources/bin/hiddeninput.exe'; + + // handle code running from a phar + if ('phar:' === substr(__FILE__, 0, 5)) { + $tmpExe = sys_get_temp_dir().'/hiddeninput.exe'; + copy($exe, $tmpExe); + $exe = $tmpExe; + } + + $value = rtrim(shell_exec($exe)); + $output->writeln(''); + + if (isset($tmpExe)) { + unlink($tmpExe); + } + + return $value; + } + + if ($this->hasSttyAvailable()) { + $sttyMode = shell_exec('stty -g'); + + shell_exec('stty -echo'); + $value = fgets($inputStream, 4096); + shell_exec(sprintf('stty %s', $sttyMode)); + + if (false === $value) { + throw new RuntimeException('Aborted.'); + } + + $value = trim($value); + $output->writeln(''); + + return $value; + } + + if (false !== $shell = $this->getShell()) { + $readCmd = 'csh' === $shell ? 'set mypassword = $<' : 'read -r mypassword'; + $command = sprintf("/usr/bin/env %s -c 'stty -echo; %s; stty echo; echo \$mypassword'", $shell, $readCmd); + $value = rtrim(shell_exec($command)); + $output->writeln(''); + + return $value; + } + + throw new RuntimeException('Unable to hide the response.'); + } + + /** + * Validates an attempt. + * + * @param callable $interviewer A callable that will ask for a question and return the result + * @param OutputInterface $output An Output instance + * @param Question $question A Question instance + * + * @return mixed The validated response + * + * @throws \Exception In case the max number of attempts has been reached and no valid response has been given + */ + private function validateAttempts(callable $interviewer, OutputInterface $output, Question $question) + { + $error = null; + $attempts = $question->getMaxAttempts(); + while (null === $attempts || $attempts--) { + if (null !== $error) { + $this->writeError($output, $error); + } + + try { + return \call_user_func($question->getValidator(), $interviewer()); + } catch (RuntimeException $e) { + throw $e; + } catch (\Exception $error) { + } + } + + throw $error; + } + + /** + * Returns a valid unix shell. + * + * @return string|bool The valid shell name, false in case no valid shell is found + */ + private function getShell() + { + if (null !== self::$shell) { + return self::$shell; + } + + self::$shell = false; + + if (file_exists('/usr/bin/env')) { + // handle other OSs with bash/zsh/ksh/csh if available to hide the answer + $test = "/usr/bin/env %s -c 'echo OK' 2> /dev/null"; + foreach (['bash', 'zsh', 'ksh', 'csh'] as $sh) { + if ('OK' === rtrim(shell_exec(sprintf($test, $sh)))) { + self::$shell = $sh; + break; + } + } + } + + return self::$shell; + } + + /** + * Returns whether Stty is available or not. + * + * @return bool + */ + private function hasSttyAvailable() + { + if (null !== self::$stty) { + return self::$stty; + } + + exec('stty 2>&1', $output, $exitcode); + + return self::$stty = 0 === $exitcode; + } +} diff --git a/lib/symfony/console/Helper/SymfonyQuestionHelper.php b/lib/symfony/console/Helper/SymfonyQuestionHelper.php new file mode 100644 index 000000000..5937741a2 --- /dev/null +++ b/lib/symfony/console/Helper/SymfonyQuestionHelper.php @@ -0,0 +1,124 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Exception\LogicException; +use Symfony\Component\Console\Formatter\OutputFormatter; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Question\ChoiceQuestion; +use Symfony\Component\Console\Question\ConfirmationQuestion; +use Symfony\Component\Console\Question\Question; +use Symfony\Component\Console\Style\SymfonyStyle; + +/** + * Symfony Style Guide compliant question helper. + * + * @author Kevin Bond + */ +class SymfonyQuestionHelper extends QuestionHelper +{ + /** + * {@inheritdoc} + * + * To be removed in 4.0 + */ + public function ask(InputInterface $input, OutputInterface $output, Question $question) + { + $validator = $question->getValidator(); + $question->setValidator(function ($value) use ($validator) { + if (null !== $validator) { + $value = $validator($value); + } else { + // make required + if (!\is_array($value) && !\is_bool($value) && 0 === \strlen($value)) { + @trigger_error('The default question validator is deprecated since Symfony 3.3 and will not be used anymore in version 4.0. Set a custom question validator if needed.', E_USER_DEPRECATED); + + throw new LogicException('A value is required.'); + } + } + + return $value; + }); + + return parent::ask($input, $output, $question); + } + + /** + * {@inheritdoc} + */ + protected function writePrompt(OutputInterface $output, Question $question) + { + $text = OutputFormatter::escapeTrailingBackslash($question->getQuestion()); + $default = $question->getDefault(); + + switch (true) { + case null === $default: + $text = sprintf(' %s:', $text); + + break; + + case $question instanceof ConfirmationQuestion: + $text = sprintf(' %s (yes/no) [%s]:', $text, $default ? 'yes' : 'no'); + + break; + + case $question instanceof ChoiceQuestion && $question->isMultiselect(): + $choices = $question->getChoices(); + $default = explode(',', $default); + + foreach ($default as $key => $value) { + $default[$key] = $choices[trim($value)]; + } + + $text = sprintf(' %s [%s]:', $text, OutputFormatter::escape(implode(', ', $default))); + + break; + + case $question instanceof ChoiceQuestion: + $choices = $question->getChoices(); + $text = sprintf(' %s [%s]:', $text, OutputFormatter::escape(isset($choices[$default]) ? $choices[$default] : $default)); + + break; + + default: + $text = sprintf(' %s [%s]:', $text, OutputFormatter::escape($default)); + } + + $output->writeln($text); + + if ($question instanceof ChoiceQuestion) { + $width = max(array_map('strlen', array_keys($question->getChoices()))); + + foreach ($question->getChoices() as $key => $value) { + $output->writeln(sprintf(" [%-${width}s] %s", $key, $value)); + } + } + + $output->write(' > '); + } + + /** + * {@inheritdoc} + */ + protected function writeError(OutputInterface $output, \Exception $error) + { + if ($output instanceof SymfonyStyle) { + $output->newLine(); + $output->error($error->getMessage()); + + return; + } + + parent::writeError($output, $error); + } +} diff --git a/lib/symfony/console/Helper/Table.php b/lib/symfony/console/Helper/Table.php new file mode 100644 index 000000000..1c439dc1b --- /dev/null +++ b/lib/symfony/console/Helper/Table.php @@ -0,0 +1,705 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Provides helpers to display a table. + * + * @author Fabien Potencier + * @author Саша Стаменковић + * @author Abdellatif Ait boudad + * @author Max Grigorian + */ +class Table +{ + /** + * Table headers. + */ + private $headers = []; + + /** + * Table rows. + */ + private $rows = []; + + /** + * Column widths cache. + */ + private $effectiveColumnWidths = []; + + /** + * Number of columns cache. + * + * @var int + */ + private $numberOfColumns; + + /** + * @var OutputInterface + */ + private $output; + + /** + * @var TableStyle + */ + private $style; + + /** + * @var array + */ + private $columnStyles = []; + + /** + * User set column widths. + * + * @var array + */ + private $columnWidths = []; + + private static $styles; + + public function __construct(OutputInterface $output) + { + $this->output = $output; + + if (!self::$styles) { + self::$styles = self::initStyles(); + } + + $this->setStyle('default'); + } + + /** + * Sets a style definition. + * + * @param string $name The style name + * @param TableStyle $style A TableStyle instance + */ + public static function setStyleDefinition($name, TableStyle $style) + { + if (!self::$styles) { + self::$styles = self::initStyles(); + } + + self::$styles[$name] = $style; + } + + /** + * Gets a style definition by name. + * + * @param string $name The style name + * + * @return TableStyle + */ + public static function getStyleDefinition($name) + { + if (!self::$styles) { + self::$styles = self::initStyles(); + } + + if (isset(self::$styles[$name])) { + return self::$styles[$name]; + } + + throw new InvalidArgumentException(sprintf('Style "%s" is not defined.', $name)); + } + + /** + * Sets table style. + * + * @param TableStyle|string $name The style name or a TableStyle instance + * + * @return $this + */ + public function setStyle($name) + { + $this->style = $this->resolveStyle($name); + + return $this; + } + + /** + * Gets the current table style. + * + * @return TableStyle + */ + public function getStyle() + { + return $this->style; + } + + /** + * Sets table column style. + * + * @param int $columnIndex Column index + * @param TableStyle|string $name The style name or a TableStyle instance + * + * @return $this + */ + public function setColumnStyle($columnIndex, $name) + { + $columnIndex = (int) $columnIndex; + + $this->columnStyles[$columnIndex] = $this->resolveStyle($name); + + return $this; + } + + /** + * Gets the current style for a column. + * + * If style was not set, it returns the global table style. + * + * @param int $columnIndex Column index + * + * @return TableStyle + */ + public function getColumnStyle($columnIndex) + { + if (isset($this->columnStyles[$columnIndex])) { + return $this->columnStyles[$columnIndex]; + } + + return $this->getStyle(); + } + + /** + * Sets the minimum width of a column. + * + * @param int $columnIndex Column index + * @param int $width Minimum column width in characters + * + * @return $this + */ + public function setColumnWidth($columnIndex, $width) + { + $this->columnWidths[(int) $columnIndex] = (int) $width; + + return $this; + } + + /** + * Sets the minimum width of all columns. + * + * @param array $widths + * + * @return $this + */ + public function setColumnWidths(array $widths) + { + $this->columnWidths = []; + foreach ($widths as $index => $width) { + $this->setColumnWidth($index, $width); + } + + return $this; + } + + public function setHeaders(array $headers) + { + $headers = array_values($headers); + if (!empty($headers) && !\is_array($headers[0])) { + $headers = [$headers]; + } + + $this->headers = $headers; + + return $this; + } + + public function setRows(array $rows) + { + $this->rows = []; + + return $this->addRows($rows); + } + + public function addRows(array $rows) + { + foreach ($rows as $row) { + $this->addRow($row); + } + + return $this; + } + + public function addRow($row) + { + if ($row instanceof TableSeparator) { + $this->rows[] = $row; + + return $this; + } + + if (!\is_array($row)) { + throw new InvalidArgumentException('A row must be an array or a TableSeparator instance.'); + } + + $this->rows[] = array_values($row); + + return $this; + } + + public function setRow($column, array $row) + { + $this->rows[$column] = $row; + + return $this; + } + + /** + * Renders table to output. + * + * Example: + * + * +---------------+-----------------------+------------------+ + * | ISBN | Title | Author | + * +---------------+-----------------------+------------------+ + * | 99921-58-10-7 | Divine Comedy | Dante Alighieri | + * | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens | + * | 960-425-059-0 | The Lord of the Rings | J. R. R. Tolkien | + * +---------------+-----------------------+------------------+ + */ + public function render() + { + $this->calculateNumberOfColumns(); + $rows = $this->buildTableRows($this->rows); + $headers = $this->buildTableRows($this->headers); + + $this->calculateColumnsWidth(array_merge($headers, $rows)); + + $this->renderRowSeparator(); + if (!empty($headers)) { + foreach ($headers as $header) { + $this->renderRow($header, $this->style->getCellHeaderFormat()); + $this->renderRowSeparator(); + } + } + foreach ($rows as $row) { + if ($row instanceof TableSeparator) { + $this->renderRowSeparator(); + } else { + $this->renderRow($row, $this->style->getCellRowFormat()); + } + } + if (!empty($rows)) { + $this->renderRowSeparator(); + } + + $this->cleanup(); + } + + /** + * Renders horizontal header separator. + * + * Example: + * + * +-----+-----------+-------+ + */ + private function renderRowSeparator() + { + if (0 === $count = $this->numberOfColumns) { + return; + } + + if (!$this->style->getHorizontalBorderChar() && !$this->style->getCrossingChar()) { + return; + } + + $markup = $this->style->getCrossingChar(); + for ($column = 0; $column < $count; ++$column) { + $markup .= str_repeat($this->style->getHorizontalBorderChar(), $this->effectiveColumnWidths[$column]).$this->style->getCrossingChar(); + } + + $this->output->writeln(sprintf($this->style->getBorderFormat(), $markup)); + } + + /** + * Renders vertical column separator. + */ + private function renderColumnSeparator() + { + return sprintf($this->style->getBorderFormat(), $this->style->getVerticalBorderChar()); + } + + /** + * Renders table row. + * + * Example: + * + * | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens | + * + * @param array $row + * @param string $cellFormat + */ + private function renderRow(array $row, $cellFormat) + { + if (empty($row)) { + return; + } + + $rowContent = $this->renderColumnSeparator(); + foreach ($this->getRowColumns($row) as $column) { + $rowContent .= $this->renderCell($row, $column, $cellFormat); + $rowContent .= $this->renderColumnSeparator(); + } + $this->output->writeln($rowContent); + } + + /** + * Renders table cell with padding. + * + * @param array $row + * @param int $column + * @param string $cellFormat + */ + private function renderCell(array $row, $column, $cellFormat) + { + $cell = isset($row[$column]) ? $row[$column] : ''; + $width = $this->effectiveColumnWidths[$column]; + if ($cell instanceof TableCell && $cell->getColspan() > 1) { + // add the width of the following columns(numbers of colspan). + foreach (range($column + 1, $column + $cell->getColspan() - 1) as $nextColumn) { + $width += $this->getColumnSeparatorWidth() + $this->effectiveColumnWidths[$nextColumn]; + } + } + + // str_pad won't work properly with multi-byte strings, we need to fix the padding + if (false !== $encoding = mb_detect_encoding($cell, null, true)) { + $width += \strlen($cell) - mb_strwidth($cell, $encoding); + } + + $style = $this->getColumnStyle($column); + + if ($cell instanceof TableSeparator) { + return sprintf($style->getBorderFormat(), str_repeat($style->getHorizontalBorderChar(), $width)); + } + + $width += Helper::strlen($cell) - Helper::strlenWithoutDecoration($this->output->getFormatter(), $cell); + $content = sprintf($style->getCellRowContentFormat(), $cell); + + return sprintf($cellFormat, str_pad($content, $width, $style->getPaddingChar(), $style->getPadType())); + } + + /** + * Calculate number of columns for this table. + */ + private function calculateNumberOfColumns() + { + if (null !== $this->numberOfColumns) { + return; + } + + $columns = [0]; + foreach (array_merge($this->headers, $this->rows) as $row) { + if ($row instanceof TableSeparator) { + continue; + } + + $columns[] = $this->getNumberOfColumns($row); + } + + $this->numberOfColumns = max($columns); + } + + private function buildTableRows($rows) + { + $unmergedRows = []; + for ($rowKey = 0; $rowKey < \count($rows); ++$rowKey) { + $rows = $this->fillNextRows($rows, $rowKey); + + // Remove any new line breaks and replace it with a new line + foreach ($rows[$rowKey] as $column => $cell) { + if (!strstr($cell, "\n")) { + continue; + } + $lines = explode("\n", str_replace("\n", "\n", $cell)); + foreach ($lines as $lineKey => $line) { + if ($cell instanceof TableCell) { + $line = new TableCell($line, ['colspan' => $cell->getColspan()]); + } + if (0 === $lineKey) { + $rows[$rowKey][$column] = $line; + } else { + $unmergedRows[$rowKey][$lineKey][$column] = $line; + } + } + } + } + + $tableRows = []; + foreach ($rows as $rowKey => $row) { + $tableRows[] = $this->fillCells($row); + if (isset($unmergedRows[$rowKey])) { + $tableRows = array_merge($tableRows, $unmergedRows[$rowKey]); + } + } + + return $tableRows; + } + + /** + * fill rows that contains rowspan > 1. + * + * @param array $rows + * @param int $line + * + * @return array + * + * @throws InvalidArgumentException + */ + private function fillNextRows(array $rows, $line) + { + $unmergedRows = []; + foreach ($rows[$line] as $column => $cell) { + if (null !== $cell && !$cell instanceof TableCell && !is_scalar($cell) && !(\is_object($cell) && method_exists($cell, '__toString'))) { + throw new InvalidArgumentException(sprintf('A cell must be a TableCell, a scalar or an object implementing __toString, %s given.', \gettype($cell))); + } + if ($cell instanceof TableCell && $cell->getRowspan() > 1) { + $nbLines = $cell->getRowspan() - 1; + $lines = [$cell]; + if (strstr($cell, "\n")) { + $lines = explode("\n", str_replace("\n", "\n", $cell)); + $nbLines = \count($lines) > $nbLines ? substr_count($cell, "\n") : $nbLines; + + $rows[$line][$column] = new TableCell($lines[0], ['colspan' => $cell->getColspan()]); + unset($lines[0]); + } + + // create a two dimensional array (rowspan x colspan) + $unmergedRows = array_replace_recursive(array_fill($line + 1, $nbLines, []), $unmergedRows); + foreach ($unmergedRows as $unmergedRowKey => $unmergedRow) { + $value = isset($lines[$unmergedRowKey - $line]) ? $lines[$unmergedRowKey - $line] : ''; + $unmergedRows[$unmergedRowKey][$column] = new TableCell($value, ['colspan' => $cell->getColspan()]); + if ($nbLines === $unmergedRowKey - $line) { + break; + } + } + } + } + + foreach ($unmergedRows as $unmergedRowKey => $unmergedRow) { + // we need to know if $unmergedRow will be merged or inserted into $rows + if (isset($rows[$unmergedRowKey]) && \is_array($rows[$unmergedRowKey]) && ($this->getNumberOfColumns($rows[$unmergedRowKey]) + $this->getNumberOfColumns($unmergedRows[$unmergedRowKey]) <= $this->numberOfColumns)) { + foreach ($unmergedRow as $cellKey => $cell) { + // insert cell into row at cellKey position + array_splice($rows[$unmergedRowKey], $cellKey, 0, [$cell]); + } + } else { + $row = $this->copyRow($rows, $unmergedRowKey - 1); + foreach ($unmergedRow as $column => $cell) { + if (!empty($cell)) { + $row[$column] = $unmergedRow[$column]; + } + } + array_splice($rows, $unmergedRowKey, 0, [$row]); + } + } + + return $rows; + } + + /** + * fill cells for a row that contains colspan > 1. + * + * @return array + */ + private function fillCells($row) + { + $newRow = []; + foreach ($row as $column => $cell) { + $newRow[] = $cell; + if ($cell instanceof TableCell && $cell->getColspan() > 1) { + foreach (range($column + 1, $column + $cell->getColspan() - 1) as $position) { + // insert empty value at column position + $newRow[] = ''; + } + } + } + + return $newRow ?: $row; + } + + /** + * @param array $rows + * @param int $line + * + * @return array + */ + private function copyRow(array $rows, $line) + { + $row = $rows[$line]; + foreach ($row as $cellKey => $cellValue) { + $row[$cellKey] = ''; + if ($cellValue instanceof TableCell) { + $row[$cellKey] = new TableCell('', ['colspan' => $cellValue->getColspan()]); + } + } + + return $row; + } + + /** + * Gets number of columns by row. + * + * @return int + */ + private function getNumberOfColumns(array $row) + { + $columns = \count($row); + foreach ($row as $column) { + $columns += $column instanceof TableCell ? ($column->getColspan() - 1) : 0; + } + + return $columns; + } + + /** + * Gets list of columns for the given row. + * + * @return array + */ + private function getRowColumns(array $row) + { + $columns = range(0, $this->numberOfColumns - 1); + foreach ($row as $cellKey => $cell) { + if ($cell instanceof TableCell && $cell->getColspan() > 1) { + // exclude grouped columns. + $columns = array_diff($columns, range($cellKey + 1, $cellKey + $cell->getColspan() - 1)); + } + } + + return $columns; + } + + /** + * Calculates columns widths. + */ + private function calculateColumnsWidth(array $rows) + { + for ($column = 0; $column < $this->numberOfColumns; ++$column) { + $lengths = []; + foreach ($rows as $row) { + if ($row instanceof TableSeparator) { + continue; + } + + foreach ($row as $i => $cell) { + if ($cell instanceof TableCell) { + $textContent = Helper::removeDecoration($this->output->getFormatter(), $cell); + $textLength = Helper::strlen($textContent); + if ($textLength > 0) { + $contentColumns = str_split($textContent, ceil($textLength / $cell->getColspan())); + foreach ($contentColumns as $position => $content) { + $row[$i + $position] = $content; + } + } + } + } + + $lengths[] = $this->getCellWidth($row, $column); + } + + $this->effectiveColumnWidths[$column] = max($lengths) + Helper::strlen($this->style->getCellRowContentFormat()) - 2; + } + } + + /** + * Gets column width. + * + * @return int + */ + private function getColumnSeparatorWidth() + { + return Helper::strlen(sprintf($this->style->getBorderFormat(), $this->style->getVerticalBorderChar())); + } + + /** + * Gets cell width. + * + * @param array $row + * @param int $column + * + * @return int + */ + private function getCellWidth(array $row, $column) + { + $cellWidth = 0; + + if (isset($row[$column])) { + $cell = $row[$column]; + $cellWidth = Helper::strlenWithoutDecoration($this->output->getFormatter(), $cell); + } + + $columnWidth = isset($this->columnWidths[$column]) ? $this->columnWidths[$column] : 0; + + return max($cellWidth, $columnWidth); + } + + /** + * Called after rendering to cleanup cache data. + */ + private function cleanup() + { + $this->effectiveColumnWidths = []; + $this->numberOfColumns = null; + } + + private static function initStyles() + { + $borderless = new TableStyle(); + $borderless + ->setHorizontalBorderChar('=') + ->setVerticalBorderChar(' ') + ->setCrossingChar(' ') + ; + + $compact = new TableStyle(); + $compact + ->setHorizontalBorderChar('') + ->setVerticalBorderChar(' ') + ->setCrossingChar('') + ->setCellRowContentFormat('%s') + ; + + $styleGuide = new TableStyle(); + $styleGuide + ->setHorizontalBorderChar('-') + ->setVerticalBorderChar(' ') + ->setCrossingChar(' ') + ->setCellHeaderFormat('%s') + ; + + return [ + 'default' => new TableStyle(), + 'borderless' => $borderless, + 'compact' => $compact, + 'symfony-style-guide' => $styleGuide, + ]; + } + + private function resolveStyle($name) + { + if ($name instanceof TableStyle) { + return $name; + } + + if (isset(self::$styles[$name])) { + return self::$styles[$name]; + } + + throw new InvalidArgumentException(sprintf('Style "%s" is not defined.', $name)); + } +} diff --git a/lib/symfony/console/Helper/TableCell.php b/lib/symfony/console/Helper/TableCell.php new file mode 100644 index 000000000..cc5145329 --- /dev/null +++ b/lib/symfony/console/Helper/TableCell.php @@ -0,0 +1,76 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * @author Abdellatif Ait boudad + */ +class TableCell +{ + private $value; + private $options = [ + 'rowspan' => 1, + 'colspan' => 1, + ]; + + /** + * @param string $value + * @param array $options + */ + public function __construct($value = '', array $options = []) + { + if (is_numeric($value) && !\is_string($value)) { + $value = (string) $value; + } + + $this->value = $value; + + // check option names + if ($diff = array_diff(array_keys($options), array_keys($this->options))) { + throw new InvalidArgumentException(sprintf('The TableCell does not support the following options: \'%s\'.', implode('\', \'', $diff))); + } + + $this->options = array_merge($this->options, $options); + } + + /** + * Returns the cell value. + * + * @return string + */ + public function __toString() + { + return $this->value; + } + + /** + * Gets number of colspan. + * + * @return int + */ + public function getColspan() + { + return (int) $this->options['colspan']; + } + + /** + * Gets number of rowspan. + * + * @return int + */ + public function getRowspan() + { + return (int) $this->options['rowspan']; + } +} diff --git a/lib/symfony/console/Helper/TableSeparator.php b/lib/symfony/console/Helper/TableSeparator.php new file mode 100644 index 000000000..e541c5315 --- /dev/null +++ b/lib/symfony/console/Helper/TableSeparator.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +/** + * Marks a row as being a separator. + * + * @author Fabien Potencier + */ +class TableSeparator extends TableCell +{ + public function __construct(array $options = []) + { + parent::__construct('', $options); + } +} diff --git a/lib/symfony/console/Helper/TableStyle.php b/lib/symfony/console/Helper/TableStyle.php new file mode 100644 index 000000000..bc9c7e94d --- /dev/null +++ b/lib/symfony/console/Helper/TableStyle.php @@ -0,0 +1,258 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\LogicException; + +/** + * Defines the styles for a Table. + * + * @author Fabien Potencier + * @author Саша Стаменковић + */ +class TableStyle +{ + private $paddingChar = ' '; + private $horizontalBorderChar = '-'; + private $verticalBorderChar = '|'; + private $crossingChar = '+'; + private $cellHeaderFormat = '%s'; + private $cellRowFormat = '%s'; + private $cellRowContentFormat = ' %s '; + private $borderFormat = '%s'; + private $padType = STR_PAD_RIGHT; + + /** + * Sets padding character, used for cell padding. + * + * @param string $paddingChar + * + * @return $this + */ + public function setPaddingChar($paddingChar) + { + if (!$paddingChar) { + throw new LogicException('The padding char must not be empty'); + } + + $this->paddingChar = $paddingChar; + + return $this; + } + + /** + * Gets padding character, used for cell padding. + * + * @return string + */ + public function getPaddingChar() + { + return $this->paddingChar; + } + + /** + * Sets horizontal border character. + * + * @param string $horizontalBorderChar + * + * @return $this + */ + public function setHorizontalBorderChar($horizontalBorderChar) + { + $this->horizontalBorderChar = $horizontalBorderChar; + + return $this; + } + + /** + * Gets horizontal border character. + * + * @return string + */ + public function getHorizontalBorderChar() + { + return $this->horizontalBorderChar; + } + + /** + * Sets vertical border character. + * + * @param string $verticalBorderChar + * + * @return $this + */ + public function setVerticalBorderChar($verticalBorderChar) + { + $this->verticalBorderChar = $verticalBorderChar; + + return $this; + } + + /** + * Gets vertical border character. + * + * @return string + */ + public function getVerticalBorderChar() + { + return $this->verticalBorderChar; + } + + /** + * Sets crossing character. + * + * @param string $crossingChar + * + * @return $this + */ + public function setCrossingChar($crossingChar) + { + $this->crossingChar = $crossingChar; + + return $this; + } + + /** + * Gets crossing character. + * + * @return string + */ + public function getCrossingChar() + { + return $this->crossingChar; + } + + /** + * Sets header cell format. + * + * @param string $cellHeaderFormat + * + * @return $this + */ + public function setCellHeaderFormat($cellHeaderFormat) + { + $this->cellHeaderFormat = $cellHeaderFormat; + + return $this; + } + + /** + * Gets header cell format. + * + * @return string + */ + public function getCellHeaderFormat() + { + return $this->cellHeaderFormat; + } + + /** + * Sets row cell format. + * + * @param string $cellRowFormat + * + * @return $this + */ + public function setCellRowFormat($cellRowFormat) + { + $this->cellRowFormat = $cellRowFormat; + + return $this; + } + + /** + * Gets row cell format. + * + * @return string + */ + public function getCellRowFormat() + { + return $this->cellRowFormat; + } + + /** + * Sets row cell content format. + * + * @param string $cellRowContentFormat + * + * @return $this + */ + public function setCellRowContentFormat($cellRowContentFormat) + { + $this->cellRowContentFormat = $cellRowContentFormat; + + return $this; + } + + /** + * Gets row cell content format. + * + * @return string + */ + public function getCellRowContentFormat() + { + return $this->cellRowContentFormat; + } + + /** + * Sets table border format. + * + * @param string $borderFormat + * + * @return $this + */ + public function setBorderFormat($borderFormat) + { + $this->borderFormat = $borderFormat; + + return $this; + } + + /** + * Gets table border format. + * + * @return string + */ + public function getBorderFormat() + { + return $this->borderFormat; + } + + /** + * Sets cell padding type. + * + * @param int $padType STR_PAD_* + * + * @return $this + */ + public function setPadType($padType) + { + if (!\in_array($padType, [STR_PAD_LEFT, STR_PAD_RIGHT, STR_PAD_BOTH], true)) { + throw new InvalidArgumentException('Invalid padding type. Expected one of (STR_PAD_LEFT, STR_PAD_RIGHT, STR_PAD_BOTH).'); + } + + $this->padType = $padType; + + return $this; + } + + /** + * Gets cell padding type. + * + * @return int + */ + public function getPadType() + { + return $this->padType; + } +} diff --git a/lib/symfony/console/Input/ArgvInput.php b/lib/symfony/console/Input/ArgvInput.php new file mode 100644 index 000000000..f604057ec --- /dev/null +++ b/lib/symfony/console/Input/ArgvInput.php @@ -0,0 +1,370 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +use Symfony\Component\Console\Exception\RuntimeException; + +/** + * ArgvInput represents an input coming from the CLI arguments. + * + * Usage: + * + * $input = new ArgvInput(); + * + * By default, the `$_SERVER['argv']` array is used for the input values. + * + * This can be overridden by explicitly passing the input values in the constructor: + * + * $input = new ArgvInput($_SERVER['argv']); + * + * If you pass it yourself, don't forget that the first element of the array + * is the name of the running application. + * + * When passing an argument to the constructor, be sure that it respects + * the same rules as the argv one. It's almost always better to use the + * `StringInput` when you want to provide your own input. + * + * @author Fabien Potencier + * + * @see http://www.gnu.org/software/libc/manual/html_node/Argument-Syntax.html + * @see http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap12.html#tag_12_02 + */ +class ArgvInput extends Input +{ + private $tokens; + private $parsed; + + /** + * @param array|null $argv An array of parameters from the CLI (in the argv format) + * @param InputDefinition|null $definition A InputDefinition instance + */ + public function __construct(array $argv = null, InputDefinition $definition = null) + { + if (null === $argv) { + $argv = $_SERVER['argv']; + } + + // strip the application name + array_shift($argv); + + $this->tokens = $argv; + + parent::__construct($definition); + } + + protected function setTokens(array $tokens) + { + $this->tokens = $tokens; + } + + /** + * {@inheritdoc} + */ + protected function parse() + { + $parseOptions = true; + $this->parsed = $this->tokens; + while (null !== $token = array_shift($this->parsed)) { + if ($parseOptions && '' == $token) { + $this->parseArgument($token); + } elseif ($parseOptions && '--' == $token) { + $parseOptions = false; + } elseif ($parseOptions && 0 === strpos($token, '--')) { + $this->parseLongOption($token); + } elseif ($parseOptions && '-' === $token[0] && '-' !== $token) { + $this->parseShortOption($token); + } else { + $this->parseArgument($token); + } + } + } + + /** + * Parses a short option. + * + * @param string $token The current token + */ + private function parseShortOption($token) + { + $name = substr($token, 1); + + if (\strlen($name) > 1) { + if ($this->definition->hasShortcut($name[0]) && $this->definition->getOptionForShortcut($name[0])->acceptValue()) { + // an option with a value (with no space) + $this->addShortOption($name[0], substr($name, 1)); + } else { + $this->parseShortOptionSet($name); + } + } else { + $this->addShortOption($name, null); + } + } + + /** + * Parses a short option set. + * + * @param string $name The current token + * + * @throws RuntimeException When option given doesn't exist + */ + private function parseShortOptionSet($name) + { + $len = \strlen($name); + for ($i = 0; $i < $len; ++$i) { + if (!$this->definition->hasShortcut($name[$i])) { + $encoding = mb_detect_encoding($name, null, true); + throw new RuntimeException(sprintf('The "-%s" option does not exist.', false === $encoding ? $name[$i] : mb_substr($name, $i, 1, $encoding))); + } + + $option = $this->definition->getOptionForShortcut($name[$i]); + if ($option->acceptValue()) { + $this->addLongOption($option->getName(), $i === $len - 1 ? null : substr($name, $i + 1)); + + break; + } else { + $this->addLongOption($option->getName(), null); + } + } + } + + /** + * Parses a long option. + * + * @param string $token The current token + */ + private function parseLongOption($token) + { + $name = substr($token, 2); + + if (false !== $pos = strpos($name, '=')) { + if (0 === \strlen($value = substr($name, $pos + 1))) { + // if no value after "=" then substr() returns "" since php7 only, false before + // see http://php.net/manual/fr/migration70.incompatible.php#119151 + if (\PHP_VERSION_ID < 70000 && false === $value) { + $value = ''; + } + array_unshift($this->parsed, $value); + } + $this->addLongOption(substr($name, 0, $pos), $value); + } else { + $this->addLongOption($name, null); + } + } + + /** + * Parses an argument. + * + * @param string $token The current token + * + * @throws RuntimeException When too many arguments are given + */ + private function parseArgument($token) + { + $c = \count($this->arguments); + + // if input is expecting another argument, add it + if ($this->definition->hasArgument($c)) { + $arg = $this->definition->getArgument($c); + $this->arguments[$arg->getName()] = $arg->isArray() ? [$token] : $token; + + // if last argument isArray(), append token to last argument + } elseif ($this->definition->hasArgument($c - 1) && $this->definition->getArgument($c - 1)->isArray()) { + $arg = $this->definition->getArgument($c - 1); + $this->arguments[$arg->getName()][] = $token; + + // unexpected argument + } else { + $all = $this->definition->getArguments(); + if (\count($all)) { + throw new RuntimeException(sprintf('Too many arguments, expected arguments "%s".', implode('" "', array_keys($all)))); + } + + throw new RuntimeException(sprintf('No arguments expected, got "%s".', $token)); + } + } + + /** + * Adds a short option value. + * + * @param string $shortcut The short option key + * @param mixed $value The value for the option + * + * @throws RuntimeException When option given doesn't exist + */ + private function addShortOption($shortcut, $value) + { + if (!$this->definition->hasShortcut($shortcut)) { + throw new RuntimeException(sprintf('The "-%s" option does not exist.', $shortcut)); + } + + $this->addLongOption($this->definition->getOptionForShortcut($shortcut)->getName(), $value); + } + + /** + * Adds a long option value. + * + * @param string $name The long option key + * @param mixed $value The value for the option + * + * @throws RuntimeException When option given doesn't exist + */ + private function addLongOption($name, $value) + { + if (!$this->definition->hasOption($name)) { + throw new RuntimeException(sprintf('The "--%s" option does not exist.', $name)); + } + + $option = $this->definition->getOption($name); + + if (null !== $value && !$option->acceptValue()) { + throw new RuntimeException(sprintf('The "--%s" option does not accept a value.', $name)); + } + + if (\in_array($value, ['', null], true) && $option->acceptValue() && \count($this->parsed)) { + // if option accepts an optional or mandatory argument + // let's see if there is one provided + $next = array_shift($this->parsed); + if ((isset($next[0]) && '-' !== $next[0]) || \in_array($next, ['', null], true)) { + $value = $next; + } else { + array_unshift($this->parsed, $next); + } + } + + if (null === $value) { + if ($option->isValueRequired()) { + throw new RuntimeException(sprintf('The "--%s" option requires a value.', $name)); + } + + if (!$option->isArray() && !$option->isValueOptional()) { + $value = true; + } + } + + if ($option->isArray()) { + $this->options[$name][] = $value; + } else { + $this->options[$name] = $value; + } + } + + /** + * {@inheritdoc} + */ + public function getFirstArgument() + { + $isOption = false; + foreach ($this->tokens as $i => $token) { + if ($token && '-' === $token[0]) { + if (false !== strpos($token, '=') || !isset($this->tokens[$i + 1])) { + continue; + } + + // If it's a long option, consider that everything after "--" is the option name. + // Otherwise, use the last char (if it's a short option set, only the last one can take a value with space separator) + $name = '-' === $token[1] ? substr($token, 2) : substr($token, -1); + if (!isset($this->options[$name]) && !$this->definition->hasShortcut($name)) { + // noop + } elseif ((isset($this->options[$name]) || isset($this->options[$name = $this->definition->shortcutToName($name)])) && $this->tokens[$i + 1] === $this->options[$name]) { + $isOption = true; + } + + continue; + } + + if ($isOption) { + $isOption = false; + continue; + } + + return $token; + } + } + + /** + * {@inheritdoc} + */ + public function hasParameterOption($values, $onlyParams = false) + { + $values = (array) $values; + + foreach ($this->tokens as $token) { + if ($onlyParams && '--' === $token) { + return false; + } + foreach ($values as $value) { + // Options with values: + // For long options, test for '--option=' at beginning + // For short options, test for '-o' at beginning + $leading = 0 === strpos($value, '--') ? $value.'=' : $value; + if ($token === $value || '' !== $leading && 0 === strpos($token, $leading)) { + return true; + } + } + } + + return false; + } + + /** + * {@inheritdoc} + */ + public function getParameterOption($values, $default = false, $onlyParams = false) + { + $values = (array) $values; + $tokens = $this->tokens; + + while (0 < \count($tokens)) { + $token = array_shift($tokens); + if ($onlyParams && '--' === $token) { + return $default; + } + + foreach ($values as $value) { + if ($token === $value) { + return array_shift($tokens); + } + // Options with values: + // For long options, test for '--option=' at beginning + // For short options, test for '-o' at beginning + $leading = 0 === strpos($value, '--') ? $value.'=' : $value; + if ('' !== $leading && 0 === strpos($token, $leading)) { + return substr($token, \strlen($leading)); + } + } + } + + return $default; + } + + /** + * Returns a stringified representation of the args passed to the command. + * + * @return string + */ + public function __toString() + { + $tokens = array_map(function ($token) { + if (preg_match('{^(-[^=]+=)(.+)}', $token, $match)) { + return $match[1].$this->escapeToken($match[2]); + } + + if ($token && '-' !== $token[0]) { + return $this->escapeToken($token); + } + + return $token; + }, $this->tokens); + + return implode(' ', $tokens); + } +} diff --git a/lib/symfony/console/Input/ArrayInput.php b/lib/symfony/console/Input/ArrayInput.php new file mode 100644 index 000000000..44c2f0d5c --- /dev/null +++ b/lib/symfony/console/Input/ArrayInput.php @@ -0,0 +1,206 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\InvalidOptionException; + +/** + * ArrayInput represents an input provided as an array. + * + * Usage: + * + * $input = new ArrayInput(['command' => 'foo:bar', 'foo' => 'bar', '--bar' => 'foobar']); + * + * @author Fabien Potencier + */ +class ArrayInput extends Input +{ + private $parameters; + + public function __construct(array $parameters, InputDefinition $definition = null) + { + $this->parameters = $parameters; + + parent::__construct($definition); + } + + /** + * {@inheritdoc} + */ + public function getFirstArgument() + { + foreach ($this->parameters as $key => $value) { + if ($key && '-' === $key[0]) { + continue; + } + + return $value; + } + } + + /** + * {@inheritdoc} + */ + public function hasParameterOption($values, $onlyParams = false) + { + $values = (array) $values; + + foreach ($this->parameters as $k => $v) { + if (!\is_int($k)) { + $v = $k; + } + + if ($onlyParams && '--' === $v) { + return false; + } + + if (\in_array($v, $values)) { + return true; + } + } + + return false; + } + + /** + * {@inheritdoc} + */ + public function getParameterOption($values, $default = false, $onlyParams = false) + { + $values = (array) $values; + + foreach ($this->parameters as $k => $v) { + if ($onlyParams && ('--' === $k || (\is_int($k) && '--' === $v))) { + return $default; + } + + if (\is_int($k)) { + if (\in_array($v, $values)) { + return true; + } + } elseif (\in_array($k, $values)) { + return $v; + } + } + + return $default; + } + + /** + * Returns a stringified representation of the args passed to the command. + * + * @return string + */ + public function __toString() + { + $params = []; + foreach ($this->parameters as $param => $val) { + if ($param && '-' === $param[0]) { + if (\is_array($val)) { + foreach ($val as $v) { + $params[] = $param.('' != $v ? '='.$this->escapeToken($v) : ''); + } + } else { + $params[] = $param.('' != $val ? '='.$this->escapeToken($val) : ''); + } + } else { + $params[] = \is_array($val) ? implode(' ', array_map([$this, 'escapeToken'], $val)) : $this->escapeToken($val); + } + } + + return implode(' ', $params); + } + + /** + * {@inheritdoc} + */ + protected function parse() + { + foreach ($this->parameters as $key => $value) { + if ('--' === $key) { + return; + } + if (0 === strpos($key, '--')) { + $this->addLongOption(substr($key, 2), $value); + } elseif ('-' === $key[0]) { + $this->addShortOption(substr($key, 1), $value); + } else { + $this->addArgument($key, $value); + } + } + } + + /** + * Adds a short option value. + * + * @param string $shortcut The short option key + * @param mixed $value The value for the option + * + * @throws InvalidOptionException When option given doesn't exist + */ + private function addShortOption($shortcut, $value) + { + if (!$this->definition->hasShortcut($shortcut)) { + throw new InvalidOptionException(sprintf('The "-%s" option does not exist.', $shortcut)); + } + + $this->addLongOption($this->definition->getOptionForShortcut($shortcut)->getName(), $value); + } + + /** + * Adds a long option value. + * + * @param string $name The long option key + * @param mixed $value The value for the option + * + * @throws InvalidOptionException When option given doesn't exist + * @throws InvalidOptionException When a required value is missing + */ + private function addLongOption($name, $value) + { + if (!$this->definition->hasOption($name)) { + throw new InvalidOptionException(sprintf('The "--%s" option does not exist.', $name)); + } + + $option = $this->definition->getOption($name); + + if (null === $value) { + if ($option->isValueRequired()) { + throw new InvalidOptionException(sprintf('The "--%s" option requires a value.', $name)); + } + + if (!$option->isValueOptional()) { + $value = true; + } + } + + $this->options[$name] = $value; + } + + /** + * Adds an argument value. + * + * @param string $name The argument name + * @param mixed $value The value for the argument + * + * @throws InvalidArgumentException When argument given doesn't exist + */ + private function addArgument($name, $value) + { + if (!$this->definition->hasArgument($name)) { + throw new InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name)); + } + + $this->arguments[$name] = $value; + } +} diff --git a/lib/symfony/console/Input/Input.php b/lib/symfony/console/Input/Input.php new file mode 100644 index 000000000..c1220316d --- /dev/null +++ b/lib/symfony/console/Input/Input.php @@ -0,0 +1,203 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\RuntimeException; + +/** + * Input is the base class for all concrete Input classes. + * + * Three concrete classes are provided by default: + * + * * `ArgvInput`: The input comes from the CLI arguments (argv) + * * `StringInput`: The input is provided as a string + * * `ArrayInput`: The input is provided as an array + * + * @author Fabien Potencier + */ +abstract class Input implements InputInterface, StreamableInputInterface +{ + protected $definition; + protected $stream; + protected $options = []; + protected $arguments = []; + protected $interactive = true; + + public function __construct(InputDefinition $definition = null) + { + if (null === $definition) { + $this->definition = new InputDefinition(); + } else { + $this->bind($definition); + $this->validate(); + } + } + + /** + * {@inheritdoc} + */ + public function bind(InputDefinition $definition) + { + $this->arguments = []; + $this->options = []; + $this->definition = $definition; + + $this->parse(); + } + + /** + * Processes command line arguments. + */ + abstract protected function parse(); + + /** + * {@inheritdoc} + */ + public function validate() + { + $definition = $this->definition; + $givenArguments = $this->arguments; + + $missingArguments = array_filter(array_keys($definition->getArguments()), function ($argument) use ($definition, $givenArguments) { + return !\array_key_exists($argument, $givenArguments) && $definition->getArgument($argument)->isRequired(); + }); + + if (\count($missingArguments) > 0) { + throw new RuntimeException(sprintf('Not enough arguments (missing: "%s").', implode(', ', $missingArguments))); + } + } + + /** + * {@inheritdoc} + */ + public function isInteractive() + { + return $this->interactive; + } + + /** + * {@inheritdoc} + */ + public function setInteractive($interactive) + { + $this->interactive = (bool) $interactive; + } + + /** + * {@inheritdoc} + */ + public function getArguments() + { + return array_merge($this->definition->getArgumentDefaults(), $this->arguments); + } + + /** + * {@inheritdoc} + */ + public function getArgument($name) + { + if (!$this->definition->hasArgument($name)) { + throw new InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name)); + } + + return isset($this->arguments[$name]) ? $this->arguments[$name] : $this->definition->getArgument($name)->getDefault(); + } + + /** + * {@inheritdoc} + */ + public function setArgument($name, $value) + { + if (!$this->definition->hasArgument($name)) { + throw new InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name)); + } + + $this->arguments[$name] = $value; + } + + /** + * {@inheritdoc} + */ + public function hasArgument($name) + { + return $this->definition->hasArgument($name); + } + + /** + * {@inheritdoc} + */ + public function getOptions() + { + return array_merge($this->definition->getOptionDefaults(), $this->options); + } + + /** + * {@inheritdoc} + */ + public function getOption($name) + { + if (!$this->definition->hasOption($name)) { + throw new InvalidArgumentException(sprintf('The "%s" option does not exist.', $name)); + } + + return \array_key_exists($name, $this->options) ? $this->options[$name] : $this->definition->getOption($name)->getDefault(); + } + + /** + * {@inheritdoc} + */ + public function setOption($name, $value) + { + if (!$this->definition->hasOption($name)) { + throw new InvalidArgumentException(sprintf('The "%s" option does not exist.', $name)); + } + + $this->options[$name] = $value; + } + + /** + * {@inheritdoc} + */ + public function hasOption($name) + { + return $this->definition->hasOption($name); + } + + /** + * Escapes a token through escapeshellarg if it contains unsafe chars. + * + * @param string $token + * + * @return string + */ + public function escapeToken($token) + { + return preg_match('{^[\w-]+$}', $token) ? $token : escapeshellarg($token); + } + + /** + * {@inheritdoc} + */ + public function setStream($stream) + { + $this->stream = $stream; + } + + /** + * {@inheritdoc} + */ + public function getStream() + { + return $this->stream; + } +} diff --git a/lib/symfony/console/Input/InputArgument.php b/lib/symfony/console/Input/InputArgument.php new file mode 100644 index 000000000..4c2b6a66c --- /dev/null +++ b/lib/symfony/console/Input/InputArgument.php @@ -0,0 +1,129 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\LogicException; + +/** + * Represents a command line argument. + * + * @author Fabien Potencier + */ +class InputArgument +{ + const REQUIRED = 1; + const OPTIONAL = 2; + const IS_ARRAY = 4; + + private $name; + private $mode; + private $default; + private $description; + + /** + * @param string $name The argument name + * @param int|null $mode The argument mode: self::REQUIRED or self::OPTIONAL + * @param string $description A description text + * @param string|string[]|null $default The default value (for self::OPTIONAL mode only) + * + * @throws InvalidArgumentException When argument mode is not valid + */ + public function __construct($name, $mode = null, $description = '', $default = null) + { + if (null === $mode) { + $mode = self::OPTIONAL; + } elseif (!\is_int($mode) || $mode > 7 || $mode < 1) { + throw new InvalidArgumentException(sprintf('Argument mode "%s" is not valid.', $mode)); + } + + $this->name = $name; + $this->mode = $mode; + $this->description = $description; + + $this->setDefault($default); + } + + /** + * Returns the argument name. + * + * @return string The argument name + */ + public function getName() + { + return $this->name; + } + + /** + * Returns true if the argument is required. + * + * @return bool true if parameter mode is self::REQUIRED, false otherwise + */ + public function isRequired() + { + return self::REQUIRED === (self::REQUIRED & $this->mode); + } + + /** + * Returns true if the argument can take multiple values. + * + * @return bool true if mode is self::IS_ARRAY, false otherwise + */ + public function isArray() + { + return self::IS_ARRAY === (self::IS_ARRAY & $this->mode); + } + + /** + * Sets the default value. + * + * @param string|string[]|null $default The default value + * + * @throws LogicException When incorrect default value is given + */ + public function setDefault($default = null) + { + if (self::REQUIRED === $this->mode && null !== $default) { + throw new LogicException('Cannot set a default value except for InputArgument::OPTIONAL mode.'); + } + + if ($this->isArray()) { + if (null === $default) { + $default = []; + } elseif (!\is_array($default)) { + throw new LogicException('A default value for an array argument must be an array.'); + } + } + + $this->default = $default; + } + + /** + * Returns the default value. + * + * @return string|string[]|null The default value + */ + public function getDefault() + { + return $this->default; + } + + /** + * Returns the description text. + * + * @return string The description text + */ + public function getDescription() + { + return $this->description; + } +} diff --git a/lib/symfony/console/Input/InputAwareInterface.php b/lib/symfony/console/Input/InputAwareInterface.php new file mode 100644 index 000000000..5a288de5d --- /dev/null +++ b/lib/symfony/console/Input/InputAwareInterface.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +/** + * InputAwareInterface should be implemented by classes that depends on the + * Console Input. + * + * @author Wouter J + */ +interface InputAwareInterface +{ + /** + * Sets the Console Input. + */ + public function setInput(InputInterface $input); +} diff --git a/lib/symfony/console/Input/InputDefinition.php b/lib/symfony/console/Input/InputDefinition.php new file mode 100644 index 000000000..53f41d73f --- /dev/null +++ b/lib/symfony/console/Input/InputDefinition.php @@ -0,0 +1,404 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\LogicException; + +/** + * A InputDefinition represents a set of valid command line arguments and options. + * + * Usage: + * + * $definition = new InputDefinition([ + * new InputArgument('name', InputArgument::REQUIRED), + * new InputOption('foo', 'f', InputOption::VALUE_REQUIRED), + * ]); + * + * @author Fabien Potencier + */ +class InputDefinition +{ + private $arguments; + private $requiredCount; + private $hasAnArrayArgument = false; + private $hasOptional; + private $options; + private $shortcuts; + + /** + * @param array $definition An array of InputArgument and InputOption instance + */ + public function __construct(array $definition = []) + { + $this->setDefinition($definition); + } + + /** + * Sets the definition of the input. + */ + public function setDefinition(array $definition) + { + $arguments = []; + $options = []; + foreach ($definition as $item) { + if ($item instanceof InputOption) { + $options[] = $item; + } else { + $arguments[] = $item; + } + } + + $this->setArguments($arguments); + $this->setOptions($options); + } + + /** + * Sets the InputArgument objects. + * + * @param InputArgument[] $arguments An array of InputArgument objects + */ + public function setArguments($arguments = []) + { + $this->arguments = []; + $this->requiredCount = 0; + $this->hasOptional = false; + $this->hasAnArrayArgument = false; + $this->addArguments($arguments); + } + + /** + * Adds an array of InputArgument objects. + * + * @param InputArgument[] $arguments An array of InputArgument objects + */ + public function addArguments($arguments = []) + { + if (null !== $arguments) { + foreach ($arguments as $argument) { + $this->addArgument($argument); + } + } + } + + /** + * @throws LogicException When incorrect argument is given + */ + public function addArgument(InputArgument $argument) + { + if (isset($this->arguments[$argument->getName()])) { + throw new LogicException(sprintf('An argument with name "%s" already exists.', $argument->getName())); + } + + if ($this->hasAnArrayArgument) { + throw new LogicException('Cannot add an argument after an array argument.'); + } + + if ($argument->isRequired() && $this->hasOptional) { + throw new LogicException('Cannot add a required argument after an optional one.'); + } + + if ($argument->isArray()) { + $this->hasAnArrayArgument = true; + } + + if ($argument->isRequired()) { + ++$this->requiredCount; + } else { + $this->hasOptional = true; + } + + $this->arguments[$argument->getName()] = $argument; + } + + /** + * Returns an InputArgument by name or by position. + * + * @param string|int $name The InputArgument name or position + * + * @return InputArgument An InputArgument object + * + * @throws InvalidArgumentException When argument given doesn't exist + */ + public function getArgument($name) + { + if (!$this->hasArgument($name)) { + throw new InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name)); + } + + $arguments = \is_int($name) ? array_values($this->arguments) : $this->arguments; + + return $arguments[$name]; + } + + /** + * Returns true if an InputArgument object exists by name or position. + * + * @param string|int $name The InputArgument name or position + * + * @return bool true if the InputArgument object exists, false otherwise + */ + public function hasArgument($name) + { + $arguments = \is_int($name) ? array_values($this->arguments) : $this->arguments; + + return isset($arguments[$name]); + } + + /** + * Gets the array of InputArgument objects. + * + * @return InputArgument[] An array of InputArgument objects + */ + public function getArguments() + { + return $this->arguments; + } + + /** + * Returns the number of InputArguments. + * + * @return int The number of InputArguments + */ + public function getArgumentCount() + { + return $this->hasAnArrayArgument ? PHP_INT_MAX : \count($this->arguments); + } + + /** + * Returns the number of required InputArguments. + * + * @return int The number of required InputArguments + */ + public function getArgumentRequiredCount() + { + return $this->requiredCount; + } + + /** + * Gets the default values. + * + * @return array An array of default values + */ + public function getArgumentDefaults() + { + $values = []; + foreach ($this->arguments as $argument) { + $values[$argument->getName()] = $argument->getDefault(); + } + + return $values; + } + + /** + * Sets the InputOption objects. + * + * @param InputOption[] $options An array of InputOption objects + */ + public function setOptions($options = []) + { + $this->options = []; + $this->shortcuts = []; + $this->addOptions($options); + } + + /** + * Adds an array of InputOption objects. + * + * @param InputOption[] $options An array of InputOption objects + */ + public function addOptions($options = []) + { + foreach ($options as $option) { + $this->addOption($option); + } + } + + /** + * @throws LogicException When option given already exist + */ + public function addOption(InputOption $option) + { + if (isset($this->options[$option->getName()]) && !$option->equals($this->options[$option->getName()])) { + throw new LogicException(sprintf('An option named "%s" already exists.', $option->getName())); + } + + if ($option->getShortcut()) { + foreach (explode('|', $option->getShortcut()) as $shortcut) { + if (isset($this->shortcuts[$shortcut]) && !$option->equals($this->options[$this->shortcuts[$shortcut]])) { + throw new LogicException(sprintf('An option with shortcut "%s" already exists.', $shortcut)); + } + } + } + + $this->options[$option->getName()] = $option; + if ($option->getShortcut()) { + foreach (explode('|', $option->getShortcut()) as $shortcut) { + $this->shortcuts[$shortcut] = $option->getName(); + } + } + } + + /** + * Returns an InputOption by name. + * + * @param string $name The InputOption name + * + * @return InputOption A InputOption object + * + * @throws InvalidArgumentException When option given doesn't exist + */ + public function getOption($name) + { + if (!$this->hasOption($name)) { + throw new InvalidArgumentException(sprintf('The "--%s" option does not exist.', $name)); + } + + return $this->options[$name]; + } + + /** + * Returns true if an InputOption object exists by name. + * + * This method can't be used to check if the user included the option when + * executing the command (use getOption() instead). + * + * @param string $name The InputOption name + * + * @return bool true if the InputOption object exists, false otherwise + */ + public function hasOption($name) + { + return isset($this->options[$name]); + } + + /** + * Gets the array of InputOption objects. + * + * @return InputOption[] An array of InputOption objects + */ + public function getOptions() + { + return $this->options; + } + + /** + * Returns true if an InputOption object exists by shortcut. + * + * @param string $name The InputOption shortcut + * + * @return bool true if the InputOption object exists, false otherwise + */ + public function hasShortcut($name) + { + return isset($this->shortcuts[$name]); + } + + /** + * Gets an InputOption by shortcut. + * + * @param string $shortcut The Shortcut name + * + * @return InputOption An InputOption object + */ + public function getOptionForShortcut($shortcut) + { + return $this->getOption($this->shortcutToName($shortcut)); + } + + /** + * Gets an array of default values. + * + * @return array An array of all default values + */ + public function getOptionDefaults() + { + $values = []; + foreach ($this->options as $option) { + $values[$option->getName()] = $option->getDefault(); + } + + return $values; + } + + /** + * Returns the InputOption name given a shortcut. + * + * @param string $shortcut The shortcut + * + * @return string The InputOption name + * + * @throws InvalidArgumentException When option given does not exist + * + * @internal + */ + public function shortcutToName($shortcut) + { + if (!isset($this->shortcuts[$shortcut])) { + throw new InvalidArgumentException(sprintf('The "-%s" option does not exist.', $shortcut)); + } + + return $this->shortcuts[$shortcut]; + } + + /** + * Gets the synopsis. + * + * @param bool $short Whether to return the short version (with options folded) or not + * + * @return string The synopsis + */ + public function getSynopsis($short = false) + { + $elements = []; + + if ($short && $this->getOptions()) { + $elements[] = '[options]'; + } elseif (!$short) { + foreach ($this->getOptions() as $option) { + $value = ''; + if ($option->acceptValue()) { + $value = sprintf( + ' %s%s%s', + $option->isValueOptional() ? '[' : '', + strtoupper($option->getName()), + $option->isValueOptional() ? ']' : '' + ); + } + + $shortcut = $option->getShortcut() ? sprintf('-%s|', $option->getShortcut()) : ''; + $elements[] = sprintf('[%s--%s%s]', $shortcut, $option->getName(), $value); + } + } + + if (\count($elements) && $this->getArguments()) { + $elements[] = '[--]'; + } + + foreach ($this->getArguments() as $argument) { + $element = '<'.$argument->getName().'>'; + if (!$argument->isRequired()) { + $element = '['.$element.']'; + } elseif ($argument->isArray()) { + $element .= ' ('.$element.')'; + } + + if ($argument->isArray()) { + $element .= '...'; + } + + $elements[] = $element; + } + + return implode(' ', $elements); + } +} diff --git a/lib/symfony/console/Input/InputInterface.php b/lib/symfony/console/Input/InputInterface.php new file mode 100644 index 000000000..b9bcf3bbc --- /dev/null +++ b/lib/symfony/console/Input/InputInterface.php @@ -0,0 +1,163 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\RuntimeException; + +/** + * InputInterface is the interface implemented by all input classes. + * + * @author Fabien Potencier + */ +interface InputInterface +{ + /** + * Returns the first argument from the raw parameters (not parsed). + * + * @return string|null The value of the first argument or null otherwise + */ + public function getFirstArgument(); + + /** + * Returns true if the raw parameters (not parsed) contain a value. + * + * This method is to be used to introspect the input parameters + * before they have been validated. It must be used carefully. + * Does not necessarily return the correct result for short options + * when multiple flags are combined in the same option. + * + * @param string|array $values The values to look for in the raw parameters (can be an array) + * @param bool $onlyParams Only check real parameters, skip those following an end of options (--) signal + * + * @return bool true if the value is contained in the raw parameters + */ + public function hasParameterOption($values, $onlyParams = false); + + /** + * Returns the value of a raw option (not parsed). + * + * This method is to be used to introspect the input parameters + * before they have been validated. It must be used carefully. + * Does not necessarily return the correct result for short options + * when multiple flags are combined in the same option. + * + * @param string|array $values The value(s) to look for in the raw parameters (can be an array) + * @param mixed $default The default value to return if no result is found + * @param bool $onlyParams Only check real parameters, skip those following an end of options (--) signal + * + * @return mixed The option value + */ + public function getParameterOption($values, $default = false, $onlyParams = false); + + /** + * Binds the current Input instance with the given arguments and options. + * + * @throws RuntimeException + */ + public function bind(InputDefinition $definition); + + /** + * Validates the input. + * + * @throws RuntimeException When not enough arguments are given + */ + public function validate(); + + /** + * Returns all the given arguments merged with the default values. + * + * @return array + */ + public function getArguments(); + + /** + * Returns the argument value for a given argument name. + * + * @param string $name The argument name + * + * @return string|string[]|null The argument value + * + * @throws InvalidArgumentException When argument given doesn't exist + */ + public function getArgument($name); + + /** + * Sets an argument value by name. + * + * @param string $name The argument name + * @param string|string[]|null $value The argument value + * + * @throws InvalidArgumentException When argument given doesn't exist + */ + public function setArgument($name, $value); + + /** + * Returns true if an InputArgument object exists by name or position. + * + * @param string|int $name The InputArgument name or position + * + * @return bool true if the InputArgument object exists, false otherwise + */ + public function hasArgument($name); + + /** + * Returns all the given options merged with the default values. + * + * @return array + */ + public function getOptions(); + + /** + * Returns the option value for a given option name. + * + * @param string $name The option name + * + * @return string|string[]|bool|null The option value + * + * @throws InvalidArgumentException When option given doesn't exist + */ + public function getOption($name); + + /** + * Sets an option value by name. + * + * @param string $name The option name + * @param string|string[]|bool|null $value The option value + * + * @throws InvalidArgumentException When option given doesn't exist + */ + public function setOption($name, $value); + + /** + * Returns true if an InputOption object exists by name. + * + * @param string $name The InputOption name + * + * @return bool true if the InputOption object exists, false otherwise + */ + public function hasOption($name); + + /** + * Is this input means interactive? + * + * @return bool + */ + public function isInteractive(); + + /** + * Sets the input interactivity. + * + * @param bool $interactive If the input should be interactive + */ + public function setInteractive($interactive); +} diff --git a/lib/symfony/console/Input/InputOption.php b/lib/symfony/console/Input/InputOption.php new file mode 100644 index 000000000..b0bd28f51 --- /dev/null +++ b/lib/symfony/console/Input/InputOption.php @@ -0,0 +1,208 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\LogicException; + +/** + * Represents a command line option. + * + * @author Fabien Potencier + */ +class InputOption +{ + const VALUE_NONE = 1; + const VALUE_REQUIRED = 2; + const VALUE_OPTIONAL = 4; + const VALUE_IS_ARRAY = 8; + + private $name; + private $shortcut; + private $mode; + private $default; + private $description; + + /** + * @param string $name The option name + * @param string|array|null $shortcut The shortcuts, can be null, a string of shortcuts delimited by | or an array of shortcuts + * @param int|null $mode The option mode: One of the VALUE_* constants + * @param string $description A description text + * @param string|string[]|int|bool|null $default The default value (must be null for self::VALUE_NONE) + * + * @throws InvalidArgumentException If option mode is invalid or incompatible + */ + public function __construct($name, $shortcut = null, $mode = null, $description = '', $default = null) + { + if (0 === strpos($name, '--')) { + $name = substr($name, 2); + } + + if (empty($name)) { + throw new InvalidArgumentException('An option name cannot be empty.'); + } + + if (empty($shortcut)) { + $shortcut = null; + } + + if (null !== $shortcut) { + if (\is_array($shortcut)) { + $shortcut = implode('|', $shortcut); + } + $shortcuts = preg_split('{(\|)-?}', ltrim($shortcut, '-')); + $shortcuts = array_filter($shortcuts); + $shortcut = implode('|', $shortcuts); + + if (empty($shortcut)) { + throw new InvalidArgumentException('An option shortcut cannot be empty.'); + } + } + + if (null === $mode) { + $mode = self::VALUE_NONE; + } elseif (!\is_int($mode) || $mode > 15 || $mode < 1) { + throw new InvalidArgumentException(sprintf('Option mode "%s" is not valid.', $mode)); + } + + $this->name = $name; + $this->shortcut = $shortcut; + $this->mode = $mode; + $this->description = $description; + + if ($this->isArray() && !$this->acceptValue()) { + throw new InvalidArgumentException('Impossible to have an option mode VALUE_IS_ARRAY if the option does not accept a value.'); + } + + $this->setDefault($default); + } + + /** + * Returns the option shortcut. + * + * @return string|null The shortcut + */ + public function getShortcut() + { + return $this->shortcut; + } + + /** + * Returns the option name. + * + * @return string The name + */ + public function getName() + { + return $this->name; + } + + /** + * Returns true if the option accepts a value. + * + * @return bool true if value mode is not self::VALUE_NONE, false otherwise + */ + public function acceptValue() + { + return $this->isValueRequired() || $this->isValueOptional(); + } + + /** + * Returns true if the option requires a value. + * + * @return bool true if value mode is self::VALUE_REQUIRED, false otherwise + */ + public function isValueRequired() + { + return self::VALUE_REQUIRED === (self::VALUE_REQUIRED & $this->mode); + } + + /** + * Returns true if the option takes an optional value. + * + * @return bool true if value mode is self::VALUE_OPTIONAL, false otherwise + */ + public function isValueOptional() + { + return self::VALUE_OPTIONAL === (self::VALUE_OPTIONAL & $this->mode); + } + + /** + * Returns true if the option can take multiple values. + * + * @return bool true if mode is self::VALUE_IS_ARRAY, false otherwise + */ + public function isArray() + { + return self::VALUE_IS_ARRAY === (self::VALUE_IS_ARRAY & $this->mode); + } + + /** + * Sets the default value. + * + * @param string|string[]|int|bool|null $default The default value + * + * @throws LogicException When incorrect default value is given + */ + public function setDefault($default = null) + { + if (self::VALUE_NONE === (self::VALUE_NONE & $this->mode) && null !== $default) { + throw new LogicException('Cannot set a default value when using InputOption::VALUE_NONE mode.'); + } + + if ($this->isArray()) { + if (null === $default) { + $default = []; + } elseif (!\is_array($default)) { + throw new LogicException('A default value for an array option must be an array.'); + } + } + + $this->default = $this->acceptValue() ? $default : false; + } + + /** + * Returns the default value. + * + * @return string|string[]|int|bool|null The default value + */ + public function getDefault() + { + return $this->default; + } + + /** + * Returns the description text. + * + * @return string The description text + */ + public function getDescription() + { + return $this->description; + } + + /** + * Checks whether the given option equals this one. + * + * @return bool + */ + public function equals(self $option) + { + return $option->getName() === $this->getName() + && $option->getShortcut() === $this->getShortcut() + && $option->getDefault() === $this->getDefault() + && $option->isArray() === $this->isArray() + && $option->isValueRequired() === $this->isValueRequired() + && $option->isValueOptional() === $this->isValueOptional() + ; + } +} diff --git a/lib/symfony/console/Input/StreamableInputInterface.php b/lib/symfony/console/Input/StreamableInputInterface.php new file mode 100644 index 000000000..d7e462f24 --- /dev/null +++ b/lib/symfony/console/Input/StreamableInputInterface.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +/** + * StreamableInputInterface is the interface implemented by all input classes + * that have an input stream. + * + * @author Robin Chalas + */ +interface StreamableInputInterface extends InputInterface +{ + /** + * Sets the input stream to read from when interacting with the user. + * + * This is mainly useful for testing purpose. + * + * @param resource $stream The input stream + */ + public function setStream($stream); + + /** + * Returns the input stream. + * + * @return resource|null + */ + public function getStream(); +} diff --git a/lib/symfony/console/Input/StringInput.php b/lib/symfony/console/Input/StringInput.php new file mode 100644 index 000000000..32d270faf --- /dev/null +++ b/lib/symfony/console/Input/StringInput.php @@ -0,0 +1,72 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * StringInput represents an input provided as a string. + * + * Usage: + * + * $input = new StringInput('foo --bar="foobar"'); + * + * @author Fabien Potencier + */ +class StringInput extends ArgvInput +{ + const REGEX_STRING = '([^\s]+?)(?:\s|(?setTokens($this->tokenize($input)); + } + + /** + * Tokenizes a string. + * + * @param string $input The input to tokenize + * + * @return array An array of tokens + * + * @throws InvalidArgumentException When unable to parse input (should never happen) + */ + private function tokenize($input) + { + $tokens = []; + $length = \strlen($input); + $cursor = 0; + while ($cursor < $length) { + if (preg_match('/\s+/A', $input, $match, null, $cursor)) { + } elseif (preg_match('/([^="\'\s]+?)(=?)('.self::REGEX_QUOTED_STRING.'+)/A', $input, $match, null, $cursor)) { + $tokens[] = $match[1].$match[2].stripcslashes(str_replace(['"\'', '\'"', '\'\'', '""'], '', substr($match[3], 1, \strlen($match[3]) - 2))); + } elseif (preg_match('/'.self::REGEX_QUOTED_STRING.'/A', $input, $match, null, $cursor)) { + $tokens[] = stripcslashes(substr($match[0], 1, \strlen($match[0]) - 2)); + } elseif (preg_match('/'.self::REGEX_STRING.'/A', $input, $match, null, $cursor)) { + $tokens[] = stripcslashes($match[1]); + } else { + // should never happen + throw new InvalidArgumentException(sprintf('Unable to parse input near "... %s ..."', substr($input, $cursor, 10))); + } + + $cursor += \strlen($match[0]); + } + + return $tokens; + } +} diff --git a/lib/symfony/console/LICENSE b/lib/symfony/console/LICENSE new file mode 100644 index 000000000..a677f4376 --- /dev/null +++ b/lib/symfony/console/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2019 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. diff --git a/lib/symfony/console/Logger/ConsoleLogger.php b/lib/symfony/console/Logger/ConsoleLogger.php new file mode 100644 index 000000000..e531bb1ca --- /dev/null +++ b/lib/symfony/console/Logger/ConsoleLogger.php @@ -0,0 +1,129 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Logger; + +use Psr\Log\AbstractLogger; +use Psr\Log\InvalidArgumentException; +use Psr\Log\LogLevel; +use Symfony\Component\Console\Output\ConsoleOutputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * PSR-3 compliant console logger. + * + * @author Kévin Dunglas + * + * @see http://www.php-fig.org/psr/psr-3/ + */ +class ConsoleLogger extends AbstractLogger +{ + const INFO = 'info'; + const ERROR = 'error'; + + private $output; + private $verbosityLevelMap = [ + LogLevel::EMERGENCY => OutputInterface::VERBOSITY_NORMAL, + LogLevel::ALERT => OutputInterface::VERBOSITY_NORMAL, + LogLevel::CRITICAL => OutputInterface::VERBOSITY_NORMAL, + LogLevel::ERROR => OutputInterface::VERBOSITY_NORMAL, + LogLevel::WARNING => OutputInterface::VERBOSITY_NORMAL, + LogLevel::NOTICE => OutputInterface::VERBOSITY_VERBOSE, + LogLevel::INFO => OutputInterface::VERBOSITY_VERY_VERBOSE, + LogLevel::DEBUG => OutputInterface::VERBOSITY_DEBUG, + ]; + private $formatLevelMap = [ + LogLevel::EMERGENCY => self::ERROR, + LogLevel::ALERT => self::ERROR, + LogLevel::CRITICAL => self::ERROR, + LogLevel::ERROR => self::ERROR, + LogLevel::WARNING => self::INFO, + LogLevel::NOTICE => self::INFO, + LogLevel::INFO => self::INFO, + LogLevel::DEBUG => self::INFO, + ]; + private $errored = false; + + public function __construct(OutputInterface $output, array $verbosityLevelMap = [], array $formatLevelMap = []) + { + $this->output = $output; + $this->verbosityLevelMap = $verbosityLevelMap + $this->verbosityLevelMap; + $this->formatLevelMap = $formatLevelMap + $this->formatLevelMap; + } + + /** + * {@inheritdoc} + */ + public function log($level, $message, array $context = []) + { + if (!isset($this->verbosityLevelMap[$level])) { + throw new InvalidArgumentException(sprintf('The log level "%s" does not exist.', $level)); + } + + $output = $this->output; + + // Write to the error output if necessary and available + if (self::ERROR === $this->formatLevelMap[$level]) { + if ($this->output instanceof ConsoleOutputInterface) { + $output = $output->getErrorOutput(); + } + $this->errored = true; + } + + // the if condition check isn't necessary -- it's the same one that $output will do internally anyway. + // We only do it for efficiency here as the message formatting is relatively expensive. + if ($output->getVerbosity() >= $this->verbosityLevelMap[$level]) { + $output->writeln(sprintf('<%1$s>[%2$s] %3$s', $this->formatLevelMap[$level], $level, $this->interpolate($message, $context)), $this->verbosityLevelMap[$level]); + } + } + + /** + * Returns true when any messages have been logged at error levels. + * + * @return bool + */ + public function hasErrored() + { + return $this->errored; + } + + /** + * Interpolates context values into the message placeholders. + * + * @author PHP Framework Interoperability Group + * + * @param string $message + * @param array $context + * + * @return string + */ + private function interpolate($message, array $context) + { + if (false === strpos($message, '{')) { + return $message; + } + + $replacements = []; + foreach ($context as $key => $val) { + if (null === $val || is_scalar($val) || (\is_object($val) && method_exists($val, '__toString'))) { + $replacements["{{$key}}"] = $val; + } elseif ($val instanceof \DateTimeInterface) { + $replacements["{{$key}}"] = $val->format(\DateTime::RFC3339); + } elseif (\is_object($val)) { + $replacements["{{$key}}"] = '[object '.\get_class($val).']'; + } else { + $replacements["{{$key}}"] = '['.\gettype($val).']'; + } + } + + return strtr($message, $replacements); + } +} diff --git a/lib/symfony/console/Output/BufferedOutput.php b/lib/symfony/console/Output/BufferedOutput.php new file mode 100644 index 000000000..8afc8931e --- /dev/null +++ b/lib/symfony/console/Output/BufferedOutput.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Output; + +/** + * @author Jean-François Simon + */ +class BufferedOutput extends Output +{ + private $buffer = ''; + + /** + * Empties buffer and returns its content. + * + * @return string + */ + public function fetch() + { + $content = $this->buffer; + $this->buffer = ''; + + return $content; + } + + /** + * {@inheritdoc} + */ + protected function doWrite($message, $newline) + { + $this->buffer .= $message; + + if ($newline) { + $this->buffer .= PHP_EOL; + } + } +} diff --git a/lib/symfony/console/Output/ConsoleOutput.php b/lib/symfony/console/Output/ConsoleOutput.php new file mode 100644 index 000000000..61dc7cbdc --- /dev/null +++ b/lib/symfony/console/Output/ConsoleOutput.php @@ -0,0 +1,152 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Output; + +use Symfony\Component\Console\Formatter\OutputFormatterInterface; + +/** + * ConsoleOutput is the default class for all CLI output. It uses STDOUT and STDERR. + * + * This class is a convenient wrapper around `StreamOutput` for both STDOUT and STDERR. + * + * $output = new ConsoleOutput(); + * + * This is equivalent to: + * + * $output = new StreamOutput(fopen('php://stdout', 'w')); + * $stdErr = new StreamOutput(fopen('php://stderr', 'w')); + * + * @author Fabien Potencier + */ +class ConsoleOutput extends StreamOutput implements ConsoleOutputInterface +{ + private $stderr; + + /** + * @param int $verbosity The verbosity level (one of the VERBOSITY constants in OutputInterface) + * @param bool|null $decorated Whether to decorate messages (null for auto-guessing) + * @param OutputFormatterInterface|null $formatter Output formatter instance (null to use default OutputFormatter) + */ + public function __construct($verbosity = self::VERBOSITY_NORMAL, $decorated = null, OutputFormatterInterface $formatter = null) + { + parent::__construct($this->openOutputStream(), $verbosity, $decorated, $formatter); + + $actualDecorated = $this->isDecorated(); + $this->stderr = new StreamOutput($this->openErrorStream(), $verbosity, $decorated, $this->getFormatter()); + + if (null === $decorated) { + $this->setDecorated($actualDecorated && $this->stderr->isDecorated()); + } + } + + /** + * {@inheritdoc} + */ + public function setDecorated($decorated) + { + parent::setDecorated($decorated); + $this->stderr->setDecorated($decorated); + } + + /** + * {@inheritdoc} + */ + public function setFormatter(OutputFormatterInterface $formatter) + { + parent::setFormatter($formatter); + $this->stderr->setFormatter($formatter); + } + + /** + * {@inheritdoc} + */ + public function setVerbosity($level) + { + parent::setVerbosity($level); + $this->stderr->setVerbosity($level); + } + + /** + * {@inheritdoc} + */ + public function getErrorOutput() + { + return $this->stderr; + } + + /** + * {@inheritdoc} + */ + public function setErrorOutput(OutputInterface $error) + { + $this->stderr = $error; + } + + /** + * Returns true if current environment supports writing console output to + * STDOUT. + * + * @return bool + */ + protected function hasStdoutSupport() + { + return false === $this->isRunningOS400(); + } + + /** + * Returns true if current environment supports writing console output to + * STDERR. + * + * @return bool + */ + protected function hasStderrSupport() + { + return false === $this->isRunningOS400(); + } + + /** + * Checks if current executing environment is IBM iSeries (OS400), which + * doesn't properly convert character-encodings between ASCII to EBCDIC. + * + * @return bool + */ + private function isRunningOS400() + { + $checks = [ + \function_exists('php_uname') ? php_uname('s') : '', + getenv('OSTYPE'), + PHP_OS, + ]; + + return false !== stripos(implode(';', $checks), 'OS400'); + } + + /** + * @return resource + */ + private function openOutputStream() + { + if (!$this->hasStdoutSupport()) { + return fopen('php://output', 'w'); + } + + return @fopen('php://stdout', 'w') ?: fopen('php://output', 'w'); + } + + /** + * @return resource + */ + private function openErrorStream() + { + return fopen($this->hasStderrSupport() ? 'php://stderr' : 'php://output', 'w'); + } +} diff --git a/lib/symfony/console/Output/ConsoleOutputInterface.php b/lib/symfony/console/Output/ConsoleOutputInterface.php new file mode 100644 index 000000000..b44ea7e05 --- /dev/null +++ b/lib/symfony/console/Output/ConsoleOutputInterface.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Output; + +/** + * ConsoleOutputInterface is the interface implemented by ConsoleOutput class. + * This adds information about stderr output stream. + * + * @author Dariusz Górecki + */ +interface ConsoleOutputInterface extends OutputInterface +{ + /** + * Gets the OutputInterface for errors. + * + * @return OutputInterface + */ + public function getErrorOutput(); + + public function setErrorOutput(OutputInterface $error); +} diff --git a/lib/symfony/console/Output/NullOutput.php b/lib/symfony/console/Output/NullOutput.php new file mode 100644 index 000000000..218f285bf --- /dev/null +++ b/lib/symfony/console/Output/NullOutput.php @@ -0,0 +1,123 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Output; + +use Symfony\Component\Console\Formatter\OutputFormatter; +use Symfony\Component\Console\Formatter\OutputFormatterInterface; + +/** + * NullOutput suppresses all output. + * + * $output = new NullOutput(); + * + * @author Fabien Potencier + * @author Tobias Schultze + */ +class NullOutput implements OutputInterface +{ + /** + * {@inheritdoc} + */ + public function setFormatter(OutputFormatterInterface $formatter) + { + // do nothing + } + + /** + * {@inheritdoc} + */ + public function getFormatter() + { + // to comply with the interface we must return a OutputFormatterInterface + return new OutputFormatter(); + } + + /** + * {@inheritdoc} + */ + public function setDecorated($decorated) + { + // do nothing + } + + /** + * {@inheritdoc} + */ + public function isDecorated() + { + return false; + } + + /** + * {@inheritdoc} + */ + public function setVerbosity($level) + { + // do nothing + } + + /** + * {@inheritdoc} + */ + public function getVerbosity() + { + return self::VERBOSITY_QUIET; + } + + /** + * {@inheritdoc} + */ + public function isQuiet() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function isVerbose() + { + return false; + } + + /** + * {@inheritdoc} + */ + public function isVeryVerbose() + { + return false; + } + + /** + * {@inheritdoc} + */ + public function isDebug() + { + return false; + } + + /** + * {@inheritdoc} + */ + public function writeln($messages, $options = self::OUTPUT_NORMAL) + { + // do nothing + } + + /** + * {@inheritdoc} + */ + public function write($messages, $newline = false, $options = self::OUTPUT_NORMAL) + { + // do nothing + } +} diff --git a/lib/symfony/console/Output/Output.php b/lib/symfony/console/Output/Output.php new file mode 100644 index 000000000..c3856cc57 --- /dev/null +++ b/lib/symfony/console/Output/Output.php @@ -0,0 +1,175 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Output; + +use Symfony\Component\Console\Formatter\OutputFormatter; +use Symfony\Component\Console\Formatter\OutputFormatterInterface; + +/** + * Base class for output classes. + * + * There are five levels of verbosity: + * + * * normal: no option passed (normal output) + * * verbose: -v (more output) + * * very verbose: -vv (highly extended output) + * * debug: -vvv (all debug output) + * * quiet: -q (no output) + * + * @author Fabien Potencier + */ +abstract class Output implements OutputInterface +{ + private $verbosity; + private $formatter; + + /** + * @param int $verbosity The verbosity level (one of the VERBOSITY constants in OutputInterface) + * @param bool $decorated Whether to decorate messages + * @param OutputFormatterInterface|null $formatter Output formatter instance (null to use default OutputFormatter) + */ + public function __construct($verbosity = self::VERBOSITY_NORMAL, $decorated = false, OutputFormatterInterface $formatter = null) + { + $this->verbosity = null === $verbosity ? self::VERBOSITY_NORMAL : $verbosity; + $this->formatter = $formatter ?: new OutputFormatter(); + $this->formatter->setDecorated($decorated); + } + + /** + * {@inheritdoc} + */ + public function setFormatter(OutputFormatterInterface $formatter) + { + $this->formatter = $formatter; + } + + /** + * {@inheritdoc} + */ + public function getFormatter() + { + return $this->formatter; + } + + /** + * {@inheritdoc} + */ + public function setDecorated($decorated) + { + $this->formatter->setDecorated($decorated); + } + + /** + * {@inheritdoc} + */ + public function isDecorated() + { + return $this->formatter->isDecorated(); + } + + /** + * {@inheritdoc} + */ + public function setVerbosity($level) + { + $this->verbosity = (int) $level; + } + + /** + * {@inheritdoc} + */ + public function getVerbosity() + { + return $this->verbosity; + } + + /** + * {@inheritdoc} + */ + public function isQuiet() + { + return self::VERBOSITY_QUIET === $this->verbosity; + } + + /** + * {@inheritdoc} + */ + public function isVerbose() + { + return self::VERBOSITY_VERBOSE <= $this->verbosity; + } + + /** + * {@inheritdoc} + */ + public function isVeryVerbose() + { + return self::VERBOSITY_VERY_VERBOSE <= $this->verbosity; + } + + /** + * {@inheritdoc} + */ + public function isDebug() + { + return self::VERBOSITY_DEBUG <= $this->verbosity; + } + + /** + * {@inheritdoc} + */ + public function writeln($messages, $options = self::OUTPUT_NORMAL) + { + $this->write($messages, true, $options); + } + + /** + * {@inheritdoc} + */ + public function write($messages, $newline = false, $options = self::OUTPUT_NORMAL) + { + $messages = (array) $messages; + + $types = self::OUTPUT_NORMAL | self::OUTPUT_RAW | self::OUTPUT_PLAIN; + $type = $types & $options ?: self::OUTPUT_NORMAL; + + $verbosities = self::VERBOSITY_QUIET | self::VERBOSITY_NORMAL | self::VERBOSITY_VERBOSE | self::VERBOSITY_VERY_VERBOSE | self::VERBOSITY_DEBUG; + $verbosity = $verbosities & $options ?: self::VERBOSITY_NORMAL; + + if ($verbosity > $this->getVerbosity()) { + return; + } + + foreach ($messages as $message) { + switch ($type) { + case OutputInterface::OUTPUT_NORMAL: + $message = $this->formatter->format($message); + break; + case OutputInterface::OUTPUT_RAW: + break; + case OutputInterface::OUTPUT_PLAIN: + $message = strip_tags($this->formatter->format($message)); + break; + } + + $this->doWrite($message, $newline); + } + } + + /** + * Writes a message to the output. + * + * @param string $message A message to write to the output + * @param bool $newline Whether to add a newline or not + */ + abstract protected function doWrite($message, $newline); +} diff --git a/lib/symfony/console/Output/OutputInterface.php b/lib/symfony/console/Output/OutputInterface.php new file mode 100644 index 000000000..ad785a4ce --- /dev/null +++ b/lib/symfony/console/Output/OutputInterface.php @@ -0,0 +1,114 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Output; + +use Symfony\Component\Console\Formatter\OutputFormatterInterface; + +/** + * OutputInterface is the interface implemented by all Output classes. + * + * @author Fabien Potencier + */ +interface OutputInterface +{ + const VERBOSITY_QUIET = 16; + const VERBOSITY_NORMAL = 32; + const VERBOSITY_VERBOSE = 64; + const VERBOSITY_VERY_VERBOSE = 128; + const VERBOSITY_DEBUG = 256; + + const OUTPUT_NORMAL = 1; + const OUTPUT_RAW = 2; + const OUTPUT_PLAIN = 4; + + /** + * Writes a message to the output. + * + * @param string|array $messages The message as an array of strings or a single string + * @param bool $newline Whether to add a newline + * @param int $options A bitmask of options (one of the OUTPUT or VERBOSITY constants), 0 is considered the same as self::OUTPUT_NORMAL | self::VERBOSITY_NORMAL + */ + public function write($messages, $newline = false, $options = 0); + + /** + * Writes a message to the output and adds a newline at the end. + * + * @param string|array $messages The message as an array of strings or a single string + * @param int $options A bitmask of options (one of the OUTPUT or VERBOSITY constants), 0 is considered the same as self::OUTPUT_NORMAL | self::VERBOSITY_NORMAL + */ + public function writeln($messages, $options = 0); + + /** + * Sets the verbosity of the output. + * + * @param int $level The level of verbosity (one of the VERBOSITY constants) + */ + public function setVerbosity($level); + + /** + * Gets the current verbosity of the output. + * + * @return int The current level of verbosity (one of the VERBOSITY constants) + */ + public function getVerbosity(); + + /** + * Returns whether verbosity is quiet (-q). + * + * @return bool true if verbosity is set to VERBOSITY_QUIET, false otherwise + */ + public function isQuiet(); + + /** + * Returns whether verbosity is verbose (-v). + * + * @return bool true if verbosity is set to VERBOSITY_VERBOSE, false otherwise + */ + public function isVerbose(); + + /** + * Returns whether verbosity is very verbose (-vv). + * + * @return bool true if verbosity is set to VERBOSITY_VERY_VERBOSE, false otherwise + */ + public function isVeryVerbose(); + + /** + * Returns whether verbosity is debug (-vvv). + * + * @return bool true if verbosity is set to VERBOSITY_DEBUG, false otherwise + */ + public function isDebug(); + + /** + * Sets the decorated flag. + * + * @param bool $decorated Whether to decorate the messages + */ + public function setDecorated($decorated); + + /** + * Gets the decorated flag. + * + * @return bool true if the output will decorate messages, false otherwise + */ + public function isDecorated(); + + public function setFormatter(OutputFormatterInterface $formatter); + + /** + * Returns current output formatter instance. + * + * @return OutputFormatterInterface + */ + public function getFormatter(); +} diff --git a/lib/symfony/console/Output/StreamOutput.php b/lib/symfony/console/Output/StreamOutput.php new file mode 100644 index 000000000..7ff602763 --- /dev/null +++ b/lib/symfony/console/Output/StreamOutput.php @@ -0,0 +1,124 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Output; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\RuntimeException; +use Symfony\Component\Console\Formatter\OutputFormatterInterface; + +/** + * StreamOutput writes the output to a given stream. + * + * Usage: + * + * $output = new StreamOutput(fopen('php://stdout', 'w')); + * + * As `StreamOutput` can use any stream, you can also use a file: + * + * $output = new StreamOutput(fopen('/path/to/output.log', 'a', false)); + * + * @author Fabien Potencier + */ +class StreamOutput extends Output +{ + private $stream; + + /** + * @param resource $stream A stream resource + * @param int $verbosity The verbosity level (one of the VERBOSITY constants in OutputInterface) + * @param bool|null $decorated Whether to decorate messages (null for auto-guessing) + * @param OutputFormatterInterface|null $formatter Output formatter instance (null to use default OutputFormatter) + * + * @throws InvalidArgumentException When first argument is not a real stream + */ + public function __construct($stream, $verbosity = self::VERBOSITY_NORMAL, $decorated = null, OutputFormatterInterface $formatter = null) + { + if (!\is_resource($stream) || 'stream' !== get_resource_type($stream)) { + throw new InvalidArgumentException('The StreamOutput class needs a stream as its first argument.'); + } + + $this->stream = $stream; + + if (null === $decorated) { + $decorated = $this->hasColorSupport(); + } + + parent::__construct($verbosity, $decorated, $formatter); + } + + /** + * Gets the stream attached to this StreamOutput instance. + * + * @return resource A stream resource + */ + public function getStream() + { + return $this->stream; + } + + /** + * {@inheritdoc} + */ + protected function doWrite($message, $newline) + { + if ($newline) { + $message .= PHP_EOL; + } + + if (false === @fwrite($this->stream, $message)) { + // should never happen + throw new RuntimeException('Unable to write output.'); + } + + fflush($this->stream); + } + + /** + * Returns true if the stream supports colorization. + * + * Colorization is disabled if not supported by the stream: + * + * This is tricky on Windows, because Cygwin, Msys2 etc emulate pseudo + * terminals via named pipes, so we can only check the environment. + * + * Reference: Composer\XdebugHandler\Process::supportsColor + * https://github.com/composer/xdebug-handler + * + * @return bool true if the stream supports colorization, false otherwise + */ + protected function hasColorSupport() + { + if ('Hyper' === getenv('TERM_PROGRAM')) { + return true; + } + + if (\DIRECTORY_SEPARATOR === '\\') { + return (\function_exists('sapi_windows_vt100_support') + && @sapi_windows_vt100_support($this->stream)) + || false !== getenv('ANSICON') + || 'ON' === getenv('ConEmuANSI') + || 'xterm' === getenv('TERM'); + } + + if (\function_exists('stream_isatty')) { + return @stream_isatty($this->stream); + } + + if (\function_exists('posix_isatty')) { + return @posix_isatty($this->stream); + } + + $stat = @fstat($this->stream); + // Check if formatted mode is S_IFCHR + return $stat ? 0020000 === ($stat['mode'] & 0170000) : false; + } +} diff --git a/lib/symfony/console/Question/ChoiceQuestion.php b/lib/symfony/console/Question/ChoiceQuestion.php new file mode 100644 index 000000000..3dca16004 --- /dev/null +++ b/lib/symfony/console/Question/ChoiceQuestion.php @@ -0,0 +1,187 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Question; + +use Symfony\Component\Console\Exception\InvalidArgumentException; + +/** + * Represents a choice question. + * + * @author Fabien Potencier + */ +class ChoiceQuestion extends Question +{ + private $choices; + private $multiselect = false; + private $prompt = ' > '; + private $errorMessage = 'Value "%s" is invalid'; + + /** + * @param string $question The question to ask to the user + * @param array $choices The list of available choices + * @param mixed $default The default answer to return + */ + public function __construct($question, array $choices, $default = null) + { + if (!$choices) { + throw new \LogicException('Choice question must have at least 1 choice available.'); + } + + parent::__construct($question, $default); + + $this->choices = $choices; + $this->setValidator($this->getDefaultValidator()); + $this->setAutocompleterValues($choices); + } + + /** + * Returns available choices. + * + * @return array + */ + public function getChoices() + { + return $this->choices; + } + + /** + * Sets multiselect option. + * + * When multiselect is set to true, multiple choices can be answered. + * + * @param bool $multiselect + * + * @return $this + */ + public function setMultiselect($multiselect) + { + $this->multiselect = $multiselect; + $this->setValidator($this->getDefaultValidator()); + + return $this; + } + + /** + * Returns whether the choices are multiselect. + * + * @return bool + */ + public function isMultiselect() + { + return $this->multiselect; + } + + /** + * Gets the prompt for choices. + * + * @return string + */ + public function getPrompt() + { + return $this->prompt; + } + + /** + * Sets the prompt for choices. + * + * @param string $prompt + * + * @return $this + */ + public function setPrompt($prompt) + { + $this->prompt = $prompt; + + return $this; + } + + /** + * Sets the error message for invalid values. + * + * The error message has a string placeholder (%s) for the invalid value. + * + * @param string $errorMessage + * + * @return $this + */ + public function setErrorMessage($errorMessage) + { + $this->errorMessage = $errorMessage; + $this->setValidator($this->getDefaultValidator()); + + return $this; + } + + /** + * Returns the default answer validator. + * + * @return callable + */ + private function getDefaultValidator() + { + $choices = $this->choices; + $errorMessage = $this->errorMessage; + $multiselect = $this->multiselect; + $isAssoc = $this->isAssoc($choices); + + return function ($selected) use ($choices, $errorMessage, $multiselect, $isAssoc) { + if ($multiselect) { + // Check for a separated comma values + if (!preg_match('/^[^,]+(?:,[^,]+)*$/', $selected, $matches)) { + throw new InvalidArgumentException(sprintf($errorMessage, $selected)); + } + + $selectedChoices = array_map('trim', explode(',', $selected)); + } else { + $selectedChoices = [trim($selected)]; + } + + $multiselectChoices = []; + foreach ($selectedChoices as $value) { + $results = []; + foreach ($choices as $key => $choice) { + if ($choice === $value) { + $results[] = $key; + } + } + + if (\count($results) > 1) { + throw new InvalidArgumentException(sprintf('The provided answer is ambiguous. Value should be one of %s.', implode(' or ', $results))); + } + + $result = array_search($value, $choices); + + if (!$isAssoc) { + if (false !== $result) { + $result = $choices[$result]; + } elseif (isset($choices[$value])) { + $result = $choices[$value]; + } + } elseif (false === $result && isset($choices[$value])) { + $result = $value; + } + + if (false === $result) { + throw new InvalidArgumentException(sprintf($errorMessage, $value)); + } + + $multiselectChoices[] = (string) $result; + } + + if ($multiselect) { + return $multiselectChoices; + } + + return current($multiselectChoices); + }; + } +} diff --git a/lib/symfony/console/Question/ConfirmationQuestion.php b/lib/symfony/console/Question/ConfirmationQuestion.php new file mode 100644 index 000000000..d871fb8a7 --- /dev/null +++ b/lib/symfony/console/Question/ConfirmationQuestion.php @@ -0,0 +1,59 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Question; + +/** + * Represents a yes/no question. + * + * @author Fabien Potencier + */ +class ConfirmationQuestion extends Question +{ + private $trueAnswerRegex; + + /** + * @param string $question The question to ask to the user + * @param bool $default The default answer to return, true or false + * @param string $trueAnswerRegex A regex to match the "yes" answer + */ + public function __construct($question, $default = true, $trueAnswerRegex = '/^y/i') + { + parent::__construct($question, (bool) $default); + + $this->trueAnswerRegex = $trueAnswerRegex; + $this->setNormalizer($this->getDefaultNormalizer()); + } + + /** + * Returns the default answer normalizer. + * + * @return callable + */ + private function getDefaultNormalizer() + { + $default = $this->getDefault(); + $regex = $this->trueAnswerRegex; + + return function ($answer) use ($default, $regex) { + if (\is_bool($answer)) { + return $answer; + } + + $answerIsTrue = (bool) preg_match($regex, $answer); + if (false === $default) { + return $answer && $answerIsTrue; + } + + return '' === $answer || $answerIsTrue; + }; + } +} diff --git a/lib/symfony/console/Question/Question.php b/lib/symfony/console/Question/Question.php new file mode 100644 index 000000000..92b9b6936 --- /dev/null +++ b/lib/symfony/console/Question/Question.php @@ -0,0 +1,246 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Question; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Exception\LogicException; + +/** + * Represents a Question. + * + * @author Fabien Potencier + */ +class Question +{ + private $question; + private $attempts; + private $hidden = false; + private $hiddenFallback = true; + private $autocompleterValues; + private $validator; + private $default; + private $normalizer; + + /** + * @param string $question The question to ask to the user + * @param mixed $default The default answer to return if the user enters nothing + */ + public function __construct($question, $default = null) + { + $this->question = $question; + $this->default = $default; + } + + /** + * Returns the question. + * + * @return string + */ + public function getQuestion() + { + return $this->question; + } + + /** + * Returns the default answer. + * + * @return mixed + */ + public function getDefault() + { + return $this->default; + } + + /** + * Returns whether the user response must be hidden. + * + * @return bool + */ + public function isHidden() + { + return $this->hidden; + } + + /** + * Sets whether the user response must be hidden or not. + * + * @param bool $hidden + * + * @return $this + * + * @throws LogicException In case the autocompleter is also used + */ + public function setHidden($hidden) + { + if ($this->autocompleterValues) { + throw new LogicException('A hidden question cannot use the autocompleter.'); + } + + $this->hidden = (bool) $hidden; + + return $this; + } + + /** + * In case the response can not be hidden, whether to fallback on non-hidden question or not. + * + * @return bool + */ + public function isHiddenFallback() + { + return $this->hiddenFallback; + } + + /** + * Sets whether to fallback on non-hidden question if the response can not be hidden. + * + * @param bool $fallback + * + * @return $this + */ + public function setHiddenFallback($fallback) + { + $this->hiddenFallback = (bool) $fallback; + + return $this; + } + + /** + * Gets values for the autocompleter. + * + * @return iterable|null + */ + public function getAutocompleterValues() + { + return $this->autocompleterValues; + } + + /** + * Sets values for the autocompleter. + * + * @param iterable|null $values + * + * @return $this + * + * @throws InvalidArgumentException + * @throws LogicException + */ + public function setAutocompleterValues($values) + { + if (\is_array($values)) { + $values = $this->isAssoc($values) ? array_merge(array_keys($values), array_values($values)) : array_values($values); + } + + if (null !== $values && !\is_array($values) && !$values instanceof \Traversable) { + throw new InvalidArgumentException('Autocompleter values can be either an array, `null` or a `Traversable` object.'); + } + + if ($this->hidden) { + throw new LogicException('A hidden question cannot use the autocompleter.'); + } + + $this->autocompleterValues = $values; + + return $this; + } + + /** + * Sets a validator for the question. + * + * @param callable|null $validator + * + * @return $this + */ + public function setValidator(callable $validator = null) + { + $this->validator = $validator; + + return $this; + } + + /** + * Gets the validator for the question. + * + * @return callable|null + */ + public function getValidator() + { + return $this->validator; + } + + /** + * Sets the maximum number of attempts. + * + * Null means an unlimited number of attempts. + * + * @param int|null $attempts + * + * @return $this + * + * @throws InvalidArgumentException in case the number of attempts is invalid + */ + public function setMaxAttempts($attempts) + { + if (null !== $attempts && $attempts < 1) { + throw new InvalidArgumentException('Maximum number of attempts must be a positive value.'); + } + + $this->attempts = $attempts; + + return $this; + } + + /** + * Gets the maximum number of attempts. + * + * Null means an unlimited number of attempts. + * + * @return int|null + */ + public function getMaxAttempts() + { + return $this->attempts; + } + + /** + * Sets a normalizer for the response. + * + * The normalizer can be a callable (a string), a closure or a class implementing __invoke. + * + * @param callable $normalizer + * + * @return $this + */ + public function setNormalizer(callable $normalizer) + { + $this->normalizer = $normalizer; + + return $this; + } + + /** + * Gets the normalizer for the response. + * + * The normalizer can ba a callable (a string), a closure or a class implementing __invoke. + * + * @return callable + */ + public function getNormalizer() + { + return $this->normalizer; + } + + protected function isAssoc($array) + { + return (bool) \count(array_filter(array_keys($array), 'is_string')); + } +} diff --git a/lib/symfony/console/README.md b/lib/symfony/console/README.md new file mode 100644 index 000000000..664a37c0e --- /dev/null +++ b/lib/symfony/console/README.md @@ -0,0 +1,20 @@ +Console Component +================= + +The Console component eases the creation of beautiful and testable command line +interfaces. + +Resources +--------- + + * [Documentation](https://symfony.com/doc/current/components/console/index.html) + * [Contributing](https://symfony.com/doc/current/contributing/index.html) + * [Report issues](https://github.com/symfony/symfony/issues) and + [send Pull Requests](https://github.com/symfony/symfony/pulls) + in the [main Symfony repository](https://github.com/symfony/symfony) + +Credits +------- + +`Resources/bin/hiddeninput.exe` is a third party binary provided within this +component. Find sources and license at https://github.com/Seldaek/hidden-input. diff --git a/lib/symfony/console/Resources/bin/hiddeninput.exe b/lib/symfony/console/Resources/bin/hiddeninput.exe new file mode 100644 index 000000000..c8cf65e8d Binary files /dev/null and b/lib/symfony/console/Resources/bin/hiddeninput.exe differ diff --git a/lib/symfony/console/Style/OutputStyle.php b/lib/symfony/console/Style/OutputStyle.php new file mode 100644 index 000000000..b1262b55d --- /dev/null +++ b/lib/symfony/console/Style/OutputStyle.php @@ -0,0 +1,155 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Style; + +use Symfony\Component\Console\Formatter\OutputFormatterInterface; +use Symfony\Component\Console\Helper\ProgressBar; +use Symfony\Component\Console\Output\ConsoleOutputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Decorates output to add console style guide helpers. + * + * @author Kevin Bond + */ +abstract class OutputStyle implements OutputInterface, StyleInterface +{ + private $output; + + public function __construct(OutputInterface $output) + { + $this->output = $output; + } + + /** + * {@inheritdoc} + */ + public function newLine($count = 1) + { + $this->output->write(str_repeat(PHP_EOL, $count)); + } + + /** + * @param int $max + * + * @return ProgressBar + */ + public function createProgressBar($max = 0) + { + return new ProgressBar($this->output, $max); + } + + /** + * {@inheritdoc} + */ + public function write($messages, $newline = false, $type = self::OUTPUT_NORMAL) + { + $this->output->write($messages, $newline, $type); + } + + /** + * {@inheritdoc} + */ + public function writeln($messages, $type = self::OUTPUT_NORMAL) + { + $this->output->writeln($messages, $type); + } + + /** + * {@inheritdoc} + */ + public function setVerbosity($level) + { + $this->output->setVerbosity($level); + } + + /** + * {@inheritdoc} + */ + public function getVerbosity() + { + return $this->output->getVerbosity(); + } + + /** + * {@inheritdoc} + */ + public function setDecorated($decorated) + { + $this->output->setDecorated($decorated); + } + + /** + * {@inheritdoc} + */ + public function isDecorated() + { + return $this->output->isDecorated(); + } + + /** + * {@inheritdoc} + */ + public function setFormatter(OutputFormatterInterface $formatter) + { + $this->output->setFormatter($formatter); + } + + /** + * {@inheritdoc} + */ + public function getFormatter() + { + return $this->output->getFormatter(); + } + + /** + * {@inheritdoc} + */ + public function isQuiet() + { + return $this->output->isQuiet(); + } + + /** + * {@inheritdoc} + */ + public function isVerbose() + { + return $this->output->isVerbose(); + } + + /** + * {@inheritdoc} + */ + public function isVeryVerbose() + { + return $this->output->isVeryVerbose(); + } + + /** + * {@inheritdoc} + */ + public function isDebug() + { + return $this->output->isDebug(); + } + + protected function getErrorOutput() + { + if (!$this->output instanceof ConsoleOutputInterface) { + return $this->output; + } + + return $this->output->getErrorOutput(); + } +} diff --git a/lib/symfony/console/Style/StyleInterface.php b/lib/symfony/console/Style/StyleInterface.php new file mode 100644 index 000000000..475c268ff --- /dev/null +++ b/lib/symfony/console/Style/StyleInterface.php @@ -0,0 +1,154 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Style; + +/** + * Output style helpers. + * + * @author Kevin Bond + */ +interface StyleInterface +{ + /** + * Formats a command title. + * + * @param string $message + */ + public function title($message); + + /** + * Formats a section title. + * + * @param string $message + */ + public function section($message); + + /** + * Formats a list. + */ + public function listing(array $elements); + + /** + * Formats informational text. + * + * @param string|array $message + */ + public function text($message); + + /** + * Formats a success result bar. + * + * @param string|array $message + */ + public function success($message); + + /** + * Formats an error result bar. + * + * @param string|array $message + */ + public function error($message); + + /** + * Formats an warning result bar. + * + * @param string|array $message + */ + public function warning($message); + + /** + * Formats a note admonition. + * + * @param string|array $message + */ + public function note($message); + + /** + * Formats a caution admonition. + * + * @param string|array $message + */ + public function caution($message); + + /** + * Formats a table. + */ + public function table(array $headers, array $rows); + + /** + * Asks a question. + * + * @param string $question + * @param string|null $default + * @param callable|null $validator + * + * @return mixed + */ + public function ask($question, $default = null, $validator = null); + + /** + * Asks a question with the user input hidden. + * + * @param string $question + * @param callable|null $validator + * + * @return mixed + */ + public function askHidden($question, $validator = null); + + /** + * Asks for confirmation. + * + * @param string $question + * @param bool $default + * + * @return bool + */ + public function confirm($question, $default = true); + + /** + * Asks a choice question. + * + * @param string $question + * @param array $choices + * @param string|int|null $default + * + * @return mixed + */ + public function choice($question, array $choices, $default = null); + + /** + * Add newline(s). + * + * @param int $count The number of newlines + */ + public function newLine($count = 1); + + /** + * Starts the progress output. + * + * @param int $max Maximum steps (0 if unknown) + */ + public function progressStart($max = 0); + + /** + * Advances the progress output X steps. + * + * @param int $step Number of steps to advance + */ + public function progressAdvance($step = 1); + + /** + * Finishes the progress output. + */ + public function progressFinish(); +} diff --git a/lib/symfony/console/Style/SymfonyStyle.php b/lib/symfony/console/Style/SymfonyStyle.php new file mode 100644 index 000000000..e0ced01b2 --- /dev/null +++ b/lib/symfony/console/Style/SymfonyStyle.php @@ -0,0 +1,429 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Style; + +use Symfony\Component\Console\Exception\RuntimeException; +use Symfony\Component\Console\Formatter\OutputFormatter; +use Symfony\Component\Console\Helper\Helper; +use Symfony\Component\Console\Helper\ProgressBar; +use Symfony\Component\Console\Helper\SymfonyQuestionHelper; +use Symfony\Component\Console\Helper\Table; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\BufferedOutput; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Question\ChoiceQuestion; +use Symfony\Component\Console\Question\ConfirmationQuestion; +use Symfony\Component\Console\Question\Question; +use Symfony\Component\Console\Terminal; + +/** + * Output decorator helpers for the Symfony Style Guide. + * + * @author Kevin Bond + */ +class SymfonyStyle extends OutputStyle +{ + const MAX_LINE_LENGTH = 120; + + private $input; + private $questionHelper; + private $progressBar; + private $lineLength; + private $bufferedOutput; + + public function __construct(InputInterface $input, OutputInterface $output) + { + $this->input = $input; + $this->bufferedOutput = new BufferedOutput($output->getVerbosity(), false, clone $output->getFormatter()); + // Windows cmd wraps lines as soon as the terminal width is reached, whether there are following chars or not. + $width = (new Terminal())->getWidth() ?: self::MAX_LINE_LENGTH; + $this->lineLength = min($width - (int) (\DIRECTORY_SEPARATOR === '\\'), self::MAX_LINE_LENGTH); + + parent::__construct($output); + } + + /** + * Formats a message as a block of text. + * + * @param string|array $messages The message to write in the block + * @param string|null $type The block type (added in [] on first line) + * @param string|null $style The style to apply to the whole block + * @param string $prefix The prefix for the block + * @param bool $padding Whether to add vertical padding + * @param bool $escape Whether to escape the message + */ + public function block($messages, $type = null, $style = null, $prefix = ' ', $padding = false, $escape = true) + { + $messages = \is_array($messages) ? array_values($messages) : [$messages]; + + $this->autoPrependBlock(); + $this->writeln($this->createBlock($messages, $type, $style, $prefix, $padding, $escape)); + $this->newLine(); + } + + /** + * {@inheritdoc} + */ + public function title($message) + { + $this->autoPrependBlock(); + $this->writeln([ + sprintf('%s', OutputFormatter::escapeTrailingBackslash($message)), + sprintf('%s', str_repeat('=', Helper::strlenWithoutDecoration($this->getFormatter(), $message))), + ]); + $this->newLine(); + } + + /** + * {@inheritdoc} + */ + public function section($message) + { + $this->autoPrependBlock(); + $this->writeln([ + sprintf('%s', OutputFormatter::escapeTrailingBackslash($message)), + sprintf('%s', str_repeat('-', Helper::strlenWithoutDecoration($this->getFormatter(), $message))), + ]); + $this->newLine(); + } + + /** + * {@inheritdoc} + */ + public function listing(array $elements) + { + $this->autoPrependText(); + $elements = array_map(function ($element) { + return sprintf(' * %s', $element); + }, $elements); + + $this->writeln($elements); + $this->newLine(); + } + + /** + * {@inheritdoc} + */ + public function text($message) + { + $this->autoPrependText(); + + $messages = \is_array($message) ? array_values($message) : [$message]; + foreach ($messages as $message) { + $this->writeln(sprintf(' %s', $message)); + } + } + + /** + * Formats a command comment. + * + * @param string|array $message + */ + public function comment($message) + { + $this->block($message, null, null, ' // ', false, false); + } + + /** + * {@inheritdoc} + */ + public function success($message) + { + $this->block($message, 'OK', 'fg=black;bg=green', ' ', true); + } + + /** + * {@inheritdoc} + */ + public function error($message) + { + $this->block($message, 'ERROR', 'fg=white;bg=red', ' ', true); + } + + /** + * {@inheritdoc} + */ + public function warning($message) + { + $this->block($message, 'WARNING', 'fg=white;bg=red', ' ', true); + } + + /** + * {@inheritdoc} + */ + public function note($message) + { + $this->block($message, 'NOTE', 'fg=yellow', ' ! '); + } + + /** + * {@inheritdoc} + */ + public function caution($message) + { + $this->block($message, 'CAUTION', 'fg=white;bg=red', ' ! ', true); + } + + /** + * {@inheritdoc} + */ + public function table(array $headers, array $rows) + { + $style = clone Table::getStyleDefinition('symfony-style-guide'); + $style->setCellHeaderFormat('%s'); + + $table = new Table($this); + $table->setHeaders($headers); + $table->setRows($rows); + $table->setStyle($style); + + $table->render(); + $this->newLine(); + } + + /** + * {@inheritdoc} + */ + public function ask($question, $default = null, $validator = null) + { + $question = new Question($question, $default); + $question->setValidator($validator); + + return $this->askQuestion($question); + } + + /** + * {@inheritdoc} + */ + public function askHidden($question, $validator = null) + { + $question = new Question($question); + + $question->setHidden(true); + $question->setValidator($validator); + + return $this->askQuestion($question); + } + + /** + * {@inheritdoc} + */ + public function confirm($question, $default = true) + { + return $this->askQuestion(new ConfirmationQuestion($question, $default)); + } + + /** + * {@inheritdoc} + */ + public function choice($question, array $choices, $default = null) + { + if (null !== $default) { + $values = array_flip($choices); + $default = $values[$default]; + } + + return $this->askQuestion(new ChoiceQuestion($question, $choices, $default)); + } + + /** + * {@inheritdoc} + */ + public function progressStart($max = 0) + { + $this->progressBar = $this->createProgressBar($max); + $this->progressBar->start(); + } + + /** + * {@inheritdoc} + */ + public function progressAdvance($step = 1) + { + $this->getProgressBar()->advance($step); + } + + /** + * {@inheritdoc} + */ + public function progressFinish() + { + $this->getProgressBar()->finish(); + $this->newLine(2); + $this->progressBar = null; + } + + /** + * {@inheritdoc} + */ + public function createProgressBar($max = 0) + { + $progressBar = parent::createProgressBar($max); + + if ('\\' !== \DIRECTORY_SEPARATOR || 'Hyper' === getenv('TERM_PROGRAM')) { + $progressBar->setEmptyBarCharacter('░'); // light shade character \u2591 + $progressBar->setProgressCharacter(''); + $progressBar->setBarCharacter('▓'); // dark shade character \u2593 + } + + return $progressBar; + } + + /** + * @return mixed + */ + public function askQuestion(Question $question) + { + if ($this->input->isInteractive()) { + $this->autoPrependBlock(); + } + + if (!$this->questionHelper) { + $this->questionHelper = new SymfonyQuestionHelper(); + } + + $answer = $this->questionHelper->ask($this->input, $this, $question); + + if ($this->input->isInteractive()) { + $this->newLine(); + $this->bufferedOutput->write("\n"); + } + + return $answer; + } + + /** + * {@inheritdoc} + */ + public function writeln($messages, $type = self::OUTPUT_NORMAL) + { + parent::writeln($messages, $type); + $this->bufferedOutput->writeln($this->reduceBuffer($messages), $type); + } + + /** + * {@inheritdoc} + */ + public function write($messages, $newline = false, $type = self::OUTPUT_NORMAL) + { + parent::write($messages, $newline, $type); + $this->bufferedOutput->write($this->reduceBuffer($messages), $newline, $type); + } + + /** + * {@inheritdoc} + */ + public function newLine($count = 1) + { + parent::newLine($count); + $this->bufferedOutput->write(str_repeat("\n", $count)); + } + + /** + * Returns a new instance which makes use of stderr if available. + * + * @return self + */ + public function getErrorStyle() + { + return new self($this->input, $this->getErrorOutput()); + } + + /** + * @return ProgressBar + */ + private function getProgressBar() + { + if (!$this->progressBar) { + throw new RuntimeException('The ProgressBar is not started.'); + } + + return $this->progressBar; + } + + private function autoPrependBlock() + { + $chars = substr(str_replace(PHP_EOL, "\n", $this->bufferedOutput->fetch()), -2); + + if (!isset($chars[0])) { + return $this->newLine(); //empty history, so we should start with a new line. + } + //Prepend new line for each non LF chars (This means no blank line was output before) + $this->newLine(2 - substr_count($chars, "\n")); + } + + private function autoPrependText() + { + $fetched = $this->bufferedOutput->fetch(); + //Prepend new line if last char isn't EOL: + if ("\n" !== substr($fetched, -1)) { + $this->newLine(); + } + } + + private function reduceBuffer($messages) + { + // We need to know if the two last chars are PHP_EOL + // Preserve the last 4 chars inserted (PHP_EOL on windows is two chars) in the history buffer + return array_map(function ($value) { + return substr($value, -4); + }, array_merge([$this->bufferedOutput->fetch()], (array) $messages)); + } + + private function createBlock($messages, $type = null, $style = null, $prefix = ' ', $padding = false, $escape = false) + { + $indentLength = 0; + $prefixLength = Helper::strlenWithoutDecoration($this->getFormatter(), $prefix); + $lines = []; + + if (null !== $type) { + $type = sprintf('[%s] ', $type); + $indentLength = \strlen($type); + $lineIndentation = str_repeat(' ', $indentLength); + } + + // wrap and add newlines for each element + foreach ($messages as $key => $message) { + if ($escape) { + $message = OutputFormatter::escape($message); + } + + $lines = array_merge($lines, explode(PHP_EOL, wordwrap($message, $this->lineLength - $prefixLength - $indentLength, PHP_EOL, true))); + + if (\count($messages) > 1 && $key < \count($messages) - 1) { + $lines[] = ''; + } + } + + $firstLineIndex = 0; + if ($padding && $this->isDecorated()) { + $firstLineIndex = 1; + array_unshift($lines, ''); + $lines[] = ''; + } + + foreach ($lines as $i => &$line) { + if (null !== $type) { + $line = $firstLineIndex === $i ? $type.$line : $lineIndentation.$line; + } + + $line = $prefix.$line; + $line .= str_repeat(' ', $this->lineLength - Helper::strlenWithoutDecoration($this->getFormatter(), $line)); + + if ($style) { + $line = sprintf('<%s>%s', $style, $line); + } + } + + return $lines; + } +} diff --git a/lib/symfony/console/Terminal.php b/lib/symfony/console/Terminal.php new file mode 100644 index 000000000..456cca11c --- /dev/null +++ b/lib/symfony/console/Terminal.php @@ -0,0 +1,137 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console; + +class Terminal +{ + private static $width; + private static $height; + + /** + * Gets the terminal width. + * + * @return int + */ + public function getWidth() + { + $width = getenv('COLUMNS'); + if (false !== $width) { + return (int) trim($width); + } + + if (null === self::$width) { + self::initDimensions(); + } + + return self::$width ?: 80; + } + + /** + * Gets the terminal height. + * + * @return int + */ + public function getHeight() + { + $height = getenv('LINES'); + if (false !== $height) { + return (int) trim($height); + } + + if (null === self::$height) { + self::initDimensions(); + } + + return self::$height ?: 50; + } + + private static function initDimensions() + { + if ('\\' === \DIRECTORY_SEPARATOR) { + if (preg_match('/^(\d+)x(\d+)(?: \((\d+)x(\d+)\))?$/', trim(getenv('ANSICON')), $matches)) { + // extract [w, H] from "wxh (WxH)" + // or [w, h] from "wxh" + self::$width = (int) $matches[1]; + self::$height = isset($matches[4]) ? (int) $matches[4] : (int) $matches[2]; + } elseif (null !== $dimensions = self::getConsoleMode()) { + // extract [w, h] from "wxh" + self::$width = (int) $dimensions[0]; + self::$height = (int) $dimensions[1]; + } + } elseif ($sttyString = self::getSttyColumns()) { + if (preg_match('/rows.(\d+);.columns.(\d+);/i', $sttyString, $matches)) { + // extract [w, h] from "rows h; columns w;" + self::$width = (int) $matches[2]; + self::$height = (int) $matches[1]; + } elseif (preg_match('/;.(\d+).rows;.(\d+).columns/i', $sttyString, $matches)) { + // extract [w, h] from "; h rows; w columns" + self::$width = (int) $matches[2]; + self::$height = (int) $matches[1]; + } + } + } + + /** + * Runs and parses mode CON if it's available, suppressing any error output. + * + * @return int[]|null An array composed of the width and the height or null if it could not be parsed + */ + private static function getConsoleMode() + { + if (!\function_exists('proc_open')) { + return; + } + + $descriptorspec = [ + 1 => ['pipe', 'w'], + 2 => ['pipe', 'w'], + ]; + $process = proc_open('mode CON', $descriptorspec, $pipes, null, null, ['suppress_errors' => true]); + if (\is_resource($process)) { + $info = stream_get_contents($pipes[1]); + fclose($pipes[1]); + fclose($pipes[2]); + proc_close($process); + + if (preg_match('/--------+\r?\n.+?(\d+)\r?\n.+?(\d+)\r?\n/', $info, $matches)) { + return [(int) $matches[2], (int) $matches[1]]; + } + } + } + + /** + * Runs and parses stty -a if it's available, suppressing any error output. + * + * @return string|null + */ + private static function getSttyColumns() + { + if (!\function_exists('proc_open')) { + return; + } + + $descriptorspec = [ + 1 => ['pipe', 'w'], + 2 => ['pipe', 'w'], + ]; + + $process = proc_open('stty -a | grep columns', $descriptorspec, $pipes, null, null, ['suppress_errors' => true]); + if (\is_resource($process)) { + $info = stream_get_contents($pipes[1]); + fclose($pipes[1]); + fclose($pipes[2]); + proc_close($process); + + return $info; + } + } +} diff --git a/lib/symfony/console/Tester/ApplicationTester.php b/lib/symfony/console/Tester/ApplicationTester.php new file mode 100644 index 000000000..dc970182e --- /dev/null +++ b/lib/symfony/console/Tester/ApplicationTester.php @@ -0,0 +1,176 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tester; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\ConsoleOutput; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Output\StreamOutput; + +/** + * Eases the testing of console applications. + * + * When testing an application, don't forget to disable the auto exit flag: + * + * $application = new Application(); + * $application->setAutoExit(false); + * + * @author Fabien Potencier + */ +class ApplicationTester +{ + private $application; + private $input; + private $statusCode; + /** + * @var OutputInterface + */ + private $output; + private $captureStreamsIndependently = false; + + public function __construct(Application $application) + { + $this->application = $application; + } + + /** + * Executes the application. + * + * Available options: + * + * * interactive: Sets the input interactive flag + * * decorated: Sets the output decorated flag + * * verbosity: Sets the output verbosity flag + * * capture_stderr_separately: Make output of stdOut and stdErr separately available + * + * @param array $input An array of arguments and options + * @param array $options An array of options + * + * @return int The command exit code + */ + public function run(array $input, $options = []) + { + $this->input = new ArrayInput($input); + if (isset($options['interactive'])) { + $this->input->setInteractive($options['interactive']); + } + + $this->captureStreamsIndependently = \array_key_exists('capture_stderr_separately', $options) && $options['capture_stderr_separately']; + if (!$this->captureStreamsIndependently) { + $this->output = new StreamOutput(fopen('php://memory', 'w', false)); + if (isset($options['decorated'])) { + $this->output->setDecorated($options['decorated']); + } + if (isset($options['verbosity'])) { + $this->output->setVerbosity($options['verbosity']); + } + } else { + $this->output = new ConsoleOutput( + isset($options['verbosity']) ? $options['verbosity'] : ConsoleOutput::VERBOSITY_NORMAL, + isset($options['decorated']) ? $options['decorated'] : null + ); + + $errorOutput = new StreamOutput(fopen('php://memory', 'w', false)); + $errorOutput->setFormatter($this->output->getFormatter()); + $errorOutput->setVerbosity($this->output->getVerbosity()); + $errorOutput->setDecorated($this->output->isDecorated()); + + $reflectedOutput = new \ReflectionObject($this->output); + $strErrProperty = $reflectedOutput->getProperty('stderr'); + $strErrProperty->setAccessible(true); + $strErrProperty->setValue($this->output, $errorOutput); + + $reflectedParent = $reflectedOutput->getParentClass(); + $streamProperty = $reflectedParent->getProperty('stream'); + $streamProperty->setAccessible(true); + $streamProperty->setValue($this->output, fopen('php://memory', 'w', false)); + } + + return $this->statusCode = $this->application->run($this->input, $this->output); + } + + /** + * Gets the display returned by the last execution of the application. + * + * @param bool $normalize Whether to normalize end of lines to \n or not + * + * @return string The display + */ + public function getDisplay($normalize = false) + { + rewind($this->output->getStream()); + + $display = stream_get_contents($this->output->getStream()); + + if ($normalize) { + $display = str_replace(PHP_EOL, "\n", $display); + } + + return $display; + } + + /** + * Gets the output written to STDERR by the application. + * + * @param bool $normalize Whether to normalize end of lines to \n or not + * + * @return string + */ + public function getErrorOutput($normalize = false) + { + if (!$this->captureStreamsIndependently) { + throw new \LogicException('The error output is not available when the tester is run without "capture_stderr_separately" option set.'); + } + + rewind($this->output->getErrorOutput()->getStream()); + + $display = stream_get_contents($this->output->getErrorOutput()->getStream()); + + if ($normalize) { + $display = str_replace(PHP_EOL, "\n", $display); + } + + return $display; + } + + /** + * Gets the input instance used by the last execution of the application. + * + * @return InputInterface The current input instance + */ + public function getInput() + { + return $this->input; + } + + /** + * Gets the output instance used by the last execution of the application. + * + * @return OutputInterface The current output instance + */ + public function getOutput() + { + return $this->output; + } + + /** + * Gets the status code returned by the last execution of the application. + * + * @return int The status code + */ + public function getStatusCode() + { + return $this->statusCode; + } +} diff --git a/lib/symfony/console/Tester/CommandTester.php b/lib/symfony/console/Tester/CommandTester.php new file mode 100644 index 000000000..a1d8ad70a --- /dev/null +++ b/lib/symfony/console/Tester/CommandTester.php @@ -0,0 +1,162 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tester; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Output\StreamOutput; + +/** + * Eases the testing of console commands. + * + * @author Fabien Potencier + * @author Robin Chalas + */ +class CommandTester +{ + private $command; + private $input; + private $output; + private $inputs = []; + private $statusCode; + + public function __construct(Command $command) + { + $this->command = $command; + } + + /** + * Executes the command. + * + * Available execution options: + * + * * interactive: Sets the input interactive flag + * * decorated: Sets the output decorated flag + * * verbosity: Sets the output verbosity flag + * + * @param array $input An array of command arguments and options + * @param array $options An array of execution options + * + * @return int The command exit code + */ + public function execute(array $input, array $options = []) + { + // set the command name automatically if the application requires + // this argument and no command name was passed + if (!isset($input['command']) + && (null !== $application = $this->command->getApplication()) + && $application->getDefinition()->hasArgument('command') + ) { + $input = array_merge(['command' => $this->command->getName()], $input); + } + + $this->input = new ArrayInput($input); + // Use an in-memory input stream even if no inputs are set so that QuestionHelper::ask() does not rely on the blocking STDIN. + $this->input->setStream(self::createStream($this->inputs)); + + if (isset($options['interactive'])) { + $this->input->setInteractive($options['interactive']); + } + + $this->output = new StreamOutput(fopen('php://memory', 'w', false)); + $this->output->setDecorated(isset($options['decorated']) ? $options['decorated'] : false); + if (isset($options['verbosity'])) { + $this->output->setVerbosity($options['verbosity']); + } + + return $this->statusCode = $this->command->run($this->input, $this->output); + } + + /** + * Gets the display returned by the last execution of the command. + * + * @param bool $normalize Whether to normalize end of lines to \n or not + * + * @return string The display + */ + public function getDisplay($normalize = false) + { + if (null === $this->output) { + throw new \RuntimeException('Output not initialized, did you execute the command before requesting the display?'); + } + + rewind($this->output->getStream()); + + $display = stream_get_contents($this->output->getStream()); + + if ($normalize) { + $display = str_replace(PHP_EOL, "\n", $display); + } + + return $display; + } + + /** + * Gets the input instance used by the last execution of the command. + * + * @return InputInterface The current input instance + */ + public function getInput() + { + return $this->input; + } + + /** + * Gets the output instance used by the last execution of the command. + * + * @return OutputInterface The current output instance + */ + public function getOutput() + { + return $this->output; + } + + /** + * Gets the status code returned by the last execution of the application. + * + * @return int The status code + */ + public function getStatusCode() + { + return $this->statusCode; + } + + /** + * Sets the user inputs. + * + * @param array $inputs An array of strings representing each input + * passed to the command input stream + * + * @return CommandTester + */ + public function setInputs(array $inputs) + { + $this->inputs = $inputs; + + return $this; + } + + private static function createStream(array $inputs) + { + $stream = fopen('php://memory', 'r+', false); + + foreach ($inputs as $input) { + fwrite($stream, $input.PHP_EOL); + } + + rewind($stream); + + return $stream; + } +} diff --git a/lib/symfony/console/Tests/ApplicationTest.php b/lib/symfony/console/Tests/ApplicationTest.php new file mode 100644 index 000000000..181e82593 --- /dev/null +++ b/lib/symfony/console/Tests/ApplicationTest.php @@ -0,0 +1,1784 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\CommandLoader\FactoryCommandLoader; +use Symfony\Component\Console\DependencyInjection\AddConsoleCommandPass; +use Symfony\Component\Console\Event\ConsoleCommandEvent; +use Symfony\Component\Console\Event\ConsoleErrorEvent; +use Symfony\Component\Console\Event\ConsoleExceptionEvent; +use Symfony\Component\Console\Event\ConsoleTerminateEvent; +use Symfony\Component\Console\Exception\CommandNotFoundException; +use Symfony\Component\Console\Helper\FormatterHelper; +use Symfony\Component\Console\Helper\HelperSet; +use Symfony\Component\Console\Input\ArgvInput; +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\NullOutput; +use Symfony\Component\Console\Output\Output; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Output\StreamOutput; +use Symfony\Component\Console\Tester\ApplicationTester; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\EventDispatcher\EventDispatcher; + +class ApplicationTest extends TestCase +{ + protected static $fixturesPath; + + private $colSize; + + protected function setUp() + { + $this->colSize = getenv('COLUMNS'); + } + + protected function tearDown() + { + putenv($this->colSize ? 'COLUMNS='.$this->colSize : 'COLUMNS'); + putenv('SHELL_VERBOSITY'); + unset($_ENV['SHELL_VERBOSITY']); + unset($_SERVER['SHELL_VERBOSITY']); + } + + public static function setUpBeforeClass() + { + self::$fixturesPath = realpath(__DIR__.'/Fixtures/'); + require_once self::$fixturesPath.'/FooCommand.php'; + require_once self::$fixturesPath.'/FooOptCommand.php'; + require_once self::$fixturesPath.'/Foo1Command.php'; + require_once self::$fixturesPath.'/Foo2Command.php'; + require_once self::$fixturesPath.'/Foo3Command.php'; + require_once self::$fixturesPath.'/Foo4Command.php'; + require_once self::$fixturesPath.'/Foo5Command.php'; + require_once self::$fixturesPath.'/FooSameCaseUppercaseCommand.php'; + require_once self::$fixturesPath.'/FooSameCaseLowercaseCommand.php'; + require_once self::$fixturesPath.'/FoobarCommand.php'; + require_once self::$fixturesPath.'/BarBucCommand.php'; + require_once self::$fixturesPath.'/FooSubnamespaced1Command.php'; + require_once self::$fixturesPath.'/FooSubnamespaced2Command.php'; + require_once self::$fixturesPath.'/TestAmbiguousCommandRegistering.php'; + require_once self::$fixturesPath.'/TestAmbiguousCommandRegistering2.php'; + } + + protected function normalizeLineBreaks($text) + { + return str_replace(PHP_EOL, "\n", $text); + } + + /** + * Replaces the dynamic placeholders of the command help text with a static version. + * The placeholder %command.full_name% includes the script path that is not predictable + * and can not be tested against. + */ + protected function ensureStaticCommandHelp(Application $application) + { + foreach ($application->all() as $command) { + $command->setHelp(str_replace('%command.full_name%', 'app/console %command.name%', $command->getHelp())); + } + } + + public function testConstructor() + { + $application = new Application('foo', 'bar'); + $this->assertEquals('foo', $application->getName(), '__construct() takes the application name as its first argument'); + $this->assertEquals('bar', $application->getVersion(), '__construct() takes the application version as its second argument'); + $this->assertEquals(['help', 'list'], array_keys($application->all()), '__construct() registered the help and list commands by default'); + } + + public function testSetGetName() + { + $application = new Application(); + $application->setName('foo'); + $this->assertEquals('foo', $application->getName(), '->setName() sets the name of the application'); + } + + public function testSetGetVersion() + { + $application = new Application(); + $application->setVersion('bar'); + $this->assertEquals('bar', $application->getVersion(), '->setVersion() sets the version of the application'); + } + + public function testGetLongVersion() + { + $application = new Application('foo', 'bar'); + $this->assertEquals('foo bar', $application->getLongVersion(), '->getLongVersion() returns the long version of the application'); + } + + public function testHelp() + { + $application = new Application(); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_gethelp.txt', $this->normalizeLineBreaks($application->getHelp()), '->getHelp() returns a help message'); + } + + public function testAll() + { + $application = new Application(); + $commands = $application->all(); + $this->assertInstanceOf('Symfony\\Component\\Console\\Command\\HelpCommand', $commands['help'], '->all() returns the registered commands'); + + $application->add(new \FooCommand()); + $commands = $application->all('foo'); + $this->assertCount(1, $commands, '->all() takes a namespace as its first argument'); + } + + public function testAllWithCommandLoader() + { + $application = new Application(); + $commands = $application->all(); + $this->assertInstanceOf('Symfony\\Component\\Console\\Command\\HelpCommand', $commands['help'], '->all() returns the registered commands'); + + $application->add(new \FooCommand()); + $commands = $application->all('foo'); + $this->assertCount(1, $commands, '->all() takes a namespace as its first argument'); + + $application->setCommandLoader(new FactoryCommandLoader([ + 'foo:bar1' => function () { return new \Foo1Command(); }, + ])); + $commands = $application->all('foo'); + $this->assertCount(2, $commands, '->all() takes a namespace as its first argument'); + $this->assertInstanceOf(\FooCommand::class, $commands['foo:bar'], '->all() returns the registered commands'); + $this->assertInstanceOf(\Foo1Command::class, $commands['foo:bar1'], '->all() returns the registered commands'); + } + + public function testRegister() + { + $application = new Application(); + $command = $application->register('foo'); + $this->assertEquals('foo', $command->getName(), '->register() registers a new command'); + } + + public function testRegisterAmbiguous() + { + $code = function (InputInterface $input, OutputInterface $output) { + $output->writeln('It works!'); + }; + + $application = new Application(); + $application + ->register('test-foo') + ->setAliases(['test']) + ->setCode($code); + + $application + ->register('test-bar') + ->setCode($code); + + $tester = new ApplicationTester($application); + $tester->run(['test']); + $this->assertContains('It works!', $tester->getDisplay(true)); + } + + public function testAdd() + { + $application = new Application(); + $application->add($foo = new \FooCommand()); + $commands = $application->all(); + $this->assertEquals($foo, $commands['foo:bar'], '->add() registers a command'); + + $application = new Application(); + $application->addCommands([$foo = new \FooCommand(), $foo1 = new \Foo1Command()]); + $commands = $application->all(); + $this->assertEquals([$foo, $foo1], [$commands['foo:bar'], $commands['foo:bar1']], '->addCommands() registers an array of commands'); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage Command class "Foo5Command" is not correctly initialized. You probably forgot to call the parent constructor. + */ + public function testAddCommandWithEmptyConstructor() + { + $application = new Application(); + $application->add(new \Foo5Command()); + } + + public function testHasGet() + { + $application = new Application(); + $this->assertTrue($application->has('list'), '->has() returns true if a named command is registered'); + $this->assertFalse($application->has('afoobar'), '->has() returns false if a named command is not registered'); + + $application->add($foo = new \FooCommand()); + $this->assertTrue($application->has('afoobar'), '->has() returns true if an alias is registered'); + $this->assertEquals($foo, $application->get('foo:bar'), '->get() returns a command by name'); + $this->assertEquals($foo, $application->get('afoobar'), '->get() returns a command by alias'); + + $application = new Application(); + $application->add($foo = new \FooCommand()); + // simulate --help + $r = new \ReflectionObject($application); + $p = $r->getProperty('wantHelps'); + $p->setAccessible(true); + $p->setValue($application, true); + $command = $application->get('foo:bar'); + $this->assertInstanceOf('Symfony\Component\Console\Command\HelpCommand', $command, '->get() returns the help command if --help is provided as the input'); + } + + public function testHasGetWithCommandLoader() + { + $application = new Application(); + $this->assertTrue($application->has('list'), '->has() returns true if a named command is registered'); + $this->assertFalse($application->has('afoobar'), '->has() returns false if a named command is not registered'); + + $application->add($foo = new \FooCommand()); + $this->assertTrue($application->has('afoobar'), '->has() returns true if an alias is registered'); + $this->assertEquals($foo, $application->get('foo:bar'), '->get() returns a command by name'); + $this->assertEquals($foo, $application->get('afoobar'), '->get() returns a command by alias'); + + $application->setCommandLoader(new FactoryCommandLoader([ + 'foo:bar1' => function () { return new \Foo1Command(); }, + ])); + + $this->assertTrue($application->has('afoobar'), '->has() returns true if an instance is registered for an alias even with command loader'); + $this->assertEquals($foo, $application->get('foo:bar'), '->get() returns an instance by name even with command loader'); + $this->assertEquals($foo, $application->get('afoobar'), '->get() returns an instance by alias even with command loader'); + $this->assertTrue($application->has('foo:bar1'), '->has() returns true for commands registered in the loader'); + $this->assertInstanceOf(\Foo1Command::class, $foo1 = $application->get('foo:bar1'), '->get() returns a command by name from the command loader'); + $this->assertTrue($application->has('afoobar1'), '->has() returns true for commands registered in the loader'); + $this->assertEquals($foo1, $application->get('afoobar1'), '->get() returns a command by name from the command loader'); + } + + public function testSilentHelp() + { + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + + $tester = new ApplicationTester($application); + $tester->run(['-h' => true, '-q' => true], ['decorated' => false]); + + $this->assertEmpty($tester->getDisplay(true)); + } + + /** + * @expectedException \Symfony\Component\Console\Exception\CommandNotFoundException + * @expectedExceptionMessage The command "foofoo" does not exist. + */ + public function testGetInvalidCommand() + { + $application = new Application(); + $application->get('foofoo'); + } + + public function testGetNamespaces() + { + $application = new Application(); + $application->add(new \FooCommand()); + $application->add(new \Foo1Command()); + $this->assertEquals(['foo'], $application->getNamespaces(), '->getNamespaces() returns an array of unique used namespaces'); + } + + public function testFindNamespace() + { + $application = new Application(); + $application->add(new \FooCommand()); + $this->assertEquals('foo', $application->findNamespace('foo'), '->findNamespace() returns the given namespace if it exists'); + $this->assertEquals('foo', $application->findNamespace('f'), '->findNamespace() finds a namespace given an abbreviation'); + $application->add(new \Foo2Command()); + $this->assertEquals('foo', $application->findNamespace('foo'), '->findNamespace() returns the given namespace if it exists'); + } + + public function testFindNamespaceWithSubnamespaces() + { + $application = new Application(); + $application->add(new \FooSubnamespaced1Command()); + $application->add(new \FooSubnamespaced2Command()); + $this->assertEquals('foo', $application->findNamespace('foo'), '->findNamespace() returns commands even if the commands are only contained in subnamespaces'); + } + + public function testFindAmbiguousNamespace() + { + $application = new Application(); + $application->add(new \BarBucCommand()); + $application->add(new \FooCommand()); + $application->add(new \Foo2Command()); + + $expectedMsg = "The namespace \"f\" is ambiguous.\nDid you mean one of these?\n foo\n foo1"; + + if (method_exists($this, 'expectException')) { + $this->expectException(CommandNotFoundException::class); + $this->expectExceptionMessage($expectedMsg); + } else { + $this->setExpectedException(CommandNotFoundException::class, $expectedMsg); + } + + $application->findNamespace('f'); + } + + public function testFindNonAmbiguous() + { + $application = new Application(); + $application->add(new \TestAmbiguousCommandRegistering()); + $application->add(new \TestAmbiguousCommandRegistering2()); + $this->assertEquals('test-ambiguous', $application->find('test')->getName()); + } + + /** + * @expectedException \Symfony\Component\Console\Exception\CommandNotFoundException + * @expectedExceptionMessage There are no commands defined in the "bar" namespace. + */ + public function testFindInvalidNamespace() + { + $application = new Application(); + $application->findNamespace('bar'); + } + + /** + * @expectedException \Symfony\Component\Console\Exception\CommandNotFoundException + * @expectedExceptionMessage Command "foo1" is not defined + */ + public function testFindUniqueNameButNamespaceName() + { + $application = new Application(); + $application->add(new \FooCommand()); + $application->add(new \Foo1Command()); + $application->add(new \Foo2Command()); + + $application->find($commandName = 'foo1'); + } + + public function testFind() + { + $application = new Application(); + $application->add(new \FooCommand()); + + $this->assertInstanceOf('FooCommand', $application->find('foo:bar'), '->find() returns a command if its name exists'); + $this->assertInstanceOf('Symfony\Component\Console\Command\HelpCommand', $application->find('h'), '->find() returns a command if its name exists'); + $this->assertInstanceOf('FooCommand', $application->find('f:bar'), '->find() returns a command if the abbreviation for the namespace exists'); + $this->assertInstanceOf('FooCommand', $application->find('f:b'), '->find() returns a command if the abbreviation for the namespace and the command name exist'); + $this->assertInstanceOf('FooCommand', $application->find('a'), '->find() returns a command if the abbreviation exists for an alias'); + } + + public function testFindCaseSensitiveFirst() + { + $application = new Application(); + $application->add(new \FooSameCaseUppercaseCommand()); + $application->add(new \FooSameCaseLowercaseCommand()); + + $this->assertInstanceOf('FooSameCaseUppercaseCommand', $application->find('f:B'), '->find() returns a command if the abbreviation is the correct case'); + $this->assertInstanceOf('FooSameCaseUppercaseCommand', $application->find('f:BAR'), '->find() returns a command if the abbreviation is the correct case'); + $this->assertInstanceOf('FooSameCaseLowercaseCommand', $application->find('f:b'), '->find() returns a command if the abbreviation is the correct case'); + $this->assertInstanceOf('FooSameCaseLowercaseCommand', $application->find('f:bar'), '->find() returns a command if the abbreviation is the correct case'); + } + + public function testFindCaseInsensitiveAsFallback() + { + $application = new Application(); + $application->add(new \FooSameCaseLowercaseCommand()); + + $this->assertInstanceOf('FooSameCaseLowercaseCommand', $application->find('f:b'), '->find() returns a command if the abbreviation is the correct case'); + $this->assertInstanceOf('FooSameCaseLowercaseCommand', $application->find('f:B'), '->find() will fallback to case insensitivity'); + $this->assertInstanceOf('FooSameCaseLowercaseCommand', $application->find('FoO:BaR'), '->find() will fallback to case insensitivity'); + } + + /** + * @expectedException \Symfony\Component\Console\Exception\CommandNotFoundException + * @expectedExceptionMessage Command "FoO:BaR" is ambiguous + */ + public function testFindCaseInsensitiveSuggestions() + { + $application = new Application(); + $application->add(new \FooSameCaseLowercaseCommand()); + $application->add(new \FooSameCaseUppercaseCommand()); + + $this->assertInstanceOf('FooSameCaseLowercaseCommand', $application->find('FoO:BaR'), '->find() will find two suggestions with case insensitivity'); + } + + public function testFindWithCommandLoader() + { + $application = new Application(); + $application->setCommandLoader(new FactoryCommandLoader([ + 'foo:bar' => $f = function () { return new \FooCommand(); }, + ])); + + $this->assertInstanceOf('FooCommand', $application->find('foo:bar'), '->find() returns a command if its name exists'); + $this->assertInstanceOf('Symfony\Component\Console\Command\HelpCommand', $application->find('h'), '->find() returns a command if its name exists'); + $this->assertInstanceOf('FooCommand', $application->find('f:bar'), '->find() returns a command if the abbreviation for the namespace exists'); + $this->assertInstanceOf('FooCommand', $application->find('f:b'), '->find() returns a command if the abbreviation for the namespace and the command name exist'); + $this->assertInstanceOf('FooCommand', $application->find('a'), '->find() returns a command if the abbreviation exists for an alias'); + } + + /** + * @dataProvider provideAmbiguousAbbreviations + */ + public function testFindWithAmbiguousAbbreviations($abbreviation, $expectedExceptionMessage) + { + putenv('COLUMNS=120'); + if (method_exists($this, 'expectException')) { + $this->expectException('Symfony\Component\Console\Exception\CommandNotFoundException'); + $this->expectExceptionMessage($expectedExceptionMessage); + } else { + $this->setExpectedException('Symfony\Component\Console\Exception\CommandNotFoundException', $expectedExceptionMessage); + } + + $application = new Application(); + $application->add(new \FooCommand()); + $application->add(new \Foo1Command()); + $application->add(new \Foo2Command()); + + $application->find($abbreviation); + } + + public function provideAmbiguousAbbreviations() + { + return [ + ['f', 'Command "f" is not defined.'], + [ + 'a', + "Command \"a\" is ambiguous.\nDid you mean one of these?\n". + " afoobar The foo:bar command\n". + " afoobar1 The foo:bar1 command\n". + ' afoobar2 The foo1:bar command', + ], + [ + 'foo:b', + "Command \"foo:b\" is ambiguous.\nDid you mean one of these?\n". + " foo:bar The foo:bar command\n". + " foo:bar1 The foo:bar1 command\n". + ' foo1:bar The foo1:bar command', + ], + ]; + } + + public function testFindCommandEqualNamespace() + { + $application = new Application(); + $application->add(new \Foo3Command()); + $application->add(new \Foo4Command()); + + $this->assertInstanceOf('Foo3Command', $application->find('foo3:bar'), '->find() returns the good command even if a namespace has same name'); + $this->assertInstanceOf('Foo4Command', $application->find('foo3:bar:toh'), '->find() returns a command even if its namespace equals another command name'); + } + + public function testFindCommandWithAmbiguousNamespacesButUniqueName() + { + $application = new Application(); + $application->add(new \FooCommand()); + $application->add(new \FoobarCommand()); + + $this->assertInstanceOf('FoobarCommand', $application->find('f:f')); + } + + public function testFindCommandWithMissingNamespace() + { + $application = new Application(); + $application->add(new \Foo4Command()); + + $this->assertInstanceOf('Foo4Command', $application->find('f::t')); + } + + /** + * @dataProvider provideInvalidCommandNamesSingle + * @expectedException \Symfony\Component\Console\Exception\CommandNotFoundException + * @expectedExceptionMessage Did you mean this + */ + public function testFindAlternativeExceptionMessageSingle($name) + { + $application = new Application(); + $application->add(new \Foo3Command()); + $application->find($name); + } + + public function provideInvalidCommandNamesSingle() + { + return [ + ['foo3:barr'], + ['fooo3:bar'], + ]; + } + + public function testFindAlternativeExceptionMessageMultiple() + { + putenv('COLUMNS=120'); + $application = new Application(); + $application->add(new \FooCommand()); + $application->add(new \Foo1Command()); + $application->add(new \Foo2Command()); + + // Command + plural + try { + $application->find('foo:baR'); + $this->fail('->find() throws a CommandNotFoundException if command does not exist, with alternatives'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\Component\Console\Exception\CommandNotFoundException', $e, '->find() throws a CommandNotFoundException if command does not exist, with alternatives'); + $this->assertRegExp('/Did you mean one of these/', $e->getMessage(), '->find() throws a CommandNotFoundException if command does not exist, with alternatives'); + $this->assertRegExp('/foo1:bar/', $e->getMessage()); + $this->assertRegExp('/foo:bar/', $e->getMessage()); + } + + // Namespace + plural + try { + $application->find('foo2:bar'); + $this->fail('->find() throws a CommandNotFoundException if command does not exist, with alternatives'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\Component\Console\Exception\CommandNotFoundException', $e, '->find() throws a CommandNotFoundException if command does not exist, with alternatives'); + $this->assertRegExp('/Did you mean one of these/', $e->getMessage(), '->find() throws a CommandNotFoundException if command does not exist, with alternatives'); + $this->assertRegExp('/foo1/', $e->getMessage()); + } + + $application->add(new \Foo3Command()); + $application->add(new \Foo4Command()); + + // Subnamespace + plural + try { + $a = $application->find('foo3:'); + $this->fail('->find() should throw an Symfony\Component\Console\Exception\CommandNotFoundException if a command is ambiguous because of a subnamespace, with alternatives'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\Component\Console\Exception\CommandNotFoundException', $e); + $this->assertRegExp('/foo3:bar/', $e->getMessage()); + $this->assertRegExp('/foo3:bar:toh/', $e->getMessage()); + } + } + + public function testFindAlternativeCommands() + { + $application = new Application(); + + $application->add(new \FooCommand()); + $application->add(new \Foo1Command()); + $application->add(new \Foo2Command()); + + try { + $application->find($commandName = 'Unknown command'); + $this->fail('->find() throws a CommandNotFoundException if command does not exist'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\Component\Console\Exception\CommandNotFoundException', $e, '->find() throws a CommandNotFoundException if command does not exist'); + $this->assertSame([], $e->getAlternatives()); + $this->assertEquals(sprintf('Command "%s" is not defined.', $commandName), $e->getMessage(), '->find() throws a CommandNotFoundException if command does not exist, without alternatives'); + } + + // Test if "bar1" command throw a "CommandNotFoundException" and does not contain + // "foo:bar" as alternative because "bar1" is too far from "foo:bar" + try { + $application->find($commandName = 'bar1'); + $this->fail('->find() throws a CommandNotFoundException if command does not exist'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\Component\Console\Exception\CommandNotFoundException', $e, '->find() throws a CommandNotFoundException if command does not exist'); + $this->assertSame(['afoobar1', 'foo:bar1'], $e->getAlternatives()); + $this->assertRegExp(sprintf('/Command "%s" is not defined./', $commandName), $e->getMessage(), '->find() throws a CommandNotFoundException if command does not exist, with alternatives'); + $this->assertRegExp('/afoobar1/', $e->getMessage(), '->find() throws a CommandNotFoundException if command does not exist, with alternative : "afoobar1"'); + $this->assertRegExp('/foo:bar1/', $e->getMessage(), '->find() throws a CommandNotFoundException if command does not exist, with alternative : "foo:bar1"'); + $this->assertNotRegExp('/foo:bar(?>!1)/', $e->getMessage(), '->find() throws a CommandNotFoundException if command does not exist, without "foo:bar" alternative'); + } + } + + public function testFindAlternativeCommandsWithAnAlias() + { + $fooCommand = new \FooCommand(); + $fooCommand->setAliases(['foo2']); + + $application = new Application(); + $application->add($fooCommand); + + $result = $application->find('foo'); + + $this->assertSame($fooCommand, $result); + } + + public function testFindAlternativeNamespace() + { + $application = new Application(); + + $application->add(new \FooCommand()); + $application->add(new \Foo1Command()); + $application->add(new \Foo2Command()); + $application->add(new \Foo3Command()); + + try { + $application->find('Unknown-namespace:Unknown-command'); + $this->fail('->find() throws a CommandNotFoundException if namespace does not exist'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\Component\Console\Exception\CommandNotFoundException', $e, '->find() throws a CommandNotFoundException if namespace does not exist'); + $this->assertSame([], $e->getAlternatives()); + $this->assertEquals('There are no commands defined in the "Unknown-namespace" namespace.', $e->getMessage(), '->find() throws a CommandNotFoundException if namespace does not exist, without alternatives'); + } + + try { + $application->find('foo2:command'); + $this->fail('->find() throws a CommandNotFoundException if namespace does not exist'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\Component\Console\Exception\CommandNotFoundException', $e, '->find() throws a CommandNotFoundException if namespace does not exist'); + $this->assertCount(3, $e->getAlternatives()); + $this->assertContains('foo', $e->getAlternatives()); + $this->assertContains('foo1', $e->getAlternatives()); + $this->assertContains('foo3', $e->getAlternatives()); + $this->assertRegExp('/There are no commands defined in the "foo2" namespace./', $e->getMessage(), '->find() throws a CommandNotFoundException if namespace does not exist, with alternative'); + $this->assertRegExp('/foo/', $e->getMessage(), '->find() throws a CommandNotFoundException if namespace does not exist, with alternative : "foo"'); + $this->assertRegExp('/foo1/', $e->getMessage(), '->find() throws a CommandNotFoundException if namespace does not exist, with alternative : "foo1"'); + $this->assertRegExp('/foo3/', $e->getMessage(), '->find() throws a CommandNotFoundException if namespace does not exist, with alternative : "foo3"'); + } + } + + public function testFindAlternativesOutput() + { + $application = new Application(); + + $application->add(new \FooCommand()); + $application->add(new \Foo1Command()); + $application->add(new \Foo2Command()); + $application->add(new \Foo3Command()); + + $expectedAlternatives = [ + 'afoobar', + 'afoobar1', + 'afoobar2', + 'foo1:bar', + 'foo3:bar', + 'foo:bar', + 'foo:bar1', + ]; + + try { + $application->find('foo'); + $this->fail('->find() throws a CommandNotFoundException if command is not defined'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\Component\Console\Exception\CommandNotFoundException', $e, '->find() throws a CommandNotFoundException if command is not defined'); + $this->assertSame($expectedAlternatives, $e->getAlternatives()); + + $this->assertRegExp('/Command "foo" is not defined\..*Did you mean one of these\?.*/Ums', $e->getMessage()); + } + } + + public function testFindNamespaceDoesNotFailOnDeepSimilarNamespaces() + { + $application = $this->getMockBuilder('Symfony\Component\Console\Application')->setMethods(['getNamespaces'])->getMock(); + $application->expects($this->once()) + ->method('getNamespaces') + ->willReturn(['foo:sublong', 'bar:sub']); + + $this->assertEquals('foo:sublong', $application->findNamespace('f:sub')); + } + + /** + * @expectedException \Symfony\Component\Console\Exception\CommandNotFoundException + * @expectedExceptionMessage Command "foo::bar" is not defined. + */ + public function testFindWithDoubleColonInNameThrowsException() + { + $application = new Application(); + $application->add(new \FooCommand()); + $application->add(new \Foo4Command()); + $application->find('foo::bar'); + } + + public function testSetCatchExceptions() + { + $application = new Application(); + $application->setAutoExit(false); + putenv('COLUMNS=120'); + $tester = new ApplicationTester($application); + + $application->setCatchExceptions(true); + $this->assertTrue($application->areExceptionsCaught()); + + $tester->run(['command' => 'foo'], ['decorated' => false]); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception1.txt', $tester->getDisplay(true), '->setCatchExceptions() sets the catch exception flag'); + + $tester->run(['command' => 'foo'], ['decorated' => false, 'capture_stderr_separately' => true]); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception1.txt', $tester->getErrorOutput(true), '->setCatchExceptions() sets the catch exception flag'); + $this->assertSame('', $tester->getDisplay(true)); + + $application->setCatchExceptions(false); + try { + $tester->run(['command' => 'foo'], ['decorated' => false]); + $this->fail('->setCatchExceptions() sets the catch exception flag'); + } catch (\Exception $e) { + $this->assertInstanceOf('\Exception', $e, '->setCatchExceptions() sets the catch exception flag'); + $this->assertEquals('Command "foo" is not defined.', $e->getMessage(), '->setCatchExceptions() sets the catch exception flag'); + } + } + + public function testAutoExitSetting() + { + $application = new Application(); + $this->assertTrue($application->isAutoExitEnabled()); + + $application->setAutoExit(false); + $this->assertFalse($application->isAutoExitEnabled()); + } + + public function testRenderException() + { + $application = new Application(); + $application->setAutoExit(false); + putenv('COLUMNS=120'); + $tester = new ApplicationTester($application); + + $tester->run(['command' => 'foo'], ['decorated' => false, 'capture_stderr_separately' => true]); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception1.txt', $tester->getErrorOutput(true), '->renderException() renders a pretty exception'); + + $tester->run(['command' => 'foo'], ['decorated' => false, 'verbosity' => Output::VERBOSITY_VERBOSE, 'capture_stderr_separately' => true]); + $this->assertContains('Exception trace', $tester->getErrorOutput(), '->renderException() renders a pretty exception with a stack trace when verbosity is verbose'); + + $tester->run(['command' => 'list', '--foo' => true], ['decorated' => false, 'capture_stderr_separately' => true]); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception2.txt', $tester->getErrorOutput(true), '->renderException() renders the command synopsis when an exception occurs in the context of a command'); + + $application->add(new \Foo3Command()); + $tester = new ApplicationTester($application); + $tester->run(['command' => 'foo3:bar'], ['decorated' => false, 'capture_stderr_separately' => true]); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception3.txt', $tester->getErrorOutput(true), '->renderException() renders a pretty exceptions with previous exceptions'); + + $tester->run(['command' => 'foo3:bar'], ['decorated' => false, 'verbosity' => Output::VERBOSITY_VERBOSE]); + $this->assertRegExp('/\[Exception\]\s*First exception/', $tester->getDisplay(), '->renderException() renders a pretty exception without code exception when code exception is default and verbosity is verbose'); + $this->assertRegExp('/\[Exception\]\s*Second exception/', $tester->getDisplay(), '->renderException() renders a pretty exception without code exception when code exception is 0 and verbosity is verbose'); + $this->assertRegExp('/\[Exception \(404\)\]\s*Third exception/', $tester->getDisplay(), '->renderException() renders a pretty exception with code exception when code exception is 404 and verbosity is verbose'); + + $tester->run(['command' => 'foo3:bar'], ['decorated' => true]); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception3decorated.txt', $tester->getDisplay(true), '->renderException() renders a pretty exceptions with previous exceptions'); + + $tester->run(['command' => 'foo3:bar'], ['decorated' => true, 'capture_stderr_separately' => true]); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception3decorated.txt', $tester->getErrorOutput(true), '->renderException() renders a pretty exceptions with previous exceptions'); + + $application = new Application(); + $application->setAutoExit(false); + putenv('COLUMNS=32'); + $tester = new ApplicationTester($application); + + $tester->run(['command' => 'foo'], ['decorated' => false, 'capture_stderr_separately' => true]); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception4.txt', $tester->getErrorOutput(true), '->renderException() wraps messages when they are bigger than the terminal'); + putenv('COLUMNS=120'); + } + + public function testRenderExceptionWithDoubleWidthCharacters() + { + $application = new Application(); + $application->setAutoExit(false); + putenv('COLUMNS=120'); + $application->register('foo')->setCode(function () { + throw new \Exception('エラーメッセージ'); + }); + $tester = new ApplicationTester($application); + + $tester->run(['command' => 'foo'], ['decorated' => false, 'capture_stderr_separately' => true]); + $this->assertStringMatchesFormatFile(self::$fixturesPath.'/application_renderexception_doublewidth1.txt', $tester->getErrorOutput(true), '->renderException() renders a pretty exceptions with previous exceptions'); + + $tester->run(['command' => 'foo'], ['decorated' => true, 'capture_stderr_separately' => true]); + $this->assertStringMatchesFormatFile(self::$fixturesPath.'/application_renderexception_doublewidth1decorated.txt', $tester->getErrorOutput(true), '->renderException() renders a pretty exceptions with previous exceptions'); + + $application = new Application(); + $application->setAutoExit(false); + putenv('COLUMNS=32'); + $application->register('foo')->setCode(function () { + throw new \Exception('コマンドの実行中にエラーが発生しました。'); + }); + $tester = new ApplicationTester($application); + $tester->run(['command' => 'foo'], ['decorated' => false, 'capture_stderr_separately' => true]); + $this->assertStringMatchesFormatFile(self::$fixturesPath.'/application_renderexception_doublewidth2.txt', $tester->getErrorOutput(true), '->renderException() wraps messages when they are bigger than the terminal'); + putenv('COLUMNS=120'); + } + + public function testRenderExceptionEscapesLines() + { + $application = new Application(); + $application->setAutoExit(false); + putenv('COLUMNS=22'); + $application->register('foo')->setCode(function () { + throw new \Exception('dont break here !'); + }); + $tester = new ApplicationTester($application); + + $tester->run(['command' => 'foo'], ['decorated' => false]); + $this->assertStringMatchesFormatFile(self::$fixturesPath.'/application_renderexception_escapeslines.txt', $tester->getDisplay(true), '->renderException() escapes lines containing formatting'); + putenv('COLUMNS=120'); + } + + public function testRenderExceptionLineBreaks() + { + $application = $this->getMockBuilder('Symfony\Component\Console\Application')->setMethods(['getTerminalWidth'])->getMock(); + $application->setAutoExit(false); + $application->expects($this->any()) + ->method('getTerminalWidth') + ->willReturn(120); + $application->register('foo')->setCode(function () { + throw new \InvalidArgumentException("\n\nline 1 with extra spaces \nline 2\n\nline 4\n"); + }); + $tester = new ApplicationTester($application); + + $tester->run(['command' => 'foo'], ['decorated' => false]); + $this->assertStringMatchesFormatFile(self::$fixturesPath.'/application_renderexception_linebreaks.txt', $tester->getDisplay(true), '->renderException() keep multiple line breaks'); + } + + public function testRenderExceptionStackTraceContainsRootException() + { + $application = new Application(); + $application->setAutoExit(false); + $application->register('foo')->setCode(function () { + throw new \Exception('Verbose exception'); + }); + + $tester = new ApplicationTester($application); + $tester->run(['command' => 'foo'], ['decorated' => false, 'verbosity' => Output::VERBOSITY_VERBOSE]); + + $this->assertContains(sprintf('() at %s:', __FILE__), $tester->getDisplay()); + } + + public function testRun() + { + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + $application->add($command = new \Foo1Command()); + $_SERVER['argv'] = ['cli.php', 'foo:bar1']; + + ob_start(); + $application->run(); + ob_end_clean(); + + $this->assertInstanceOf('Symfony\Component\Console\Input\ArgvInput', $command->input, '->run() creates an ArgvInput by default if none is given'); + $this->assertInstanceOf('Symfony\Component\Console\Output\ConsoleOutput', $command->output, '->run() creates a ConsoleOutput by default if none is given'); + + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + + $this->ensureStaticCommandHelp($application); + $tester = new ApplicationTester($application); + + $tester->run([], ['decorated' => false]); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_run1.txt', $tester->getDisplay(true), '->run() runs the list command if no argument is passed'); + + $tester->run(['--help' => true], ['decorated' => false]); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_run2.txt', $tester->getDisplay(true), '->run() runs the help command if --help is passed'); + + $tester->run(['-h' => true], ['decorated' => false]); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_run2.txt', $tester->getDisplay(true), '->run() runs the help command if -h is passed'); + + $tester->run(['command' => 'list', '--help' => true], ['decorated' => false]); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_run3.txt', $tester->getDisplay(true), '->run() displays the help if --help is passed'); + + $tester->run(['command' => 'list', '-h' => true], ['decorated' => false]); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_run3.txt', $tester->getDisplay(true), '->run() displays the help if -h is passed'); + + $tester->run(['--ansi' => true]); + $this->assertTrue($tester->getOutput()->isDecorated(), '->run() forces color output if --ansi is passed'); + + $tester->run(['--no-ansi' => true]); + $this->assertFalse($tester->getOutput()->isDecorated(), '->run() forces color output to be disabled if --no-ansi is passed'); + + $tester->run(['--version' => true], ['decorated' => false]); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_run4.txt', $tester->getDisplay(true), '->run() displays the program version if --version is passed'); + + $tester->run(['-V' => true], ['decorated' => false]); + $this->assertStringEqualsFile(self::$fixturesPath.'/application_run4.txt', $tester->getDisplay(true), '->run() displays the program version if -v is passed'); + + $tester->run(['command' => 'list', '--quiet' => true]); + $this->assertSame('', $tester->getDisplay(), '->run() removes all output if --quiet is passed'); + $this->assertFalse($tester->getInput()->isInteractive(), '->run() sets off the interactive mode if --quiet is passed'); + + $tester->run(['command' => 'list', '-q' => true]); + $this->assertSame('', $tester->getDisplay(), '->run() removes all output if -q is passed'); + $this->assertFalse($tester->getInput()->isInteractive(), '->run() sets off the interactive mode if -q is passed'); + + $tester->run(['command' => 'list', '--verbose' => true]); + $this->assertSame(Output::VERBOSITY_VERBOSE, $tester->getOutput()->getVerbosity(), '->run() sets the output to verbose if --verbose is passed'); + + $tester->run(['command' => 'list', '--verbose' => 1]); + $this->assertSame(Output::VERBOSITY_VERBOSE, $tester->getOutput()->getVerbosity(), '->run() sets the output to verbose if --verbose=1 is passed'); + + $tester->run(['command' => 'list', '--verbose' => 2]); + $this->assertSame(Output::VERBOSITY_VERY_VERBOSE, $tester->getOutput()->getVerbosity(), '->run() sets the output to very verbose if --verbose=2 is passed'); + + $tester->run(['command' => 'list', '--verbose' => 3]); + $this->assertSame(Output::VERBOSITY_DEBUG, $tester->getOutput()->getVerbosity(), '->run() sets the output to debug if --verbose=3 is passed'); + + $tester->run(['command' => 'list', '--verbose' => 4]); + $this->assertSame(Output::VERBOSITY_VERBOSE, $tester->getOutput()->getVerbosity(), '->run() sets the output to verbose if unknown --verbose level is passed'); + + $tester->run(['command' => 'list', '-v' => true]); + $this->assertSame(Output::VERBOSITY_VERBOSE, $tester->getOutput()->getVerbosity(), '->run() sets the output to verbose if -v is passed'); + + $tester->run(['command' => 'list', '-vv' => true]); + $this->assertSame(Output::VERBOSITY_VERY_VERBOSE, $tester->getOutput()->getVerbosity(), '->run() sets the output to verbose if -v is passed'); + + $tester->run(['command' => 'list', '-vvv' => true]); + $this->assertSame(Output::VERBOSITY_DEBUG, $tester->getOutput()->getVerbosity(), '->run() sets the output to verbose if -v is passed'); + + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + $application->add(new \FooCommand()); + $tester = new ApplicationTester($application); + + $tester->run(['command' => 'foo:bar', '--no-interaction' => true], ['decorated' => false]); + $this->assertSame('called'.PHP_EOL, $tester->getDisplay(), '->run() does not call interact() if --no-interaction is passed'); + + $tester->run(['command' => 'foo:bar', '-n' => true], ['decorated' => false]); + $this->assertSame('called'.PHP_EOL, $tester->getDisplay(), '->run() does not call interact() if -n is passed'); + } + + public function testRunWithGlobalOptionAndNoCommand() + { + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + $application->getDefinition()->addOption(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL)); + + $output = new StreamOutput(fopen('php://memory', 'w', false)); + $input = new ArgvInput(['cli.php', '--foo', 'bar']); + + $this->assertSame(0, $application->run($input, $output)); + } + + /** + * Issue #9285. + * + * If the "verbose" option is just before an argument in ArgvInput, + * an argument value should not be treated as verbosity value. + * This test will fail with "Not enough arguments." if broken + */ + public function testVerboseValueNotBreakArguments() + { + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + $application->add(new \FooCommand()); + + $output = new StreamOutput(fopen('php://memory', 'w', false)); + + $input = new ArgvInput(['cli.php', '-v', 'foo:bar']); + $application->run($input, $output); + + $this->addToAssertionCount(1); + + $input = new ArgvInput(['cli.php', '--verbose', 'foo:bar']); + $application->run($input, $output); + + $this->addToAssertionCount(1); + } + + public function testRunReturnsIntegerExitCode() + { + $exception = new \Exception('', 4); + + $application = $this->getMockBuilder('Symfony\Component\Console\Application')->setMethods(['doRun'])->getMock(); + $application->setAutoExit(false); + $application->expects($this->once()) + ->method('doRun') + ->willThrowException($exception); + + $exitCode = $application->run(new ArrayInput([]), new NullOutput()); + + $this->assertSame(4, $exitCode, '->run() returns integer exit code extracted from raised exception'); + } + + public function testRunDispatchesIntegerExitCode() + { + $passedRightValue = false; + + // We can assume here that some other test asserts that the event is dispatched at all + $dispatcher = new EventDispatcher(); + $dispatcher->addListener('console.terminate', function (ConsoleTerminateEvent $event) use (&$passedRightValue) { + $passedRightValue = (4 === $event->getExitCode()); + }); + + $application = new Application(); + $application->setDispatcher($dispatcher); + $application->setAutoExit(false); + + $application->register('test')->setCode(function (InputInterface $input, OutputInterface $output) { + throw new \Exception('', 4); + }); + + $tester = new ApplicationTester($application); + $tester->run(['command' => 'test']); + + $this->assertTrue($passedRightValue, '-> exit code 4 was passed in the console.terminate event'); + } + + public function testRunReturnsExitCodeOneForExceptionCodeZero() + { + $exception = new \Exception('', 0); + + $application = $this->getMockBuilder('Symfony\Component\Console\Application')->setMethods(['doRun'])->getMock(); + $application->setAutoExit(false); + $application->expects($this->once()) + ->method('doRun') + ->willThrowException($exception); + + $exitCode = $application->run(new ArrayInput([]), new NullOutput()); + + $this->assertSame(1, $exitCode, '->run() returns exit code 1 when exception code is 0'); + } + + public function testRunDispatchesExitCodeOneForExceptionCodeZero() + { + $passedRightValue = false; + + // We can assume here that some other test asserts that the event is dispatched at all + $dispatcher = new EventDispatcher(); + $dispatcher->addListener('console.terminate', function (ConsoleTerminateEvent $event) use (&$passedRightValue) { + $passedRightValue = (1 === $event->getExitCode()); + }); + + $application = new Application(); + $application->setDispatcher($dispatcher); + $application->setAutoExit(false); + + $application->register('test')->setCode(function (InputInterface $input, OutputInterface $output) { + throw new \Exception(); + }); + + $tester = new ApplicationTester($application); + $tester->run(['command' => 'test']); + + $this->assertTrue($passedRightValue, '-> exit code 1 was passed in the console.terminate event'); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage An option with shortcut "e" already exists. + */ + public function testAddingOptionWithDuplicateShortcut() + { + $dispatcher = new EventDispatcher(); + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + $application->setDispatcher($dispatcher); + + $application->getDefinition()->addOption(new InputOption('--env', '-e', InputOption::VALUE_REQUIRED, 'Environment')); + + $application + ->register('foo') + ->setAliases(['f']) + ->setDefinition([new InputOption('survey', 'e', InputOption::VALUE_REQUIRED, 'My option with a shortcut.')]) + ->setCode(function (InputInterface $input, OutputInterface $output) {}) + ; + + $input = new ArrayInput(['command' => 'foo']); + $output = new NullOutput(); + + $application->run($input, $output); + } + + /** + * @expectedException \LogicException + * @dataProvider getAddingAlreadySetDefinitionElementData + */ + public function testAddingAlreadySetDefinitionElementData($def) + { + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + $application + ->register('foo') + ->setDefinition([$def]) + ->setCode(function (InputInterface $input, OutputInterface $output) {}) + ; + + $input = new ArrayInput(['command' => 'foo']); + $output = new NullOutput(); + $application->run($input, $output); + } + + public function getAddingAlreadySetDefinitionElementData() + { + return [ + [new InputArgument('command', InputArgument::REQUIRED)], + [new InputOption('quiet', '', InputOption::VALUE_NONE)], + [new InputOption('query', 'q', InputOption::VALUE_NONE)], + ]; + } + + public function testGetDefaultHelperSetReturnsDefaultValues() + { + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + + $helperSet = $application->getHelperSet(); + + $this->assertTrue($helperSet->has('formatter')); + } + + public function testAddingSingleHelperSetOverwritesDefaultValues() + { + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + + $application->setHelperSet(new HelperSet([new FormatterHelper()])); + + $helperSet = $application->getHelperSet(); + + $this->assertTrue($helperSet->has('formatter')); + + // no other default helper set should be returned + $this->assertFalse($helperSet->has('dialog')); + $this->assertFalse($helperSet->has('progress')); + } + + public function testOverwritingDefaultHelperSetOverwritesDefaultValues() + { + $application = new CustomApplication(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + + $application->setHelperSet(new HelperSet([new FormatterHelper()])); + + $helperSet = $application->getHelperSet(); + + $this->assertTrue($helperSet->has('formatter')); + + // no other default helper set should be returned + $this->assertFalse($helperSet->has('dialog')); + $this->assertFalse($helperSet->has('progress')); + } + + public function testGetDefaultInputDefinitionReturnsDefaultValues() + { + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + + $inputDefinition = $application->getDefinition(); + + $this->assertTrue($inputDefinition->hasArgument('command')); + + $this->assertTrue($inputDefinition->hasOption('help')); + $this->assertTrue($inputDefinition->hasOption('quiet')); + $this->assertTrue($inputDefinition->hasOption('verbose')); + $this->assertTrue($inputDefinition->hasOption('version')); + $this->assertTrue($inputDefinition->hasOption('ansi')); + $this->assertTrue($inputDefinition->hasOption('no-ansi')); + $this->assertTrue($inputDefinition->hasOption('no-interaction')); + } + + public function testOverwritingDefaultInputDefinitionOverwritesDefaultValues() + { + $application = new CustomApplication(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + + $inputDefinition = $application->getDefinition(); + + // check whether the default arguments and options are not returned any more + $this->assertFalse($inputDefinition->hasArgument('command')); + + $this->assertFalse($inputDefinition->hasOption('help')); + $this->assertFalse($inputDefinition->hasOption('quiet')); + $this->assertFalse($inputDefinition->hasOption('verbose')); + $this->assertFalse($inputDefinition->hasOption('version')); + $this->assertFalse($inputDefinition->hasOption('ansi')); + $this->assertFalse($inputDefinition->hasOption('no-ansi')); + $this->assertFalse($inputDefinition->hasOption('no-interaction')); + + $this->assertTrue($inputDefinition->hasOption('custom')); + } + + public function testSettingCustomInputDefinitionOverwritesDefaultValues() + { + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + + $application->setDefinition(new InputDefinition([new InputOption('--custom', '-c', InputOption::VALUE_NONE, 'Set the custom input definition.')])); + + $inputDefinition = $application->getDefinition(); + + // check whether the default arguments and options are not returned any more + $this->assertFalse($inputDefinition->hasArgument('command')); + + $this->assertFalse($inputDefinition->hasOption('help')); + $this->assertFalse($inputDefinition->hasOption('quiet')); + $this->assertFalse($inputDefinition->hasOption('verbose')); + $this->assertFalse($inputDefinition->hasOption('version')); + $this->assertFalse($inputDefinition->hasOption('ansi')); + $this->assertFalse($inputDefinition->hasOption('no-ansi')); + $this->assertFalse($inputDefinition->hasOption('no-interaction')); + + $this->assertTrue($inputDefinition->hasOption('custom')); + } + + public function testRunWithDispatcher() + { + $application = new Application(); + $application->setAutoExit(false); + $application->setDispatcher($this->getDispatcher()); + + $application->register('foo')->setCode(function (InputInterface $input, OutputInterface $output) { + $output->write('foo.'); + }); + + $tester = new ApplicationTester($application); + $tester->run(['command' => 'foo']); + $this->assertEquals('before.foo.after.'.PHP_EOL, $tester->getDisplay()); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage error + */ + public function testRunWithExceptionAndDispatcher() + { + $application = new Application(); + $application->setDispatcher($this->getDispatcher()); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + + $application->register('foo')->setCode(function (InputInterface $input, OutputInterface $output) { + throw new \RuntimeException('foo'); + }); + + $tester = new ApplicationTester($application); + $tester->run(['command' => 'foo']); + } + + public function testRunDispatchesAllEventsWithException() + { + $application = new Application(); + $application->setDispatcher($this->getDispatcher()); + $application->setAutoExit(false); + + $application->register('foo')->setCode(function (InputInterface $input, OutputInterface $output) { + $output->write('foo.'); + + throw new \RuntimeException('foo'); + }); + + $tester = new ApplicationTester($application); + $tester->run(['command' => 'foo']); + $this->assertContains('before.foo.error.after.', $tester->getDisplay()); + } + + public function testRunDispatchesAllEventsWithExceptionInListener() + { + $dispatcher = $this->getDispatcher(); + $dispatcher->addListener('console.command', function () { + throw new \RuntimeException('foo'); + }); + + $application = new Application(); + $application->setDispatcher($dispatcher); + $application->setAutoExit(false); + + $application->register('foo')->setCode(function (InputInterface $input, OutputInterface $output) { + $output->write('foo.'); + }); + + $tester = new ApplicationTester($application); + $tester->run(['command' => 'foo']); + $this->assertContains('before.error.after.', $tester->getDisplay()); + } + + /** + * @requires PHP 7 + */ + public function testRunWithError() + { + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + + $application->register('dym')->setCode(function (InputInterface $input, OutputInterface $output) { + $output->write('dym.'); + + throw new \Error('dymerr'); + }); + + $tester = new ApplicationTester($application); + + try { + $tester->run(['command' => 'dym']); + $this->fail('Error expected.'); + } catch (\Error $e) { + $this->assertSame('dymerr', $e->getMessage()); + } + } + + public function testRunAllowsErrorListenersToSilenceTheException() + { + $dispatcher = $this->getDispatcher(); + $dispatcher->addListener('console.error', function (ConsoleErrorEvent $event) { + $event->getOutput()->write('silenced.'); + + $event->setExitCode(0); + }); + + $dispatcher->addListener('console.command', function () { + throw new \RuntimeException('foo'); + }); + + $application = new Application(); + $application->setDispatcher($dispatcher); + $application->setAutoExit(false); + + $application->register('foo')->setCode(function (InputInterface $input, OutputInterface $output) { + $output->write('foo.'); + }); + + $tester = new ApplicationTester($application); + $tester->run(['command' => 'foo']); + $this->assertContains('before.error.silenced.after.', $tester->getDisplay()); + $this->assertEquals(ConsoleCommandEvent::RETURN_CODE_DISABLED, $tester->getStatusCode()); + } + + public function testConsoleErrorEventIsTriggeredOnCommandNotFound() + { + $dispatcher = new EventDispatcher(); + $dispatcher->addListener('console.error', function (ConsoleErrorEvent $event) { + $this->assertNull($event->getCommand()); + $this->assertInstanceOf(CommandNotFoundException::class, $event->getError()); + $event->getOutput()->write('silenced command not found'); + }); + + $application = new Application(); + $application->setDispatcher($dispatcher); + $application->setAutoExit(false); + + $tester = new ApplicationTester($application); + $tester->run(['command' => 'unknown']); + $this->assertContains('silenced command not found', $tester->getDisplay()); + $this->assertEquals(1, $tester->getStatusCode()); + } + + /** + * @group legacy + * @expectedDeprecation The "ConsoleEvents::EXCEPTION" event is deprecated since Symfony 3.3 and will be removed in 4.0. Listen to the "ConsoleEvents::ERROR" event instead. + */ + public function testLegacyExceptionListenersAreStillTriggered() + { + $dispatcher = $this->getDispatcher(); + $dispatcher->addListener('console.exception', function (ConsoleExceptionEvent $event) { + $event->getOutput()->write('caught.'); + + $event->setException(new \RuntimeException('replaced in caught.')); + }); + + $application = new Application(); + $application->setDispatcher($dispatcher); + $application->setAutoExit(false); + + $application->register('foo')->setCode(function (InputInterface $input, OutputInterface $output) { + throw new \RuntimeException('foo'); + }); + + $tester = new ApplicationTester($application); + $tester->run(['command' => 'foo']); + $this->assertContains('before.caught.error.after.', $tester->getDisplay()); + $this->assertContains('replaced in caught.', $tester->getDisplay()); + } + + /** + * @requires PHP 7 + */ + public function testErrorIsRethrownIfNotHandledByConsoleErrorEvent() + { + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + $application->setDispatcher(new EventDispatcher()); + + $application->register('dym')->setCode(function (InputInterface $input, OutputInterface $output) { + new \UnknownClass(); + }); + + $tester = new ApplicationTester($application); + + try { + $tester->run(['command' => 'dym']); + $this->fail('->run() should rethrow PHP errors if not handled via ConsoleErrorEvent.'); + } catch (\Error $e) { + $this->assertSame($e->getMessage(), 'Class \'UnknownClass\' not found'); + } + } + + /** + * @requires PHP 7 + * @expectedException \LogicException + * @expectedExceptionMessage error + */ + public function testRunWithErrorAndDispatcher() + { + $application = new Application(); + $application->setDispatcher($this->getDispatcher()); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + + $application->register('dym')->setCode(function (InputInterface $input, OutputInterface $output) { + $output->write('dym.'); + + throw new \Error('dymerr'); + }); + + $tester = new ApplicationTester($application); + $tester->run(['command' => 'dym']); + $this->assertContains('before.dym.error.after.', $tester->getDisplay(), 'The PHP Error did not dispached events'); + } + + /** + * @requires PHP 7 + */ + public function testRunDispatchesAllEventsWithError() + { + $application = new Application(); + $application->setDispatcher($this->getDispatcher()); + $application->setAutoExit(false); + + $application->register('dym')->setCode(function (InputInterface $input, OutputInterface $output) { + $output->write('dym.'); + + throw new \Error('dymerr'); + }); + + $tester = new ApplicationTester($application); + $tester->run(['command' => 'dym']); + $this->assertContains('before.dym.error.after.', $tester->getDisplay(), 'The PHP Error did not dispached events'); + } + + /** + * @requires PHP 7 + */ + public function testRunWithErrorFailingStatusCode() + { + $application = new Application(); + $application->setDispatcher($this->getDispatcher()); + $application->setAutoExit(false); + + $application->register('dus')->setCode(function (InputInterface $input, OutputInterface $output) { + $output->write('dus.'); + + throw new \Error('duserr'); + }); + + $tester = new ApplicationTester($application); + $tester->run(['command' => 'dus']); + $this->assertSame(1, $tester->getStatusCode(), 'Status code should be 1'); + } + + public function testRunWithDispatcherSkippingCommand() + { + $application = new Application(); + $application->setDispatcher($this->getDispatcher(true)); + $application->setAutoExit(false); + + $application->register('foo')->setCode(function (InputInterface $input, OutputInterface $output) { + $output->write('foo.'); + }); + + $tester = new ApplicationTester($application); + $exitCode = $tester->run(['command' => 'foo']); + $this->assertContains('before.after.', $tester->getDisplay()); + $this->assertEquals(ConsoleCommandEvent::RETURN_CODE_DISABLED, $exitCode); + } + + public function testRunWithDispatcherAccessingInputOptions() + { + $noInteractionValue = null; + $quietValue = null; + + $dispatcher = $this->getDispatcher(); + $dispatcher->addListener('console.command', function (ConsoleCommandEvent $event) use (&$noInteractionValue, &$quietValue) { + $input = $event->getInput(); + + $noInteractionValue = $input->getOption('no-interaction'); + $quietValue = $input->getOption('quiet'); + }); + + $application = new Application(); + $application->setDispatcher($dispatcher); + $application->setAutoExit(false); + + $application->register('foo')->setCode(function (InputInterface $input, OutputInterface $output) { + $output->write('foo.'); + }); + + $tester = new ApplicationTester($application); + $tester->run(['command' => 'foo', '--no-interaction' => true]); + + $this->assertTrue($noInteractionValue); + $this->assertFalse($quietValue); + } + + public function testRunWithDispatcherAddingInputOptions() + { + $extraValue = null; + + $dispatcher = $this->getDispatcher(); + $dispatcher->addListener('console.command', function (ConsoleCommandEvent $event) use (&$extraValue) { + $definition = $event->getCommand()->getDefinition(); + $input = $event->getInput(); + + $definition->addOption(new InputOption('extra', null, InputOption::VALUE_REQUIRED)); + $input->bind($definition); + + $extraValue = $input->getOption('extra'); + }); + + $application = new Application(); + $application->setDispatcher($dispatcher); + $application->setAutoExit(false); + + $application->register('foo')->setCode(function (InputInterface $input, OutputInterface $output) { + $output->write('foo.'); + }); + + $tester = new ApplicationTester($application); + $tester->run(['command' => 'foo', '--extra' => 'some test value']); + + $this->assertEquals('some test value', $extraValue); + } + + /** + * @group legacy + */ + public function testTerminalDimensions() + { + $application = new Application(); + $originalDimensions = $application->getTerminalDimensions(); + $this->assertCount(2, $originalDimensions); + + $width = 80; + if ($originalDimensions[0] == $width) { + $width = 100; + } + + $application->setTerminalDimensions($width, 80); + $this->assertSame([$width, 80], $application->getTerminalDimensions()); + } + + public function testSetRunCustomDefaultCommand() + { + $command = new \FooCommand(); + + $application = new Application(); + $application->setAutoExit(false); + $application->add($command); + $application->setDefaultCommand($command->getName()); + + $tester = new ApplicationTester($application); + $tester->run([], ['interactive' => false]); + $this->assertEquals('called'.PHP_EOL, $tester->getDisplay(), 'Application runs the default set command if different from \'list\' command'); + + $application = new CustomDefaultCommandApplication(); + $application->setAutoExit(false); + + $tester = new ApplicationTester($application); + $tester->run([], ['interactive' => false]); + + $this->assertEquals('called'.PHP_EOL, $tester->getDisplay(), 'Application runs the default set command if different from \'list\' command'); + } + + public function testSetRunCustomDefaultCommandWithOption() + { + $command = new \FooOptCommand(); + + $application = new Application(); + $application->setAutoExit(false); + $application->add($command); + $application->setDefaultCommand($command->getName()); + + $tester = new ApplicationTester($application); + $tester->run(['--fooopt' => 'opt'], ['interactive' => false]); + + $this->assertEquals('called'.PHP_EOL.'opt'.PHP_EOL, $tester->getDisplay(), 'Application runs the default set command if different from \'list\' command'); + } + + public function testSetRunCustomSingleCommand() + { + $command = new \FooCommand(); + + $application = new Application(); + $application->setAutoExit(false); + $application->add($command); + $application->setDefaultCommand($command->getName(), true); + + $tester = new ApplicationTester($application); + + $tester->run([]); + $this->assertContains('called', $tester->getDisplay()); + + $tester->run(['--help' => true]); + $this->assertContains('The foo:bar command', $tester->getDisplay()); + } + + /** + * @requires function posix_isatty + */ + public function testCanCheckIfTerminalIsInteractive() + { + $application = new CustomDefaultCommandApplication(); + $application->setAutoExit(false); + + $tester = new ApplicationTester($application); + $tester->run(['command' => 'help']); + + $this->assertFalse($tester->getInput()->hasParameterOption(['--no-interaction', '-n'])); + + $inputStream = $tester->getInput()->getStream(); + $this->assertEquals($tester->getInput()->isInteractive(), @posix_isatty($inputStream)); + } + + public function testRunLazyCommandService() + { + $container = new ContainerBuilder(); + $container->addCompilerPass(new AddConsoleCommandPass()); + $container + ->register('lazy-command', LazyCommand::class) + ->addTag('console.command', ['command' => 'lazy:command']) + ->addTag('console.command', ['command' => 'lazy:alias']) + ->addTag('console.command', ['command' => 'lazy:alias2']); + $container->compile(); + + $application = new Application(); + $application->setCommandLoader($container->get('console.command_loader')); + $application->setAutoExit(false); + + $tester = new ApplicationTester($application); + + $tester->run(['command' => 'lazy:command']); + $this->assertSame("lazy-command called\n", $tester->getDisplay(true)); + + $tester->run(['command' => 'lazy:alias']); + $this->assertSame("lazy-command called\n", $tester->getDisplay(true)); + + $tester->run(['command' => 'lazy:alias2']); + $this->assertSame("lazy-command called\n", $tester->getDisplay(true)); + + $command = $application->get('lazy:command'); + $this->assertSame(['lazy:alias', 'lazy:alias2'], $command->getAliases()); + } + + /** + * @expectedException \Symfony\Component\Console\Exception\CommandNotFoundException + */ + public function testGetDisabledLazyCommand() + { + $application = new Application(); + $application->setCommandLoader(new FactoryCommandLoader(['disabled' => function () { return new DisabledCommand(); }])); + $application->get('disabled'); + } + + public function testHasReturnsFalseForDisabledLazyCommand() + { + $application = new Application(); + $application->setCommandLoader(new FactoryCommandLoader(['disabled' => function () { return new DisabledCommand(); }])); + $this->assertFalse($application->has('disabled')); + } + + public function testAllExcludesDisabledLazyCommand() + { + $application = new Application(); + $application->setCommandLoader(new FactoryCommandLoader(['disabled' => function () { return new DisabledCommand(); }])); + $this->assertArrayNotHasKey('disabled', $application->all()); + } + + protected function getDispatcher($skipCommand = false) + { + $dispatcher = new EventDispatcher(); + $dispatcher->addListener('console.command', function (ConsoleCommandEvent $event) use ($skipCommand) { + $event->getOutput()->write('before.'); + + if ($skipCommand) { + $event->disableCommand(); + } + }); + $dispatcher->addListener('console.terminate', function (ConsoleTerminateEvent $event) use ($skipCommand) { + $event->getOutput()->writeln('after.'); + + if (!$skipCommand) { + $event->setExitCode(ConsoleCommandEvent::RETURN_CODE_DISABLED); + } + }); + $dispatcher->addListener('console.error', function (ConsoleErrorEvent $event) { + $event->getOutput()->write('error.'); + + $event->setError(new \LogicException('error.', $event->getExitCode(), $event->getError())); + }); + + return $dispatcher; + } + + /** + * @requires PHP 7 + */ + public function testErrorIsRethrownIfNotHandledByConsoleErrorEventWithCatchingEnabled() + { + $application = new Application(); + $application->setAutoExit(false); + $application->setDispatcher(new EventDispatcher()); + + $application->register('dym')->setCode(function (InputInterface $input, OutputInterface $output) { + new \UnknownClass(); + }); + + $tester = new ApplicationTester($application); + + try { + $tester->run(['command' => 'dym']); + $this->fail('->run() should rethrow PHP errors if not handled via ConsoleErrorEvent.'); + } catch (\Error $e) { + $this->assertSame($e->getMessage(), 'Class \'UnknownClass\' not found'); + } + } +} + +class CustomApplication extends Application +{ + /** + * Overwrites the default input definition. + * + * @return InputDefinition An InputDefinition instance + */ + protected function getDefaultInputDefinition() + { + return new InputDefinition([new InputOption('--custom', '-c', InputOption::VALUE_NONE, 'Set the custom input definition.')]); + } + + /** + * Gets the default helper set with the helpers that should always be available. + * + * @return HelperSet A HelperSet instance + */ + protected function getDefaultHelperSet() + { + return new HelperSet([new FormatterHelper()]); + } +} + +class CustomDefaultCommandApplication extends Application +{ + /** + * Overwrites the constructor in order to set a different default command. + */ + public function __construct() + { + parent::__construct(); + + $command = new \FooCommand(); + $this->add($command); + $this->setDefaultCommand($command->getName()); + } +} + +class LazyCommand extends Command +{ + public function execute(InputInterface $input, OutputInterface $output) + { + $output->writeln('lazy-command called'); + } +} + +class DisabledCommand extends Command +{ + public function isEnabled() + { + return false; + } +} diff --git a/lib/symfony/console/Tests/Command/CommandTest.php b/lib/symfony/console/Tests/Command/CommandTest.php new file mode 100644 index 000000000..9a135d325 --- /dev/null +++ b/lib/symfony/console/Tests/Command/CommandTest.php @@ -0,0 +1,442 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Command; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Helper\FormatterHelper; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\StringInput; +use Symfony\Component\Console\Output\NullOutput; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Tester\CommandTester; + +class CommandTest extends TestCase +{ + protected static $fixturesPath; + + public static function setUpBeforeClass() + { + self::$fixturesPath = __DIR__.'/../Fixtures/'; + require_once self::$fixturesPath.'/TestCommand.php'; + } + + public function testConstructor() + { + $command = new Command('foo:bar'); + $this->assertEquals('foo:bar', $command->getName(), '__construct() takes the command name as its first argument'); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage The command defined in "Symfony\Component\Console\Command\Command" cannot have an empty name. + */ + public function testCommandNameCannotBeEmpty() + { + (new Application())->add(new Command()); + } + + public function testSetApplication() + { + $application = new Application(); + $command = new \TestCommand(); + $command->setApplication($application); + $this->assertEquals($application, $command->getApplication(), '->setApplication() sets the current application'); + $this->assertEquals($application->getHelperSet(), $command->getHelperSet()); + } + + public function testSetApplicationNull() + { + $command = new \TestCommand(); + $command->setApplication(null); + $this->assertNull($command->getHelperSet()); + } + + public function testSetGetDefinition() + { + $command = new \TestCommand(); + $ret = $command->setDefinition($definition = new InputDefinition()); + $this->assertEquals($command, $ret, '->setDefinition() implements a fluent interface'); + $this->assertEquals($definition, $command->getDefinition(), '->setDefinition() sets the current InputDefinition instance'); + $command->setDefinition([new InputArgument('foo'), new InputOption('bar')]); + $this->assertTrue($command->getDefinition()->hasArgument('foo'), '->setDefinition() also takes an array of InputArguments and InputOptions as an argument'); + $this->assertTrue($command->getDefinition()->hasOption('bar'), '->setDefinition() also takes an array of InputArguments and InputOptions as an argument'); + $command->setDefinition(new InputDefinition()); + } + + public function testAddArgument() + { + $command = new \TestCommand(); + $ret = $command->addArgument('foo'); + $this->assertEquals($command, $ret, '->addArgument() implements a fluent interface'); + $this->assertTrue($command->getDefinition()->hasArgument('foo'), '->addArgument() adds an argument to the command'); + } + + public function testAddOption() + { + $command = new \TestCommand(); + $ret = $command->addOption('foo'); + $this->assertEquals($command, $ret, '->addOption() implements a fluent interface'); + $this->assertTrue($command->getDefinition()->hasOption('foo'), '->addOption() adds an option to the command'); + } + + public function testSetHidden() + { + $command = new \TestCommand(); + $command->setHidden(true); + $this->assertTrue($command->isHidden()); + } + + public function testGetNamespaceGetNameSetName() + { + $command = new \TestCommand(); + $this->assertEquals('namespace:name', $command->getName(), '->getName() returns the command name'); + $command->setName('foo'); + $this->assertEquals('foo', $command->getName(), '->setName() sets the command name'); + + $ret = $command->setName('foobar:bar'); + $this->assertEquals($command, $ret, '->setName() implements a fluent interface'); + $this->assertEquals('foobar:bar', $command->getName(), '->setName() sets the command name'); + } + + /** + * @dataProvider provideInvalidCommandNames + */ + public function testInvalidCommandNames($name) + { + if (method_exists($this, 'expectException')) { + $this->expectException('InvalidArgumentException'); + $this->expectExceptionMessage(sprintf('Command name "%s" is invalid.', $name)); + } else { + $this->setExpectedException('InvalidArgumentException', sprintf('Command name "%s" is invalid.', $name)); + } + + $command = new \TestCommand(); + $command->setName($name); + } + + public function provideInvalidCommandNames() + { + return [ + [''], + ['foo:'], + ]; + } + + public function testGetSetDescription() + { + $command = new \TestCommand(); + $this->assertEquals('description', $command->getDescription(), '->getDescription() returns the description'); + $ret = $command->setDescription('description1'); + $this->assertEquals($command, $ret, '->setDescription() implements a fluent interface'); + $this->assertEquals('description1', $command->getDescription(), '->setDescription() sets the description'); + } + + public function testGetSetHelp() + { + $command = new \TestCommand(); + $this->assertEquals('help', $command->getHelp(), '->getHelp() returns the help'); + $ret = $command->setHelp('help1'); + $this->assertEquals($command, $ret, '->setHelp() implements a fluent interface'); + $this->assertEquals('help1', $command->getHelp(), '->setHelp() sets the help'); + $command->setHelp(''); + $this->assertEquals('', $command->getHelp(), '->getHelp() does not fall back to the description'); + } + + public function testGetProcessedHelp() + { + $command = new \TestCommand(); + $command->setHelp('The %command.name% command does... Example: php %command.full_name%.'); + $this->assertContains('The namespace:name command does...', $command->getProcessedHelp(), '->getProcessedHelp() replaces %command.name% correctly'); + $this->assertNotContains('%command.full_name%', $command->getProcessedHelp(), '->getProcessedHelp() replaces %command.full_name%'); + + $command = new \TestCommand(); + $command->setHelp(''); + $this->assertContains('description', $command->getProcessedHelp(), '->getProcessedHelp() falls back to the description'); + + $command = new \TestCommand(); + $command->setHelp('The %command.name% command does... Example: php %command.full_name%.'); + $application = new Application(); + $application->add($command); + $application->setDefaultCommand('namespace:name', true); + $this->assertContains('The namespace:name command does...', $command->getProcessedHelp(), '->getProcessedHelp() replaces %command.name% correctly in single command applications'); + $this->assertNotContains('%command.full_name%', $command->getProcessedHelp(), '->getProcessedHelp() replaces %command.full_name% in single command applications'); + } + + public function testGetSetAliases() + { + $command = new \TestCommand(); + $this->assertEquals(['name'], $command->getAliases(), '->getAliases() returns the aliases'); + $ret = $command->setAliases(['name1']); + $this->assertEquals($command, $ret, '->setAliases() implements a fluent interface'); + $this->assertEquals(['name1'], $command->getAliases(), '->setAliases() sets the aliases'); + } + + public function testSetAliasesNull() + { + $command = new \TestCommand(); + $this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('InvalidArgumentException'); + $command->setAliases(null); + } + + public function testGetSynopsis() + { + $command = new \TestCommand(); + $command->addOption('foo'); + $command->addArgument('bar'); + $this->assertEquals('namespace:name [--foo] [--] []', $command->getSynopsis(), '->getSynopsis() returns the synopsis'); + } + + public function testAddGetUsages() + { + $command = new \TestCommand(); + $command->addUsage('foo1'); + $command->addUsage('foo2'); + $this->assertContains('namespace:name foo1', $command->getUsages()); + $this->assertContains('namespace:name foo2', $command->getUsages()); + } + + public function testGetHelper() + { + $application = new Application(); + $command = new \TestCommand(); + $command->setApplication($application); + $formatterHelper = new FormatterHelper(); + $this->assertEquals($formatterHelper->getName(), $command->getHelper('formatter')->getName(), '->getHelper() returns the correct helper'); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage Cannot retrieve helper "formatter" because there is no HelperSet defined. + */ + public function testGetHelperWithoutHelperSet() + { + $command = new \TestCommand(); + $command->getHelper('formatter'); + } + + public function testMergeApplicationDefinition() + { + $application1 = new Application(); + $application1->getDefinition()->addArguments([new InputArgument('foo')]); + $application1->getDefinition()->addOptions([new InputOption('bar')]); + $command = new \TestCommand(); + $command->setApplication($application1); + $command->setDefinition($definition = new InputDefinition([new InputArgument('bar'), new InputOption('foo')])); + + $r = new \ReflectionObject($command); + $m = $r->getMethod('mergeApplicationDefinition'); + $m->setAccessible(true); + $m->invoke($command); + $this->assertTrue($command->getDefinition()->hasArgument('foo'), '->mergeApplicationDefinition() merges the application arguments and the command arguments'); + $this->assertTrue($command->getDefinition()->hasArgument('bar'), '->mergeApplicationDefinition() merges the application arguments and the command arguments'); + $this->assertTrue($command->getDefinition()->hasOption('foo'), '->mergeApplicationDefinition() merges the application options and the command options'); + $this->assertTrue($command->getDefinition()->hasOption('bar'), '->mergeApplicationDefinition() merges the application options and the command options'); + + $m->invoke($command); + $this->assertEquals(3, $command->getDefinition()->getArgumentCount(), '->mergeApplicationDefinition() does not try to merge twice the application arguments and options'); + } + + public function testMergeApplicationDefinitionWithoutArgsThenWithArgsAddsArgs() + { + $application1 = new Application(); + $application1->getDefinition()->addArguments([new InputArgument('foo')]); + $application1->getDefinition()->addOptions([new InputOption('bar')]); + $command = new \TestCommand(); + $command->setApplication($application1); + $command->setDefinition($definition = new InputDefinition([])); + + $r = new \ReflectionObject($command); + $m = $r->getMethod('mergeApplicationDefinition'); + $m->setAccessible(true); + $m->invoke($command, false); + $this->assertTrue($command->getDefinition()->hasOption('bar'), '->mergeApplicationDefinition(false) merges the application and the command options'); + $this->assertFalse($command->getDefinition()->hasArgument('foo'), '->mergeApplicationDefinition(false) does not merge the application arguments'); + + $m->invoke($command, true); + $this->assertTrue($command->getDefinition()->hasArgument('foo'), '->mergeApplicationDefinition(true) merges the application arguments and the command arguments'); + + $m->invoke($command); + $this->assertEquals(2, $command->getDefinition()->getArgumentCount(), '->mergeApplicationDefinition() does not try to merge twice the application arguments'); + } + + public function testRunInteractive() + { + $tester = new CommandTester(new \TestCommand()); + + $tester->execute([], ['interactive' => true]); + + $this->assertEquals('interact called'.PHP_EOL.'execute called'.PHP_EOL, $tester->getDisplay(), '->run() calls the interact() method if the input is interactive'); + } + + public function testRunNonInteractive() + { + $tester = new CommandTester(new \TestCommand()); + + $tester->execute([], ['interactive' => false]); + + $this->assertEquals('execute called'.PHP_EOL, $tester->getDisplay(), '->run() does not call the interact() method if the input is not interactive'); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage You must override the execute() method in the concrete command class. + */ + public function testExecuteMethodNeedsToBeOverridden() + { + $command = new Command('foo'); + $command->run(new StringInput(''), new NullOutput()); + } + + /** + * @expectedException \Symfony\Component\Console\Exception\InvalidOptionException + * @expectedExceptionMessage The "--bar" option does not exist. + */ + public function testRunWithInvalidOption() + { + $command = new \TestCommand(); + $tester = new CommandTester($command); + $tester->execute(['--bar' => true]); + } + + public function testRunReturnsIntegerExitCode() + { + $command = new \TestCommand(); + $exitCode = $command->run(new StringInput(''), new NullOutput()); + $this->assertSame(0, $exitCode, '->run() returns integer exit code (treats null as 0)'); + + $command = $this->getMockBuilder('TestCommand')->setMethods(['execute'])->getMock(); + $command->expects($this->once()) + ->method('execute') + ->willReturn('2.3'); + $exitCode = $command->run(new StringInput(''), new NullOutput()); + $this->assertSame(2, $exitCode, '->run() returns integer exit code (casts numeric to int)'); + } + + public function testRunWithApplication() + { + $command = new \TestCommand(); + $command->setApplication(new Application()); + $exitCode = $command->run(new StringInput(''), new NullOutput()); + + $this->assertSame(0, $exitCode, '->run() returns an integer exit code'); + } + + public function testRunReturnsAlwaysInteger() + { + $command = new \TestCommand(); + + $this->assertSame(0, $command->run(new StringInput(''), new NullOutput())); + } + + public function testRunWithProcessTitle() + { + $command = new \TestCommand(); + $command->setApplication(new Application()); + $command->setProcessTitle('foo'); + $this->assertSame(0, $command->run(new StringInput(''), new NullOutput())); + if (\function_exists('cli_set_process_title')) { + if (null === @cli_get_process_title() && 'Darwin' === PHP_OS) { + $this->markTestSkipped('Running "cli_get_process_title" as an unprivileged user is not supported on MacOS.'); + } + $this->assertEquals('foo', cli_get_process_title()); + } + } + + public function testSetCode() + { + $command = new \TestCommand(); + $ret = $command->setCode(function (InputInterface $input, OutputInterface $output) { + $output->writeln('from the code...'); + }); + $this->assertEquals($command, $ret, '->setCode() implements a fluent interface'); + $tester = new CommandTester($command); + $tester->execute([]); + $this->assertEquals('interact called'.PHP_EOL.'from the code...'.PHP_EOL, $tester->getDisplay()); + } + + public function getSetCodeBindToClosureTests() + { + return [ + [true, 'not bound to the command'], + [false, 'bound to the command'], + ]; + } + + /** + * @dataProvider getSetCodeBindToClosureTests + */ + public function testSetCodeBindToClosure($previouslyBound, $expected) + { + $code = createClosure(); + if ($previouslyBound) { + $code = $code->bindTo($this); + } + + $command = new \TestCommand(); + $command->setCode($code); + $tester = new CommandTester($command); + $tester->execute([]); + $this->assertEquals('interact called'.PHP_EOL.$expected.PHP_EOL, $tester->getDisplay()); + } + + public function testSetCodeWithStaticClosure() + { + $command = new \TestCommand(); + $command->setCode(self::createClosure()); + $tester = new CommandTester($command); + $tester->execute([]); + + if (\PHP_VERSION_ID < 70000) { + // Cannot bind static closures in PHP 5 + $this->assertEquals('interact called'.PHP_EOL.'not bound'.PHP_EOL, $tester->getDisplay()); + } else { + // Can bind static closures in PHP 7 + $this->assertEquals('interact called'.PHP_EOL.'bound'.PHP_EOL, $tester->getDisplay()); + } + } + + private static function createClosure() + { + return function (InputInterface $input, OutputInterface $output) { + $output->writeln(isset($this) ? 'bound' : 'not bound'); + }; + } + + public function testSetCodeWithNonClosureCallable() + { + $command = new \TestCommand(); + $ret = $command->setCode([$this, 'callableMethodCommand']); + $this->assertEquals($command, $ret, '->setCode() implements a fluent interface'); + $tester = new CommandTester($command); + $tester->execute([]); + $this->assertEquals('interact called'.PHP_EOL.'from the code...'.PHP_EOL, $tester->getDisplay()); + } + + public function callableMethodCommand(InputInterface $input, OutputInterface $output) + { + $output->writeln('from the code...'); + } +} + +// In order to get an unbound closure, we should create it outside a class +// scope. +function createClosure() +{ + return function (InputInterface $input, OutputInterface $output) { + $output->writeln($this instanceof Command ? 'bound to the command' : 'not bound to the command'); + }; +} diff --git a/lib/symfony/console/Tests/Command/HelpCommandTest.php b/lib/symfony/console/Tests/Command/HelpCommandTest.php new file mode 100644 index 000000000..ce9d8d4fe --- /dev/null +++ b/lib/symfony/console/Tests/Command/HelpCommandTest.php @@ -0,0 +1,71 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Command; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Command\HelpCommand; +use Symfony\Component\Console\Command\ListCommand; +use Symfony\Component\Console\Tester\CommandTester; + +class HelpCommandTest extends TestCase +{ + public function testExecuteForCommandAlias() + { + $command = new HelpCommand(); + $command->setApplication(new Application()); + $commandTester = new CommandTester($command); + $commandTester->execute(['command_name' => 'li'], ['decorated' => false]); + $this->assertContains('list [options] [--] []', $commandTester->getDisplay(), '->execute() returns a text help for the given command alias'); + $this->assertContains('format=FORMAT', $commandTester->getDisplay(), '->execute() returns a text help for the given command alias'); + $this->assertContains('raw', $commandTester->getDisplay(), '->execute() returns a text help for the given command alias'); + } + + public function testExecuteForCommand() + { + $command = new HelpCommand(); + $commandTester = new CommandTester($command); + $command->setCommand(new ListCommand()); + $commandTester->execute([], ['decorated' => false]); + $this->assertContains('list [options] [--] []', $commandTester->getDisplay(), '->execute() returns a text help for the given command'); + $this->assertContains('format=FORMAT', $commandTester->getDisplay(), '->execute() returns a text help for the given command'); + $this->assertContains('raw', $commandTester->getDisplay(), '->execute() returns a text help for the given command'); + } + + public function testExecuteForCommandWithXmlOption() + { + $command = new HelpCommand(); + $commandTester = new CommandTester($command); + $command->setCommand(new ListCommand()); + $commandTester->execute(['--format' => 'xml']); + $this->assertContains('getDisplay(), '->execute() returns an XML help text if --xml is passed'); + } + + public function testExecuteForApplicationCommand() + { + $application = new Application(); + $commandTester = new CommandTester($application->get('help')); + $commandTester->execute(['command_name' => 'list']); + $this->assertContains('list [options] [--] []', $commandTester->getDisplay(), '->execute() returns a text help for the given command'); + $this->assertContains('format=FORMAT', $commandTester->getDisplay(), '->execute() returns a text help for the given command'); + $this->assertContains('raw', $commandTester->getDisplay(), '->execute() returns a text help for the given command'); + } + + public function testExecuteForApplicationCommandWithXmlOption() + { + $application = new Application(); + $commandTester = new CommandTester($application->get('help')); + $commandTester->execute(['command_name' => 'list', '--format' => 'xml']); + $this->assertContains('list [--raw] [--format FORMAT] [--] [<namespace>]', $commandTester->getDisplay(), '->execute() returns a text help for the given command'); + $this->assertContains('getDisplay(), '->execute() returns an XML help text if --format=xml is passed'); + } +} diff --git a/lib/symfony/console/Tests/Command/ListCommandTest.php b/lib/symfony/console/Tests/Command/ListCommandTest.php new file mode 100644 index 000000000..57687d4c6 --- /dev/null +++ b/lib/symfony/console/Tests/Command/ListCommandTest.php @@ -0,0 +1,113 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tests\Command; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Tester\CommandTester; + +class ListCommandTest extends TestCase +{ + public function testExecuteListsCommands() + { + $application = new Application(); + $commandTester = new CommandTester($command = $application->get('list')); + $commandTester->execute(['command' => $command->getName()], ['decorated' => false]); + + $this->assertRegExp('/help\s{2,}Displays help for a command/', $commandTester->getDisplay(), '->execute() returns a list of available commands'); + } + + public function testExecuteListsCommandsWithXmlOption() + { + $application = new Application(); + $commandTester = new CommandTester($command = $application->get('list')); + $commandTester->execute(['command' => $command->getName(), '--format' => 'xml']); + $this->assertRegExp('/