8000 diff --git a/.appveyor.yml b/.appveyor.yml index 34d3a703337e9..4b417c5dfc122 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -54,6 +54,7 @@ test_script: - SET X=0 - SET SYMFONY_PHPUNIT_SKIPPED_TESTS=phpunit.skipped - copy /Y c:\php\php.ini-min c:\php\php.ini + - IF %APPVEYOR_REPO_BRANCH% neq master (rm -Rf src\Symfony\Bridge\PhpUnit) - php phpunit src\Symfony --exclude-group benchmark,intl-data || SET X=!errorlevel! - copy /Y c:\php\php.ini-max c:\php\php.ini - php phpunit src\Symfony --exclude-group benchmark,intl-data || SET X=!errorlevel! diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 0bef18db33536..e459d1e55f616 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -2,6 +2,23 @@ /src/Symfony/Component/Console/Logger/ConsoleLogger.php @dunglas # DependencyInjection /src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php @dunglas +# Form +/src/Symfony/Bridge/Twig/Extension/FormExtension.php @xabbuh +/src/Symfony/Bridge/Twig/Form/* @xabbuh +/src/Symfony/Bridge/Twig/Node/FormThemeNode.php @xabbuh +/src/Symfony/Bridge/Twig/Node/RenderBlockNode.php @xabbuh +/src/Symfony/Bridge/Twig/Node/SearchAndRenderBlockNode.php @xabbuh +/src/Symfony/Bridge/Twig/Tests/Extension/FormExtension* @xabbuh +/src/Symfony/Bridge/Twig/Tests/Node/FormThemeTest.php @xabbuh +/src/Symfony/Bridge/Twig/Tests/TokenParser/FormThemeTokenParserTest.php @xabbuh +/src/Symfony/Bridge/Twig/TokenParser/FormThemeTokenParser.php @xabbuh +/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/FormPass.php @xabbuh +/src/Symfony/Bundle/FrameworkBundle/Resources/views/* @xabbuh +/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/FormHelper.php @xabbuh +/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/FormPassTest.php @xabbuh +/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/FormHelperTableLayoutTest.php @xabbuh +/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/FormHelperDivLayoutTest.php @xabbuh +/src/Symfony/Component/Form/* @xabbuh # HttpKernel /src/Symfony/Component/HttpKernel/Log/Logger.php @dunglas # LDAP diff --git a/.github/build-packages.php b/.github/build-packages.php index b09cea2fe230a..e61eae51df550 100644 --- a/.github/build-packages.php +++ b/.github/build-packages.php @@ -16,7 +16,7 @@ $mergeBase = trim(shell_exec(sprintf('git merge-base "%s" HEAD', array_shift($dirs)))); $packages = array(); -$flags = \PHP_VERSION_ID >= 50400 ? JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE : 0; +$flags = JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE; foreach ($dirs as $k => $dir) { if (!system("git diff --name-only $mergeBase -- $dir", $exitStatus)) { diff --git a/.php_cs.dist b/.php_cs.dist index ebeb4150d8461..fd6832920b206 100644 --- a/.php_cs.dist +++ b/.php_cs.dist @@ -5,26 +5,26 @@ if (!file_exists(__DIR__.'/src')) { } return PhpCsFixer\Config::create() - ->setRules(array( + ->setRules([ '@Symfony' => true, '@Symfony:risky' => true, '@PHPUnit48Migration:risky' => true, 'php_unit_no_expectation_annotation' => false, // part of `PHPUnitXYMigration:risky` ruleset, to be enabled when PHPUnit 4.x support will be dropped, as we don't want to rewrite exceptions handling twice - 'array_syntax' => array('syntax' => 'long'), + 'array_syntax' => ['syntax' => 'short'], 'fopen_flags' => false, 'ordered_imports' => true, 'protected_to_private' => false, // Part of @Symfony:risky in PHP-CS-Fixer 2.13.0. To be removed from the config file once upgrading - 'native_function_invocation' => array('include' => array('@compiler_optimized'), 'scope' => 'namespaced'), + 'native_function_invocation' => ['include' => ['@compiler_optimized'], 'scope' => 'namespaced'], // Part of future @Symfony ruleset in PHP-CS-Fixer To be removed from the config file once upgrading - 'phpdoc_types_order' => array('null_adjustment' => 'always_last', 'sort_algorithm' => 'none'), - )) + 'phpdoc_types_order' => ['null_adjustment' => 'always_last', 'sort_algorithm' => 'none'], + ]) ->setRiskyAllowed(true) ->setFinder( PhpCsFixer\Finder::create() ->in(__DIR__.'/src') - ->append(array(__FILE__)) - ->exclude(array( + ->append([__FILE__]) + ->exclude([ 'Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/Fixtures', // directories containing files with content that is autogenerated by `var_export`, which breaks CS in output code 'Symfony/Component/DependencyInjection/Tests/Fixtures', @@ -40,7 +40,7 @@ return PhpCsFixer\Config::create() 'Symfony/Bundle/FrameworkBundle/Resources/views/Form', // explicit trigger_error tests 'Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/', - )) + ]) // Support for older PHPunit version ->notPath('Symfony/Bridge/PhpUnit/SymfonyTestsListener.php') // file content autogenerated by `var_export` diff --git a/.travis.yml b/.travis.yml index 79b484ee7ee95..3949845f897a8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: php -dist: trusty +dist: xenial git: depth: 2 @@ -11,22 +11,23 @@ addons: - language-pack-fr-base - ldap-utils - slapd - - librabbitmq-dev - zookeeperd - libzookeeper-mt-dev + - rabbitmq-server env: global: - MIN_PHP=7.1.3 - SYMFONY_PROCESS_PHP_TEST_BINARY=~/.phpenv/shims/php - MESSENGER_AMQP_DSN=amqp://localhost/%2f/messages + - MESSENGER_REDIS_DSN=redis://127.0.0.1:7001/messages matrix: include: - php: 7.1 - - php: 7.1 - env: deps=high - php: 7.2 + env: deps=high + - php: 7.3 env: deps=low fast_finish: true @@ -45,10 +46,19 @@ services: - docker before_install: + - | + # Enable Sury ppa + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 6B05F25D762E3157 + sudo add-apt-repository -y ppa:ondrej/php + sudo rm /etc/apt/sources.list.d/google-chrome.list + sudo rm /etc/apt/sources.list.d/mongodb-3.4.list + sudo apt update + sudo apt install -y librabbitmq-dev libsodium-dev + - | # Start Redis cluster - docker pull grokzen/redis-cluster:4.0.8 - docker run -d -p 7000:7000 -p 7001:7001 -p 7002:7002 -p 7003:7003 -p 7004:7004 -p 7005:7005 --name redis-cluster grokzen/redis-cluster:4.0.8 + docker pull grokzen/redis-cluster:5.0.4 + docker run -d -p 7000:7000 -p 7001:7001 -p 7002:7002 -p 7003:7003 -p 7004:7004 -p 7005:7005 --name redis-cluster grokzen/redis-cluster:5.0.4 export REDIS_CLUSTER_HOSTS='localhost:7000 localhost:7001 localhost:7002 localhost:7003 localhost:7004 localhost:7005' - | @@ -56,6 +66,9 @@ before_install: set -e stty cols 120 mkdir /tmp/slapd + if [ ! -e /tmp/slapd-modules ]; then + [ -d /usr/lib/openldap ] && ln -s /usr/lib/openldap /tmp/slapd-modules || ln -s /usr/lib/ldap /tmp/slapd-modules + fi slapd -f src/Symfony/Component/Ldap/Tests/Fixtures/conf/slapd.conf -h ldap://localhost:3389 & [ -d ~/.composer ] || mkdir ~/.composer cp .composer/* ~/.composer/ @@ -105,6 +118,7 @@ before_install: local ext_name=$1 local ext_so=$2 local INI=$3 + local input=${4:-yes} local ext_dir=$(php -r "echo ini_get('extension_dir');") local ext_cache=~/php-ext/$(basename $ext_dir)/$ext_name @@ -113,7 +127,7 @@ before_install: else rm ~/.pearrc /tmp/pear 2>/dev/null || true mkdir -p $ext_cache - echo yes | pecl install -f $ext_name && + echo $input | pecl install -f $ext_name && cp $ext_dir/$ext_so $ext_cache fi } @@ -136,7 +150,6 @@ before_install: echo session.gc_probability = 0 >> $INI echo opcache.enable_cli = 1 >> $INI echo apc.enable_cli = 1 >> $INI - echo extension = redis.so >> $INI echo extension = memcached.so >> $INI done @@ -146,26 +159,20 @@ before_install: export PHP=$PHP phpenv global $PHP INI=~/.phpenv/versions/$PHP/etc/conf.d/travis.ini - - # Install librabbitmq - wget http://ftp.debian.org/debian/pool/main/libr/librabbitmq/librabbitmq-dev_0.5.2-2_amd64.deb - wget http://ftp.debian.org/debian/pool/main/libr/librabbitmq/librabbitmq1_0.5.2-2_amd64.deb - sudo dpkg -i librabbitmq1_0.5.2-2_amd64.deb librabbitmq-dev_0.5.2-2_amd64.deb - if ! php --ri sodium > /dev/null; then - # install libsodium - sudo add-apt-repository ppa:ondrej/php -y - sudo apt-get update -q - sudo apt-get install libsodium-dev -y tfold ext.libsodium tpecl libsodium sodium.so $INI fi - tfold ext.apcu tpecl apcu-5.1.6 apcu.so $INI - tfold ext.mongodb tpecl mongodb-1.5.2 mongodb.so $INI - tfold ext.amqp tpecl amqp-1.9.3 amqp.so $INI - tfold ext.igbinary tpecl igbinary-2.0.6 igbinary.so $INI - tfold ext.zookeeper tpecl zookeeper-0.5.0 zookeeper.so $INI + tfold ext.apcu tpecl apcu-5.1.16 apcu.so $INI + tfold ext.mongodb tpecl mongodb-1.6.0alpha1 mongodb.so $INI + tfold ext.igbinary tpecl igbinary-2.0.8 igbinary.so $INI + tfold ext.zookeeper tpecl zookeeper-0.7.1 zookeeper.so $INI + tfold ext.amqp tpecl amqp-1.9.4 amqp.so $INI + tfold ext.redis tpecl redis-4.3.0 redis.so $INI "no" done + - | + # List all php extensions with versions + - php -r 'foreach (get_loaded_extensions() as $extension) echo $extension . " " . phpversion($extension) . PHP_EOL;' - | # Load fixtures @@ -199,6 +206,12 @@ install: SYMFONY_VERSION=$(cat composer.json | grep '^ *"dev-master". *"[1-9]' | grep -o '[0-9.]*') fi + - | + # Skip the phpunit-bridge on not-master branches when $deps is empty + if [[ ! $deps && $TRAVIS_BRANCH != master ]]; then + COMPONENTS=$(find src/Symfony -mindepth 3 -type f -name phpunit.xml.dist -not -wholename '*/Bridge/PhpUnit/*' -printf '%h\n') + fi + - | # Install symfony/flex if [[ $deps = low ]]; then @@ -226,7 +239,7 @@ install: break fi phpenv global $PHP - ([[ $deps ]] && cd src/Symfony/Component/HttpFoundation; composer config platform.ext-mongodb 1.5.2; composer require --dev --no-update mongodb/mongodb) + ([[ $deps ]] && cd src/Symfony/Component/HttpFoundation; composer config platform.ext-mongodb 1.6.0; composer require --dev --no-update mongodb/mongodb) tfold 'composer update' $COMPOSER_UP tfold 'phpunit install' ./phpunit install if [[ $deps = high ]]; then diff --git a/CHANGELOG-4.1.md b/CHANGELOG-4.1.md index ad6a91cdd0b1b..3a013e2504087 100644 --- a/CHANGELOG-4.1.md +++ b/CHANGELOG-4.1.md @@ -7,6 +7,61 @@ in 4.1 minor versions. To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v4.1.0...v4.1.1 +* 4.1.10 (2019-01-06) + + * bug #29494 [HttpFoundation] Fix request uri when it starts with double slashes (alquerci) + * bug #29697 [DI] Fixed wrong factory method in exception (Wojciech Gorczyca) + * bug #29679 [HttpKernel] Correctly Render Signed URIs Containing Fragments (zanbaldwin) + * bug #29754 Ensure final input of CommandTester works with default (Firehed) + * bug #29695 [Form] Do not ignore the choice groups for caching (vudaltsov) + * bug #29738 [Intl] handle null date and time types (xabbuh) + * bug #29708 [FrameworkBundle] access the container getting it from the kernel (xabbuh) + * bug #29704 [FrameworkBundle] improve errors in tests missing the BrowserKit component (xabbuh) + * bug #29617 [Console] Add specific replacement for help text in single command applications (codedmonkey) + * bug #29714 [Event Dispatcher] fixed 29703: TraceableEventDispatcher reset() callStack to null (mlievertz) + * bug #29597 [DI] fix reporting bindings on overriden services as unused (nicolas-grekas) + * bug #29639 [Yaml] detect circular references (xabbuh) + * bug #29626 [Routing] fix trailing slash redirections involving a trailing var (nicolas-grekas) + * bug #29411 [EventDispatcher] Revers event tracing order (ro0NL) + * bug #29533 Fixed public directory when configured in composer.json (alexander-schranz) + * bug #29619 [Console] OutputFormatter: move strtolower to createStyleFromString (ogizanagi) + * bug #29621 [Security] Prefer clone() over unserialize(serialize()) for user refreshment (chalasr) + * bug #29542 [Routing] fix dumping same-path routes with placeholders (nicolas-grekas) + * bug #29587 [Debug] ignore underscore vs backslash namespaces in DebugClassLoader (nicolas-grekas) + * bug #29584 [FrameworkBundle] fix describing routes with no controllers (nicolas-grekas) + * bug #29582 [DI] move RegisterServiceSubscribersPass before DecoratorServicePass (kbond) + * bug #29527 [TwigBridge][Form] Prevent multiple rendering of form collection prototypes (Shoplifter) + * bug #29571 [Yaml] ensures that the mb_internal_encoding is reset to its initial value (Jörn Lang) + * bug #29513 [Hackday][Serializer] Deserialization ignores argument type hint from phpdoc for array in constructor argument (karser) + * bug #29323 [Security] defer log message in guard authenticator (eschultz-magix) + * bug #29531 [Validator] Added IBAN format for Vatican City State (raulfraile) + * bug #29501 [Form] filter out invalid language values (xabbuh) + * bug #29307 [Form] Filter arrays out of scalar form types (nicolas-grekas) + * bug #29500 [Form] filter out invalid Intl values (xabbuh) + * bug #29499 [Validator] Fixed grouped composite constraints (HeahDude) + +* 4.1.9 (2018-12-06) + + * security #cve-2018-19790 [Security\Http] detect bad redirect targets using backslashes (xabbuh) + * security #cve-2018-19789 [Form] Filter file uploads out of regular form types (nicolas-grekas) + * bug #29436 [Cache] Fixed Memcached adapter doClear()to call flush() (raitocz) + * bug #29441 [Routing] ignore trailing slash for non-GET requests (nicolas-grekas) + * bug #29444 [Workflow] Fixed BC break for Workflow metadata (lyrixx) + * bug #29432 [DI] dont inline when lazy edges are found (nicolas-grekas) + * bug #29413 [Serializer] fixed DateTimeNormalizer to maintain microseconds when a different timezone required (rvitaliy) + * bug #29424 [Routing] fix taking verb into account when redirecting (nicolas-grekas) + * bug #29414 [DI] Fix dumping expressions accessing single-use private services (chalasr) + * bug #29375 [Validator] Allow `ConstraintViolation::__toString()` to expose codes that are not null or emtpy strings (phansys) + * bug #29376 [EventDispatcher] Fix eventListener wrapper loop in TraceableEventDispatcher (jderusse) + * bug #29386 undeprecate the single-colon notation for controllers (fbourigault) + * bug #29393 [DI] fix edge case in InlineServiceDefinitionsPass (nicolas-grekas) + * bug #29380 [Routing] fix greediness of trailing slash (nicolas-grekas) + * bug #29343 [Form] Handle all case variants of "nan" when parsing a number (mwhudson, xabbuh) + * bug #29373 [Routing] fix trailing slash redirection (nicolas-grekas) + * bug #29355 [PropertyAccess] calculate cache keys for property setters depending on the value (xabbuh) + * bug #29369 [DI] fix combinatorial explosion when analyzing the service graph (nicolas-grekas) + * bug #29349 [Debug] workaround opcache bug mutating "$this" !?! (nicolas-grekas) + * 4.1.8 (2018-11-26) * bug #29318 [Console] Move back root exception to stack trace in verbose mode (chalasr) diff --git a/CHANGELOG-4.2.md b/CHANGELOG-4.2.md index 5e748a7913537..f45e7e7273ab4 100644 --- a/CHANGELOG-4.2.md +++ b/CHANGELOG-4.2.md @@ -7,6 +7,275 @@ in 4.2 minor versions. To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v4.2.0...v4.2.1 +* 4.2.8 (2019-05-01) + + * bug #31338 Revert "bug #30620 [FrameworkBundle][HttpFoundation] make session service resettable (dmaicher)" (nicolas-grekas) + * bug #31326 fix ConsoleFormatter - call to a member function format() on string (keksa) + * bug #31331 [Workflow] Fixed dumping when many transition with same name exist (lyrixx) + * bug #31302 [FramworkBundle] mark any env vars found in the ide setting as used (nicolas-grekas) + * bug #31290 [TwigBundle] Use the apply tag instead of the filter tag (greg0ire) + * bug #31275 [Translator] Preserve default domain when extracting strings from php files (Stadly) + * bug #31240 Fix url matcher edge cases with trailing slash (arjenm) + * bug #31201 [Form] resolve class name parameters (xabbuh) + * bug #31213 [WebProfilerBundle] Intercept redirections only for HTML format (javiereguiluz) + * bug #31210 [PhpUnitBridge] fix reading phpunit.xml on bootstrap (nicolas-grekas) + * bug #31023 [Routing] Fix route URL generation in CLI context (X-Coder264) + * bug #31117 [FrameworkBundle] fix math depth handler configuration (Raulnet) + * bug #31182 [Routing] fix trailing slash matching with empty-matching trailing vars (nicolas-grekas) + * bug #31167 [Routing] fix matching trailing vars with defaults (nicolas-grekas) + * bug #31164 [Validator] fix LegacyTranslatorProxy (nicolas-grekas) + * bug #31156 [FrameworkBundle] call method with Translator component only (xabbuh) + +* 4.2.7 (2019-04-17) + + * bug #31107 [Routing] fix trailing slash redirection with non-greedy trailing vars (nicolas-grekas) + * bug #31108 [FrameworkBundle] decorate the ValidatorBuilder's translator with LegacyTranslatorProxy (nicolas-grekas) + * bug #31121 [HttpKernel] Fix get session when the request stack is empty (yceruto) + * bug #31084 [HttpFoundation] Make MimeTypeExtensionGuesser case insensitive (vermeirentony) + * bug #31142 Revert "bug #30423 [Security] Rework firewall's access denied rule (dimabory)" (chalasr) + * security #cve-2019-10910 [DI] Check service IDs are valid (nicolas-grekas) + * security #cve-2019-10909 [FrameworkBundle][Form] Fix XSS issues in the form theme of the PHP templating engine (stof) + * security #cve-2019-10912 [Cache][PHPUnit Bridge] Prevent destructors with side-effects from being unserialized (nicolas-grekas) + * security #cve-2019-10911 [Security] Add a separator in the remember me cookie hash (pborreli) + * security #cve-2019-10913 [HttpFoundation] reject invalid method override (nicolas-grekas) + +* 4.2.6 (2019-04-16) + + * bug #31088 [DI] fix removing non-shared definition while inlining them (nicolas-grekas) + * bug #29944 [DI] Overriding services autowired by name under _defaults bind not working (przemyslaw-bogusz, renanbr) + * bug #30993 [FrameworkBundle] Fix for Controller DEPRECATED when using composer --optimized (aweelex) + * bug #31076 [HttpKernel] Fixed LoggerDataCollector crashing on empty file (althaus) + * bug #31071 property normalizer should also pass format and context to isAllowedAttribute (dbu) + * bug #31059 Show more accurate message in profiler when missing stopwatch (linaori) + * bug #31026 [Serializer] Add default object class resolver (jdecool) + * bug #31031 [Serializer] MetadataAwareNameConverter: Do not assume that property names are strings (soyuka) + * bug #31043 [VarExporter] support PHP7.4 __serialize & __unserialize (nicolas-grekas) + * bug #30423 [Security] Rework firewall's access denied rule (dimabory) + * bug #31020 [VarExporter] fix exporting classes with private constructors (nicolas-grekas) + * bug #31012 [Process] Fix missing $extraDirs when open_basedir returns (arsonik) + * bug #30852 [Console] fix buildTableRows when Colspan is use with content too long (Raulnet) + * bug #30950 [Serializer] Also validate callbacks when given in the normalizer context (dbu) + * bug #30907 [Serializer] Respect ignored attributes in cache key of normalizer (dbu) + * bug #30085 Fix TestRunner compatibility to PhpUnit 8 (alexander-schranz) + * bug #30999 Fix dark themed componnents (ro0NL) + * bug #30977 [serializer] prevent mixup in normalizer of the object to populate (dbu) + * bug #30976 [Debug] Fixed error handling when an error is already handled when another error is already handled (5) (lyrixx) + * bug #30979 Fix the configurability of CoreExtension deps in standalone usage (stof) + * bug #30918 [Cache] fix using ProxyAdapter inside TagAwareAdapter (dmaicher) + * bug #30961 [Form] fix translating file validation error message (xabbuh) + * bug #30951 Handle case where no translations were found (greg0ire) + * bug #29800 [Validator] Only traverse arrays that are cascaded into (corphi) + * bug #30921 [Translator] Warm up the translations cache in dev (tgalopin) + * bug #30922 [TwigBridge] fix horizontal spacing of inlined Bootstrap forms (xabbuh) + * bug #30860 [Profiler] Fix dark theme elements color (dFayet) + * bug #30895 [Form] turn failed file uploads into form errors (xabbuh) + * bug #30919 [Translator] Fix wrong dump for PO files (deguif) + * bug #30889 [DependencyInjection] Fix a wrong error when using a factory (Simperfit) + * bug #30911 [Console] Fix table trailing backslash (maidmaid) + * bug #30903 [Messenger] Uses the `SerializerStamp` when deserializing the envelope (sroze) + * bug #30879 [Form] Php doc fixes and cs + optimizations (Jules Pietri) + * bug #30883 [Console] Fix stty not reset when aborting in QuestionHelper::autocomplete() (Simperfit) + * bug #30878 [Console] Fix inconsistent result for choice questions in non-interactive mode (chalasr) + * bug #30825 [Routing] Fix: annotation loader ignores method's default values (voronkovich) + +* 4.2.5 (2019-04-02) + + * bug #30660 [Bridge][Twig] DebugCommand - fix escaping and filter (SpacePossum) + * bug #30784 [Translator] Add resource path to exception message for schema valida… (jschaedl) + * bug #30720 Fix getSetMethodNormalizer to correctly ignore the attributes specified in "ignored_attributes" (Emmanuel BORGES) + * bug #30749 [Serializer] Added check of constuctor modifiers to AbstractNormalizer (NekaKawaii) + * bug #30776 [Routing] Fix routes annotation loading with glob pattern (snoob) + * bug #30773 [DependencyInjection] Fix hardcoded hotPathTagName (jderusse) + * bug #30737 [Validator] Improve constraint default option check (vudaltsov) + * bug #30736 [Validator] Fix annotation default for @Count and @Length (vudaltsov) + * bug #30621 [Cache] Ensure key exists before checking array value (jrjohnson) + * bug #30711 [Serializer] Use object class resolver when extracting attributes (joelwurtz) + * bug #30641 [FrameworkBundle] properly describe service definitions without class (xabbuh) + * bug #30620 [FrameworkBundle][HttpFoundation] make session service resettable (dmaicher) + * bug #30648 Debug finalized config in debug:config (ro0NL) + * bug #30640 [Phpunit] fixed support for PHP 5.3 (fabpot) + * bug #30616 Fix case when multiple loaders are providing paths for the same namespace (yceruto) + * bug #30595 Do not validate child constraints if form has no validation groups (maryo) + * bug #30440 [TwigBridge] Fix DebugCommand when chain loader is involved (yceruto) + * bug #30479 Check if Client exists when test.client does not exist, to provide clearer exception message (SerkanYildiz) + * bug #30597 [Form] Added ResetInterface to CachingFactoryDecorator (HeahDude) + * bug #30593 Fixed usage of TranslatorInterface in form extension (fixes #30591) (althaus) + * feature #30584 [Intl] Add compile binary (ro0NL) + * bug #30487 Fix Cache error while using anonymous class (Emmanuel BORGES) + * bug #30576 [Cache] fix LockRegistry (nicolas-grekas) + * bug #30548 Correct language code for ukrainian language (stanleyk) + * bug #30518 [Cache] Fix perf when using RedisCluster by reducing roundtrips to the servers (nicolas-grekas) + * bug #30515 [Cache] Only delete one key at a time when on Predis + Cluster (andrerom) + * bug #30511 [Process] fix using argument $php of new PhpProcess() (nicolas-grekas) + * bug #30507 [Routing] Fixed XML options resolution (Jules Pietri) + * bug #30506 [TwigBridge] remove deprecation triggered when using Twig 2.7 (nicolas-grekas) + * bug #30496 [PHPUnit-Bridge] override some Composer environment variables (nicoweb) + * bug #30505 [TwigBridge] Remove usages of the spaceless tag (nicolas-grekas) + * bug #30466 [Messenger] Make 'headers' key optional for encoded messages (yceruto) + * bug #30474 compatibility with phpunit8 (garak) + * bug #30497 [HttpKernel] Change default log level for output streams (yceruto) + * bug #30498 [translation] Update defaut format from yml to yaml (GaryPEGEOT) + * bug #30490 Don't resolve the Deprecation error handler mode until a deprecation is triggered (Emmanuel BORGES) + * bug #30396 [Form] Avoid a form type extension appears many times in debug:form (markitosgv) + * bug #30361 [PropertyInfo] Fix undefined variable fromConstructor when passing context to getTypes (mantis) + * bug #30361 [PropertyInfo] Fix undefined variable fromConstructor when passing context to getTypes (mantis, OskarStark) + * bug #30410 [Monolog] Really reset logger when calling logger::reset() (lyrixx) + * bug #30437 [Debug] detect annotations before blank docblock lines (xabbuh) + * bug #30417 Autoconfig: don't automatically tag decorators (dunglas) + * bug #30392 [PropertyAccess] Fixed PropertyPathBuilder remove that fails to reset internal indexes (GregOriol) + +* 4.2.4 (2019-03-03) + + * bug #30383 [WebProfilerBundle] toolbar: invisible route name in Firefox (inmarelibero) + * bug #26532 [HttpKernel] Correctly merging cache directives in HttpCache/ResponseCacheStrategy (aschempp) + * bug #30363 Fixed the DebugClassLoader compatibility with eval()'d code on Darwin (skalpa) + * bug #30329 [Form] IntegerType: reject submitted non-integer numbers (xabbuh) + * bug #30331 [Cache] fix warming up cache.system and apcu (nicolas-grekas) + * bug #30347 [Security] Change FormAuthenticator if condition (PReimers) + * bug #30354 [Console] handles multi-byte characters in autocomplete (jls-esokia) + * bug #30351 Fix getItems() performance issue with RedisCluster (php-redis) (andrerom) + * bug #30350 [VarDumper] Keep a ref to objects to ensure their handle cannot be reused while cloning (nicolas-grekas) + * bug #30327 [HttpKernel] Fix possible infinite loop of exceptions (enumag) + * bug #27601 [Routing] fix URL generation with look-around requirements (nasimnabavi) + * bug #30277 [Console] Prevent ArgvInput::getFirstArgument() from returning an option value (chalasr) + * bug #29981 [Security] Complain about an empty decision strategy (corphi) + * bug #29822 [EventDispatcher] Fix unknown priority (ro0NL) + * bug #30324 [Validator] Fixed duplicate UUID (ralfkuehnel) + * bug #30265 [Form] do not validate non-submitted form fields in PATCH requests (xabbuh) + * bug #30313 Avoid mutating the Finder when building the iterator (stof) + * bug #30294 [FrameworkBundle] Fix Descriptor throwing on non existent parent (GuilhemN) + * bug #30271 [Console] Fix command testing with missing user inputs (chalasr) + * bug #30278 Remove unnecessary ProgressBar stdout writes (fixes flickering) (ostrolucky) + * bug #30274 [VarDumper] fix serializing Stub instances (nicolas-grekas) + * bug #30273 [Validator] Added missing use statement for UnexpectedTypeException (devrck) + * bug #30247 Don't resolve the Deprecation error handler mode until a deprecation is triggered (ossinkine) + * bug #30264 [Debug][ErrorHandler] Preserve next error handler (fancyweb) + * bug #30245 fix lost namespace in eval (fizzka) + * bug #30090 [FrameworkBundle] add constraint validators before optimizations (xabbuh) + * feature #30126 [Form] forward valid numeric values to transform() (xabbuh) + * bug #30122 [Security] fix switch user without having current token (Antoine Lamirault) + * bug #30136 use PropertyAccessorInterface instead of PropertyAccessor (nick-zh) + * bug #30124 Fix KernelTestCase compatibility for PhpUnit 8 (bis) (nicolas-grekas) + * bug #30061 [Form] render integer types with grouping as text input (xabbuh) + * bug #30063 [Form] don't lose int precision with not needed type casts (xabbuh) + * bug #30076 [Form] ignore _method forms in NativeRequestHandler (xabbuh) + * bug #30084 Fix KernelTestCase compatibility for PhpUnit 8 (alexander-schranz) + * bug #30093 [DependencyInjection] add $lazyLoad context to the generated code for lazy non-shared service by PhpDumper (XuruDragon) + * bug #30102 [Workflow] Graphviz dumper escape not always a string (Korbeil) + * bug #29884 [Form] CsrfValidationListener marks the token as invalid if it is not a string (umpirsky) + * bug #30058 [Routing] fix perf issue when dumping large number of routes (nicolas-grekas) + * bug #30062 [Form] do not overwrite the constraint being evaluated (xabbuh) + * bug #30074 Fix wrong value in file id attribute for Xliff 2.0 (deguif) + * bug #30078 [Messenger] Fix DataCollector template (ottaviano) + * bug #30087 [PhpUnitBridge] fix PHP 5.3 compat (nicolas-grekas) + +* 4.2.3 (2019-02-03) + + * bug #30050 [Cache] fix pruning pdo cache for vendors that throw on execute (bendavies) + * bug #30046 [DI] Fix dumping Doctrine-like service graphs (nicolas-grekas) + * bug #30028 [Form] fix some docblocks and type checks (xabbuh) + * bug #30037 Disable Twig in the profiler menu when Twig is not used (javiereguiluz) + * bug #30026 [VarDumper] dont implement Serializable in Stub (nicolas-grekas) + * bug #30034 [Config] ensure moving away from Serializable wont break cache:clear (nicolas-grekas) + * bug #29532 [Messenger] fixed RabbitMQ arguments not passed as integer values (thePanz) + * bug #30013 [Routing] dont redirect routes with greedy trailing vars with no explicit slash (nicolas-grekas) + * bug #30006 [Security] don't do nested calls to serialize() (nicolas-grekas, Renan) + * bug #30007 [FrameworkBundle] Support use of hyphen in asset package name (damaya, XuruDragon) + * bug #30004 Fix format strings for deprecation notices (TysonAndre) + * bug #29984 [VarDumper] Fixed search bar (ro0NL) + * bug #29764 [HttpFoundation] Check file exists before unlink (adam-mospan) + * bug #29783 [HttpFoundation] MemcachedSessionHandler::close() must close connection (grachevko) + * bug #29794 Always pass $key to NullAdapter->createCacheItem (TysonAndre) + * bug #29844 [Console] Fixed #29835: ConfirmationQuestion with default true for answer '0' (mrthehud) + * bug #29869 [Debug][ErrorHandler] Preserve our error handler when a logger sets another one (fancyweb) + * bug #29900 [Cache] PDO-based cache pool table autocreation does not work (errogaht) + * bug #29926 [Form] Changed UrlType input type to text when default_protocol is not null (MatTheCat) + * bug #29961 [Translation] Concatenated translation messages (Stadly) + * bug #29847 [Cache] fix used variable name (xabbuh) + * bug #29920 [Debug][DebugClassLoader] Match more cases for final, deprecated and internal classes / methods extends (fancyweb) + * bug #29922 Avoid dots in generated class names (derrabus) + * bug #29863 [Security] Do not mix password_*() API with libsodium one (chalasr) + * bug #29894 [DependencyInjection] the string "0" is a valid service identifier (xabbuh) + * bug #29885 Update MimeType extensions (fabpot) + * bug #29875 [TwigBridge] fix compatibility with Twig >= 2.6.1 (xabbuh) + * bug #29873 [Debug] remove return type hint for PHP 5 compatibility (xabbuh) + * bug #29837 Fix SwiftMailerHandler to support Monolog's latest reset functionality (Seldaek) + * bug #29853 Revert "bug #29597 [DI] fix reporting bindings on overriden services as unused" (mmarynich) + * bug #29833 [DebugClassLoader] expose proxyfied findFile() method (fancyweb) + +* 4.2.2 (2019-01-06) + + * bug #29494 [HttpFoundation] Fix request uri when it starts with double slashes (alquerci) + * bug #29697 [DI] Fixed wrong factory method in exception (Wojciech Gorczyca) + * bug #29679 [HttpKernel] Correctly Render Signed URIs Containing Fragments (zanbaldwin) + * bug #29754 Ensure final input of CommandTester works with default (Firehed) + * bug #29695 [Form] Do not ignore the choice groups for caching (vudaltsov) + * bug #29738 [Intl] handle null date and time types ( 8000 xabbuh) + * bug #29708 [FrameworkBundle] access the container getting it from the kernel (xabbuh) + * bug #29676 [HttpFoundation] Fix erasing cookies issue (eiannone) + * bug #29741 [VarExporter] fix exporting array indexes (xabbuh) + * bug #29704 [FrameworkBundle] improve errors in tests missing the BrowserKit component (xabbuh) + * bug #29721 [SecurityBundle] Fix traceable voters (ro0NL) + * bug #29617 [Console] Add specific replacement for help text in single command applications (codedmonkey) + * bug #29714 [Event Dispatcher] fixed 29703: TraceableEventDispatcher reset() callStack to null (mlievertz) + * bug #29597 [DI] fix reporting bindings on overriden services as unused (nicolas-grekas) + * bug #29639 [Yaml] detect circular references (xabbuh) + * bug #29644 [Cache] fix bad optim (nicolas-grekas) + * bug #29648 [Cache] fix Simple\Psr6Cache proxying of metadata (nicolas-grekas) + * bug #29569 [FrameworkBundle] decouple debug:autowiring from phpdocumentor/reflection-docblock (SerkanYildiz) + * bug #29546 [DI] map snake-case ids of service subscribers to camel-case autowiring aliases (nicolas-grekas) + * bug #29409 Fix env fallback to an unresolved variable (jderusse) + * bug #29626 [Routing] fix trailing slash redirections involving a trailing var (nicolas-grekas) + * bug #29411 [EventDispatcher] Revers event tracing order (ro0NL) + * bug #29533 Fixed public directory when configured in composer.json (alexander-schranz) + * bug #29619 [Console] OutputFormatter: move strtolower to createStyleFromString (ogizanagi) + * bug #29621 [Security] Prefer clone() over unserialize(serialize()) for user refreshment (chalasr) + * bug #29591 [Cache] Fix undefined variable in ArrayTrait (eXtreme) + * bug #29558 [Messenger] Restore message handlers laziness (chalasr) + * bug #29589 [VarExporter] dont call userland code with uninitialized objects (nicolas-grekas) + * bug #29542 [Routing] fix dumping same-path routes with placeholders (nicolas-grekas) + * bug #29587 [Debug] ignore underscore vs backslash namespaces in DebugClassLoader (nicolas-grekas) + * bug #29584 [FrameworkBundle] fix describing routes with no controllers (nicolas-grekas) + * bug #29582 [DI] move RegisterServiceSubscribersPass before DecoratorServicePass (kbond) + * bug #29527 [TwigBridge][Form] Prevent multiple rendering of form collection prototypes (Shoplifter) + * bug #29571 [Yaml] ensures that the mb_internal_encoding is reset to its initial value (Jörn Lang) + * bug #29513 [Hackday][Serializer] Deserialization ignores argument type hint from phpdoc for array in constructor argument (karser) + * bug #29323 [Security] defer log message in guard authenticator (eschultz-magix) + * bug #29539 [WebProfilerBundle][TwigBundle] CSS fixes (ro0NL) + * bug #29543 [Cache] Don't erase processed redis dsn (chalasr) + * bug #29531 [Validator] Added IBAN format for Vatican City State (raulfraile) + * bug #29501 [Form] filter out invalid language values (xabbuh) + * bug #29307 [Form] Filter arrays out of scalar form types (nicolas-grekas) + * bug #29500 [Form] filter out invalid Intl values (xabbuh) + * bug #29499 [Validator] Fixed grouped composite constraints (HeahDude) + +* 4.2.1 (2018-12-06) + + * security #cve-2018-19790 [Security\Http] detect bad redirect targets using backslashes (xabbuh) + * security #cve-2018-19789 [Form] Filter file uploads out of regular form types (nicolas-grekas) + * bug #29481 [TwigBridge] Deprecating legacy Twig paths in DebugCommand and simplifications (yceruto) + * bug #29436 [Cache] Fixed Memcached adapter doClear()to call flush() (raitocz) + * bug #29482 Fixes sprintf(): Too few arguments in MessageFormatter::choiceFormat (stephanedelprat) + * bug #29461 [Contracts] extract LocaleAwareInterface out of TranslatorInterface (nicolas-grekas) + * bug #29446 [VarExporter] fix dumping private properties from abstract classes (nicolas-grekas) + * bug #29441 [Routing] ignore trailing slash for non-GET requests (nicolas-grekas) + * bug #29445 [FrameworkBundle] Fix empty output for debug:autowiring when reflection-docblock is not installed (chalasr) + * bug #29444 [Workflow] Fixed BC break for Workflow metadata (lyrixx) + * bug #29432 [DI] dont inline when lazy edges are found (nicolas-grekas) + * bug #29413 [Serializer] fixed DateTimeNormalizer to maintain microseconds when a different timezone required (rvitaliy) + * bug #29424 [Routing] fix taking verb into account when redirecting (nicolas-grekas) + * bug #29418 [VarExporter] fix dumping protected property from abstract classes (nicolas-grekas) + * bug #29414 [DI] Fix dumping expressions accessing single-use private services (chalasr) + * bug #28853 [LDAP] Add TIMEOUT Option to LDAP Connection Options (lmatte7) + * bug #29399 [FrameworkBundle] define doctrine as default_pdo_provider only if the package is installed (nicolas-grekas) + * bug #29375 [Validator] Allow `ConstraintViolation::__toString()` to expose codes that are not null or emtpy strings (phansys) + * bug #29376 [EventDispatcher] Fix eventListener wrapper loop in TraceableEventDispatcher (jderusse) + * bug #29386 undeprecate the single-colon notation for controllers (fbourigault) + * bug #29393 [DI] fix edge case in InlineServiceDefinitionsPass (nicolas-grekas) + * bug #29394 [Config] fix path exclusion during glob discovery (nicolas-grekas) + * bug #29395 [FrameworkBundle][Messenger] Restore check for messenger serializer default id (ogizanagi) + * bug #29380 [Routing] fix greediness of trailing slash (nicolas-grekas) + * 4.2.0 (2018-11-30) * bug #29343 [Form] Handle all case variants of "nan" when parsing a number (mwhudson, xabbuh) diff --git a/CHANGELOG-4.3.md b/CHANGELOG-4.3.md new file mode 100644 index 0000000000000..a045a58269496 --- /dev/null +++ b/CHANGELOG-4.3.md @@ -0,0 +1,280 @@ +CHANGELOG for 4.3.x +=================== + +This changelog references the relevant changes (bug and security fixes) done +in 4.3 minor versions. + +To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash +To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v4.3.0...v4.3.1 + +* 4.3.0-BETA1 (2019-05-09) + + * feature #31249 [Translator] Set sources when extracting strings from php files (Stadly) + * feature #31365 [Intl] Made countries ISO 3166 compliant + exclude Zzzz script code (ro0NL) + * feature #31060 [Validator] Make API endpoint for NotCompromisedPasswordValidator configurable (xelan) + * feature #31353 [FrameworkBundle] Show injected services for iterator and array arguments (jschaedl) + * feature #31350 [Intl] Rename Regions to Countries (ro0NL) + * feature #31364 [Bridge/PhpUnit] Extract all the code but shebang from bin/simple-phpunit (JustBlackBird) + * feature #30985 [Form] Keep preferred choices order in ChoiceType (vudaltsov) + * feature #31288 [Messenger] RoutableMessageBus route to default bus (dirk39) + * feature #31292 [Validator] Allow intl timezones (ro0NL) + * feature #30970 [Messenger] Adding failure transport support (weaverryan) + * feature #31318 [Intl] Compile localized timezone offset name (ro0NL) + * feature #31248 [Translator] Add sources when dumping qt files (Stadly) + * feature #31280 [WebServerBundle] Change the default pidfile location to cache directory (jschaedl) + * feature #31293 [Form] Remove default option grouping in TimezoneType (ro0NL) + * feature #31262 [Intl] Update timezones to ICU 64.2 + compile zone to country mapping (ro0NL) + * feature #31295 [Intl] Add timezone offset utilities (ro0NL) + * feature #30958 [Messenger] Allows to register handlers on a specific transport (sroze) + * feature #31061 [BridgeDoctrineMessenger] Doctrine ping connection middleware (insidestyles) + * feature #31282 [Messenger] Add WorkerStoppedEvent (chalasr) + * feature #31138 [Security] Dispatch an event when "logout user on change" steps in (Simperfit) + * feature #31242 Update LoggingTranslator to log the change of a locale (gmponos) + * feature #30917 [Messenger] Add a redis stream transport (soyuka, alexander-schranz) + * feature #31195 [Form] Add intltimezone input to TimezoneType (ro0NL) + * feature #31134 [Routing] do not encode comma in query and fragment (Tobion) + * feature #31220 [TwigBridge] bootstrap4 file_widget: allow setting label attributes declared in label_attr (AngelFQC) + * feature #31204 [Messenger] ease testing and allow forking the middleware stack (nicolas-grekas) + * feature #30370 [Cache] Add optimized FileSystem & Redis TagAware Adapters (andrerom) + * feature #28831 [Intl] Add Timezones (ro0NL) + * feature #31170 [Security] deprecate BCryptPasswordEncoder in favor of NativePasswordEncoder (nicolas-grekas) + * feature #31140 [Security] Add NativePasswordEncoder (nicolas-grekas) + * feature #31130 [VarDumper] add caster for WeakReference instances of PHP 7.4 (nicolas-grekas) + * feature #31082 [Form] Show all option normalizers on debug:form command (yceruto) + * feature #30957 [Messenger] Remove base64_encode & use addslashes (weaverryan) + * feature #30717 [Serializer] Use name converter when normalizing constraint violation list (norkunas) + * feature #28846 [Intl] Simplify API (ro0NL) + * feature #31093 [PhpUnitBridge] ClockMock does not mock gmdate() (Simperfit) + * feature #29211 [PhpUnitBridge] Url encoded deprecations helper config (greg0ire) + * feature #31062 [Dotenv] Deprecate useage of "putenv" (Nyholm) + * feature #31021 [Cache] Added command for list all available cache pools (Nyholm) + * feature #31027 [Config] Deprecate TreeBuilder::root (gharlan) + * feature #31019 [Security] Replace Argon2*PasswordEncoder by SodiumPasswordEncoder (chalasr) + * feature #30997 [Console] Add callback support to Console\Question autocompleter (Mikkel Paulson) + * feature #30959 [FrameworkBundle] [TwigBundle] Move the hinclude key away from templating (Simperfit) + * feature #30968 [Security] Add Argon2idPasswordEncoder (chalasr) + * feature #30963 [Serializer] Experimental for ObjectListExtractor (joelwurtz) + * feature #30933 [Routing][ObjectRouteLoader] Allow invokable route loader services (fancyweb) + * feature #30897 [DIC] Add a `require` env var processor (mpdude) + * feature #30964 [HttpKernel] Add a "short" trace header format, make header configurable (mpdude) + * feature #29935 [DI] Fix bad error message for unused bind under _defaults (przemyslaw-bogusz) + * feature #30962 [DoctrineBridge] Deprecated implicit optimization in DoctrineChoiceLoader (HeahDude) + * feature #30862 [Routing] UrlHelper to get absolute URL for a path (vudaltsov) + * feature #30607 [Serializer] Add Support of recursive denormalization on object_to_populate (jewome62) + * feature #30429 [Form] group_by as callback returns array (antonch1989) + * feature #30887 [FrameworkBundle] fix search in debug autowiring (sez-open) + * feature #30935 Use env variable to create anytype of lock store (jderusse) + * feature #30932 [Validator] Add an option to disable NotCompromisedPasswordValidator (lyrixx) + * feature #30909 [Translator] Add comments when dumping po files (deguif) + * feature #30913 [Messenger] Uses an `AmqpStamp` to provide flags and attributes (sroze) + * feature #30900 [Validator] add new `Timezone` validation constraint (phansys) + * feature #30915 [Serializer] Add datetimezone normalizer (jewome62) + * feature #28937 Improve Translator caching (rpkamp) + * feature #30904 [Serializer] provide new ObjectPropertyListExtractorInterface (dmaicher) + * feature #30902 [Workflow] The TransitionEvent is able to modify the context (lyrixx) + * feature #30908 [Workflow] Added workflow_transition_blockers twig function (lyrixx) + * feature #30893 Add "input" option to NumberType (fancyweb, Bernhard Schussek) + * feature #30898 [Validator] Wire NotCompromisedPassword in FrameworkBundle and handle non UTF-8 password (tgalopin) + * feature #30890 [Workflow] Changed initial_places to initial_marking, added property (HeahDude, lyrixx) + * feature #30906 [symfony/HttpKernel] Throws an error when the generated class name is invalid. (drupol) + * feature #30892 [DomCrawler] Improve Crawler HTML5 parser need detection (tgalopin) + * feature #30901 Renamed NotPwned to NotCompromisedPassword (javiereguiluz) + * feature #30020 [Messenger] Ensure message is handled only once per handler (keulinho, sroze) + * feature #30545 #30536 PropertyAccessor->getValue disable exception (dimabory) + * feature #30008 [messenger] Adds a stamp to provide a routing key on message publishing (G15N, sroze) + * feature #29097 [Messenger] Add a "in-memory://" transport (GaryPEGEOT, sroze) + * feature #30537 [HttpClient] logger integration (antonch1989, nicolas-grekas) + * feature #30853 [Twig] Remove TemplatedEmail::template() (fabpot) + * feature #30757 [Messenger] Adding MessageCountAwareInterface to get transport message count (weaverryan) + * feature #28929 [HttpKernel][Framework] Locale aware services (neghmurken) + * feature #29306 [DomCrawler] Optionally use html5-php to parse HTML (tgalopin) + * feature #30255 [DependencyInjection] Invokable Factory Services (zanbaldwin) + * feature #30843 [HttpClient] Add ScopingHttpClient::forBaseUri() + tweak MockHttpClient (nicolas-grekas) + * feature #30844 [Cache] add logs on early-recomputation and locking (nicolas-grekas) + * feature #30520 [RouterDebugCommand] add link to Controllers (nicoweb) + * feature #30212 [DI] Add support for "wither" methods - for greater immutable services (nicolas-grekas) + * feature #30674 [FrameworkBundle] change the way http clients are configured by leveraging ScopingHttpClient (nicolas-grekas) + * feature #29312 [EventDispatcher] Split events across requests (ro0NL) + * feature #30827 [TwigBridge] Add template file link to debug:twig command (yceruto) + * feature #30826 [Form] Add file links for described classes in debug:form command (yceruto) + * feature #30813 New PHPUnit assertions for the WebTestCase (Pierstoval, fabpot) + * feature #27738 [Validator] Add a HaveIBeenPwned password validator (dunglas) + * feature #30690 Changing messenger bus id from 'message_bus' to 'messenger.default_bus' (THERAGE Kévin) + * feature #30810 [Inflector] remove "internal" marker from the component (nicolas-grekas) + * feature #26890 [Inflector] Support pluralization in the inflector (mbabker) + * feature #28637 [Validator] add number constraints (jschaedl) + * feature #30754 [Messenger] New messenger:stop-workers Command (weaverryan) + * feature #30707 [Messenger][DX] Allow stamps to be passed directly to MessageBusInterface::dispatch() (weaverryan) + * feature #29007 [Messenger] Add a Doctrine transport (vincenttouzet) + * feature #30628 Making the serializer configurable by transport (weaverryan) + * feature #30569 [FrameworkBundle][HttpKernel] Provide intuitive error message when a controller fails because it's not registered as a service (moynzzz) + * feature #26484 [Validator] String normalization options for string-based validators (renan-taranto) + * feature #30320 [Form][TwigBridge] Add row_attr to form theme (alexander-schranz) + * feature #30371 [OptionsResolver] Add a new method addNormalizer and normalization hierarchy (yceruto) + * feature #27735 [Validator][DoctrineBridge][FWBundle] Automatic data validation (dunglas) + * feature #30758 [PropertyAccess] Allow Can Accessor in Property Access (ragboyjr) + * feature #30116 [Filesystem] Fix mirroring a directory into itself or in his child with realpath checks (Fleuv, XuruDragon) + * feature #28879 [Debug] Mimic __toString php behavior in FlattenException (Deltachaos) + * feature #29495 [Ldap] Implement pagination (kevans91) + * feature #29448 [Ldap] Entry move support (kevans91) + * feature #30741 Add the Mailer component (fabpot) + * feature #30780 Fix some exception previous type hints (fabpot) + * feature #30729 [HttpKernel] change $previous argument for HttpException to \Throwable (sGy1980de) + * feature #30744 [Finder] Throw a dedicated exception for non-existing directory (xelan) + * feature #30759 [Messenger] Adding the "sync" transport to call handlers synchronously (weaverryan) + * feature #30772 [Contracts][EventDispatcher] move the Event class to symfony/contracts (nicolas-grekas) + * feature #30708 [Messenger] ReceiverInterface::handle() to get() & Worker with prioritized transports (weaverryan) + * feature #27648 [Lock] Added MongoDBStore (Joe Bennett) + * feature #30752 [HttpClient] use "nyholm/psr7" by default in Psr18Client (nicolas-grekas) + * feature #30671 Add optional parameter `prefetching` for AMQP connection (fbouchery) + * feature #25707 [DI] ServiceProviderInterface, implementation for ServiceLocator (kejwmen) + * feature #30606 [Validator] allow brackets in the optional query string (Emmanuel BORGES) + * feature #29476 [Messenger] Add a command to setup transports (vincenttouzet) + * feature #30719 [Mime] Add BodyRendererInterface (fabpot) + * feature #30664 [Finder] Get filename without extension (antonch1989) + * feature #30645 Alias for each assets package (gpenverne) + * feature #30706 [PropertyInfo] Add possibility to extract private and protected properties in reflection extractor (joelwurtz) + * feature #27808 [DI] Deprecate non-string default envs (ro0NL) + * feature #30691 [Contracts][EventDispatcher] add EventDispatcherInterface to symfony/contracts and use it where possible (nicolas-grekas) + * feature #20978 [Form] TransformationFailedException: Support specifying message to display (ogizanagi) + * feature #30676 Avoid dispatching SendMessageToTransportsEvent on redeliver (weaverryan) + * feature #26555 [Validator] Add constraint on unique elements collection(Assert\Unique) (zenmate, nicolas-grekas) + * feature #27684 [FrameworkBundle] Debug container environment variables (ro0NL) + * feature #30666 [Form][Console] Use dumper (ro0NL) + * feature #30559 [HttpClient] Parse common API error formats for better exception messages (dunglas) + * feature #28898 [Console] Add dumper (ro0NL) + * feature #30629 [HttpClient] added CachingHttpClient (fabpot) + * feature #30602 [BrowserKit] Add support for HttpClient (fabpot, THERAGE Kévin) + * feature #30651 Allow user to set the project dir (tdutrion) + * feature #30654 [HttpClient] Add a ScopingHttpClient (XuruDragon) + * feature #30388 [Security] undeprecate the RoleHierarchyInterface (xabbuh) + * feature #30652 Fixing a bug where messenger:consume could send message to wrong bus (weaverryan) + * feature #30650 Dispatching two events when a message is sent & handled (weaverryan) + * feature #30557 [Messenger] Worker events + global retry functionality (weaverryan) + * feature #30468 [Workflow] Added support for many inital places (lyrixx) + * feature #30448 [Finder] Ignore paths from .gitignore #26714 (amaabdou) + * feature #30625 [HttpKernel] add RealHttpKernel: handle requests with HttpClientInterface (fabpot) + * feature #30508 [Routing] Exposed "utf8" option, defaults "locale" and "format" in configuration (Jules Pietri) + * feature #28920 [EventDispatcher] swap arguments of dispatch() to allow registering events by FQCN (nicolas-grekas) + * feature #30605 [Cache] added DSN support for rediss in AbstractAdapter and RedisTrait (alex-vasilchenko-md) + * feature #30604 [HttpClient] add MockHttpClient (nicolas-grekas) + * feature #21035 [FrameworkBundle] Deprecate the Templating component integration (dunglas, fabpot) + * feature #30567 [HttpClient] exceptions carry response (antonch1989) + * feature #28849 [Messenger] Support for handling messages after current bus is finished (Nyholm) + * feature #29538 [Workflow] Add colors to workflow dumps (alexislefebvre) + * feature #28975 [DI] Add an url EnvProcessor (jderusse) + * feature #30419 [FrameworkBundle] Add integration of http-client component (Ioni14, nicoweb) + * feature #30583 [Messenger] Display a nice error when connection fail (lyrixx) + * feature #30450 [Profiler] Render the performance graph with SVG (Tom32i) + * feature #29130 [Serializer] Normalize constraint violation parameters (ogizanagi) + * feature #28330 [MonologBridge] Add monolog processors adding route and command info (trakos) + * feature #30339 [Monolog] Disable DebugLogger in CLI (lyrixx) + * feature #30584 [Intl] Add compile binary (ro0NL) + * feature #30579 Using AMQP auto-setup in all cases, not just in debug (weaverryan) + * feature #30348 [DependencyInjection] Add ability to define an index for service in an injected service locator argument (XuruDragon, nicolas-grekas) + * feature #30469 Create a hyperlink to interfaces/classes that can be autowired (SerkanYildiz) + * feature #30334 [DI] add ReverseContainer: a locator that turns services back to their ids (nicolas-grekas) + * feature #30539 [Messenger] deprecate LoggingMiddleware in favor of providing a logger to SendMessageMiddleware (nicolas-grekas) + * feature #30556 [HttpClient] Allow to pass user/pw as an array (dunglas) + * feature #30547 [HttpClient] Add new bearer option (dunglas) + * feature #29303 [Messenger] add welcome notice when running the command (nicolas-grekas) + * feature #30541 [BrowserKit] Rename Client to Browser (fabpot) + * feature #30504 [DI] replace "nullable" env processor by improving the "default" one (nicolas-grekas) + * feature #30499 [HttpClient] add ResponseInterface::toArray() (nicolas-grekas) + * feature #30472 [Translation] Add XLIFF 1 source to metadata to differentiate from attr (ostrolucky) + * feature #30484 [Mime] added Headers::toArray() (fabpot) + * feature #30482 [Mime] Fix support for date form parts (fabpot) + * feature #30385 [SecurityBundle] Validate the IPs configured in access_control (javiereguiluz) + * feature #30413 [HttpClient][Contracts] introduce component and related contracts (nicolas-grekas) + * feature #30377 [Validator] add MIR card scheme (antonch1989) + * feature #29146 [Workflow] Added a context to `Workflow::apply()` (lyrixx) + * feature #30433 [Form] Allow to disable and customize PercentType symbol (Ken Stanley, OskarStark) + * feature #30408 [HttpKernel] Better exception page when the invokable controller returns nothing (dimabory) + * feature #30325 [HttpKernel] Prevent search engines from indexing dev applications (GaryPEGEOT) + * feature #30390 [FrameworkBundle] Fix UrlGenerator::generate to return an empty string instead of null (Emmanuel BORGES) + * feature #30375 [Messenger] Added transport agnostic exception (nikossvnk, lolmx) + * feature #29254 [FrameworkBundle] Added the condition routing option to the debug router command (soufianZantar) + * feature #30286 Drop more usages of Serializable (nicolas-grekas) + * feature #30379 [FrameworkBundle][Routing] allow boolean container parameters for routes (dmaicher) + * feature #29661 [Filesystem] Support resources and deprecate using arrays in dumpFile() and appendToFile() (thewilkybarkid) + * feature #30358 [Form] be able to specify the input format for times (xabbuh) + * feature #30416 Mime messages (fabpot) + * feature #22048 [Security] deprecate the Role and SwitchUserRole classes (xabbuh) + * feature #30345 [Monolog] Added a way to configure the ConsoleFormatter from the ConsoleHandler (lyrixx) + * feature #30357 [TwigBridge] rename parent_form() to form_parent() (xabbuh) + * feature #30257 [DependencyInjection] Allow to choose an index for tagged collection (deguif, XuruDragon) + * feature #30311 [VarDumper] Implement DsCaster (enumag) + * feature #27570 [PropertyInfo] Added support for extract type from default value (tsantos84) + * feature #28919 [DX][WebProfilerBundle] Add Pretty Print functionality for Request Content (SamFleming) + * feature #28723 [Form] deprecate custom formats with HTML5 widgets (xabbuh) + * feature #29865 [Console] Added suggestions for missing packages (przemyslaw-bogusz) + * feature #30301 [VarDumper] add link to source next to class names (nicolas-grekas) + * feature #30225 publish message with custom queue options : flags | attributes (fedor.f, insidestyles) + * feature #30249 [Routing] deprecate some router options (Tobion) + * feature #30267 [Form] add option to render NumberType as type="number" (xabbuh) + * feature #28969 [Form] deprecate using invalid names for buttons (xabbuh) + * feature #29887 [Form] Add input_format option to DateType and DateTimeType (fancyweb) + * feature #30051 Drop \Serializable implementations (renanbr) + * feature #30236 Add element to ghost in Exception (przemyslaw-bogusz) + * feature #30120 [FrameworkBundle][Translation] Added support for PHP files with trans() in translation commands (yceruto) + * feature #28812 [Form] add a convenience method to get the parent form in Twig templates (xabbuh) + * feature #29121 [FrameworkBundle][Translation] Add support for Translator paths, Twig paths and Translator aware services paths in commands (yceruto) + * feature #28477 [Validator] Add new json Validator (zairigimad) + * feature #30126 [Form] forward valid numeric values to transform() (xabbuh) + * feature #28635 [Form] Add label_translation_parameters, help_translation_parameters and attr_translation_parameters options to base form type (webnet-fr) + * feature #29767 Nullable environment variable processor (bpolaszek) + * feature #30111 [SecurityBundle] Deprecate the normalization of the cookie names (javiereguiluz) + * feature #30027 [FrameworkBundle] Add sid_length and sid_bits_per_character session ini options in session configuration (XuruDragon) + * feature #30075 [DependencyInjection] Added information about deprecated aliases in debug:autowiring (XuruDragon) + * feature #30024 [Debug] Display more details in the simple error page of Debug (javiereguiluz) + * feature #30052 [Security] Replace serialization API (renanbr) + * feature #27898 [Yaml] Fixed invalid Parser behavior (guiguiboy) + * feature #29753 [Console] Add an iterate method to the ProgressBar class (jvasseur) + * feature #29999 [PropertyAccess] speed up accessing object properties (xabbuh) + * feature #29641 [Validator] NotBlank: add a new option to allow null values (dunglas) + * feature #28721 [Form] deprecate some options for single_text widgets (xabbuh) + * feature #29936 [Mime] Add a set of default content-types for some extensions (fabpot) + * feature #28865 [Routing] allow using compiled matchers and generators without dumping PHP code (nicolas-grekas) + * feature #29236 [Cache] deprecate all PSR-16 adapters, provide Psr16Cache instead (nicolas-grekas) + * feature #29958 introducing native php serialize() support for Messenger transport (weaverryan, xabbuh) + * feature #29861 [Form][TwigBridge] Add help_html (mpiot) + * feature #29968 [DI] Added support for deprecating aliases (j92, Renan) + * feature #29850 [FrameworkBundle] xliff-version option to translation update command (andrewwro) + * feature #29896 [Mime] Add the component (fabpot) + * feature #29862 Add block prefix to csrf token field (alexander-schranz) + * feature #29881 [BrowserKit] Various changes to the Response class (fabpot) + * feature #29813 [FrameworkBundle] Remove ControllerTrait::isFormValid() (lyrixx) + * feature #29148 Load original file metadata when loading Xliff 1.2 files (eternoendless) + * feature #29840 [FrameworkBundle] pass project dir into the assets install command (xabbuh) + * feature #29821 [VarDumper] add caster for OpenSSL X.509 resources (nicolas-grekas) + * feature #29781 [DI] Add trim env processor (ogizanagi) + * feature #28902 [Debug] Detect virtual methods using @method (ro0NL) + * feature #29780 [Profiler] Still show locale and fallback locale even if no trans used (ogizanagi) + * feature #29680 [Form] Add new block_prefix option for an easy form theming (yceruto) + * feature #29528 [DebugBundle] Added 'theme' option to change the color of dump() when rendered inside templates (dem3trio) + * feature #24576 [FrameworkBundle] Added `ControllerTrait::isFormValid` (lyrixx) + * feature #29483 [HttpKernel] Set the default locale early (thewilkybarkid) + * feature #29186 [HttpKernel] Increase priority of AddRequestFormatsListener (thewilkybarkid) + * feature #29658 [Validator] Choices constraint improvement (nikophil) + * feature #29283 [Serializer] CsvEncoder no header option (encode / decode) (redecs) + * feature #29718 [PHPUnit bridge] Bump php version of PHPUnit-bridge (gmponos) + * feature #29599 [Routing] Allow force-generation of trailing parameters using eg "/exports/news.{!_format}" (zavulon) + * feature #29613 [VarDumper] Use hyperlinks in CliDescriptor (ogizanagi) + * feature #28581 [DomCrawler] return empty string on `Crawler::text()` and `Crawler::html()` instead of an exception (respinoza) + * feature #29286 [WebProfilerBundle] Enable translation filters (ro0NL) + * feature #29517 [Hackday][Messenger] Add an alias for transport.symfony_serializer so SerializerInterface can be autowired (karser) + * feature #29108 [DI] compute autowiring error messages lazily (nicolas-grekas) + * feature #29235 [VarDumper] add support for links in CliDumper (nicolas-grekas) + * feature #29541 [FrameworkBundle] Stop calling Kernel::boot() twice in cli (chalasr) + * feature #28931 [PhpUnitBridge] Added ClassExistsMock (ro0NL) + * feature #29504 [Validator] Add support for UATP card validation (raulfraile) + * feature #29168 [Console] Add hyperlinks support (ostrolucky) + * feature #29439 [PhpUnitBridge] install PHPUnit 7 on PHP 7.1 and fix requir. for PHPUnit 6 (gregurco) + * feature #29452 [Form] Shortcut debug:form for partial type name (ro0NL) + * feature #28954 [Debug] Mark ErrorHandler and ExceptionHandler classes as final (fancyweb) + * feature #28479 [Validator] Checking a BIC along with an IBAN (sylfabre) + * feature #28858 [DI] Deprecated using env vars with cannotBeEmpty() (ro0NL) + * feature #28976 [DI] Add a "default" EnvProcessor (jderusse) + * feature #29127 [DomCrawler] Added return of element name in `extract()` method (andrey-helldar) + * feature #29145 [Workflow] Trigger `entered` event for subject entering in the Workflow for the first time (lyrixx) + diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 1c329f2146c3e..ec97d42d4b87b 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -10,74 +10,77 @@ Symfony is the result of the work of many people who made the code better - Christian Flothmann (xabbuh) - Tobias Schultze (tobion) - Christophe Coevoet (stof) + - Robin Chalas (chalas_r) - Jordi Boggiano (seldaek) - Victor Berchet (victor) - - Robin Chalas (chalas_r) - Kévin Dunglas (dunglas) - Maxime Steinhausser (ogizanagi) + - Ryan Weaver (weaverryan) - Jakub Zalas (jakubzalas) - Johannes S (johannes) - - Kris Wallsmith (kriswallsmith) - - Ryan Weaver (weaverryan) - Javier Eguiluz (javier.eguiluz) + - Kris Wallsmith (kriswallsmith) + - Roland Franssen (ro0) - Grégoire Pineau (lyrixx) - Hugo Hamon (hhamon) - - Roland Franssen (ro0) - Abdellatif Ait boudad (aitboudad) + - Samuel ROZE (sroze) - Romain Neutron (romain) - Pascal Borreli (pborreli) - Wouter De Jong (wouterj) - Joseph Bielawski (stloyd) - Karma Dordrak (drak) - - Samuel ROZE (sroze) - Lukas Kahwe Smith (lsmith) - Martin Hasoň (hason) - Jeremy Mikola (jmikola) + - Yonel Ceruto (yonelceruto) - Jean-François Simon (jfsimon) + - Jules Pietri (heah) - Benjamin Eberlei (beberlei) - Igor Wiedler (igorw) - - Jules Pietri (heah) - - Yonel Ceruto (yonelceruto) - Eriksen Costa (eriksencosta) - Guilhem Niot (energetick) + - Hamza Amrouche (simperfit) - Sarah Khalil (saro0h) - Jonathan Wage (jwage) - - Hamza Amrouche (simperfit) - Tobias Nyholm (tobias) + - Lynn van der Berg (kjarli) - Diego Saint Esteben (dosten) - - Iltar van der Berg (kjarli) - Alexandre Salomé (alexandresalome) - William Durand (couac) - ornicar - - Francis Besset (francisbesset) + - Jérémy DERUSSÉ (jderusse) - Dany Maillard (maidmaid) + - Francis Besset (francisbesset) - stealth35 ‏ (stealth35) - Alexander Mols (asm89) - - Bulat Shakirzyanov (avalanche123) - Matthias Pigulla (mpdude) - - Peter Rehm (rpet) + - Bulat Shakirzyanov (avalanche123) + - Alexander M. Turek (derrabus) - Saša Stamenković (umpirsky) - - Pierre du Plessis (pierredup) + - Peter Rehm (rpet) - Kevin Bond (kbond) + - Pierre du Plessis (pierredup) - Henrik Bjørnskov (henrikbjorn) - Miha Vrhovnik - - Jérémy DERUSSÉ (jderusse) - Diego Saint Esteben (dii3g0) - - Alexander M. Turek (derrabus) - Konstantin Kudryashov (everzet) - - Bilal Amarni (bamarni) - - Florin Patan (florinpatan) - Gábor Egyed (1ed) - - Mathieu Piot (mpiot) + - Bilal Amarni (bamarni) - Titouan Galopin (tgalopin) + - Grégoire Paris (greg0ire) + - Mathieu Piot (mpiot) + - David Maicher (dmaicher) + - Florin Patan (florinpatan) + - Gabriel Ostrolucký (gadelat) + - Valentin Udaltsov (vudaltsov) - Vladimir Reznichenko (kalessil) + - Jáchym Toušek (en 8000 umag) + - Konstantin Myakshin (koc) - Michel Weimerskirch (mweimerskirch) - Andrej Hudec (pulzarraider) - - Konstantin Myakshin (koc) - Eric Clemmons (ericclemmons) - - Jáchym Toušek (enumag) - Charles Sarrazin (csarrazi) - - David Maicher (dmaicher) - Christian Raue - Issei Murasawa (issei_m) - Arnout Boks (aboks) @@ -86,59 +89,61 @@ Symfony is the result of the work of many people who made the code better - Dariusz Górecki (canni) - Douglas Greenshields (shieldo) - Dariusz Ruminski - - Grégoire Paris (greg0ire) - Lee McDermott - Brandon Turner - Luis Cordova (cordoval) - Graham Campbell (graham) + - David Buchmann (dbu) - Daniel Holmes (dholmes) - Toni Uebernickel (havvg) - Bart van den Burg (burgov) - Jordan Alliot (jalliot) - Jérôme Tamarelle (gromnan) - John Wards (johnwards) + - Thomas Calvet (fancyweb) - Fran Moreno (franmomu) - - Valentin Udaltsov (vudaltsov) - Antoine Hérault (herzult) - Paráda József (paradajozsef) - Arnaud Le Blanc (arnaud-lb) - Maxime STEINHAUSSER - Michal Piotrowski (eventhorizon) - - gadelat (gadelat) - Tim Nagel (merk) - Brice BERNARD (brikou) - Baptiste Clavié (talus) + - Chris Wilkinson (thewilkybarkid) - marc.weistroff - - David Buchmann (dbu) + - Tomáš Votruba (tomas_votruba) - lenar - Alexander Schwenn (xelaris) - Włodzimierz Gajda (gajdaw) - - Tomáš Votruba (tomas_votruba) + - Jérôme Vasseur (jvasseur) - Peter Kokot (maastermedia) - Jacob Dreesen (jdreesen) - Florian Voutzinos (florianv) - Colin Frei + - Javier Spagnoletti (phansys) - Adrien Brault (adrienbrault) - Joshua Thijssen + - Daniel Wehner (dawehner) - excelwebzone - Gordon Franke (gimler) + - Sebastiaan Stok (sstok) - Fabien Pennequin (fabienpennequin) + - Théo FIDRY (theofidry) - Eric GELOEN (gelo) - - Sebastiaan Stok (sstok) - - Jérôme Vasseur (jvasseur) + - Joel Wurtz (brouznouf) - Lars Strojny (lstrojny) - - Daniel Wehner (dawehner) - Tugdual Saunier (tucksaun) - - Javier Spagnoletti (phansys) - - Théo FIDRY (theofidry) - Robert Schönthal (digitalkaoz) - Florian Lonqueu-Brochard (florianlb) - - Chris Wilkinson (thewilkybarkid) + - Oskar Stark (oskarstark) - Stefano Sala (stefano.sala) - Evgeniy (ewgraf) - Alex Pott - Vincent AUBERT (vincent) - Juti Noppornpitak (shiroyuki) + - Teoh Han Hui (teohhanhui) + - Anthony MARTIN (xurudragon) - Tigran Azatyan (tigranazatyan) - Sebastian Hörl (blogsh) - Daniel Gomes (danielcsgomes) @@ -147,60 +152,66 @@ Symfony is the result of the work of many people who made the code better - Arnaud Kleinpeter (nanocom) - Jannik Zschiesche (apfelbox) - Guilherme Blanco (guilhermeblanco) + - SpacePossum - Pablo Godel (pgodel) - Jérémie Augustin (jaugustin) + - Oleg Voronkovich - Andréia Bohner (andreia) - Philipp Wahala (hifi) - Julien Falque (julienfalque) - Rafael Dohms (rdohms) - jwdeitch - - Teoh Han Hui (teohhanhui) - Mikael Pajunen - - Joel Wurtz (brouznouf) - - Oleg Voronkovich + - Niels Keurentjes (curry684) - Vyacheslav Pavlov - Richard van Laak (rvanlaak) - Richard Shank (iampersistent) - Thomas Rabaix (rande) - Rouven Weßling (realityking) + - François-Xavier de Guillebon (de-gui_f) - Clemens Tolboom - Helmer Aaviksoo + - Alessandro Chitolina (alekitto) - Hiromi Hishida (77web) - - Niels Keurentjes (curry684) - Matthieu Ouellette-Vachon (maoueh) - Michał Pipa (michal.pipa) - Dawid Nowak - - Gabriel Ostrolucký - Amal Raghav (kertz) - Jonathan Ingram (jonathaningram) - Artur Kotyrba + - Tyson Andre - GDIBass - - SpacePossum + - Jan Schädlich (jschaedl) - jeremyFreeAgent (Jérémy Romey) (jeremyfreeagent) - James Halsall (jaitsu) - Matthieu Napoli (mnapoli) - Florent Mata (fmata) - Warnar Boekkooi (boekkooi) - - Alessandro Chitolina (alekitto) - Dmitrii Chekaliuk (lazyhammer) - Clément JOBEILI (dator) + - Marek Štípek (maryo) - Daniel Espendiller - Possum - Dorian Villet (gnutix) + - George Mponos (gmponos) - Sergey Linnik (linniksa) - Richard Miller (mr_r_miller) - Albert Casademont (acasademont) - Mario A. Alvarez Garcia (nomack84) - Dennis Benkert (denderello) - DQNEO + - Samuel NELA (snela) + - Alexander Schranz (alexander-schranz) + - Gregor Harlan (gharlan) + - Gary PEGEOT (gary-p) + - Ruben Gonzalez (rubenrua) - Benjamin Dulau (dbenjamin) - Mathieu Lemoine (lemoinem) - - Thomas Calvet (fancyweb) - Christian Schmidt - Andreas Hucks (meandmymonkey) + - Tom Van Looy (tvlooy) - Noel Guilbert (noel) - Yanick Witschi (toflar) - - Marek Štípek (maryo) - Stepan Anchugov (kix) - bronze1man - sun (sun) @@ -215,19 +226,23 @@ Symfony is the result of the work of many people who made the code better - fivestar - Dominique Bongiraud - Jeremy Livingston (jeremylivingston) + - Vincent Touzet (vincenttouzet) - Michael Lee (zerustech) - Matthieu Auger (matthieuauger) - Leszek Prabucki (l3l0) + - Fabien Bourigault (fbourigault) - François Zaninotto (fzaninotto) - Dustin Whittle (dustinwhittle) - jeff - John Kary (johnkary) + - Andreas Schempp (aschempp) - Justin Hileman (bobthecow) - Blanchon Vincent (blanchonvincent) - Michele Orselli (orso) - - Tom Van Looy (tvlooy) - Sven Paulus (subsven) + - Maxime Veber (nek-) - Rui Marinho (ruimarinho) + - Massimiliano Arione (garak) - Eugene Wissner - Pascal Montoya - Julien Brochet (mewt) @@ -235,16 +250,16 @@ Symfony is the result of the work of many people who made the code better - Tristan Darricau (nicofuma) - Marcel Beerta (mazen) - Pavel Batanov (scaytrase) + - Mantis Development - Loïc Faugeron - Hidde Wieringa (hiddewie) - Marco Pivetta (ocramius) - Rob Frawley 2nd (robfrawley) - julien pauli (jpauli) - Lorenz Schori - - Oskar Stark (oskarstark) - Sébastien Lavoie (lavoiesl) - - Gregor Harlan (gharlan) - Dariusz + - Michael Babker (mbabker) - Francois Zaninotto - Alexander Kotynia (olden) - Daniel Tschinder @@ -253,19 +268,18 @@ Symfony is the result of the work of many people who made the code better - Elnur Abdurrakhimov (elnur) - Manuel Reinhard (sprain) - Danny Berger (dpb587) - - Ruben Gonzalez (rubenrua) - Adam Prager (padam87) - Benoît Burnichon (bburnichon) - Roman Marintšenko (inori) - Xavier Montaña Carreras (xmontana) - - François-Xavier de Guillebon (de-gui_f) + - Rémon van de Kamp (rpkamp) - Mickaël Andrieu (mickaelandrieu) - - Maxime Veber (nek-) - Xavier Perez - Arjen Brouwer (arjenjb) - Katsuhiro OGAWA - Patrick McDougle (patrick-mcdougle) - Alif Rachmawadi + - Anton Chernikov (anton_ch1989) - Kristen Gilden (kgilden) - Pierre-Yves LEBECQ (pylebecq) - Jordan Samouh (jordansamouh) @@ -280,46 +294,50 @@ Symfony is the result of the work of many people who made the code better - GordonsLondon - Jan Sorgalla (jsor) - Ray - - Tyson Andre - Chekote - Thomas Adam - Viktor Bocharskyi (bocharsky_bw) - Jhonny Lidfors (jhonne) - Diego Agulló (aeoris) - - Andreas Schempp (aschempp) - jdhoek - - Massimiliano Arione (garak) - Bob den Otter (bopp) + - Thomas Schulz (king2500) + - Frank de Jonge (frenkynet) + - Andre Rømcke (andrerom) - Nikita Konstantinov - Wodor Wodorski - Thomas Lallement (raziel057) - mcfedr (mcfedr) - Colin O'Dell (colinodell) - - Fabien Bourigault (fbourigault) - Giorgio Premi - - Jan Schädlich (jschaedl) + - renanbr + - Alex Rock (pierstoval) + - Ben Davies (bendavies) - Beau Simensen (simensen) - Michael Hirschler (mvhirsch) - Robert Kiss (kepten) + - Zan Baldwin (zanderbaldwin) - Roumen Damianoff (roumen) - Antonio J. García Lagar (ajgarlag) - Kim Hemsø Rasmussen (kimhemsoe) + - Przemysław Bogusz (przemyslaw-bogusz) + - Pascal Luna (skalpa) - Wouter Van Hecke - Jérôme Parmentier (lctrs) - - Michael Babker (mbabker) - Peter Kruithof (pkruithof) - Michael Holm (hollo) - - Remon van de Kamp (rpkamp) + - Mathieu Lechat - Marc Weistroff (futurecat) - Christian Schmidt + - Patrick Landolt (scube) - MatTheCat - Chad Sikorra (chadsikorra) - Chris Smith (cs278) - Florian Klein (docteurklein) - - Gary PEGEOT (gary-p) - Manuel Kiessling (manuelkiessling) - Atsuhiro KUBO (iteman) - rudy onfroy (ronfroy) + - Serkan Yildiz (srknyldz) - Andrew Moore (finewolf) - Bertrand Zuchuat (garfield-fr) - Sullivan SENECHAL (soullivaneuh) @@ -331,6 +349,7 @@ Symfony is the result of the work of many people who made the code better - Jerzy Zawadzki (jzawadzki) - Wouter J - Ismael Ambrosi (iambrosi) + - Emmanuel BORGES (eborges78) - François Pluchino (francoispluchino) - Aurelijus Valeiša (aurelijus) - Jan Decavele (jandc) @@ -339,28 +358,31 @@ Symfony is the result of the work of many people who made the code better - Tiago Ribeiro (fixe) - Hidde Boomsma (hboomsma) - John Bafford (jbafford) + - Raul Fraile (raulfraile) + - David Prévot - Adrian Rudnik (kreischweide) - Francesc Rosàs (frosas) - Romain Pierre (romain-pierre) - Julien Galenski (ruian) - Bongiraud Dominique - janschoenherr - - Thomas Schulz (king2500) + - Emanuele Gaspari (inmarelibero) - Dariusz Rumiński - - Frank de Jonge (frenkynet) - Berny Cantos (xphere81) - Thierry Thuon (lepiaf) - Ricard Clau (ricardclau) - Mark Challoner (markchalloner) - Gennady Telegin (gtelegin) - - Ben Davies (bendavies) - Erin Millard - Artur Melo (restless) - Matthew Lewinski (lewinski) - Magnus Nordlander (magnusnordlander) - Thomas Royer (cydonia7) + - Nicolas LEFEVRE (nicoweb) - alquerci + - Mateusz Sip (mateusz_sip) - Francesco Levorato + - Dmitrii Poddubnyi (karser) - Vitaliy Zakharov (zakharovvi) - Tobias Sjösten (tobiassjosten) - Gyula Sallai (salla) @@ -368,13 +390,15 @@ Symfony is the result of the work of many people who made the code better - Christian Gärtner (dagardner) - Tomasz Kowalczyk (thunderer) - Artur Eshenbrener + - Andreas Braun + - Arjen van der Meijden - Damien Alexandre (damienalexandre) - Thomas Perez (scullwm) - Felix Labrecque - Yaroslav Kiliba - Terje Bråten - - Mathieu Lechat - Robbert Klarenbeek (robbertkl) + - Eric Masoero (eric-masoero) - JhonnyL - David Badura (davidbadura) - hossein zolfi (ocean) @@ -386,8 +410,13 @@ Symfony is the result of the work of many people who made the code better - Stéphane PY (steph_py) - Philipp Kräutli (pkraeutli) - Grzegorz (Greg) Zdanowski (kiler129) + - Iker Ibarguren (ikerib) - Kirill chEbba Chebunin (chebba) + - Stadly - Greg Thornton (xdissent) + - Quynh Xuan Nguyen (xuanquynh) + - Martin Hujer (martinhujer) + - Philipp Cordes - Costin Bereveanu (schniper) - Loïc Chardonnet (gnusat) - Marek Kalnik (marekkalnik) @@ -401,10 +430,8 @@ Symfony is the result of the work of many people who made the code better - Nicolas Dewez (nicolas_dewez) - Endre Fejes - Tobias Naumann (tna) - - George Mponos (gmponos) - Daniel Beyer - Shein Alexey - - Alex Rock Ancelet (pierstoval) - Romain Gautier (mykiwi) - Joe Lencioni - Daniel Tschinder @@ -417,7 +444,6 @@ Symfony is the result of the work of many people who made the code better - Karoly Negyesi (chx) - Ivan Kurnosov - Xavier HAUSHERR - - David Prévot - Albert Jessurum (ajessu) - Laszlo Korte - Miha Vrhovnik @@ -430,7 +456,6 @@ Symfony is the result of the work of many people who made the code better - Karel Souffriau - Christophe L. (christophelau) - Anthon Pang (robocoder) - - Emanuele Gaspari (inmarelibero) - Sébastien Santoro (dereckson) - Brian King - Michel Salib (michelsalib) @@ -440,17 +465,16 @@ Symfony is the result of the work of many people who made the code better - Valentin Jonovs (valentins-jonovs) - Jeanmonod David (jeanmonod) - Christopher Davis (chrisguitarguy) + - Webnet team (webnet) - Jan Schumann - Niklas Fiekas - Markus Bachmann (baachi) - lancergr - - Zan Baldwin - Mihai Stancu - Ivan Nikolaev (destillat) - Olivier Dolbeau (odolbeau) - Jan Rosier (rosier) - Alessandro Lai (jean85) - - Pascal Luna (skalpa) - Arturs Vonda - Josip Kruslin - Asmir Mustafic (goetas) @@ -459,10 +483,12 @@ Symfony is the result of the work of many people who made the code better - EdgarPE - Florian Pfitzer (marmelatze) - Asier Illarramendi (doup) - - Andreas Braun + - Martijn Cuppens + - Vlad Gregurco (vgregurco) + - Maciej Malarz (malarzm) - Boris Vujicic (boris.vujicic) - Chris Sedlmayr (catchamonkey) - - Mateusz Sip (mateusz_sip) + - Dmytro Borysovskyi (dmytr0) - Kamil Kokot (pamil) - Seb Koelen - Christoph Mewes (xrstf) @@ -470,9 +496,11 @@ Symfony is the result of the work of many people who made the code better - Ariel Ferrandini (aferrandini) - Dirk Pahl (dirkaholic) - cedric lombardot (cedriclombardot) + - Tim Goudriaan (codedmonkey) - Jonas Flodén (flojon) - Gonzalo Vilaseca (gonzalovilaseca) - Marcin Sikoń (marphi) + - Denis Brumann (dbrumann) - Dominik Zogg (dominik.zogg) - Marek Pietrzak - Luc Vieillescazes (iamluc) @@ -480,11 +508,14 @@ Symfony is the result of the work of many people who made the code better - Christian Wahler - Gintautas Miselis - Rob Bast + - Roberto Espinoza (respinoza) - Zander Baldwin - Adam Harvey - Anton Bakai - Rhodri Pugh (rodnaph) + - Sam Fleming (sam_fleming) - Alex Bakhturin + - Pol Dellaiera (drupol) - insekticid - Alexander Obuhovich (aik099) - boombatower @@ -501,7 +532,6 @@ Symfony is the result of the work of many people who made the code better - Sebastian Bergmann - Miroslav Sustek - Pablo Díez (pablodip) - - Martin Hujer (martinhujer) - Kevin McBride - Sergio Santoro - Robin van der Vleuten (robinvdvleuten) @@ -509,7 +539,6 @@ Symfony is the result of the work of many people who made the code better - Manuel de Ruiter (manuel) - Eduardo Oliveira (entering) - Ilya Antipenko (aivus) - - Iker Ibarguren (ikerib) - Ricardo Oliveira (ricardolotr) - Roy Van Ginneken (rvanginneken) - ondrowan @@ -531,10 +560,10 @@ Symfony is the result of the work of many people who made the code better - Jakub Škvára (jskvara) - Andrew Udvare (audvare) - alexpods - - Arjen van der Meijden - Adam Szaraniec (mimol) - Dariusz Ruminski - Erik Trapman (eriktrapman) + - Rokas Mikalkėnas (rokasm) - De Cock Xavier (xdecock) - Almog Baku (almogbaku) - Scott Arciszewski @@ -542,6 +571,7 @@ Symfony is the result of the work of many people who made the code better - Norbert Orzechowicz (norzechowicz) - Denis Charrier (brucewouaigne) - Matthijs van den Bos (matthijs) + - Jaik Dean (jaikdean) - Lenard Palko - Nils Adermann (naderman) - Gábor Fási @@ -555,6 +585,8 @@ Symfony is the result of the work of many people who made the code better - Dawid Pakuła (zulusx) - Florian Rey (nervo) - Rodrigo Borrego Bernabé (rodrigobb) + - Emanuele Iannone + - Jörn Lang (j.lang) - Denis Gorbachev (starfall) - Peter van Dommelen - Tim van Densen @@ -581,12 +613,15 @@ Symfony is the result of the work of many people who made the code better - Aurélien Fredouelle - Pavel Campr (pcampr) - Johnny Robeson (johnny) + - Marko Kaznovac (kaznovac) - Disquedur - Michiel Boeckaert (milio) - Geoffrey Tran (geoff) - Jan Behrens - Mantas Var (mvar) - Sebastian Krebs + - Piotr Stankowski + - Baptiste Leduc (bleduc) - Laurent VOULLEMIER (lvo) - Jean-Christophe Cuvelier [Artack] - Simon DELICATA @@ -597,8 +632,10 @@ Symfony is the result of the work of many people who made the code better - Sebastian Blum - aubx - Marvin Butkereit + - Renan - Ricky Su (ricky) - Gildas Quéméner (gquemener) + - Kyle Evans (kevans91) - Charles-Henri Bruyand - Max Rath (drak3) - Stéphane Escandell (sescandell) @@ -607,7 +644,9 @@ Symfony is the result of the work of many people who made the code better - Sinan Eldem - Alexandre Dupuy (satchette) - Malte Blättermann - - Andre Rømcke (andrerom) + - Desjardins Jérôme (jewome62) + - Kévin THERAGE (kevin_therage) + - Simeon Kolev (simeon_kolev9) - Nahuel Cuesta (ncuesta) - Chris Boden (cboden) - Christophe Villeger (seragan) @@ -616,47 +655,48 @@ Symfony is the result of the work of many people who made the code better - Stefan Gehrig (sgehrig) - Hany el-Kerdany - Wang Jingyu - - Webnet team (webnet) - Åsmund Garfors - Gunnstein Lye (glye) - Maxime Douailin - Jean Pasdeloup (pasdeloup) + - Sylvain Fabre (sylfabre) - Benjamin Cremer (bcremer) - Javier López (loalf) - Reinier Kip - Geoffrey Brier (geoffrey-brier) - - Vlad Gregurco (vgregurco) - Vladimir Tsykun - Dustin Dobervich (dustin10) - dantleech - Anne-Sophie Bachelard (annesophie) - Sebastian Marek (proofek) + - Guilhem N (guilhemn) - Erkhembayar Gantulga (erheme318) + - zenmate - Michal Trojanowski - David Fuhr - Max Grigorian (maxakawizard) - DerManoMann - Rostyslav Kinash - - Maciej Malarz (malarzm) + - Dennis Fridrich (dfridrich) - Daisuke Ohata - Vincent Simonin - Alex Bogomazov (alebo) - maxime.steinhausser + - dFayet - adev - Stefan Warman - Arkadius Stefanski (arkadius) - Tristan Maindron (tmaindron) + - Behnoush Norouzali (behnoush) - Wesley Lancel - Ke WANG (yktd26) - Ivo Bathke (ivoba) - Strate - Anton A. Sumin - Israel J. Carberry - - Tim Goudriaan (codedmonkey) - Miquel Rodríguez Telep (mrtorrent) - Sergey Kolodyazhnyy (skolodyazhnyy) - umpirski - - Denis Brumann (dbrumann) - Quentin de Longraye (quentinus95) - Chris Heng (gigablah) - Shaun Simmons (simshaun) @@ -666,15 +706,15 @@ Symfony is the result of the work of many people who made the code better - Sergey (upyx) - Michael Devery (mickadoo) - Antoine Corcy - - Dmitrii Poddubnyi (karser) - Sascha Grossenbacher + - Emanuele Panzeri (thepanz) - Szijarto Tamas + - Gocha Ossinkine (ossinkine) - Robin Lehrmann (robinlehrmann) - Catalin Dan - Jaroslav Kuba - Stephan Vock - Benjamin Zikarsky (bzikarsky) - - Roberto Espinoza (respinoza) - Simon Schick (simonsimcity) - redstar504 - Tristan Roussel @@ -690,6 +730,7 @@ Symfony is the result of the work of many people who made the code better - Paweł Wacławczyk (pwc) - Oleg Zinchenko (cystbear) - Baptiste Meyer (meyerbaptiste) + - Tales Santos (tsantos84) - Johannes Klauss (cloppy) - Evan Villemez - fzerorubigd @@ -698,20 +739,26 @@ Symfony is the result of the work of many people who made the code better - Tiago Brito (blackmx) - - Richard van den Brand (ricbra) + - Thomas Bisignani (toma) - develop - flip111 - Greg Anderson - VJ + - RJ Garcia - Delf Tonder (leberknecht) + - Raulnet - Mark Sonnabaum - Massimiliano Braglia (massimilianobraglia) - Richard Quadling + - Raphaëll Roussel - jochenvdv - Arturas Smorgun (asarturas) - Alexander Volochnev (exelenz) - Michael Piecko - yclian - Aleksey Prilipko + - Tomas Norkūnas (norkunas) + - Andrew Berry - twifty - Indra Gunawan (guind) - Peter Ward @@ -720,17 +767,18 @@ Symfony is the result of the work of many people who made the code better - Dominik Ritter (dritter) - Sebastian Grodzicki (sgrodzicki) - Jeroen van den Enden (stoefke) + - nikos.sotiropoulos - Pascal Helfenstein - Anthony GRASSIOT (antograssiot) - Baldur Rensch (brensch) - Pierre Rineau - Vladyslav Petrovych - Alex Xandra Albert Sim - - Alexander Schranz (alexander-schranz) - Carson Full - Sergey Yastrebov - Trent Steel (trsteel88) - Yuen-Chi Lian + - Tarjei Huse (tarjei) - Besnik Br - Jose Gonzalez - Oleksii Zhurbytskyi @@ -740,6 +788,7 @@ Symfony is the result of the work of many people who made the code better - Dave Marshall (davedevelopment) - Jakub Kulhan (jakubkulhan) - avorobiev + - Grégoire Penverne (gpenverne) - Venu - Lars Vierbergen - Jonatan Männchen @@ -747,11 +796,14 @@ Symfony is the result of the work of many people who made the code better - Andrew Tchircoff (andrewtch) - michaelwilliams - 1emming + - Matthias Althaus - Leevi Graham (leevigraham) - Nykopol (nykopol) + - Tri Pham (phamuyentri) - Jordan Deitch - Casper Valdemar Poulsen - Josiah (josiah) + - Greg ORIOL - Joschi Kuphal - John Bohn (jbohn) - Marc Morera (mmoreram) @@ -760,6 +812,7 @@ Symfony is the result of the work of many people who made the code better - Noah Heck (myesain) - Christian Soronellas (theunic) - Johann Pardanaud + - fedor.f - Yosmany Garcia (yosmanyga) - Wouter de Wild - Antoine M (amakdessi) @@ -772,9 +825,10 @@ Symfony is the result of the work of many people who made the code better - possum - Denis Zunke (donalberto) - Ahmadou Waly Ndiaye (waly) - - Philipp Cordes - Ahmed TAILOULOUTE (ahmedtai) + - Jonathan Johnson (jrjohnson) - Olivier Maisonneuve (olineuve) + - Pedro Miguel Maymone de Resende (pedroresende) - Masterklavi - Francis Turmel (fturmel) - Nikita Nefedov (nikita2206) @@ -784,11 +838,11 @@ Symfony is the result of the work of many people who made the code better - Jayson Xu (superjavason) - Christopher Hertel (chertel) - Hubert Lenoir (hubert_lenoir) - - Jaik Dean (jaikdean) - fago - Harm van Tilborg - Jan Prieser - GDIBass + - Antoine Lamirault - Adrien Lucas (adrienlucas) - Zhuravlev Alexander (scif) - James Michael DuPont @@ -799,10 +853,11 @@ Symfony is the result of the work of many people who made the code better - Rafał Wrzeszcz (rafalwrzeszcz) - Vincent CHALAMON (vincentchalamon) - Reen Lokum + - Andreas Möller (localheinz) - Martin Parsiegla (spea) - - Nguyen Xuan Quynh (xuanquynh) - Quentin Schuler - Pierre Vanliefland (pvanliefland) + - Roy Klutman (royklutman) - Sofiane HADDAG (sofhad) - frost-nzcr4 - Bozhidar Hristov @@ -811,6 +866,7 @@ Symfony is the result of the work of many people who made the code better - Abhoryo - Fabian Vogler (fabian) - Korvin Szanto + - soyuka - Stéphan Kochen - Arjan Keeman - Alaattin Kahramanlar (alaattin) @@ -822,7 +878,6 @@ Symfony is the result of the work of many people who made the code better - Julie Hourcade (juliehde) - Dmitry Parnas (parnas) - Paul LE CORRE - - Emanuele Iannone - Tony Malzhacker - Mathieu MARCHOIS - Cyril Quintin (cyqui) @@ -838,14 +893,13 @@ Symfony is the result of the work of many people who made the code better - Calin Mihai Pristavu - David Marín Carreño (davefx) - Fabien LUCAS (flucas2) - - Jörn Lang (j.lang) - Omar Yepez (oyepez003) - Gawain Lynch (gawain) - - Samuel NELA (snela) - mwsaz - Jelle Kapitein - Benoît Bourgeois - mantulo + - Stefan Kruppa - corphi - grizlik - Derek ROTH @@ -862,6 +916,7 @@ Symfony is the result of the work of many people who made the code better - Christian Morgan - Alexander Miehe (engerim) - Morgan Auchede (mauchede) + - Sascha Dens (saschadens) - Don Pinkster - Maksim Muruev - Emil Einarsson @@ -877,13 +932,13 @@ Symfony is the result of the work of many people who made the code better - Forfarle (forfarle) - Harry Walter (haswalt) - Johnson Page (jwpage) + - Michael Käfer (michael_kaefer) - Ruben Gonzalez (rubenruateltek) - Michael Roterman (wtfzdotnet) - Arno Geurts - Adán Lobato (adanlobato) - Ian Jenkins (jenkoian) - Matthew Davis (mdavis1982) - - Sam Fleming (sam_fleming) - Maks - Antoine LA - den @@ -891,8 +946,11 @@ Symfony is the result of the work of many people who made the code better - omerida - Gábor Tóth - Daniel Cestari + - Matt Janssen - David Lima + - Stéphane Delprat - Brian Freytag (brianfreytag) + - Samuele Lilli (doncallisto) - Brunet Laurent (lbrunet) - Florent Viel (luxifer) - Mikhail Yurasov (mym) @@ -907,32 +965,39 @@ Symfony is the result of the work of many people who made the code better - Rootie - Kyle - Daniel Alejandro Castro Arellano (lexcast) - - Raul Fraile (raulfraile) - sensio - - Baptiste Leduc (bleduc) + - Chris Tanaskoski + - Thomas Jarrand + - Antoine Bluchet (soyuka) - Sebastien Morel (plopix) - Patrick Kaufmann - - Piotr Stankowski - Anton Dyshkant - Reece Fowell (reecefowell) - Mátyás Somfai (smatyas) - stefan.r + - Guillaume Gammelin - Valérian Galliat - d-ph + - Renan Taranto (renan-taranto) + - Thomas Talbot (ioni) - Rikijs Murgs - Ben Ramsey (ramsey) - Amaury Leroux de Lens (amo__) - Christian Jul Jensen - Alexandre GESLIN (alexandregeslin) - The Whole Life to Learn + - Mikkel Paulson - ergiegonzaga - Farhad Safarov + - Alexis Lefebvre - Liverbool (liverbool) - Sam Malone - Phan Thanh Ha (haphan) - Chris Jones (leek) + - neghmurken - xaav - Mahmoud Mostafa (mahmoud) + - Ahmed Abdou - Pieter - Michael Tibben - Billie Thompson @@ -940,9 +1005,12 @@ Symfony is the result of the work of many people who made the code better - Sander Marechal - Franz Wilding (killerpoke) - ProgMiner + - Jonas Elfering - Oleg Golovakhin (doc_tr) + - Joost van Driel - Icode4Food (icode4food) - Radosław Benkel + - EStyles (insidestyles) - kevin.nadin - jean pasqualini (darkilliant) - Ross Motley (rossmotley) @@ -956,29 +1024,32 @@ Symfony is the result of the work of many people who made the code better - Sander Coolen (scoolen) - Nicolas Le Goff (nlegoff) - Ben Oman - - Guilhem N (guilhemn) - Chris de Kok - Andreas Kleemann - Manuele Menozzi + - zairig imad (zairigimad) - Anton Babenko (antonbabenko) - Irmantas Šiupšinskas (irmantas) - Danilo Silva - Arnaud PETITPAS (apetitpa) + - Ken Stanley - Zachary Tong (polyfractal) - Ashura - Hryhorii Hrebiniuk - johnstevenson - - Dennis Fridrich (dfridrich) + - Antonio Pauletich (x-coder264) - hamza - dantleech - Bastien DURAND (deamon) - Xavier Leune - Rudy Onfroy - Tero Alén (tero) + - Stanislav Kocanda - DerManoMann - Guillaume Royer - Artem (digi) - boite + - Silvio Ginter - MGDSoft - Vadim Tyukov (vatson) - David Wolter (davewww) @@ -986,7 +1057,10 @@ Symfony is the result of the work of many people who made the code better - chispita - Wojciech Sznapka - Gavin Staniforth + - Ksaveras Šakys (xawiers) - Ariel J. Birnbaum + - Danijel Obradović + - Pablo Borowicz - Mathieu Santostefano - Arjan Keeman - Máximo Cuadros (mcuadros) @@ -994,23 +1068,26 @@ Symfony is the result of the work of many people who made the code better - tamirvs - julien.galenski - Christian Neff + - Chris Tiearney - Oliver Hoff - Ole Rößner (basster) + - Faton (notaf) + - Tom Houdmont - Per Sandström (per) - Goran Juric - Laurent Ghirardotti (laurentg) - Nicolas Macherey - Guido Donnari - AKeeman (akeeman) + - Mert Simsek (mrtsmsk0) - Lin Clark - Jeremy David (jeremy.david) - - Gocha Ossinkine (ossinkine) + - Jordi Rejas - Troy McCabe - Ville Mattila - ilyes kooli - gr1ev0us - mlazovla - - Behnoush norouzali (behnoush) - Max Beutel - Antanas Arvasevicius - Pierre Dudoret @@ -1019,9 +1096,11 @@ Symfony is the result of the work of many people who made the code better - nacho - Piotr Antosik (antek88) - Artem Lopata + - Patrick Reimers (preimers) - Sergey Novikov (s12v) - Marcos Quesada (marcos_quesada) - Matthew Vickery (mattvick) + - MARYNICH Mikhail (mmarynich-ext) - Viktor Novikov (panzer_commander) - Paul Mitchum (paul-m) - Angel Koilov (po_taka) @@ -1034,11 +1113,14 @@ Symfony is the result of the work of many people who made the code better - Dominic Tubach - Nikita Konstantinov - Martijn Evers + - Vitaliy Ryaboy (vitaliy) - Benjamin Paap (benjaminpaap) - Christian - Denis Golubovskiy (bukashk0zzz) - Sergii Smertin (nfx) + - Mikkel Paulson - Michał Strzelecki + - Soner Sayakci - hugofonseca (fonsecas72) - Martynas Narbutas - Toon Verwerft (veewee) @@ -1058,9 +1140,11 @@ Symfony is the result of the work of many people who made the code better - Tadas Gliaubicas (tadcka) - Thanos Polymeneas (thanos) - Benoit Garret + - Maximilian Ruta (deltachaos) - Jakub Sacha - Olaf Klischat - orlovv + - Claude Dioudonnat - Jonathan Hedstrom - Peter Smeets (darkspartan) - Jhonny Lidfors (jhonny) @@ -1074,6 +1158,7 @@ Symfony is the result of the work of many people who made the code better - Andrew Tch - Alexander Cheprasov - Rodrigo Díez Villamuera (rodrigodiez) + - James Hudson - e-ivanov - Einenlum - Jochen Bayer (jocl) @@ -1084,14 +1169,15 @@ Symfony is the result of the work of many people who made the code better - wizhippo - Mathias STRASSER (roukmoute) - Thomason, James + - Gordienko Vladislav - Viacheslav Sychov + - Alexandre Quercia (alquerci) - Helmut Hummel (helhum) - Matt Brunt - Carlos Ortega Huetos - rpg600 - Péter Buri (burci) - kaiwa - - RJ Garcia - Charles Sanquer (csanquer) - Albert Ganiev (helios-ag) - Neil Katin @@ -1103,14 +1189,17 @@ Symfony is the result of the work of many people who made the code better - Artem Kolesnikov (tyomo4ka) - Gustavo Adrian - Yannick + - Vladimir Luchaninov (luchaninov) - spdionis - rchoquet - gitlost - Taras Girnyk - Eduardo García Sanz (coma) + - Sergio (deverad) - James Gilliland - fduch (fduch) - David de Boer (ddeboer) + - Eno Mullaraj (emullaraj) - Ryan Rogers - Klaus Purer - arnaud (arnooo999) @@ -1120,6 +1209,7 @@ Symfony is the result of the work of many people who made the code better - antograssiot - Ilya Vertakov - Brooks Boyd + - johnillo - Roger Webb - Dmitriy Simushev - Pawel Smolinski @@ -1129,12 +1219,12 @@ Symfony is the result of the work of many people who made the code better - Max Voloshin (maxvoloshin) - Nicolas Fabre (nfabre) - Raul Rodriguez (raul782) - - Patrick Landolt (scube) - WybrenKoelmans - Derek Lambert - MightyBranch - Kacper Gunia (cakper) - Peter Thompson (petert82) + - error56 - Felicitus - Krzysztof Przybyszewski - alexpozzi @@ -1144,7 +1234,6 @@ Symfony is the result of the work of many people who made the code better - Vacheslav Silyutin - Juan Traverso - Alain Flaus (halundra) - - Tarjei Huse (tarjei) - tsufeki - Philipp Strube - Clement Herreman (clemherreman) @@ -1158,13 +1247,13 @@ Symfony is the result of the work of many people who made the code better - Dmitri Petmanson - heccjj - Alexandre Melard + - Jonathan (jls-esokia) - Jay Klehr - Sergey Yuferev - Tobias Stöckler - Mario Young - Ilia (aliance) - Chris McCafferty (cilefen) - - Grégoire Penverne (gpenverne) - Mo Di (modi) - Pablo Schläpfer - Gert de Pagter @@ -1172,6 +1261,8 @@ Symfony is the result of the work of many people who made the code better - Quique Porta (quiqueporta) - stoccc - Tomasz Szymczyk (karion) + - Alex Vasilchenko + - sez-open - Xavier Coureau - ConneXNL - Aharon Perkel @@ -1187,9 +1278,13 @@ Symfony is the result of the work of many people who made the code better - Lars Ambrosius Wallenborn (larsborn) - Oriol Mangas Abellan (oriolman) - Sebastian Göttschkes (sgoettschkes) + - Toni Peric (tperic) - Tatsuya Tsuruoka - Ross Tuck + - Andreas Erhard - Kévin Gomez (kevin) + - Mihai Nica (redecs) + - Soufian EZ-ZANTAR (soezz) - Andrei Igna - azine - Dawid Sajdak @@ -1218,6 +1313,7 @@ Symfony is the result of the work of many people who made the code better - Sébastien HOUZÉ - Jingyu Wang - steveYeah + - BENOIT POLASZEK (bpolaszek) - Samy Dindane (dinduks) - Keri Henare (kerihenare) - Cédric Lahouste (rapotor) @@ -1229,24 +1325,30 @@ Symfony is the result of the work of many people who made the code better - Andy Raines - Anthony Ferrara - Geoffrey Pécro (gpekz) + - Jérémy DECOOL (jdecool) - Klaas Cuvelier (kcuvelier) + - Flavien Knuchel (knuch) - Mathieu TUDISCO (mathieutu) - markusu49 - Steve Frécinaux - Constantine Shtompel - Jules Lamur - Renato Mendes Figueiredo + - Eric Stern - ShiraNai7 - Antal Áron (antalaron) - Markus Fasselt (digilist) - Vašek Purchart (vasek-purchart) - Janusz Jabłoński (yanoosh) + - Fleuv - Sandro Hopf - Łukasz Makuch - George Giannoulopoulos + - Alexander Pasichnick - Luis Ramirez (luisdeimos) - Daniel Richter (richtermeister) - ChrisC + - JL - Ilya Biryukov - Kim Laï Trinh - Jason Desrosiers @@ -1255,25 +1357,24 @@ Symfony is the result of the work of many people who made the code better - Philip Frank - Lance McNearney - Giorgio Premi - - Andrew Berry - ncou - Ian Carroll - caponica - Daniel Kay (danielkay-cp) - Matt Daum (daum) - Alberto Pirovano (geezmo) - - Nicolas LEFEVRE (nicoweb) - Pete Mitchell (peterjmit) - Tom Corrigan (tomcorrigan) - Luis Galeas - Martin Pärtel + - Frédéric Bouchery (fbouchery) - Patrick Daley (padrig) - Xavier Briand (xavierbriand) 8000 - Max Summe - WedgeSama - Felds Liscia - Chihiro Adachi (chihiro-adachi) - - Emanuele Panzeri (thepanz) + - Raphaëll Roussel - Tadcka - Beth Binkovitz - Gonzalo Míguez @@ -1281,10 +1382,14 @@ Symfony is the result of the work of many people who made the code better - Adrien Moiruad - Tomaz Ahlin - Philip Ardery + - Nasim - Marcus Stöhr (dafish) + - Daniel González Zaballos (dem3trio) - Emmanuel Vella (emmanuel.vella) - - Jonathan Johnson (jrjohnson) + - Guillaume BRETOU (guiguiboy) + - Dāvis Zālītis (k0d3r1s) - Carsten Nielsen (phreaknerd) + - Roger Guasch (rogerguasch) - Mathieu Rochette - Jay Severson - René Kerner @@ -1296,6 +1401,7 @@ Symfony is the result of the work of many people who made the code better - Mathieu Morlon - Daniel Tschinder - Arnaud CHASSEUX + - Wojciech Gorczyca - Rafał Muszyński (rafmus90) - Sébastien Decrême (sebdec) - Timothy Anido (xanido) @@ -1308,15 +1414,19 @@ Symfony is the result of the work of many people who made the code better - Jon Gotlin (jongotlin) - Michael Dowling (mtdowling) - Karlos Presumido (oneko) - - Sylvain Fabre (sylfabre) + - Tony Vermeiren (tony) - Thomas Counsell - BilgeXA - r1pp3rj4ck + - phydevs - Robert Queck - Peter Bouwdewijn - mlively - Amine Matmati + - caalholm + - Nouhail AL FIDI (alfidi) - Fabian Steiner (fabstei) + - Felipy Tavares Amorim (felipyamorim) - Klaus Silveira (klaussilveira) - Thomas Chmielowiec (chmielot) - Jānis Lukss @@ -1333,11 +1443,13 @@ Symfony is the result of the work of many people who made the code better - Mike Meier - Tim Jabs - Sebastian Ionescu + - Pablo Ogando Ferreira - Thomas Ploch - Simon Neidhold - Valentin VALCIU - Jeremiah VALERIE - Julien Menth + - Yannick Snobbert - Kevin Dew - James Cowgill - 1ma (jautenim) @@ -1352,9 +1464,9 @@ Symfony is the result of the work of many people who made the code better - Lance Chen - Ciaran McNulty (ciaranmcnulty) - Andrew (drew) + - Giso Stallenberg (gisostallenberg) - kor3k kor3k (kor3k) - Stelian Mocanita (stelian) - - Thomas Bisignani (toma) - Justin (wackymole) - Flavian (2much) - Gautier Deuette @@ -1363,6 +1475,7 @@ Symfony is the result of the work of many people who made the code better - Keith Maika - Mephistofeles - Hoffmann András + - LubenZA - Olivier - Cyril PASCAL - pscheit @@ -1372,8 +1485,11 @@ Symfony is the result of the work of many people who made the code better - moldcraft - Antoine Bellion (abellion) - Ramon Kleiss (akathos) + - Antonio Peric-Mazar (antonioperic) - César Suárez (csuarez) - Bjorn Twachtmann (dotbjorn) + - Tobias Genberg (lorceroth) + - Luis Tacón (lutacon) - Nicolas Badey (nico-b) - Shane Preece (shane) - Johannes Goslar @@ -1386,16 +1502,20 @@ Symfony is the result of the work of many people who made the code better - Gavin Staniforth - Alessandro Tagliapietra (alex88) - Biji (biji) + - Alex Teterin (errogaht) - Gunnar Lium (gunnarlium) - Tiago Garcia (tiagojsag) - Artiom - Jakub Simon - Bouke Haarsma + - mlievertz + - Enrico Schultz - Evert Harmeling - mschop - Alan Poulain - Martin Eckhardt - natechicago + - Sergei Gorjunov - Jonathan Poston - Adrian Olek (adrianolek) - Jody Mickey (jwmickey) @@ -1403,11 +1523,12 @@ Symfony is the result of the work of many people who made the code better - Leonid Terentyev (li0n) - Martynas Sudintas (martiis) - ryunosuke - - zenmate - victoria - Francisco Facioni (fran6co) - Iwan van Staveren (istaveren) - Povilas S. (povilas) + - Laurent Negre (raulnet) + - Evrard Boulou - pborreli - Boris Betzholz - Eric Caron @@ -1418,13 +1539,18 @@ Symfony is the result of the work of many people who made the code better - catch - Alexandre Segura - Josef Cech + - Andrii Boiko - Harold Iedema + - Ikhsan Agustian - Arnau González (arnaugm) - Simon Bouland (bouland) + - Jibé Barth (jibbarth) - Matthew Foster (mfoster) + - Reyo Stallenberg (reyostallenberg) - Paul Seiffert (seiffert) - Vasily Khayrulin (sirian) - Stefan Koopmanschap (skoop) + - Stas Soroka (stasyan) - Stefan Hüsges (tronsha) - Jake Bishop (yakobeyak) - Dan Blows @@ -1462,7 +1588,6 @@ Symfony is the result of the work of many people who made the code better - Gunther Konig - Mickael GOETZ - Maciej Schmidt - - Greg ORIOL - Dennis Væversted - nuncanada - flack @@ -1483,9 +1608,11 @@ Symfony is the result of the work of many people who made the code better - loru88 - Romain Dorgueil - Christopher Parotat + - Dennis Haarbrink - me_shaon - 蝦米 - Grayson Koonce (breerly) + - Andrey Helldar (helldar) - Karim Cassam Chenaï (ka) - Maksym Slesarenko (maksym_slesarenko) - Michal Kurzeja (mkurzeja) @@ -1494,14 +1621,17 @@ Symfony is the result of the work of many people who made the code better - Denis (yethee) - Andrew Zhilin (zhil) - Sjors Ottjes + - azjezz - Andy Stanberry - Felix Marezki - Normunds - Luiz “Felds” Liscia - Thomas Rothe + - Martin - nietonfir - alefranz - David Barratt + - Andrea Giannantonio - Pavel.Batanov - avi123 - Pavel Prischepa @@ -1521,13 +1651,18 @@ Symfony is the result of the work of many people who made the code better - efeen - Nicolas Pion - Muhammed Akbulut + - Aaron Somi + - Karoly Gossler (connorhu) - Michał Dąbrowski (defrag) + - Konstantin Grachev (grachevko) - Simone Fumagalli (hpatoio) - Brian Graham (incognito) - Kevin Vergauwen (innocenzo) - Alessio Baglio (ioalessio) + - Jan van Thoor (janvt) - Johannes Müller (johmue) - Jordi Llonch (jordillonch) + - Nicholas Ruunu (nicholasruunu) - Cédric Dugat (ph3nol) - Philip Dahlstrøm (phidah) - Milos Colakovic (project2481) @@ -1537,6 +1672,7 @@ Symfony is the result of the work of many people who made the code better - Artem Lopata (bumz) - alex - Nicole Cordes + - Nicolas PHILIPPE - Roman Orlov - VolCh - Alexey Popkov @@ -1552,6 +1688,7 @@ Symfony is the result of the work of many people who made the code better - Bram Van der Sype (brammm) - Guile (guile) - Julien Moulin (lizjulien) + - Raito Akehanareru (raito) - Mauro Foti (skler) - Yannick Warnier (ywarnier) - Kevin Decherf @@ -1573,6 +1710,7 @@ Symfony is the result of the work of many people who made the code better - Trevor Suarez - gedrox - Alan Bondarchuk + - Joe Bennett - dropfen - Andrey Chernykh - Edvinas Klovas @@ -1580,38 +1718,46 @@ Symfony is the result of the work of many people who made the code better - Peter Breuls - Chansig - Tischoi + - Andreas Hasenack - J Bruni - Fritz Michael Gschwantner - Alexey Prilipko - Dmitriy Fedorenko - vlakoff - bertillon + - thib92 - Rudolf Ratusiński - Bertalan Attila - AmsTaFF (amstaff) - Simon Müller (boscho) - Yannick Bensacq (cibou) + - Damien (damien_vauchel) - Frédéric G. Marand (fgm) - Freek Van der Herten (freekmurze) - Luca Genuzio (genuzio) - Hans Nilsson (hansnilsson) - Andrew Marcinkevičius (ifdattic) - Ioana Hazsda (ioana-hazsda) + - Jacek Jędrzejewski (jacek.jedrzejewski) - Jan Marek (janmarek) - Mark de Haan (markdehaan) - Dan Patrick (mdpatrick) - Pedro Magalhães (pmmaga) - Rares Vlaseanu (raresvla) - tante kinast (tante) + - Ahmed Hannachi (tiecoders) - Vincent LEFORT (vlefort) - Darryl Hein (xmmedia) - Sadicov Vladimir (xtech) - Kevin EMO (zarcox) + - Andrzej - Alexander Zogheb - Rémi Blaise + - Nicolas Séverin - Joel Marcey - David Christmann - root + - pf - Vincent Chalnot - James Hudson - Tom Maguire @@ -1621,6 +1767,7 @@ Symfony is the result of the work of many people who made the code better - Oleg Andreyev - neFAST - Pierre Rineau + - Florian Morello - Maxim Lovchikov - adenkejawen - Florent SEVESTRE (aniki-taicho) @@ -1634,6 +1781,7 @@ Symfony is the result of the work of many people who made the code better - Jonny Schmid (schmidjon) - Götz Gottwald - Veres Lajos + - Nick Chiu - grifx - Robert Campbell - Matt Lehner @@ -1668,8 +1816,10 @@ Symfony is the result of the work of many people who made the code better - skafandri - Derek Bonner - Alan Chen + - insidestyles - Maerlyn - Even André Fiskvik + - Александр Ли - Arjan Keeman - Erik van Wingerden - Valouleloup @@ -1681,12 +1831,14 @@ Symfony is the result of the work of many people who made the code better - Javan Eskander - Lenar Lõhmus - Cristian Gonzalez + - MusikAnimal - AlberT - hainey - Juan M Martínez - Gilles Gauthier - ddebree - Kuba Werłos + - Gyula Szucs - Tomas Liubinas - Alex - Jan Hort @@ -1702,8 +1854,10 @@ Symfony is the result of the work of many people who made the code better - Brieuc THOMAS (brieucthomas) - Masao Maeda (brtriver) - Darius Leskauskas (darles) + - david perez (davidpv) - David Joos (djoos) - Denis Klementjev (dklementjev) + - Dominik Hajduk (dominikalp) - Tomáš Polívka (draczris) - Dennis Smink (dsmink) - Franz Liedke (franzliedke) @@ -1720,11 +1874,13 @@ Symfony is the result of the work of many people who made the code better - Kevin Verschaeve (keversc) - Kevin Herrera (kherge) - Luis Ramón López López (lrlopez) + - Mehdi Mabrouk (mehdidev) - Bart Reunes (metalarend) - Muriel (metalmumu) - Michael Pohlers (mick_the_big) - mlpo (mlpo) - Marek Šimeček (mssimi) + - Dmitriy Tkachenko (neka) - Cayetano Soriano Gallego (neoshadybeat) - Olivier Laviale (olvlvl) - Ondrej Machulda (ondram) @@ -1736,6 +1892,7 @@ Symfony is the result of the work of many people who made the code better - Yorkie Chadwick (yorkie76) - GuillaumeVerdon - Philipp Keck + - Angel Fernando Quiroz Campos - Ondrej Mirtes - akimsko - Youpie @@ -1754,6 +1911,8 @@ Symfony is the result of the work of many people who made the code better - Damian Sromek - Ben - Evgeniy Tetenchuk + - Shrey Puranik + - Lars Moelleken - dasmfm - Mathias Geat - Arnaud Buathier (arnapou) @@ -1769,11 +1928,13 @@ Symfony is the result of the work of many people who made the code better - Ramon Henrique Ornelas (ramonornela) - Ricardo de Vries (ricknox) - Markus S. (staabm) + - Thomas Dutrion (theocrite) - Till Klampaeckel (till) - Tobias Weinert (tweini) - Ulf Reimers (ureimers) - Wotre - goohib + - Tom Counsell - Xavier HAUSHERR - Ron Gähler - Edwin Hageman @@ -1811,6 +1972,7 @@ Symfony is the result of the work of many people who made the code better - Jörg Rühl - wesleyh - sergey + - Michael Hudson-Doyle - Daniel Bannert - Karim Miladi - Michael Genereux @@ -1833,6 +1995,7 @@ Symfony is the result of the work of many people who made the code better - Şəhriyar İmanov - Alexis BOYER - Kaipi Yann + - adam-mospan - Sam Williams - Guillaume Aveline - Adrian Philipp @@ -1840,11 +2003,13 @@ Symfony is the result of the work of many people who made the code better - Kasperki - Tammy D - Daniel STANCU + - Ryan Rud - Ondrej Slinták - vlechemin - Brian Corrigan - Ladislav Tánczos - Skorney + - Lucas Matte - fmarchalemisys - mieszko4 - Steve Preston @@ -1868,7 +2033,6 @@ Symfony is the result of the work of many people who made the code better - Shude - Ondřej Führer - Sema - - Michael Käfer - Elan Ruusamäe - Thorsten Hallwas - Michael Squires @@ -1878,13 +2042,14 @@ Symfony is the result of the work of many people who made the code better - zorn - Yuriy Potemkin - Emilie Lorenzo + - enomotodev - Edvin Hultberg - Benjamin Long - - Matt Janssen - Ben Miller - Peter Gribanov - kwiateusz - jspee + - Ilya Bulakh - David Soria Parra - Sergiy Sokolenko - Ahmed Abdulrahman @@ -1914,7 +2079,6 @@ Symfony is the result of the work of many people who made the code better - phc - Дмитрий Пацура - ilyes kooli - - Marko Kaznovac - Matthias Althaus - Michaël VEROUX - Julia @@ -1944,7 +2108,6 @@ Symfony is the result of the work of many people who made the code better - Damon Jones (damon__jones) - Łukasz Giza (destroyer) - Daniel Londero (dlondero) - - Samuele Lilli (doncallisto) - Sebastian Landwehr (dword123) - Adel ELHAIBA (eadel) - Damián Nohales (eagleoneraptor) @@ -1977,6 +2140,8 @@ Symfony is the result of the work of many people who made the code better - samuel laulhau (lalop) - Laurent Bachelier (laurentb) - Luís Cobucci (lcobucci) + - Mehdi Achour (machour) + - Marcos Gómez Vilches (markitosgv) - Matthieu Mota (matthieumota) - Matthieu Moquet (mattketmo) - Moritz Borgmann (mborgmann) @@ -1986,13 +2151,12 @@ Symfony is the result of the work of many people who made the code better - Ala Eddine Khefifi (nayzo) - emilienbouard (neime) - Nicholas Byfleet (nickbyfleet) - - Tomas Norkūnas (norkunas) - Marco Petersen (ocrampete16) - ollie harridge (ollietb) + - Dimitri Gritsajuk (ottaviano) - Paul Andrieux (paulandrieux) - Paweł Szczepanek (pauluz) - Philippe Degeeter (pdegeeter) - - Pedro Miguel Maymone de Resende (pedroresende) - Christian López Espínola (penyaskito) - Petr Jaroš (petajaros) - Philipp Hoffmann (philipphoffmann) @@ -2000,24 +2164,25 @@ Symfony is the result of the work of many people who made the code better - Daniel Perez Pinazo (pitiflautico) - Phil Taylor (prazgod) - Maxim Pustynnikov (pustynnikov) + - Ralf Kuehnel (ralfkuehnel) - Brayden Williams (redstar504) - Rich Sage (richsage) - - Rokas Mikalkėnas (rokasm) - Bart Ruysseveldt (ruyss) - - Sascha Dens (saschadens) - scourgen hung (scourgen) - Sebastian Busch (sebu) - Sepehr Lajevardi (sepehr) - André Filipe Gonçalves Neves (seven) - Bruno Ziegler (sfcoder) - Andrea Giuliano (shark) + - Thomas Baumgartner (shoplifter) - Schuyler Jager (sjager) - Volker (skydiablo) - - Serkan Yildiz (srknyldz) - Julien Sanchez (sumbobyboys) - Guillermo Gisinger (t3chn0r) - Markus Tacker (tacker) + - Tom Newby (tomnewbyau) - Andrew Clark (tqt_andrew_clark) + - David Lumaye (tux1124) - Tyler Stroud (tystr) - Moritz Kraft (userfriendly) - Víctor Mateo (victormateo) @@ -2036,9 +2201,9 @@ Symfony is the result of the work of many people who made the code better - drublic - Andreas Streichardt - Pascal Hofmann - - Stefan Kruppa - smokeybear87 - Gustavo Adrian + - damaya - Kevin Weber - Ben Scott - Dionysis Arvanitis @@ -2059,6 +2224,7 @@ Symfony is the result of the work of many people who made the code better - Andrew Carter (andrewcarteruk) - Adam Elsodaney (archfizz) - Gregório Bonfante Borba (bonfante) + - Bogdan Rancichi (devck) - Daniel Kolvik (dkvk) - Marc Lemay (flug) - Henne Van Och (hennevo) diff --git a/LICENSE b/LICENSE index 21d7fb9e2f29b..a677f43763ca4 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2018 Fabien Potencier +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 diff --git a/UPGRADE-4.0.md b/UPGRADE-4.0.md index fe43d0cf99737..8947744a9f180 100644 --- a/UPGRADE-4.0.md +++ b/UPGRADE-4.0.md @@ -92,7 +92,7 @@ Console ```php $commandTester = new CommandTester($command); - $commandTester->setInputs(array('AppBundle', 'Yes')); + $commandTester->setInputs(['AppBundle', 'Yes']); $commandTester->execute(); ``` @@ -230,7 +230,7 @@ DependencyInjection supported. * The ``strict`` attribute in service arguments has been removed. - The attribute is ignored since 3.0, so you can simply remove it. + The attribute is ignored since 3.0, you can remove it. * Top-level anonymous services in XML are no longer supported. @@ -343,23 +343,23 @@ Form Before: ```php - $builder->add('custom_locales', LocaleType::class, array( + $builder->add('custom_locales', LocaleType::class, [ 'choices' => $availableLocales, - )); + ]); ``` After: ```php - $builder->add('custom_locales', LocaleType::class, array( + $builder->add('custom_locales', LocaleType::class, [ 'choices' => $availableLocales, 'choice_loader' => null, - )); + ]); // or - $builder->add('custom_locales', LocaleType::class, array( + $builder->add('custom_locales', LocaleType::class, [ 'choice_loader' => new CallbackChoiceLoader(function () { return $this->getAvailableLocales(); }), - )); + ]); ``` * Removed `ChoiceLoaderInterface` implementation in `TimezoneType`. Use the "choice_loader" option instead. @@ -844,7 +844,7 @@ TwigBridge use Symfony\Bridge\Twig\Form\TwigRendererEngine; // ... - $rendererEngine = new TwigRendererEngine(array('form_div_layout.html.twig')); + $rendererEngine = new TwigRendererEngine(['form_div_layout.html.twig']); $rendererEngine->setEnvironment($twig); $twig->addExtension(new FormExtension(new TwigRenderer($rendererEngine, $csrfTokenManager))); ``` @@ -852,12 +852,12 @@ TwigBridge After: ```php - $rendererEngine = new TwigRendererEngine(array('form_div_layout.html.twig'), $twig); - $twig->addRuntimeLoader(new \Twig_FactoryRuntimeLoader(array( + $rendererEngine = new TwigRendererEngine(['form_div_layout.html.twig'], $twig); + $twig->addRuntimeLoader(new \Twig_FactoryRuntimeLoader([ TwigRenderer::class => function () use ($rendererEngine, $csrfTokenManager) { return new TwigRenderer($rendererEngine, $csrfTokenManager); }, - ))); + ])); $twig->addExtension(new FormExtension()); ``` @@ -1092,13 +1092,13 @@ Yaml Before: ```php - Yaml::dump(array('foo' => new A(), 'bar' => 1), 0, 0, true); + Yaml::dump(['foo' => new A(), 'bar' => 1], 0, 0, true); ``` After: ```php - Yaml::dump(array('foo' => new A(), 'bar' => 1), 0, 0, Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE); + Yaml::dump(['foo' => new A(), 'bar' => 1], 0, 0, Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE); ``` * Removed support for passing `true`/`false` as the fifth argument to the @@ -1107,13 +1107,13 @@ Yaml Before: ```php - Yaml::dump(array('foo' => new A(), 'bar' => 1), 0, 0, false, true); + Yaml::dump(['foo' => new A(), 'bar' => 1], 0, 0, false, true); ``` After: ```php - Yaml::dump(array('foo' => new A(), 'bar' => 1), 0, 0, false, Yaml::DUMP_OBJECT); + Yaml::dump(['foo' => new A(), 'bar' => 1], 0, 0, false, Yaml::DUMP_OBJECT); ``` * The `!!php/object` tag to indicate dumped PHP objects was removed in favor of diff --git a/UPGRADE-4.2.md b/UPGRADE-4.2.md index a2fadabc95b14..90cd04d01e69a 100644 --- a/UPGRADE-4.2.md +++ b/UPGRADE-4.2.md @@ -68,6 +68,7 @@ Finder Form ---- + * The `symfony/translation` dependency has been removed - run `composer require symfony/translation` if you need the component * The `getExtendedType()` method of the `FormTypeExtensionInterface` is deprecated and will be removed in 5.0. Type extensions must implement the static `getExtendedTypes()` method instead and return an iterable of extended types. @@ -285,6 +286,23 @@ Messenger ``` * The `EncoderInterface` and `DecoderInterface` interfaces have been replaced by a unified `Symfony\Component\Messenger\Transport\Serialization\SerializerInterface`. Each interface method have been merged untouched into the `Serializer` interface, so you can simply merge your two implementations together and implement the new interface. + * The `HandlerLocator` class was replaced with `Symfony\Component\Messenger\Handler\HandlersLocator`. + + Before: + ```php + new HandlerLocator([ + YourMessage::class => $handlerCallable, + ]); + ``` + + After: + ```php + new HandlersLocator([ + YourMessage::class => [ + $handlerCallable, + ] + ]); + ``` Monolog ------- @@ -364,6 +382,7 @@ TwigBundle Validator --------- + * The `symfony/translation` dependency has been removed - run `composer require symfony/translation` if you need the component * The `checkMX` and `checkHost` options of the `Email` constraint are deprecated * The component is now decoupled from `symfony/translation` and uses `Symfony\Contracts\Translation\TranslatorInterface` instead * The `ValidatorBuilderInterface` has been deprecated and `ValidatorBuilder` made final diff --git a/UPGRADE-4.3.md b/UPGRADE-4.3.md new file mode 100644 index 0000000000000..a2ea3f28710e8 --- /dev/null +++ b/UPGRADE-4.3.md @@ -0,0 +1,302 @@ +UPGRADE FROM 4.2 to 4.3 +======================= + +BrowserKit +---------- + + * Renamed `Client` to `AbstractBrowser` + * Marked `Response` final. + * Deprecated `Response::buildHeader()` + * Deprecated `Response::getStatus()`, use `Response::getStatusCode()` instead + +Cache +----- + + * The `psr/simple-cache` dependency has been removed - run `composer require psr/simple-cache` if you need it. + * Deprecated all PSR-16 adapters, use `Psr16Cache` or `Symfony\Contracts\Cache\CacheInterface` implementations instead. + * Deprecated `SimpleCacheAdapter`, use `Psr16Adapter` instead. + +Config +------ + + * Deprecated using environment variables with `cannotBeEmpty()` if the value is validated with `validate()` + * Deprecated the `root()` method in `TreeBuilder`, pass the root node information to the constructor instead + +DependencyInjection +------------------- + + * Deprecated support for non-string default env() parameters + + Before: + ```yaml + parameters: + env(NAME): 1.5 + ``` + + After: + ```yaml + parameters: + env(NAME): '1.5' + ``` + +Doctrine Bridge +--------------- + + * Passing an `IdReader` to the `DoctrineChoiceLoader` when the query cannot be optimized with single id field has been deprecated, pass `null` instead + * Not passing an `IdReader` to the `DoctrineChoiceLoader` when the query can be optimized with single id field has been deprecated + +Dotenv +------ + + * First parameter of `Dotenv::__construct()` will be changed from `true` to `false` in Symfony 5.0. A deprecation warning + is triggered if no parameter is provided. Use `$usePutenv = true` to upgrade without breaking changes. + +EventDispatcher +--------------- + + * The signature of the `EventDispatcherInterface::dispatch()` method should be updated to `dispatch($event, string $eventName = null)`, not doing so is deprecated + * The `Event` class has been deprecated, use `Symfony\Contracts\EventDispatcher\Event` instead + +Form +---- + + * Using the `format` option of `DateType` and `DateTimeType` when the `html5` option is enabled is deprecated. + * Using names for buttons that do not start with a letter, a digit, or an underscore is deprecated and will lead to an + exception in 5.0. + * Using names for buttons that do not contain only letters, digits, underscores, hyphens, and colons is deprecated and + will lead to an exception in 5.0. + * Using the `date_format`, `date_widget`, and `time_widget` options of the `DateTimeType` when the `widget` option is + set to `single_text` is deprecated. + +FrameworkBundle +--------------- + + * Not passing the project directory to the constructor of the `AssetsInstallCommand` is deprecated. This argument will + be mandatory in 5.0. + * Deprecated the "Psr\SimpleCache\CacheInterface" / "cache.app.simple" service, use "Symfony\Contracts\Cache\CacheInterface" / "cache.app" instead. + * The `generate()` method of the `UrlGenerator` class can return an empty string instead of null. + +HttpFoundation +-------------- + + * The `MimeTypeGuesserInterface` and `ExtensionGuesserInterface` interfaces have been deprecated, + use `Symfony\Component\Mime\MimeTypesInterface` instead. + * The `MimeType` and `MimeTypeExtensionGuesser` classes have been deprecated, + use `Symfony\Component\Mime\MimeTypes` instead. + * The `FileBinaryMimeTypeGuesser` class has been deprecated, + use `Symfony\Component\Mime\FileBinaryMimeTypeGuesser` instead. + * The `FileinfoMimeTypeGuesser` class has been deprecated, + use `Symfony\Component\Mime\FileinfoMimeTypeGuesser` instead. + +HttpKernel +---------- + + * Renamed `Client` to `HttpKernelBrowser` + * Renamed `FilterControllerArgumentsEvent` to `ControllerArgumentsEvent` + * Renamed `FilterControllerEvent` to `ControllerEvent` + * Renamed `FilterResponseEvent` to `ResponseEvent` + * Renamed `GetResponseEvent` to `RequestEvent` + * Renamed `GetResponseForControllerResultEvent` to `ViewEvent` + * Renamed `GetResponseForExceptionEvent` to `ExceptionEvent` + * Renamed `PostResponseEvent` to `TerminateEvent` + * Deprecated `TranslatorListener` in favor of `LocaleAwareListener` + +Intl +---- + + * Deprecated `ResourceBundle` namespace + * Deprecated `Intl::getCurrencyBundle()`, use `Currencies` instead + * Deprecated `Intl::getLanguageBundle()`, use `Languages` or `Scripts` instead + * Deprecated `Intl::getLocaleBundle()`, use `Locales` instead + * Deprecated `Intl::getRegionBundle()`, use `Countries` instead + +Messenger +--------- + + * `Amqp` transport does not throw `\AMQPException` anymore, catch `TransportException` instead. + * Deprecated the `LoggingMiddleware` class, pass a logger to `SendMessageMiddleware` instead. + +Routing +------- + + * The `generator_base_class`, `generator_cache_class`, `matcher_base_class`, and `matcher_cache_class` router + options have been deprecated. + * Implementing `Serializable` for `Route` and `CompiledRoute` is deprecated; if you serialize them, please + ensure your unserialization logic can recover from a failure related to an updated serialization format + +Security +-------- + + * The `Role` and `SwitchUserRole` classes are deprecated and will be removed in 5.0. Use strings for roles + instead. + * The `getReachableRoles()` method of the `RoleHierarchyInterface` is deprecated and will be removed in 5.0. + Role hierarchies must implement the `getReachableRoleNames()` method instead and return roles as strings. + * The `getRoles()` method of the `TokenInterface` is deprecated. Tokens must implement the `getRoleNames()` + method instead and return roles as strings. + * The `ListenerInterface` is deprecated, turn your listeners into callables instead. + * The `Firewall::handleRequest()` method is deprecated, use `Firewall::callListeners()` instead. + * The `AbstractToken::serialize()`, `AbstractToken::unserialize()`, + `AuthenticationException::serialize()` and `AuthenticationException::unserialize()` + methods are now final, use `__serialize()` and `__unserialize()` instead. + + Before: + ```php + public function serialize() + { + return [$this->myLocalVar, parent::serialize()]; + } + + public function unserialize($serialized) + { + [$this->myLocalVar, $parentSerialized] = unserialize($serialized); + parent::unserialize($parentSerialized); + } + ``` + + After: + ```php + public function __serialize(): array + { + return [$this->myLocalVar, parent::__serialize()]; + } + + public function __unserialize(array $data): void + { + [$this->myLocalVar, $parentData] = $data; + parent::__unserialize($parentData); + } + ``` + + * The `Argon2iPasswordEncoder` class has been deprecated, use `SodiumPasswordEncoder` instead. + * The `BCryptPasswordEncoder` class has been deprecated, use `NativePasswordEncoder` instead. + * Not implementing the methods `__serialize` and `__unserialize` in classes implementing + the `TokenInterface` is deprecated + +SecurityBundle +-------------- + + * Configuring encoders using `argon2i` or `bcrypt` as algorithm has been deprecated, use `auto` instead. + +TwigBridge +---------- + + * deprecated the `$requestStack` and `$requestContext` arguments of the + `HttpFoundationExtension`, pass a `Symfony\Component\HttpFoundation\UrlHelper` + instance as the only argument instead + +Workflow +-------- + + * `initial_place` is deprecated in favour of `initial_marking`. + + Before: + ```yaml + framework: + workflows: + article: + initial_place: draft + ``` + + After: + ```yaml + framework: + workflows: + article: + initial_marking: [draft] + ``` + + * `MarkingStoreInterface::setMarking()` will have a third argument in Symfony 5.0. + + Before: + ```php + class MyMarkingStore implements MarkingStoreInterface + { + public function setMarking($subject, Marking $marking) + { + } + } + ``` + + After: + ```php + class MyMarkingStore implements MarkingStoreInterface + { + public function setMarking($subject, Marking $marking , array $context = []) + { + } + } + ``` + + * `MultipleStateMarkingStore` is deprecated. Use `MethodMarkingStore` instead. + + Before: + ```yaml + framework: + workflows: + type: workflow + article: + marking_store: + type: multiple + arguments: states + ``` + + After: + ```yaml + framework: + workflows: + type: workflow + article: + marking_store: + type: method + property: states + ``` + + * `SingleStateMarkingStore` is deprecated. Use `MethodMarkingStore` instead. + + Before: + ```yaml + framework: + workflows: + article: + marking_store: + arguments: state + ``` + + After: + ```yaml + framework: + workflows: + type: state_machine + article: + marking_store: + type: method + property: state + ``` + + * Using a workflow with a single state marking is deprecated. Use a state machine instead. + + Before: + ```yaml + framework: + workflows: + article: + type: workflow + marking_store: + type: single_state + ``` + + After: + ```yaml + framework: + workflows: + article: + type: state_machine + marking_store: + # type: single_state # Since the single_state marking store is deprecated, use method instead + type: method + ``` + +Yaml +---- + + * Using a mapping inside a multi-line string is deprecated and will throw a `ParseException` in 5.0. diff --git a/UPGRADE-5.0.md b/UPGRADE-5.0.md index 4ec9e24f4f0ce..6f71eb546c8a7 100644 --- a/UPGRADE-5.0.md +++ b/UPGRADE-5.0.md @@ -4,12 +4,18 @@ UPGRADE FROM 4.x to 5.0 BrowserKit ---------- + * Removed `Client`, use `AbstractBrowser` instead + * Removed the possibility to extend `Response` by making it final. + * Removed `Response::buildHeader()` + * Removed `Response::getStatus()`, use `Response::getStatusCode()` instead * The `Client::submit()` method has a new `$serverParameters` argument. Cache ----- * Removed `CacheItem::getPreviousTags()`, use `CacheItem::getMetadata()` instead. + * Removed all PSR-16 adapters, use `Psr16Cache` or `Symfony\Contracts\Cache\CacheInterface` implementations instead. + * Removed `SimpleCacheAdapter`, use `Psr16Adapter` instead. Config ------ @@ -18,6 +24,8 @@ Config * Added the `getChildNodeDefinitions()` method to `ParentNodeDefinitionInterface`. * The `Processor` class has been made final * Removed `FileLoaderLoadException`, use `LoaderLoadException` instead. + * Using environment variables with `cannotBeEmpty()` if the value is validated with `validate()` will throw an exception. + * Removed the `root()` method in `TreeBuilder`, pass the root node information to the constructor instead Console ------- @@ -47,22 +55,51 @@ DependencyInjection * Removed the `TypedReference::canBeAutoregistered()` and `TypedReference::getRequiringClass()` methods. * Removed support for auto-discovered extension configuration class which does not implement `ConfigurationInterface`. + * Removed support for non-string default env() parameters + + Before: + ```yaml + parameters: + env(NAME): 1.5 + ``` + + After: + ```yaml + parameters: + env(NAME): '1.5' + ``` DoctrineBridge -------------- * Deprecated injecting `ClassMetadataFactory` in `DoctrineExtractor`, an instance of `EntityManagerInterface` should be injected instead + * Passing an `IdReader` to the `DoctrineChoiceLoader` when the query cannot be optimized with single id field will throw an exception, pass `null` instead + * Not passing an `IdReader` to the `DoctrineChoiceLoader` when the query can be optimized with single id field will throw an exception + DomCrawler ---------- * The `Crawler::children()` method has a new `$selector` argum 8000 ent. +Dotenv +------ + + * First parameter `$usePutenv` of `Dotenv::__construct()` now default to `false`. + EventDispatcher --------------- * The `TraceableEventDispatcherInterface` has been removed. + * The signature of the `EventDispatcherInterface::dispatch()` method has been updated to `dispatch($event, string $eventName = null)` + * The `Event` class has been removed, use `Symfony\Contracts\EventDispatcher\Event` instead + +Filesystem +---------- + + * The `Filesystem::dumpFile()` method no longer supports arrays in the `$content` argument. + * The `Filesystem::appendToFile()` method no longer supports arrays in the `$content` argument. Finder ------ @@ -72,6 +109,12 @@ Finder Form ---- + * Removed support for using the `format` option of `DateType` and `DateTimeType` when the `html5` option is enabled. + * Using names for buttons that do not start with a letter, a digit, or an underscore leads to an exception. + * Using names for buttons that do not contain only letters, digits, underscores, hyphens, and colons leads to an + exception. + * Using the `date_format`, `date_widget`, and `time_widget` options of the `DateTimeType` when the `widget` option is + set to `single_text` is not supported anymore. * The `getExtendedType()` method was removed from the `FormTypeExtensionInterface`. It is replaced by the the static `getExtendedTypes()` method which must return an iterable of extended types. @@ -126,6 +169,8 @@ Form FrameworkBundle --------------- + * The project dir argument of the constructor of `AssetsInstallCommand` is required. + * Removed support for `bundle:controller:action` syntax to reference controllers. Use `serviceOrFqcn::method` instead where `serviceOrFqcn` is either the service ID when using controllers as services or the FQCN of the controller. @@ -157,6 +202,7 @@ FrameworkBundle * The `Templating\Helper\TranslatorHelper::transChoice()` method has been removed, use the `trans()` one instead with a `%count%` parameter. * Removed support for legacy translations directories `src/Resources/translations/` and `src/Resources//translations/`, use `translations/` instead. * Support for the legacy directory structure in `translation:update` and `debug:translation` commands has been removed. + * Removed the "Psr\SimpleCache\CacheInterface" / "cache.app.simple" service, use "Symfony\Contracts\Cache\CacheInterface" / "cache.app" instead. HttpFoundation -------------- @@ -166,15 +212,46 @@ HttpFoundation * The `getSession()` method of the `Request` class throws an exception when session is null. * The default value of the "$secure" and "$samesite" arguments of Cookie's constructor changed respectively from "false" to "null" and from "null" to "lax". + * The `MimeTypeGuesserInterface` and `ExtensionGuesserInterface` interfaces have been removed, + use `Symfony\Component\Mime\MimeTypesInterface` instead. + * The `MimeType` and `MimeTypeExtensionGuesser` classes have been removed, + use `Symfony\Component\Mime\MimeTypes` instead. + * The `FileBinaryMimeTypeGuesser` class has been removed, + use `Symfony\Component\Mime\FileBinaryMimeTypeGuesser` instead. + * The `FileinfoMimeTypeGuesser` class has been removed, + use `Symfony\Component\Mime\FileinfoMimeTypeGuesser` instead. HttpKernel ---------- + * Removed `Client`, use `HttpKernelBrowser` instead * The `Kernel::getRootDir()` and the `kernel.root_dir` parameter have been removed * The `KernelInterface::getName()` and the `kernel.name` parameter have been removed * Removed the first and second constructor argument of `ConfigDataCollector` - * Removed `ConfigDataCollector::getApplicationName()` + * Removed `ConfigDataCollector::getApplicationName()` * Removed `ConfigDataCollector::getApplicationVersion()` + * Removed `FilterControllerArgumentsEvent`, use `ControllerArgumentsEvent` instead + * Removed `FilterControllerEvent`, use `ControllerEvent` instead + * Removed `FilterResponseEvent`, use `ResponseEvent` instead + * Removed `GetResponseEvent`, use `RequestEvent` instead + * Removed `GetResponseForControllerResultEvent`, use `ViewEvent` instead + * Removed `GetResponseForExceptionEvent`, use `ExceptionEvent` instead + * Removed `PostResponseEvent`, use `TerminateEvent` instead + * Removed `TranslatorListener` in favor of `LocaleAwareListener` + +Intl +---- + + * Removed `ResourceBundle` namespace + * Removed `Intl::getLanguageBundle()`, use `Languages` or `Scripts` instead + * Removed `Intl::getCurrencyBundle()`, use `Currencies` instead + * Removed `Intl::getLocaleBundle()`, use `Locales` instead + * Removed `Intl::getRegionBundle()`, use `Countries` instead + +Messenger +--------- + + * The `LoggingMiddleware` class has been removed, pass a logger to `SendMessageMiddleware` instead. Monolog ------- @@ -200,9 +277,22 @@ Process $process = Process::fromShellCommandline('ls -l'); ``` +Routing +------- + + * The `generator_base_class`, `generator_cache_class`, `matcher_base_class`, and `matcher_cache_class` router + options have been removed. + * `Route` and `CompiledRoute` don't implement `Serializable` anymore; if you serialize them, please + ensure your unserialization logic can recover from a failure related to an updated serialization format + Security -------- + * The `Role` and `SwitchUserRole` classes have been removed. + * The `getReachableRoles()` method of the `RoleHierarchy` class has been removed. It has been replaced by the new + `getReachableRoleNames()` method. + * The `getRoles()` method has been removed from the `TokenInterface`. It has been replaced by the new + `getRoleNames()` method. * The `ContextListener::setLogoutOnUserChange()` method has been removed. * The `Symfony\Component\Security\Core\User\AdvancedUserInterface` has been removed. * The `ExpressionVoter::addExpressionLanguageProvider()` method has been removed. @@ -213,6 +303,44 @@ Security * `SimpleAuthenticatorInterface`, `SimpleFormAuthenticatorInterface`, `SimplePreAuthenticatorInterface`, `SimpleAuthenticationProvider`, `SimpleAuthenticationHandler`, `SimpleFormAuthenticationListener` and `SimplePreAuthenticationListener` have been removed. Use Guard instead. + * The `ListenerInterface` has been removed, turn your listeners into callables instead. + * The `Firewall::handleRequest()` method has been removed, use `Firewall::callListeners()` instead. + * `\Serializable` interface has been removed from `AbstractToken` and `AuthenticationException`, + thus `serialize()` and `unserialize()` aren't available. + Use `__serialize()` and `__unserialize()` instead. + + Before: + ```php + public function serialize() + { + return [$this->myLocalVar, parent::serialize()]; + } + + public function unserialize($serialized) + { + [$this->myLocalVar, $parentSerialized] = unserialize($serialized); + parent::unserialize($parentSerialized); + } + ``` + + After: + ```php + public function __serialize(): array + { + return [$this->myLocalVar, parent::__serialize()]; + } + + public function __unserialize(array $data): void + { + [$this->myLocalVar, $parentData] = $data; + parent::__unserialize($parentData); + } + ``` + + * The `Argon2iPasswordEncoder` class has been removed, use `SodiumPasswordEncoder` instead. + * The `BCryptPasswordEncoder` class has been removed, use `NativePasswordEncoder` instead. + * Classes implementing the `TokenInterface` must implement the two new methods + `__serialize` and `__unserialize` SecurityBundle -------------- @@ -228,6 +356,12 @@ SecurityBundle use Guard instead. * The `SimpleFormFactory` and `SimplePreAuthenticationFactory` classes have been removed, use Guard instead. + * The names of the cookies configured in the `logout.delete_cookies` option are + no longer normalized. If any of your cookie names has dashes they won't be + changed to underscores. + Before: `my-cookie` deleted the `my_cookie` cookie (with an underscore). + After: `my-cookie` deletes the `my-cookie` cookie (with a dash). + * Configuring encoders using `argon2i` or `bcrypt` as algorithm is not supported anymore, use `auto` instead. Serializer ---------- @@ -250,6 +384,13 @@ TwigBundle * The default value (`false`) of the `twig.strict_variables` configuration option has been changed to `%kernel.debug%`. * The `transchoice` tag and filter have been removed, use the `trans` ones instead with a `%count%` parameter. * Removed support for legacy templates directories `src/Resources/views/` and `src/Resources//views/`, use `templates/` and `templates/bundles//` instead. + +TwigBridge +---------- + + * removed the `$requestStack` and `$requestContext` arguments of the + `HttpFoundationExtension`, pass a `Symfony\Component\HttpFoundation\UrlHelper` + instance as the only argument instead Validator -------- @@ -272,3 +413,73 @@ Workflow * `add` method has been removed use `addWorkflow` method in `Workflow\Registry` instead. * `SupportStrategyInterface` has been removed, use `WorkflowSupportStrategyInterface` instead. * `ClassInstanceSupportStrategy` has been removed, use `InstanceOfSupportStrategy` instead. + * `MarkingStoreInterface::setMarking()` has a third argument: `array $context = []`. + * Removed support of `initial_place`. Use `initial_places` instead. + * `MultipleStateMarkingStore` has been removed. Use `MethodMarkingStore` instead. + + Before: + ```yaml + framework: + workflows: + type: workflow + article: + marking_store: + type: multiple + arguments: states + ``` + + After: + ```yaml + framework: + workflows: + type: workflow + article: + marking_store: + property: states + ``` + * `SingleStateMarkingStore` has been removed. Use `MethodMarkingStore` instead. + + Before: + ```yaml + framework: + workflows: + article: + marking_store: + arguments: state + ``` + + After: + ```yaml + framework: + workflows: + article: + marking_store: + property: state + ``` + + + * Support for using a workflow with a single state marking is dropped. Use a state machine instead. + + Before: + ```yaml + framework: + workflows: + article: + type: workflow + marking_store: + type: single_state + ``` + + After: + ```yaml + framework: + workflows: + article: + type: state_machine + ``` + +Yaml +---- + + * The parser is now stricter and will throw a `ParseException` when a + mapping is found inside a multi-line string. diff --git a/composer.json b/composer.json index 839e21780b88d..fc233e2430d5a 100644 --- a/composer.json +++ b/composer.json @@ -22,17 +22,19 @@ "doctrine/event-manager": "~1.0", "doctrine/persistence": "~1.0", "fig/link-util": "^1.0", - "twig/twig": "^1.35|^2.4.4", + "twig/twig": "^1.40|^2.9", "psr/cache": "~1.0", "psr/container": "^1.0", "psr/link": "^1.0", "psr/log": "~1.0", "psr/simple-cache": "^1.0", - "symfony/contracts": "^1.0", + "symfony/contracts": "^1.1", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-intl-icu": "~1.0", + "symfony/polyfill-intl-idn": "^1.10", "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php72": "~1.5" + "symfony/polyfill-php72": "~1.5", + "symfony/polyfill-php73": "^1.11" }, "replace": { "symfony/asset": "self.version", @@ -53,6 +55,7 @@ "symfony/finder": "self.version", "symfony/form": "self.version", "symfony/framework-bundle": "self.version", + "symfony/http-client": "self.version", "symfony/http-foundation": "self.version", "symfony/http-kernel": "self.version", "symfony/inflector": "self.version", @@ -60,6 +63,7 @@ "symfony/ldap": "self.version", "symfony/lock": "self.version", "symfony/messenger": "self.version", + "symfony/mime": "self.version", "symfony/monolog-bridge": "self.version", "symfony/options-resolver": "self.version", "symfony/process": "self.version", @@ -97,28 +101,23 @@ "doctrine/orm": "~2.4,>=2.4.5", "doctrine/reflection": "~1.0", "doctrine/doctrine-bundle": "~1.4", + "masterminds/html5": "^2.6", "monolog/monolog": "~1.11", + "nyholm/psr7": "^1.0", "ocramius/proxy-manager": "~0.4|~1.0|~2.0", "predis/predis": "~1.1", + "psr/http-client": "^1.0", "egulias/email-validator": "~1.2,>=1.2.8|~2.0", "symfony/phpunit-bridge": "~3.4|~4.0", "symfony/security-acl": "~2.8|~3.0", "phpdocumentor/reflection-docblock": "^3.0|^4.0" }, "conflict": { + "masterminds/html5": "<2.6", "phpdocumentor/reflection-docblock": "<3.0||>=3.2.0,<3.2.2", "phpdocumentor/type-resolver": "<0.3.0", "phpunit/phpunit": "<5.4.3" }, - "provide": { - "psr/cache-implementation": "1.0", - "psr/container-implementation": "1.0", - "psr/log-implementation": "1.0", - "psr/simple-cache-implementation": "1.0", - "symfony/cache-contracts": "1.0", - "symfony/service-contracts": "1.0", - "symfony/translation-contracts": "1.0" - }, "autoload": { "psr-4": { "Symfony\\Bridge\\Doctrine\\": "src/Symfony/Bridge/Doctrine/", @@ -147,7 +146,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "4.2-dev" + "dev-master": "4.3-dev" } } } diff --git a/phpunit b/phpunit index 9975195309a81..5bbbf0ded1863 100755 --- a/phpunit +++ b/phpunit @@ -1,7 +1,7 @@ #!/usr/bin/env php + @@ -70,9 +71,10 @@ Doctrine\Common\Cache Symfony\Component\Cache Symfony\Component\Cache\Tests\Fixtures - Symfony\Component\Cache\Traits - Symfony\Component\Console - Symfony\Component\HttpFoundation + Symfony\Component\Cache\Tests\Traits + Symfony\Component\Cache\Traits + Symfony\Component\Console + Symfony\Component\HttpFoundation diff --git a/src/Symfony/Bridge/Doctrine/CHANGELOG.md b/src/Symfony/Bridge/Doctrine/CHANGELOG.md index c333361d4a37f..b9baff3763c6c 100644 --- a/src/Symfony/Bridge/Doctrine/CHANGELOG.md +++ b/src/Symfony/Bridge/Doctrine/CHANGELOG.md @@ -1,6 +1,14 @@ CHANGELOG ========= +4.3.0 +----- + + * changed guessing of DECIMAL to set the `input` option of `NumberType` to string + * deprecated not passing an `IdReader` to the `DoctrineChoiceLoader` when query can be optimized with a single id field + * deprecated passing an `IdReader` to the `DoctrineChoiceLoader` when entities have a composite id + * added two Messenger middleware: `DoctrinePingConnectionMiddleware` and `DoctrineCloseConnectionMiddleware` + 4.2.0 ----- diff --git a/src/Symfony/Bridge/Doctrine/ContainerAwareEventManager.php b/src/Symfony/Bridge/Doctrine/ContainerAwareEventManager.php index 20aaae85a1e46..66b99ecf62065 100644 --- a/src/Symfony/Bridge/Doctrine/ContainerAwareEventManager.php +++ b/src/Symfony/Bridge/Doctrine/ContainerAwareEventManager.php @@ -27,8 +27,8 @@ class ContainerAwareEventManager extends EventManager * * => */ - private $listeners = array(); - private $initialized = array(); + private $listeners = []; + private $initialized = []; private $container; public function __construct(ContainerInterface $container) diff --git a/src/Symfony/Bridge/Doctrine/DataCollector/DoctrineDataCollector.php b/src/Symfony/Bridge/Doctrine/DataCollector/DoctrineDataCollector.php index 8cebac72c1614..d4a86a7d54403 100644 --- a/src/Symfony/Bridge/Doctrine/DataCollector/DoctrineDataCollector.php +++ b/src/Symfony/Bridge/Doctrine/DataCollector/DoctrineDataCollector.php @@ -33,7 +33,7 @@ class DoctrineDataCollector extends DataCollector /** * @var DebugStack[] */ - private $loggers = array(); + private $loggers = []; public function __construct(ManagerRegistry $registry) { @@ -58,24 +58,24 @@ public function addLogger($name, DebugStack $logger) */ public function collect(Request $request, Response $response, \Exception $exception = null) { - $queries = array(); + $queries = []; foreach ($this->loggers as $name => $logger) { $queries[$name] = $this->sanitizeQueries($name, $logger->queries); } - $this->data = array( + $this->data = [ 'queries' => $queries, 'connections' => $this->connections, 'managers' => $this->managers, - ); + ]; } public function reset() { - $this->data = array(); + $this->data = []; foreach ($this->loggers as $logger) { - $logger->queries = array(); + $logger->queries = []; $logger->currentQuery = 0; } } @@ -133,10 +133,10 @@ private function sanitizeQuery($connectionName, $query) { $query['explainable'] = true; if (null === $query['params']) { - $query['params'] = array(); + $query['params'] = []; } if (!\is_array($query['params'])) { - $query['params'] = array($query['params']); + $query['params'] = [$query['params']]; } foreach ($query['params'] as $j => $param) { if (isset($query['types'][$j])) { @@ -180,12 +180,12 @@ private function sanitizeParam($var): array $className = \get_class($var); return method_exists($var, '__toString') ? - array(sprintf('/* Object(%s): */"%s"', $className, $var->__toString()), false) : - array(sprintf('/* Object(%s) */', $className), false); + [sprintf('/* Object(%s): */"%s"', $className, $var->__toString()), false] : + [sprintf('/* Object(%s) */', $className), false]; } if (\is_array($var)) { - $a = array(); + $a = []; $original = true; foreach ($var as $k => $v) { list($value, $orig) = $this->sanitizeParam($v); @@ -193,13 +193,13 @@ private function sanitizeParam($var): array $a[$k] = $value; } - return array($a, $original); + return [$a, $original]; } if (\is_resource($var)) { - return array(sprintf('/* Resource(%s) */', get_resource_type($var)), false); + return [sprintf('/* Resource(%s) */', get_resource_type($var)), false]; } - return array($var, true); + return [$var, true]; } } diff --git a/src/Symfony/Bridge/Doctrine/DependencyInjection/AbstractDoctrineExtension.php b/src/Symfony/Bridge/Doctrine/DependencyInjection/AbstractDoctrineExtension.php index 94687b758f390..a36b55eb16c0c 100644 --- a/src/Symfony/Bridge/Doctrine/DependencyInjection/AbstractDoctrineExtension.php +++ b/src/Symfony/Bridge/Doctrine/DependencyInjection/AbstractDoctrineExtension.php @@ -27,12 +27,12 @@ abstract class AbstractDoctrineExtension extends Extension /** * Used inside metadata driver method to simplify aggregation of data. */ - protected $aliasMap = array(); + protected $aliasMap = []; /** * Used inside metadata driver method to simplify aggregation of data. */ - protected $drivers = array(); + protected $drivers = []; /** * @param array $objectManager A configured object manager @@ -46,10 +46,10 @@ protected function loadMappingInformation(array $objectManager, ContainerBuilder // automatically register bundle mappings foreach (array_keys($container->getParameter('kernel.bundles')) as $bundle) { if (!isset($objectManager['mappings'][$bundle])) { - $objectManager['mappings'][$bundle] = array( + $objectManager['mappings'][$bundle] = [ 'mapping' => true, 'is_bundle' => true, - ); + ]; } } } @@ -59,11 +59,11 @@ protected function loadMappingInformation(array $objectManager, ContainerBuilder continue; } - $mappingConfig = array_replace(array( + $mappingConfig = array_replace([ 'dir' => false, 'type' => false, 'prefix' => false, - ), (array) $mappingConfig); + ], (array) $mappingConfig); $mappingConfig['dir'] = $container->getParameterBag()->resolveValue($mappingConfig['dir']); // a bundle configuration is detected by realizing that the specified dir is not absolute and existing @@ -153,7 +153,7 @@ protected function getMappingDriverBundleConfigDefaults(array $bundleConfig, \Re } if (!$bundleConfig['dir']) { - if (\in_array($bundleConfig['type'], array('annotation', 'staticphp'))) { + if (\in_array($bundleConfig['type'], ['annotation', 'staticphp'])) { $bundleConfig['dir'] = $bundleDir.'/'.$this->getMappingObjectDefaultName(); } else { $bundleConfig['dir'] = $bundleDir.'/'.$this->getMappingResourceConfigDirectory(); @@ -197,25 +197,25 @@ protected function registerMappingDrivers($objectManager, ContainerBuilder $cont } $mappingDriverDef->setArguments($args); } elseif ('annotation' == $driverType) { - $mappingDriverDef = new Definition('%'.$this->getObjectManagerElementName('metadata.'.$driverType.'.class%'), array( + $mappingDriverDef = new Definition('%'.$this->getObjectManagerElementName('metadata.'.$driverType.'.class%'), [ new Reference($this->getObjectManagerElementName('metadata.annotation_reader')), array_values($driverPaths), - )); + ]); } else { - $mappingDriverDef = new Definition('%'.$this->getObjectManagerElementName('metadata.'.$driverType.'.class%'), array( + $mappingDriverDef = new Definition('%'.$this->getObjectManagerElementName('metadata.'.$driverType.'.class%'), [ array_values($driverPaths), - )); + ]); } $mappingDriverDef->setPublic(false); if (false !== strpos($mappingDriverDef->getClass(), 'yml') || false !== strpos($mappingDriverDef->getClass(), 'xml')) { - $mappingDriverDef->setArguments(array(array_flip($driverPaths))); - $mappingDriverDef->addMethodCall('setGlobalBasename', array('mapping')); + $mappingDriverDef->setArguments([array_flip($driverPaths)]); + $mappingDriverDef->addMethodCall('setGlobalBasename', ['mapping']); } $container->setDefinition($mappingService, $mappingDriverDef); foreach ($driverPaths as $prefix => $driverPath) { - $chainDriverDef->addMethodCall('addDriver', array(new Reference($mappingService), $prefix)); + $chainDriverDef->addMethodCall('addDriver', [new Reference($mappingService), $prefix]); } } @@ -240,7 +240,7 @@ protected function assertValidMappingConfiguration(array $mappingConfig, $object throw new \InvalidArgumentException(sprintf('Specified non-existing directory "%s" as Doctrine mapping source.', $mappingConfig['dir'])); } - if (!\in_array($mappingConfig['type'], array('xml', 'yml', 'annotation', 'php', 'staticphp'))) { + if (!\in_array($mappingConfig['type'], ['xml', 'yml', 'annotation', 'php', 'staticphp'])) { throw new \InvalidArgumentException(sprintf('Can only configure "xml", "yml", "annotation", "php" or '. '"staticphp" through the DoctrineBundle. Use your own bundle to configure other metadata drivers. '. 'You can register them by adding a new driver to the '. @@ -326,11 +326,11 @@ protected function loadCacheDriver($cacheName, $objectManagerName, array $cacheD $cacheDef = new Definition($memcachedClass); $memcachedInstance = new Definition($memcachedInstanceClass); $memcachedInstance->setPrivate(true); - $memcachedInstance->addMethodCall('addServer', array( + $memcachedInstance->addMethodCall('addServer', [ $memcachedHost, $memcachedPort, - )); + ]); $container->setDefinition($this->getObjectManagerElementName(sprintf('%s_memcached_instance', $objectManagerName)), $memcachedInstance); - $cacheDef->addMethodCall('setMemcached', array(new Reference($this->getObjectManagerElementName(sprintf('%s_memcached_instance', $objectManagerName))))); + $cacheDef->addMethodCall('setMemcached', [new Reference($this->getObjectManagerElementName(sprintf('%s_memcached_instance', $objectManagerName)))]); break; case 'redis': $redisClass = !empty($cacheDriver['class']) ? $cacheDriver['class'] : '%'.$this->getObjectManagerElementName('cache.redis.class').'%'; @@ -340,11 +340,11 @@ protected function loadCacheDriver($cacheName, $objectManagerName, array $cacheD $cacheDef = new Definition($redisClass); $redisInstance = new Definition($redisInstanceClass); $redisInstance->setPrivate(true); - $redisInstance->addMethodCall('connect', array( + $redisInstance->addMethodCall('connect', [ $redisHost, $redisPort, - )); + ]); $container->setDefinition($this->getObjectManagerElementName(sprintf('%s_redis_instance', $objectManagerName)), $redisInstance); - $cacheDef->addMethodCall('setRedis', array(new Reference($this->getObjectManagerElementName(sprintf('%s_redis_instance', $objectManagerName))))); + $cacheDef->addMethodCall('setRedis', [new Reference($this->getObjectManagerElementName(sprintf('%s_redis_instance', $objectManagerName)))]); break; case 'apc': case 'apcu': @@ -373,7 +373,7 @@ protected function loadCacheDriver($cacheName, $objectManagerName, array $cacheD $cacheDriver['namespace'] = $namespace; } - $cacheDef->addMethodCall('setNamespace', array($cacheDriver['namespace'])); + $cacheDef->addMethodCall('setNamespace', [$cacheDriver['namespace']]); $container->setDefinition($cacheDriverServiceId, $cacheDef); @@ -396,10 +396,10 @@ protected function fixManagersAutoMappings(array $managerConfigs, array $bundles continue 2; } } - $managerConfigs[$autoMappedManager]['mappings'][$bundle] = array( + $managerConfigs[$autoMappedManager]['mappings'][$bundle] = [ 'mapping' => true, 'is_bundle' => true, - ); + ]; } $managerConfigs[$autoMappedManager]['auto_mapping'] = false; } diff --git a/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterEventListenersAndSubscribersPass.php b/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterEventListenersAndSubscribersPass.php index 46904ebbf4cb5..deaa64e7c9084 100644 --- a/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterEventListenersAndSubscribersPass.php +++ b/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterEventListenersAndSubscribersPass.php @@ -66,13 +66,13 @@ private function addTaggedSubscribers(ContainerBuilder $container) foreach ($taggedSubscribers as $taggedSubscriber) { list($id, $tag) = $taggedSubscriber; - $connections = isset($tag['connection']) ? array($tag['connection']) : array_keys($this->connections); + $connections = isset($tag['connection']) ? [$tag['connection']] : array_keys($this->connections); foreach ($connections as $con) { if (!isset($this->connections[$con])) { throw new RuntimeException(sprintf('The Doctrine connection "%s" referenced in service "%s" does not exist. Available connections names: %s', $con, $id, implode(', ', array_keys($this->connections)))); } - $this->getEventManagerDef($container, $con)->addMethodCall('addEventSubscriber', array(new Reference($id))); + $this->getEventManagerDef($container, $con)->addMethodCall('addEventSubscriber', [new Reference($id)]); } } } @@ -81,7 +81,7 @@ private function addTaggedListeners(ContainerBuilder $container) { $listenerTag = $this->tagPrefix.'.event_listener'; $taggedListeners = $this->findAndSortTags($listenerTag, $container); - $listenerRefs = array(); + $listenerRefs = []; foreach ($taggedListeners as $taggedListener) { list($id, $tag) = $taggedListener; @@ -89,7 +89,7 @@ private function addTaggedListeners(ContainerBuilder $container) throw new InvalidArgumentException(sprintf('Doctrine event listener "%s" must specify the "event" attribute.', $id)); } - $connections = isset($tag['connection']) ? array($tag['connection']) : array_keys($this->connections); + $connections = isset($tag['connection']) ? [$tag['connection']] : array_keys($this->connections); foreach ($connections as $con) { if (!isset($this->connections[$con])) { throw new RuntimeException(sprintf('The Doctrine connection "%s" referenced in service "%s" does not exist. Available connections names: %s', $con, $id, implode(', ', array_keys($this->connections)))); @@ -97,7 +97,7 @@ private function addTaggedListeners(ContainerBuilder $container) $listenerRefs[$con][$id] = new Reference($id); // we add one call per event per service so we have the correct order - $this->getEventManagerDef($container, $con)->addMethodCall('addEventListener', array(array($tag['event']), $id)); + $this->getEventManagerDef($container, $con)->addMethodCall('addEventListener', [[$tag['event']], $id]); } } @@ -135,12 +135,12 @@ private function getEventManagerDef(ContainerBuilde 8000 r $container, $name) */ private function findAndSortTags($tagName, ContainerBuilder $container) { - $sortedTags = array(); + $sortedTags = []; foreach ($container->findTaggedServiceIds($tagName, true) as $serviceId => $tags) { foreach ($tags as $attributes) { $priority = isset($attributes['priority']) ? $attributes['priority'] : 0; - $sortedTags[$priority][] = array($serviceId, $attributes); + $sortedTags[$priority][] = [$serviceId, $attributes]; } } diff --git a/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterMappingsPass.php b/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterMappingsPass.php index 44ad1562196b1..5b1d78fbf82c8 100644 --- a/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterMappingsPass.php +++ b/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterMappingsPass.php @@ -24,8 +24,8 @@ * The compiler pass is meant to register the mappings with the metadata * chain driver corresponding to one of the object managers. * - * For concrete implementations that are easy to use, see the - * RegisterXyMappingsPass classes in the DoctrineBundle resp. + * For concrete implementations, see the RegisterXyMappingsPass classes + * in the DoctrineBundle resp. * DoctrineMongodbBundle, DoctrineCouchdbBundle and DoctrinePhpcrBundle. * * @author David Buchmann @@ -50,7 +50,7 @@ abstract class RegisterMappingsPass implements CompilerPassInterface /** * List of potential container parameters that hold the object manager name * to register the mappings with the correct metadata driver, for example - * array('acme.manager', 'doctrine.default_entity_manager'). + * ['acme.manager', 'doctrine.default_entity_manager']. * * @var string[] */ @@ -117,7 +117,7 @@ abstract class RegisterMappingsPass implements CompilerPassInterface * register alias * @param string[] $aliasMap Map of alias to namespace */ - public function __construct($driver, array $namespaces, array $managerParameters, string $driverPattern, $enabledParameter = false, string $configurationPattern = '', string $registerAliasMethodName = '', array $aliasMap = array()) + public function __construct($driver, array $namespaces, array $managerParameters, string $driverPattern, $enabledParameter = false, string $configurationPattern = '', string $registerAliasMethodName = '', array $aliasMap = []) { $this->driver = $driver; $this->namespaces = $namespaces; @@ -146,7 +146,7 @@ public function process(ContainerBuilder $container) // Definition for a Doctrine\Common\Persistence\Mapping\Driver\MappingDriverChain $chainDriverDef = $container->getDefinition($chainDriverDefService); foreach ($this->namespaces as $namespace) { - $chainDriverDef->addMethodCall('addDriver', array($mappingDriverDef, $namespace)); + $chainDriverDef->addMethodCall('addDriver', [$mappingDriverDef, $namespace]); } if (!\count($this->aliasMap)) { @@ -157,7 +157,7 @@ public function process(ContainerBuilder $container) // Definition of the Doctrine\...\Configuration class specific to the Doctrine flavour. $configurationServiceDefinition = $container->getDefinition($configurationServiceName); foreach ($this->aliasMap as $alias => $namespace) { - $configurationServiceDefinition->addMethodCall($this->registerAliasMethodName, array($alias, $namespace)); + $configurationServiceDefinition->addMethodCall($this->registerAliasMethodName, [$alias, $namespace]); } } diff --git a/src/Symfony/Bridge/Doctrine/Form/ChoiceList/DoctrineChoiceLoader.php b/src/Symfony/Bridge/Doctrine/Form/ChoiceList/DoctrineChoiceLoader.php index 4b7b1ebe341d4..cd040d12a9b03 100644 --- a/src/Symfony/Bridge/Doctrine/Form/ChoiceList/DoctrineChoiceLoader.php +++ b/src/Symfony/Bridge/Doctrine/Form/ChoiceList/DoctrineChoiceLoader.php @@ -42,16 +42,33 @@ class DoctrineChoiceLoader implements ChoiceLoaderInterface * * @param ObjectManager $manager The object manager * @param string $class The class name of the loaded objects - * @param IdReader $idReader The reader for the object IDs + * @param IdReader|null $idReader The reader for the object IDs * @param EntityLoaderInterface|null $objectLoader The objects loader */ public function __construct(ObjectManager $manager, string $class, IdReader $idReader = null, EntityLoaderInterface $objectLoader = null) { $classMetadata = $manager->getClassMetadata($class); + if ($idReader && !$idReader->isSingleId()) { + @trigger_error(sprintf('Passing an instance of "%s" to "%s" with an entity class "%s" that has a composite id is deprecated since Symfony 4.3 and will throw an exception in 5.0.', IdReader::class, __CLASS__, $class), E_USER_DEPRECATED); + + // In Symfony 5.0 + // throw new \InvalidArgumentException(sprintf('The second argument `$idReader` of "%s" must be null when the query cannot be optimized because of composite id fields.', __METHOD__)); + } + + if ((5 > \func_num_args() || false !== func_get_arg(4)) && null === $idReader) { + $idReader = new IdReader($manager, $classMetadata); + + if ($idReader->isSingleId()) { + @trigger_error(sprintf('Not explicitly passing an instance of "%s" to "%s" when it can optimize single id entity "%s" has been deprecated in 4.3 and will not apply any optimization in 5.0.', IdReader::class, __CLASS__, $class), E_USER_DEPRECATED); + } else { + $idReader = null; + } + } + $this->manager = $manager; $this->class = $classMetadata->getName(); - $this->idReader = $idReader ?: new IdReader($manager, $classMetadata); + $this->idReader = $idReader; $this->objectLoader = $objectLoader; } @@ -78,16 +95,16 @@ public function loadValuesForChoices(array $choices, $value = null) { // Performance optimization if (empty($choices)) { - return array(); + return []; } // Optimize performance for single-field identifiers. We already // know that the IDs are used as values - $optimize = null === $value || \is_array($value) && $value[0] === $this->idReader; + $optimize = $this->idReader && (null === $value || \is_array($value) && $value[0] === $this->idReader); // Attention: This optimization does not check choices for existence if ($optimize && !$this->choiceList && $this->idReader->isSingleId()) { - $values = array(); + $values = []; // Maintain order and indices of the given objects foreach ($choices as $i => $object) { @@ -115,17 +132,17 @@ public function loadChoicesForValues(array $values, $value = null) // statements, consequently no test fails when this code is removed. // https://github.com/symfony/symfony/pull/8981#issuecomment-24230557 if (empty($values)) { - return array(); + return []; } // Optimize performance in case we have an object loader and // a single-field identifier - $optimize = null === $value || \is_array($value) && $this->idReader === $value[0]; + $optimize = $this->idReader && (null === $value || \is_array($value) && $this->idReader === $value[0]); if ($optimize && !$this->choiceList && $this->objectLoader && $this->idReader->isSingleId()) { $unorderedObjects = $this->objectLoader->getEntitiesByIds($this->idReader->getIdField(), $values); - $objectsById = array(); - $objects = array(); + $objectsById = []; + $objects = []; // Maintain order and indices from the given $values // An alternative approach to the following loop is to add the diff --git a/src/Symfony/Bridge/Doctrine/Form/ChoiceList/IdReader.php b/src/Symfony/Bridge/Doctrine/Form/ChoiceList/IdReader.php index 381d02fcbe046..3509d9b03b329 100644 --- a/src/Symfony/Bridge/Doctrine/Form/ChoiceList/IdReader.php +++ b/src/Symfony/Bridge/Doctrine/Form/ChoiceList/IdReader.php @@ -43,7 +43,7 @@ public function __construct(ObjectManager $om, ClassMetadata $classMetadata) $this->om = $om; $this->classMetadata = $classMetadata; $this->singleId = 1 === \count($ids); - $this->intId = $this->singleId && \in_array($idType, array('integer', 'smallint', 'bigint')); + $this->intId = $this->singleId && \in_array($idType, ['integer', 'smallint', 'bigint']); $this->idField = current($ids); // single field association are resolved, since the schema column could be an int diff --git a/src/Symfony/Bridge/Doctrine/Form/ChoiceList/ORMQueryBuilderLoader.php b/src/Symfony/Bridge/Doctrine/Form/ChoiceList/ORMQueryBuilderLoader.php index a2f1fa7ae8b70..96f5e2f5f1868 100644 --- a/src/Symfony/Bridge/Doctrine/Form/ChoiceList/ORMQueryBuilderLoader.php +++ b/src/Symfony/Bridge/Doctrine/Form/ChoiceList/ORMQueryBuilderLoader.php @@ -64,7 +64,7 @@ public function getEntitiesByIds($identifier, array $values) // Guess type $entity = current($qb->getRootEntities()); $metadata = $qb->getEntityManager()->getClassMetadata($entity); - if (\in_array($metadata->getTypeOfField($identifier), array('integer', 'bigint', 'smallint'))) { + if (\in_array($metadata->getTypeOfField($identifier), ['integer', 'bigint', 'smallint'])) { $parameterType = Connection::PARAM_INT_ARRAY; // Filter out non-integer values (e.g. ""). If we don't, some @@ -72,7 +72,7 @@ public function getEntitiesByIds($identifier, array $values) $values = array_values(array_filter($values, function ($v) { return (string) $v === (string) (int) $v || ctype_digit($v); })); - } elseif (\in_array($metadata->getTypeOfField($identifier), array('uuid', 'guid'))) { + } elseif (\in_array($metadata->getTypeOfField($identifier), ['uuid', 'guid'])) { $parameterType = Connection::PARAM_STR_ARRAY; // Like above, but we just filter out empty strings. @@ -83,7 +83,7 @@ public function getEntitiesByIds($identifier, array $values) $parameterType = Connection::PARAM_STR_ARRAY; } if (!$values) { - return array(); + return []; } return $qb->andWhere($where) diff --git a/src/Symfony/Bridge/Doctrine/Form/DataTransformer/CollectionToArrayTransformer.php b/src/Symfony/Bridge/Doctrine/Form/DataTransformer/CollectionToArrayTransformer.php index 4010512ba9c49..3202dae97f5c2 100644 --- a/src/Symfony/Bridge/Doctrine/Form/DataTransformer/CollectionToArrayTransformer.php +++ b/src/Symfony/Bridge/Doctrine/Form/DataTransformer/CollectionToArrayTransformer.php @@ -31,7 +31,7 @@ class CollectionToArrayTransformer implements DataTransformerInterface public function transform($collection) { if (null === $collection) { - return array(); + return []; } // For cases when the collection getter returns $collection->toArray() @@ -57,7 +57,7 @@ public function transform($collection) public function reverseTransform($array) { if ('' === $array || null === $array) { - $array = array(); + $array = []; } else { $array = (array) $array; } diff --git a/src/Symfony/Bridge/Doctrine/Form/DoctrineOrmExtension.php b/src/Symfony/Bridge/Doctrine/Form/DoctrineOrmExtension.php index fe86b103cbb34..891754a1da08f 100644 --- a/src/Symfony/Bridge/Doctrine/Form/DoctrineOrmExtension.php +++ b/src/Symfony/Bridge/Doctrine/Form/DoctrineOrmExtension.php @@ -26,9 +26,9 @@ public function __construct(ManagerRegistry $registry) protected function loadTypes() { - return array( + return [ new EntityType($this->registry), - ); + ]; } protected function loadTypeGuesser() diff --git a/src/Symfony/Bridge/Doctrine/Form/DoctrineOrmTypeGuesser.php b/src/Symfony/Bridge/Doctrine/Form/DoctrineOrmTypeGuesser.php index e85145d1eafbf..34fb04aed283e 100644 --- a/src/Symfony/Bridge/Doctrine/Form/DoctrineOrmTypeGuesser.php +++ b/src/Symfony/Bridge/Doctrine/Form/DoctrineOrmTypeGuesser.php @@ -26,7 +26,7 @@ class DoctrineOrmTypeGuesser implements FormTypeGuesserInterface { protected $registry; - private $cache = array(); + private $cache = []; public function __construct(ManagerRegistry $registry) { @@ -39,7 +39,7 @@ public function __construct(ManagerRegistry $registry) public function guessType($class, $property) { if (!$ret = $this->getMetadata($class)) { - return new TypeGuess('Symfony\Component\Form\Extension\Core\Type\TextType', array(), Guess::LOW_CONFIDENCE); + return new TypeGuess('Symfony\Component\Form\Extension\Core\Type\TextType', [], Guess::LOW_CONFIDENCE); } list($metadata, $name) = $ret; @@ -48,45 +48,46 @@ public function guessType($class, $property) $multiple = $metadata->isCollectionValuedAssociation($property); $mapping = $metadata->getAssociationMapping($property); - return new TypeGuess('Symfony\Bridge\Doctrine\Form\Type\EntityType', array('em' => $name, 'class' => $mapping['targetEntity'], 'multiple' => $multiple), Guess::HIGH_CONFIDENCE); + return new TypeGuess('Symfony\Bridge\Doctrine\Form\Type\EntityType', ['em' => $name, 'class' => $mapping['targetEntity'], 'multiple' => $multiple], Guess::HIGH_CONFIDENCE); } switch ($metadata->getTypeOfField($property)) { case Type::TARRAY: case Type::SIMPLE_ARRAY: - return new TypeGuess('Symfony\Component\Form\Extension\Core\Type\CollectionType', array(), Guess::MEDIUM_CONFIDENCE); + return new TypeGuess('Symfony\Component\Form\Extension\Core\Type\CollectionType', [], Guess::MEDIUM_CONFIDENCE); case Type::BOOLEAN: - return new TypeGuess('Symfony\Component\Form\Extension\Core\Type\CheckboxType', array(), Guess::HIGH_CONFIDENCE); + return new TypeGuess('Symfony\Component\Form\Extension\Core\Type\CheckboxType', [], Guess::HIGH_CONFIDENCE); case Type::DATETIME: case Type::DATETIMETZ: case 'vardatetime': - return new TypeGuess('Symfony\Component\Form\Extension\Core\Type\DateTimeType', array(), Guess::HIGH_CONFIDENCE); + return new TypeGuess('Symfony\Component\Form\Extension\Core\Type\DateTimeType', [], Guess::HIGH_CONFIDENCE); case 'datetime_immutable': case 'datetimetz_immutable': - return new TypeGuess('Symfony\Component\Form\Extension\Core\Type\DateTimeType', array('input' => 'datetime_immutable'), Guess::HIGH_CONFIDENCE); + return new TypeGuess('Symfony\Component\Form\Extension\Core\Type\DateTimeType', ['input' => 'datetime_immutable'], Guess::HIGH_CONFIDENCE); case 'dateinterval': - return new TypeGuess('Symfony\Component\Form\Extension\Core\Type\DateIntervalType', array(), Guess::HIGH_CONFIDENCE); + return new TypeGuess('Symfony\Component\Form\Extension\Core\Type\DateIntervalType', [], Guess::HIGH_CONFIDENCE); case Type::DATE: - return new TypeGuess('Symfony\Component\Form\Extension\Core\Type\DateType', array(), Guess::HIGH_CONFIDENCE); + return new TypeGuess('Symfony\Component\Form\Extension\Core\Type\DateType', [], Guess::HIGH_CONFIDENCE); case 'date_immutable': - return new TypeGuess('Symfony\Component\Form\Extension\Core\Type\DateType', array('input' => 'datetime_immutable'), Guess::HIGH_CONFIDENCE); + return new TypeGuess('Symfony\Component\Form\Extension\Core\Type\DateType', ['input' => 'datetime_immutable'], Guess::HIGH_CONFIDENCE); case Type::TIME: - return new TypeGuess('Symfony\Component\Form\Extension\Core\Type\TimeType', array(), Guess::HIGH_CONFIDENCE); + return new TypeGuess('Symfony\Component\Form\Extension\Core\Type\TimeType', [], Guess::HIGH_CONFIDENCE); case 'time_immutable': - return new TypeGuess('Symfony\Component\Form\Extension\Core\Type\TimeType', array('input' => 'datetime_immutable'), Guess::HIGH_CONFIDENCE); + return new TypeGuess('Symfony\Component\Form\Extension\Core\Type\TimeType', ['input' => 'datetime_immutable'], Guess::HIGH_CONFIDENCE); case Type::DECIMAL: + return new TypeGuess('Symfony\Component\Form\Extension\Core\Type\NumberType', ['input' => 'string'], Guess::MEDIUM_CONFIDENCE); case Type::FLOAT: - return new TypeGuess('Symfony\Component\Form\Extension\Core\Type\NumberType', array(), Guess::MEDIUM_CONFIDENCE); + return new TypeGuess('Symfony\Component\Form\Extension\Core\Type\NumberType', [], Guess::MEDIUM_CONFIDENCE); case Type::INTEGER: case Type::BIGINT: case Type::SMALLINT: - return new TypeGuess('Symfony\Component\Form\Extension\Core\Type\IntegerType', array(), Guess::MEDIUM_CONFIDENCE); + return new TypeGuess('Symfony\Component\Form\Extension\Core\Type\IntegerType', [], Guess::MEDIUM_CONFIDENCE); case Type::STRING: - return new TypeGuess('Symfony\Component\Form\Extension\Core\Type\TextType', array(), Guess::MEDIUM_CONFIDENCE); + return new TypeGuess('Symfony\Component\Form\Extension\Core\Type\TextType', [], Guess::MEDIUM_CONFIDENCE); case Type::TEXT: - return new TypeGuess('Symfony\Component\Form\Extension\Core\Type\TextareaType', array(), Guess::MEDIUM_CONFIDENCE); + return new TypeGuess('Symfony\Component\Form\Extension\Core\Type\TextareaType', [], Guess::MEDIUM_CONFIDENCE); default: - return new TypeGuess('Symfony\Component\Form\Extension\Core\Type\TextType', array(), Guess::LOW_CONFIDENCE); + return new TypeGuess('Symfony\Component\Form\Extension\Core\Type\TextType', [], Guess::LOW_CONFIDENCE); } } @@ -141,7 +142,7 @@ public function guessMaxLength($class, $property) return new ValueGuess($mapping['length'], Guess::HIGH_CONFIDENCE); } - if (\in_array($ret[0]->getTypeOfField($property), array(Type::DECIMAL, Type::FLOAT))) { + if (\in_array($ret[0]->getTypeOfField($property), [Type::DECIMAL, Type::FLOAT])) { return new ValueGuess(null, Guess::MEDIUM_CONFIDENCE); } } @@ -154,7 +155,7 @@ public function guessPattern($class, $property) { $ret = $this->getMetadata($class); if ($ret && isset($ret[0]->fieldMappings[$property]) && !$ret[0]->hasAssociation($property)) { - if (\in_array($ret[0]->getTypeOfField($property), array(Type::DECIMAL, Type::FLOAT))) { + if (\in_array($ret[0]->getTypeOfField($property), [Type::DECIMAL, Type::FLOAT])) { return new ValueGuess(null, Guess::MEDIUM_CONFIDENCE); } } @@ -165,14 +166,14 @@ protected function getMetadata($class) // normalize class name $class = self::getRealClass(ltrim($class, '\\')); - if (array_key_exists($class, $this->cache)) { + if (\array_key_exists($class, $this->cache)) { return $this->cache[$class]; } $this->cache[$class] = null; foreach ($this->registry->getManagers() as $name => $em) { try { - return $this->cache[$class] = array($em->getClassMetadata($class), $name); + return $this->cache[$class] = [$em->getClassMetadata($class), $name]; } catch (MappingException $e) { // not an entity or mapped super class } catch (LegacyMappingException $e) { diff --git a/src/Symfony/Bridge/Doctrine/Form/EventListener/MergeDoctrineCollectionListener.php b/src/Symfony/Bridge/Doctrine/Form/EventListener/MergeDoctrineCollectionListener.php index 22517181f7597..1ec496b781c5d 100644 --- a/src/Symfony/Bridge/Doctrine/Form/EventListener/MergeDoctrineCollectionListener.php +++ b/src/Symfony/Bridge/Doctrine/Form/EventListener/MergeDoctrineCollectionListener.php @@ -31,11 +31,11 @@ public static function getSubscribedEvents() { // Higher priority than core MergeCollectionListener so that this one // is called before - return array( - FormEvents::SUBMIT => array( - array('onSubmit', 5), - ), - ); + return [ + FormEvents::SUBMIT => [ + ['onSubmit', 5], + ], + ]; } public function onSubmit(FormEvent $event) diff --git a/src/Symfony/Bridge/Doctrine/Form/Type/DoctrineType.php b/src/Symfony/Bridge/Doctrine/Form/Type/DoctrineType.php index f97dcf24a9422..88f9cf9101c7d 100644 --- a/src/Symfony/Bridge/Doctrine/Form/Type/DoctrineType.php +++ b/src/Symfony/Bridge/Doctrine/Form/Type/DoctrineType.php @@ -11,6 +11,7 @@ namespace Symfony\Bridge\Doctrine\Form\Type; +use Doctrine\Common\Collections\Collection; use Doctrine\Common\Persistence\ManagerRegistry; use Doctrine\Common\Persistence\ObjectManager; use Symfony\Bridge\Doctrine\Form\ChoiceList\DoctrineChoiceLoader; @@ -36,12 +37,12 @@ abstract class DoctrineType extends AbstractType implements ResetInterface /** * @var IdReader[] */ - private $idReaders = array(); + private $idReaders = []; /** * @var DoctrineChoiceLoader[] */ - private $choiceLoaders = array(); + private $choiceLoaders = []; /** * Creates the label for a choice. @@ -107,7 +108,7 @@ public function __construct(ManagerRegistry $registry) public function buildForm(FormBuilderInterface $builder, array $options) { - if ($options['multiple']) { + if ($options['multiple'] && interface_exists(Collection::class)) { $builder ->addEventSubscriber(new MergeDoctrineCollectionListener()) ->addViewTransformer(new CollectionToArrayTransformer(), true) @@ -127,11 +128,11 @@ public function configureOptions(OptionsResolver $resolver) // also if concrete Type can return important QueryBuilder parts to generate // hash key we go for it as well if (!$options['query_builder'] || false !== ($qbParts = $this->getQueryBuilderPartsForCachingHash($options['query_builder']))) { - $hash = CachingFactoryDecorator::generateHash(array( + $hash = CachingFactoryDecorator::generateHash([ $options['em'], $options['class'], $qbParts, - )); + ]); if (isset($this->choiceLoaders[$hash])) { return $this->choiceLoaders[$hash]; @@ -149,7 +150,8 @@ public function configureOptions(OptionsResolver $resolver) $options['em'], $options['class'], $options['id_reader'], - $entityLoader + $entityLoader, + false ); if (null !== $hash) { @@ -161,14 +163,11 @@ public function configureOptions(OptionsResolver $resolver) }; $choiceName = function (Options $options) { - /** @var IdReader $idReader */ - $idReader = $options['id_reader']; - // If the object has a single-column, numeric ID, use that ID as // field name. We can only use numeric IDs as names, as we cannot // guarantee that a non-numeric ID contains a valid form name - if ($idReader->isIntId()) { - return array(__CLASS__, 'createChoiceName'); + if ($options['id_reader'] instanceof IdReader && $options['id_reader']->isIntId()) { + return [__CLASS__, 'createChoiceName']; } // Otherwise, an incrementing integer is used as name automatically @@ -179,12 +178,9 @@ public function configureOptions(OptionsResolver $resolver) // are indexed by an incrementing integer. // Use the ID/incrementing integer as choice value. $choiceValue = function (Options $options) { - /** @var IdReader $idReader */ - $idReader = $options['id_reader']; - // If the entity has a single-column ID, use that ID as value - if ($idReader->isSingleId()) { - return array($idReader, 'getIdValue'); + if ($options['id_reader'] instanceof IdReader && $options['id_reader']->isSingleId()) { + return [$options['id_reader'], 'getIdValue']; } // Otherwise, an incrementing integer is used as value automatically @@ -213,7 +209,7 @@ public function configureOptions(OptionsResolver $resolver) // for equal query builders $queryBuilderNormalizer = function (Options $options, $queryBuilder) { if (\is_callable($queryBuilder)) { - $queryBuilder = \call_user_func($queryBuilder, $options['em']->getRepository($options['class'])); + $queryBuilder = $queryBuilder($options['em']->getRepository($options['class'])); } return $queryBuilder; @@ -222,10 +218,10 @@ public function configureOptions(OptionsResolver $resolver) // Set the "id_reader" option via the normalizer. This option is not // supposed to be set by the user. $idReaderNormalizer = function (Options $options) { - $hash = CachingFactoryDecorator::generateHash(array( + $hash = CachingFactoryDecorator::generateHash([ $options['em'], $options['class'], - )); + ]); // The ID reader is a utility that is needed to read the object IDs // when generating the field values. The callback generating the @@ -238,28 +234,32 @@ public function configureOptions(OptionsResolver $resolver) $this->idReaders[$hash] = new IdReader($options['em'], $classMetadata); } - return $this->idReaders[$hash]; + if ($this->idReaders[$hash]->isSingleId()) { + return $this->idReaders[$hash]; + } + + return null; }; - $resolver->setDefaults(array( + $resolver->setDefaults([ 'em' => null, 'query_builder' => null, 'choices' => null, 'choice_loader' => $choiceLoader, - 'choice_label' => array(__CLASS__, 'createChoiceLabel'), + 'choice_label' => [__CLASS__, 'createChoiceLabel'], 'choice_name' => $choiceName, 'choice_value' => $choiceValue, 'id_reader' => null, // internal 'choice_translation_domain' => false, - )); + ]); - $resolver->setRequired(array('class')); + $resolver->setRequired(['class']); $resolver->setNormalizer('em', $emNormalizer); $resolver->setNormalizer('query_builder', $queryBuilderNormalizer); $resolver->setNormalizer('id_reader', $idReaderNormalizer); - $resolver->setAllowedTypes('em', array('null', 'string', 'Doctrine\Common\Persistence\ObjectManager')); + $resolver->setAllowedTypes('em', ['null', 'string', 'Doctrine\Common\Persistence\ObjectManager']); } /** @@ -280,6 +280,6 @@ public function getParent() public function reset() { - $this->choiceLoaders = array(); + $this->choiceLoaders = []; } } diff --git a/src/Symfony/Bridge/Doctrine/Form/Type/EntityType.php b/src/Symfony/Bridge/Doctrine/Form/Type/EntityType.php index fa4176ee18f38..b6c598350c0a8 100644 --- a/src/Symfony/Bridge/Doctrine/Form/Type/EntityType.php +++ b/src/Symfony/Bridge/Doctrine/Form/Type/EntityType.php @@ -29,7 +29,7 @@ public function configureOptions(OptionsResolver $resolver) // for equal query builders $queryBuilderNormalizer = function (Options $options, $queryBuilder) { if (\is_callable($queryBuilder)) { - $queryBuilder = \call_user_func($queryBuilder, $options['em']->getRepository($options['class'])); + $queryBuilder = $queryBuilder($options['em']->getRepository($options['class'])); if (null !== $queryBuilder && !$queryBuilder instanceof QueryBuilder) { throw new UnexpectedTypeException($queryBuilder, 'Doctrine\ORM\QueryBuilder'); @@ -40,7 +40,7 @@ public function configureOptions(OptionsResolver $resolver) }; $resolver->setNormalizer('query_builder', $queryBuilderNormalizer); - $resolver->setAllowedTypes('query_builder', array('null', 'callable', 'Doctrine\ORM\QueryBuilder')); + $resolver->setAllowedTypes('query_builder', ['null', 'callable', 'Doctrine\ORM\QueryBuilder']); } /** @@ -78,10 +78,10 @@ public function getBlockPrefix() */ public function getQueryBuilderPartsForCachingHash($queryBuilder) { - return array( + return [ $queryBuilder->getQuery()->getSQL(), - array_map(array($this, 'parameterToArray'), $queryBuilder->getParameters()->toArray()), - ); + array_map([$this, 'parameterToArray'], $queryBuilder->getParameters()->toArray()), + ]; } /** @@ -91,6 +91,6 @@ public function getQueryBuilderPartsForCachingHash($queryBuilder) */ private function parameterToArray(Parameter $parameter) { - return array($parameter->getName(), $parameter->getType(), $parameter->getValue()); + return [$parameter->getName(), $parameter->getType(), $parameter->getValue()]; } } diff --git a/src/Symfony/Bridge/Doctrine/LICENSE b/src/Symfony/Bridge/Doctrine/LICENSE index 21d7fb9e2f29b..a677f43763ca4 100644 --- a/src/Symfony/Bridge/Doctrine/LICENSE +++ b/src/Symfony/Bridge/Doctrine/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2018 Fabien Potencier +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 diff --git a/src/Symfony/Bridge/Doctrine/Logger/DbalLogger.php b/src/Symfony/Bridge/Doctrine/Logger/DbalLogger.php index 0200c2657a0e6..63880a6d614a0 100644 --- a/src/Symfony/Bridge/Doctrine/Logger/DbalLogger.php +++ b/src/Symfony/Bridge/Doctrine/Logger/DbalLogger.php @@ -42,7 +42,7 @@ public function startQuery($sql, array $params = null, array $types = null) } if (null !== $this->logger) { - $this->log($sql, null === $params ? array() : $this->normalizeParams($params)); + $this->log($sql, null === $params ? [] : $this->normalizeParams($params)); } } diff --git a/src/Symfony/Bridge/Doctrine/ManagerRegistry.php b/src/Symfony/Bridge/Doctrine/ManagerRegistry.php index bf73c0036d030..ae481b572628e 100644 --- a/src/Symfony/Bridge/Doctrine/ManagerRegistry.php +++ b/src/Symfony/Bridge/Doctrine/ManagerRegistry.php @@ -46,7 +46,7 @@ protected function resetService($name) $manager = $this->container->get($name); if (!$manager instanceof LazyLoadingInterface) { - throw new \LogicException(sprintf('Resetting a non-lazy manager service is not supported. Set the "%s" service as lazy and require "symfony/proxy-manager-bridge" in your composer.json file instead.', $name)); + throw new \LogicException('Resetting a non-lazy manager service is not supported. '.(interface_exists(LazyLoadingInterface::class) ? sprintf('Declare the "%s" service as lazy.', $name) : 'Try running "composer require symfony/proxy-manager-bridge".')); } $manager->setProxyInitializer(\Closure::bind( function (&$wrappedInstance, Lazy 8000 LoadingInterface $manager) use ($name) { diff --git a/src/Symfony/Bridge/Doctrine/Messenger/DoctrineCloseConnectionMiddleware.php b/src/Symfony/Bridge/Doctrine/Messenger/DoctrineCloseConnectionMiddleware.php new file mode 100644 index 0000000000000..6520ac0a35952 --- /dev/null +++ b/src/Symfony/Bridge/Doctrine/Messenger/DoctrineCloseConnectionMiddleware.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Messenger; + +use Doctrine\Common\Persistence\ManagerRegistry; +use Doctrine\ORM\EntityManagerInterface; +use Symfony\Component\Messenger\Envelope; +use Symfony\Component\Messenger\Middleware\MiddlewareInterface; +use Symfony\Component\Messenger\Middleware\StackInterface; + +/** + * Closes connection and therefore saves number of connections. + * + * @author Fuong + * + * @experimental in 4.3 + */ +class DoctrineCloseConnectionMiddleware implements MiddlewareInterface +{ + private $managerRegistry; + private $entityManagerName; + + public function __construct(ManagerRegistry $managerRegistry, string $entityManagerName = null) + { + $this->managerRegistry = $managerRegistry; + $this->entityManagerName = $entityManagerName; + } + + /** + * {@inheritdoc} + */ + public function handle(Envelope $envelope, StackInterface $stack): Envelope + { + $entityManager = $this->managerRegistry->getManager($this->entityManagerName); + + if (!$entityManager instanceof EntityManagerInterface) { + throw new \InvalidArgumentException(sprintf('The ObjectManager with name "%s" must be an instance of EntityManagerInterface', $this->entityManagerName)); + } + + try { + $connection = $entityManager->getConnection(); + + return $stack->next()->handle($envelope, $stack); + } finally { + $connection->close(); + } + } +} diff --git a/src/Symfony/Bridge/Doctrine/Messenger/DoctrinePingConnectionMiddleware.php b/src/Symfony/Bridge/Doctrine/Messenger/DoctrinePingConnectionMiddleware.php new file mode 100644 index 0000000000000..021d7a8392065 --- /dev/null +++ b/src/Symfony/Bridge/Doctrine/Messenger/DoctrinePingConnectionMiddleware.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\Bridge\Doctrine\Messenger; + +use Doctrine\Common\Persistence\ManagerRegistry; +use Doctrine\ORM\EntityManagerInterface; +use Symfony\Component\Messenger\Envelope; +use Symfony\Component\Messenger\Middleware\MiddlewareInterface; +use Symfony\Component\Messenger\Middleware\StackInterface; + +/** + * Checks whether the connection is still open or reconnects otherwise. + * + * @author Fuong + * + * @experimental in 4.3 + */ +class DoctrinePingConnectionMiddleware implements MiddlewareInterface +{ + private $managerRegistry; + private $entityManagerName; + + public function __construct(ManagerRegistry $managerRegistry, string $entityManagerName = null) + { + $this->managerRegistry = $managerRegistry; + $this->entityManagerName = $entityManagerName; + } + + /** + * {@inheritdoc} + */ + public function handle(Envelope $envelope, StackInterface $stack): Envelope + { + $entityManager = $this->managerRegistry->getManager($this->entityManagerName); + + if (!$entityManager instanceof EntityManagerInterface) { + throw new \InvalidArgumentException(sprintf('The ObjectManager with name "%s" must be an instance of EntityManagerInterface', $this->entityManagerName)); + } + + $connection = $entityManager->getConnection(); + + if (!$connection->ping()) { + $connection->close(); + $connection->connect(); + } + + if (!$entityManager->isOpen()) { + $this->managerRegistry->resetManager($this->entityManagerName); + } + + return $stack->next()->handle($envelope, $stack); + } +} diff --git a/src/Symfony/Bridge/Doctrine/PropertyInfo/DoctrineExtractor.php b/src/Symfony/Bridge/Doctrine/PropertyInfo/DoctrineExtractor.php index 030b09ecca6e5..49d10a486bfe8 100644 --- a/src/Symfony/Bridge/Doctrine/PropertyInfo/DoctrineExtractor.php +++ b/src/Symfony/Bridge/Doctrine/PropertyInfo/DoctrineExtractor.php @@ -42,14 +42,14 @@ public function __construct($entityManager) @trigger_error(sprintf('Injecting an instance of "%s" in "%s" is deprecated since Symfony 4.2, inject an instance of "%s" instead.', ClassMetadataFactory::class, __CLASS__, EntityManagerInterface::class), E_USER_DEPRECATED); $this->classMetadataFactory = $entityManager; } else { - throw new \InvalidArgumentException(sprintf('$entityManager must be an instance of "%s", "%s" given.', EntityManagerInterface::class, \is_object($entityManager) ? \get_class($entityManager) : \gettype($entityManager))); + throw new \TypeError(sprintf('$entityManager must be an instance of "%s", "%s" given.', EntityManagerInterface::class, \is_object($entityManager) ? \get_class($entityManager) : \gettype($entityManager))); } } /** * {@inheritdoc} */ - public function getProperties($class, array $context = array()) + public function getProperties($class, array $context = []) { try { $metadata = $this->entityManager ? $this->entityManager->getClassMetadata($class) : $this->classMetadataFactory->getMetadataFor($class); @@ -75,7 +75,7 @@ public function getProperties($class, array $context = array()) /** * {@inheritdoc} */ - public function getTypes($class, $property, array $context = array()) + public function getTypes($class, $property, array $context = []) { try { $metadata = $this->entityManager ? $this->entityManager->getClassMetadata($class) : $this->classMetadataFactory->getMetadataFor($class); @@ -97,7 +97,7 @@ public function getTypes($class, $property, array $context = array()) $nullable = false; } - return array(new Type(Type::BUILTIN_TYPE_OBJECT, $nullable, $class)); + return [new Type(Type::BUILTIN_TYPE_OBJECT, $nullable, $class)]; } $collectionKeyType = Type::BUILTIN_TYPE_INT; @@ -124,18 +124,18 @@ public function getTypes($class, $property, array $context = array()) } } - return array(new Type( + return [new Type( Type::BUILTIN_TYPE_OBJECT, false, 'Doctrine\Common\Collections\Collection', true, new Type($collectionKeyType), new Type(Type::BUILTIN_TYPE_OBJECT, false, $class) - )); + )]; } if ($metadata instanceof ClassMetadataInfo && class_exists('Doctrine\ORM\Mapping\Embedded') && isset($metadata->embeddedClasses[$property])) { - return array(new Type(Type::BUILTIN_TYPE_OBJECT, false, $metadata->embeddedClasses[$property]['class'])); + return [new Type(Type::BUILTIN_TYPE_OBJECT, false, $metadata->embeddedClasses[$property]['class'])]; } if ($metadata->hasField($property)) { @@ -148,30 +148,30 @@ public function getTypes($class, $property, array $context = array()) case DBALType::DATETIMETZ: case 'vardatetime': case DBALType::TIME: - return array(new Type(Type::BUILTIN_TYPE_OBJECT, $nullable, 'DateTime')); + return [new Type(Type::BUILTIN_TYPE_OBJECT, $nullable, 'DateTime')]; case 'date_immutable': case 'datetime_immutable': case 'datetimetz_immutable': case 'time_immutable': - return array(new Type(Type::BUILTIN_TYPE_OBJECT, $nullable, 'DateTimeImmutable')); + return [new Type(Type::BUILTIN_TYPE_OBJECT, $nullable, 'DateTimeImmutable')]; case 'dateinterval': - return array(new Type(Type::BUILTIN_TYPE_OBJECT, $nullable, 'DateInterval')); + return [new Type(Type::BUILTIN_TYPE_OBJECT, $nullable, 'DateInterval')]; case DBALType::TARRAY: - return array(new Type(Type::BUILTIN_TYPE_ARRAY, $nullable, null, true)); + return [new Type(Type::BUILTIN_TYPE_ARRAY, $nullable, null, true)]; case DBALType::SIMPLE_ARRAY: - return array(new Type(Type::BUILTIN_TYPE_ARRAY, $nullable, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_STRING))); + return [new Type(Type::BUILTIN_TYPE_ARRAY, $nullable, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_STRING))]; case DBALType::JSON_ARRAY: - return array(new Type(Type::BUILTIN_TYPE_ARRAY, $nullable, null, true)); + return [new Type(Type::BUILTIN_TYPE_ARRAY, $nullable, null, true)]; default: $builtinType = $this->getPhpType($typeOfField); - return $builtinType ? array(new Type($builtinType, $nullable)) : null; + return $builtinType ? [new Type($builtinType, $nullable)] : null; } } } diff --git a/src/Symfony/Bridge/Doctrine/RegistryInterface.php b/src/Symfony/Bridge/Doctrine/RegistryInterface.php index 9bc98217c918f..6928f8afd4f9c 100644 --- a/src/Symfony/Bridge/Doctrine/RegistryInterface.php +++ b/src/Symfony/Bridge/Doctrine/RegistryInterface.php @@ -52,7 +52,7 @@ public function getEntityManagers(); * it makes sense to get a new one to replace the closed one. * * Be warned that you will get a brand new entity manager as - * the existing one is not useable anymore. This means that any + * the existing one is not usable anymore. This means that any * other object with a dependency on this entity manager will * hold an obsolete reference. You can inject the registry instead * to avoid this problem. diff --git a/src/Symfony/Bridge/Doctrine/Security/RememberMe/DoctrineTokenProvider.php b/src/Symfony/Bridge/Doctrine/Security/RememberMe/DoctrineTokenProvider.php index 5e41b10e14bb2..64515fac71840 100644 --- a/src/Symfony/Bridge/Doctrine/Security/RememberMe/DoctrineTokenProvider.php +++ b/src/Symfony/Bridge/Doctrine/Security/RememberMe/DoctrineTokenProvider.php @@ -53,8 +53,8 @@ public function loadTokenBySeries($series) // the alias for lastUsed works around case insensitivity in PostgreSQL $sql = 'SELECT class, username, value, lastUsed AS last_used' .' FROM rememberme_token WHERE series=:series'; - $paramValues = array('series' => $series); - $paramTypes = array('series' => \PDO::PARAM_STR); + $paramValues = ['series' => $series]; + $paramTypes = ['series' => \PDO::PARAM_STR]; $stmt = $this->conn->executeQuery($sql, $paramValues, $paramTypes); $row = $stmt->fetch(\PDO::FETCH_ASSOC); @@ -71,8 +71,8 @@ public function loadTokenBySeries($series) public function deleteTokenBySeries($series) { $sql = 'DELETE FROM rememberme_token WHERE series=:series'; - $paramValues = array('series' => $series); - $paramTypes = array('series' => \PDO::PARAM_STR); + $paramValues = ['series' => $series]; + $paramTypes = ['series' => \PDO::PARAM_STR]; $this->conn->executeUpdate($sql, $paramValues, $paramTypes); } @@ -83,16 +83,16 @@ public function updateToken($series, $tokenValue, \DateTime $lastUsed) { $sql = 'UPDATE rememberme_token SET value=:value, lastUsed=:lastUsed' .' WHERE series=:series'; - $paramValues = array( + $paramValues = [ 'value' => $tokenValue, 'lastUsed' => $lastUsed, 'series' => $series, - ); - $paramTypes = array( + ]; + $paramTypes = [ 'value' => \PDO::PARAM_STR, 'lastUsed' => DoctrineType::DATETIME, 'series' => \PDO::PARAM_STR, - ); + ]; $updated = $this->conn->executeUpdate($sql, $paramValues, $paramTypes); if ($updated < 1) { throw new TokenNotFoundException('No token found.'); @@ -107,20 +107,20 @@ public function createNewToken(PersistentTokenInterface $token) $sql = 'INSERT INTO rememberme_token' .' (class, username, series, value, lastUsed)' .' VALUES (:class, :username, :series, :value, :lastUsed)'; - $paramValues = array( + $paramValues = [ 'class' => $token->getClass(), 'username' => $token->getUsername(), 'series' => $token->getSeries(), 'value' => $token->getTokenValue(), 'lastUsed' => $token->getLastUsed(), - ); - $paramTypes = array( + ]; + $paramTypes = [ 'class' => \PDO::PARAM_STR, 'username' => \PDO::PARAM_STR, 'series' => \PDO::PARAM_STR, 'value' => \PDO::PARAM_STR, 'lastUsed' => DoctrineType::DATETIME, - ); + ]; $this->conn->executeUpdate($sql, $paramValues, $paramTypes); } } diff --git a/src/Symfony/Bridge/Doctrine/Security/User/EntityUserProvider.php b/src/Symfony/Bridge/Doctrine/Security/User/EntityUserProvider.php index 726548e1744e8..20f68399571f9 100644 --- a/src/Symfony/Bridge/Doctrine/Security/User/EntityUserProvider.php +++ b/src/Symfony/Bridge/Doctrine/Security/User/EntityUserProvider.php @@ -20,7 +20,7 @@ /** * Wrapper around a Doctrine ObjectManager. * - * Provides easy to use provisioning for Doctrine entity users. + * Provides provisioning for Doctrine entity users. * * @author Fabien Potencier * @author Johannes M. Schmitt @@ -48,7 +48,7 @@ public function loadUserByUsername($username) { $repository = $this->getRepository(); if (null !== $this->property) { - $user = $repository->findOneBy(array($this->property => $username)); + $user = $repository->findOneBy([$this->property => $username]); } else { if (!$repository instanceof UserLoaderInterface) { throw new \InvalidArgumentException(sprintf('You must either make the "%s" entity Doctrine Repository ("%s") implement "Symfony\Bridge\Doctrine\Security\User\UserLoaderInterface" or set the "property" option in the corresponding entity provider configuration.', $this->classOrAlias, \get_class($repository))); diff --git a/src/Symfony/Bridge/Doctrine/Test/DoctrineTestHelper.php b/src/Symfony/Bridge/Doctrine/Test/DoctrineTestHelper.php index 06dc628475e9f..24aa66a7dda46 100644 --- a/src/Symfony/Bridge/Doctrine/Test/DoctrineTestHelper.php +++ b/src/Symfony/Bridge/Doctrine/Test/DoctrineTestHelper.php @@ -13,9 +13,12 @@ use Doctrine\Common\Annotations\AnnotationReader; use Doctrine\Common\Cache\ArrayCache; +use Doctrine\Common\Persistence\Mapping\Driver\MappingDriverChain; +use Doctrine\Common\Persistence\Mapping\Driver\SymfonyFileLocator; use Doctrine\ORM\Configuration; use Doctrine\ORM\EntityManager; use Doctrine\ORM\Mapping\Driver\AnnotationDriver; +use Doctrine\ORM\Mapping\Driver\XmlDriver; use PHPUnit\Framework\TestCase; /** @@ -42,10 +45,10 @@ public static function createTestEntityManager(Configuration $config = null) $config = self::createTestConfiguration(); } - $params = array( + $params = [ 'driver' => 'pdo_sqlite', 'memory' => true, - ); + ]; return EntityManager::create($params, $config); } @@ -56,7 +59,7 @@ public static function createTestEntityManager(Configuration $config = null) public static function createTestConfiguration() { $config = new Configuration(); - $config->setEntityNamespaces(array('SymfonyTestsDoctrine' => 'Symfony\Bridge\Doctrine\Tests\Fixtures')); + $config->setEntityNamespaces(['SymfonyTestsDoctrine' => 'Symfony\Bridge\Doctrine\Tests\Fixtures']); $config->setAutoGenerateProxyClasses(true); $config->setProxyDir(\sys_get_temp_dir()); $config->setProxyNamespace('SymfonyTests\Doctrine'); @@ -67,6 +70,28 @@ public static function createTestConfiguration() return $config; } + /** + * @return Configuration + */ + public static function createTestConfigurationWithXmlLoader() + { + $config = static::createTestConfiguration(); + + $driverChain = new MappingDriverChain(); + $driverChain->addDriver( + new XmlDriver( + new SymfonyFileLocator( + [__DIR__.'/../Tests/Resources/orm' => 'Symfony\\Bridge\\Doctrine\\Tests\\Fixtures'], '.orm.xml' + ) + ), + 'Symfony\\Bridge\\Doctrine\\Tests\\Fixtures' + ); + + $config->setMetadataDriverImpl($driverChain); + + return $config; + } + /** * This class cannot be instantiated. */ diff --git a/src/Symfony/Bridge/Doctrine/Test/TestRepositoryFactory.php b/src/Symfony/Bridge/Doctrine/Test/TestRepositoryFactory.php index b7cbafa947aef..e7df3702ebf1f 100644 --- a/src/Symfony/Bridge/Doctrine/Test/TestRepositoryFactory.php +++ b/src/Symfony/Bridge/Doctrine/Test/TestRepositoryFactory.php @@ -24,7 +24,7 @@ final class TestRepositoryFactory implements RepositoryFactory /** * @var ObjectRepository[] */ - private $repositoryList = array(); + private $repositoryList = []; /** * {@inheritdoc} diff --git a/src/Symfony/Bridge/Doctrine/Tests/ContainerAwareEventManagerTest.php b/src/Symfony/Bridge/Doctrine/Tests/ContainerAwareEventManagerTest.php index f40d1ca710ed1..2e97edb1d2e14 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/ContainerAwareEventManagerTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/ContainerAwareEventManagerTest.php @@ -43,15 +43,15 @@ public function testRemoveEventListener() $this->evm->addEventListener('foo', 'bar'); $this->evm->addEventListener('foo', $listener = new MyListener()); - $listeners = array('foo' => array('_service_bar' => 'bar', spl_object_hash($listener) => $listener)); + $listeners = ['foo' => ['_service_bar' => 'bar', spl_object_hash($listener) => $listener]]; $this->assertSame($listeners, $this->evm->getListeners()); $this->assertSame($listeners['foo'], $this->evm->getListeners('foo')); $this->evm->removeEventListener('foo', $listener); - $this->assertSame(array('_service_bar' => 'bar'), $this->evm->getListeners('foo')); + $this->assertSame(['_service_bar' => 'bar'], $this->evm->getListeners('foo')); $this->evm->removeEventListener('foo', 'bar'); - $this->assertSame(array(), $this->evm->getListeners('foo')); + $this->assertSame([], $this->evm->getListeners('foo')); } } diff --git a/src/Symfony/Bridge/Doctrine/Tests/DataCollector/DoctrineDataCollectorTest.php b/src/Symfony/Bridge/Doctrine/Tests/DataCollector/DoctrineDataCollectorTest.php index 8e9abd618b690..259532879016e 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/DataCollector/DoctrineDataCollectorTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/DataCollector/DoctrineDataCollectorTest.php @@ -22,27 +22,27 @@ class DoctrineDataCollectorTest extends TestCase { public function testCollectConnections() { - $c = $this->createCollector(array()); + $c = $this->createCollector([]); $c->collect(new Request(), new Response()); - $this->assertEquals(array('default' => 'doctrine.dbal.default_connection'), $c->getConnections()); + $this->assertEquals(['default' => 'doctrine.dbal.default_connection'], $c->getConnections()); } public function testCollectManagers() { - $c = $this->createCollector(array()); + $c = $this->createCollector([]); $c->collect(new Request(), new Response()); - $this->assertEquals(array('default' => 'doctrine.orm.default_entity_manager'), $c->getManagers()); + $this->assertEquals(['default' => 'doctrine.orm.default_entity_manager'], $c->getManagers()); } public function testCollectQueryCount() { - $c = $this->createCollector(array()); + $c = $this->createCollector([]); $c->collect(new Request(), new Response()); $this->assertEquals(0, $c->getQueryCount()); - $queries = array( - array('sql' => 'SELECT * FROM table1', 'params' => array(), 'types' => array(), 'executionMS' => 0), - ); + $queries = [ + ['sql' => 'SELECT * FROM table1', 'params' => [], 'types' => [], 'executionMS' => 0], + ]; $c = $this->createCollector($queries); $c->collect(new Request(), new Response()); $this->assertEquals(1, $c->getQueryCount()); @@ -50,21 +50,21 @@ public function testCollectQueryCount() public function testCollectTime() { - $c = $this->createCollector(array()); + $c = $this->createCollector([]); $c->collect(new Request(), new Response()); $this->assertEquals(0, $c->getTime()); - $queries = array( - array('sql' => 'SELECT * FROM table1', 'params' => array(), 'types' => array(), 'executionMS' => 1), - ); + $queries = [ + ['sql' => 'SELECT * FROM table1', 'params' => [], 'types' => [], 'executionMS' => 1], + ]; $c = $this->createCollector($queries); $c->collect(new Request(), new Response()); $this->assertEquals(1, $c->getTime()); - $queries = array( - array('sql' => 'SELECT * FROM table1', 'params' => array(), 'types' => array(), 'executionMS' => 1), - array('sql' => 'SELECT * FROM table2', 'params' => array(), 'types' => array(), 'executionMS' => 2), - ); + $queries = [ + ['sql' => 'SELECT * FROM table1', 'params' => [], 'types' => [], 'executionMS' => 1], + ['sql' => 'SELECT * FROM table2', 'params' => [], 'types' => [], 'executionMS' => 2], + ]; $c = $this->createCollector($queries); $c->collect(new Request(), new Response()); $this->assertEquals(3, $c->getTime()); @@ -75,9 +75,9 @@ public function testCollectTime() */ public function testCollectQueries($param, $types, $expected, $explainable) { - $queries = array( - array('sql' => 'SELECT * FROM table1 WHERE field1 = ?1', 'params' => array($param), 'types' => $types, 'executionMS' => 1), - ); + $queries = [ + ['sql' => 'SELECT * FROM table1 WHERE field1 = ?1', 'params' => [$param], 'types' => $types, 'executionMS' => 1], + ]; $c = $this->createCollector($queries); $c->collect(new Request(), new Response()); @@ -88,32 +88,32 @@ public function testCollectQueries($param, $types, $expected, $explainable) public function testCollectQueryWithNoParams() { - $queries = array( - array('sql' => 'SELECT * FROM table1', 'params' => array(), 'types' => array(), 'executionMS' => 1), - array('sql' => 'SELECT * FROM table1', 'params' => null, 'types' => null, 'executionMS' => 1), - ); + $queries = [ + ['sql' => 'SELECT * FROM table1', 'params' => [], 'types' => [], 'executionMS' => 1], + ['sql' => 'SELECT * FROM table1', 'params' => null, 'types' => null, 'executionMS' => 1], + ]; $c = $this->createCollector($queries); $c->collect(new Request(), new Response()); $collectedQueries = $c->getQueries(); - $this->assertEquals(array(), $collectedQueries['default'][0]['params']); + $this->assertEquals([], $collectedQueries['default'][0]['params']); $this->assertTrue($collectedQueries['default'][0]['explainable']); - $this->assertEquals(array(), $collectedQueries['default'][1]['params']); + $this->assertEquals([], $collectedQueries['default'][1]['params']); $this->assertTrue($collectedQueries['default'][1]['explainable']); } public function testReset() { - $queries = array( - array('sql' => 'SELECT * FROM table1', 'params' => array(), 'types' => array(), 'executionMS' => 1), - ); + $queries = [ + ['sql' => 'SELECT * FROM table1', 'params' => [], 'types' => [], 'executionMS' => 1], + ]; $c = $this->createCollector($queries); $c->collect(new Request(), new Response()); $c->reset(); $c->collect(new Request(), new Response()); - $this->assertEquals(array('default' => array()), $c->getQueries()); + $this->assertEquals(['default' => []], $c->getQueries()); } /** @@ -121,9 +121,9 @@ public function testReset() */ public function testSerialization($param, $types, $expected, $explainable) { - $queries = array( - array('sql' => 'SELECT * FROM table1 WHERE field1 = ?1', 'params' => array($param), 'types' => $types, 'executionMS' => 1), - ); + $queries = [ + ['sql' => 'SELECT * FROM table1 WHERE field1 = ?1', 'params' => [$param], 'types' => $types, 'executionMS' => 1], + ]; $c = $this->createCollector($queries); $c->collect(new Request(), new Response()); $c = unserialize(serialize($c)); @@ -135,25 +135,25 @@ public function testSerialization($param, $types, $expected, $explainable) public function paramProvider() { - $tests = array( - array('some value', array(), 'some value', true), - array(1, array(), 1, true), - array(true, array(), true, true), - array(null, array(), null, true), - array(new \DateTime('2011-09-11'), array('date'), '2011-09-11', true), - array(fopen(__FILE__, 'r'), array(), '/* Resource(stream) */', false), - array(new \stdClass(), array(), '/* Object(stdClass) */', false), - array( + $tests = [ + ['some value', [], 'some value', true], + [1, [], 1, true], + [true, [], true, true], + [null, [], null, true], + [new \DateTime('2011-09-11'), ['date'], '2011-09-11', true], + [fopen(__FILE__, 'r'), [], '/* Resource(stream) */', false], + [new \stdClass(), [], '/* Object(stdClass) */', false], + [ new StringRepresentableClass(), - array(), + [], '/* Object(Symfony\Bridge\Doctrine\Tests\DataCollector\StringRepresentableClass): */"string representation"', false, - ), - ); + ], + ]; if (version_compare(Version::VERSION, '2.6', '>=')) { - $tests[] = array('this is not a date', array('date'), 'this is not a date', false); - $tests[] = array(new \stdClass(), array('date'), '/* Object(stdClass) */', false); + $tests[] = ['this is not a date', ['date'], 'this is not a date', false]; + $tests[] = [new \stdClass(), ['date'], '/* Object(stdClass) */', false]; } return $tests; @@ -172,11 +172,11 @@ private function createCollector($queries) $registry ->expects($this->any()) ->method('getConnectionNames') - ->will($this->returnValue(array('default' => 'doctrine.dbal.default_connection'))); + ->will($this->returnValue(['default' => 'doctrine.dbal.default_connection'])); $registry ->expects($this->any()) ->method('getManagerNames') - ->will($this->returnValue(array('default' => 'doctrine.orm.default_entity_manager'))); + ->will($this->returnValue(['default' => 'doctrine.orm.default_entity_manager'])); $registry->expects($this->any()) ->method('getConnection') ->will($this->returnValue($connection)); diff --git a/src/Symfony/Bridge/Doctrine/Tests/DependencyInjection/CompilerPass/RegisterEventListenersAndSubscribersPassTest.php b/src/Symfony/Bridge/Doctrine/Tests/DependencyInjection/CompilerPass/RegisterEventListenersAndSubscribersPassTest.php index dfdfecc094a0a..ca75437b769f4 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/DependencyInjection/CompilerPass/RegisterEventListenersAndSubscribersPassTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/DependencyInjection/CompilerPass/RegisterEventListenersAndSubscribersPassTest.php @@ -46,7 +46,7 @@ public function testExceptionOnAbstractTaggedListener() $abstractDefinition = new Definition('stdClass'); $abstractDefinition->setAbstract(true); - $abstractDefinition->addTag('doctrine.event_listener', array('event' => 'test')); + $abstractDefinition->addTag('doctrine.event_listener', ['event' => 'test']); $container->setDefinition('a', $abstractDefinition); @@ -60,30 +60,30 @@ public function testProcessEventListenersWithPriorities() $container ->register('a', 'stdClass') ->setPublic(false) - ->addTag('doctrine.event_listener', array( + ->addTag('doctrine.event_listener', [ 'event' => 'bar', - )) - ->addTag('doctrine.event_listener', array( + ]) + ->addTag('doctrine.event_listener', [ 'event' => 'foo', 'priority' => -5, - )) - ->addTag('doctrine.event_listener', array( + ]) + ->addTag('doctrine.event_listener', [ 'event' => 'foo_bar', 'priority' => 3, - )) + ]) ; $container ->register('b', 'stdClass') - ->addTag('doctrine.event_listener', array( + ->addTag('doctrine.event_listener', [ 'event' => 'foo', - )) + ]) ; $container ->register('c', 'stdClass') - ->addTag('doctrine.event_listener', array( + ->addTag('doctrine.event_listener', [ 'event' => 'foo_bar', 'priority' => 4, - )) + ]) ; $this->process($container); @@ -91,24 +91,24 @@ public function testProcessEventListenersWithPriorities() $methodCalls = $eventManagerDef->getMethodCalls(); $this->assertEquals( - array( - array('addEventListener', array(array('foo_bar'), 'c')), - array('addEventListener', array(array('foo_bar'), 'a')), - array('addEventListener', array(array('bar'), 'a')), - array('addEventListener', array(array('foo'), 'b')), - array('addEventListener', array(array('foo'), 'a')), - ), + [ + ['addEventListener', [['foo_bar'], 'c']], + ['addEventListener', [['foo_bar'], 'a']], + ['addEventListener', [['bar'], 'a']], + ['addEventListener', [['foo'], 'b']], + ['addEventListener', [['foo'], 'a']], + ], $methodCalls ); $serviceLocatorDef = $container->getDefinition((string) $eventManagerDef->getArgument(0)); $this->assertSame(ServiceLocator::class, $serviceLocatorDef->getClass()); $this->assertEquals( - array( + [ 'c' => new ServiceClosureArgument(new Reference('c')), 'a' => new ServiceClosureArgument(new Reference('a')), 'b' => new ServiceClosureArgument(new Reference('b')), - ), + ], $serviceLocatorDef->getArgument(0) ); } @@ -119,25 +119,25 @@ public function testProcessEventListenersWithMultipleConnections() $container ->register('a', 'stdClass') - ->addTag('doctrine.event_listener', array( + ->addTag('doctrine.event_listener', [ 'event' => 8000 'onFlush', - )) + ]) ; $container ->register('b', 'stdClass') - ->addTag('doctrine.event_listener', array( + ->addTag('doctrine.event_listener', [ 'event' => 'onFlush', 'connection' => 'default', - )) + ]) ; $container ->register('c', 'stdClass') - ->addTag('doctrine.event_listener', array( + ->addTag('doctrine.event_listener', [ 'event' => 'onFlush', 'connection' => 'second', - )) + ]) ; $this->process($container); @@ -146,40 +146,40 @@ public function testProcessEventListenersWithMultipleConnections() // first connection $this->assertEquals( - array( - array('addEventListener', array(array('onFlush'), 'a')), - array('addEventListener', array(array('onFlush'), 'b')), - ), + [ + ['addEventListener', [['onFlush'], 'a']], + ['addEventListener', [['onFlush'], 'b']], + ], $eventManagerDef->getMethodCalls() ); $serviceLocatorDef = $container->getDefinition((string) $eventManagerDef->getArgument(0)); $this->assertSame(ServiceLocator::class, $serviceLocatorDef->getClass()); $this->assertEquals( - array( + [ 'a' => new ServiceClosureArgument(new Reference('a')), 'b' => new ServiceClosureArgument(new Reference('b')), - ), + ], $serviceLocatorDef->getArgument(0) ); // second connection $secondEventManagerDef = $container->getDefinition('doctrine.dbal.second_connection.event_manager'); $this->assertEquals( - array( - array('addEventListener', array(array('onFlush'), 'a')), - array('addEventListener', array(array('onFlush'), 'c')), - ), + [ + ['addEventListener', [['onFlush'], 'a']], + ['addEventListener', [['onFlush'], 'c']], + ], $secondEventManagerDef->getMethodCalls() ); $serviceLocatorDef = $container->getDefinition((string) $secondEventManagerDef->getArgument(0)); $this->assertSame(ServiceLocator::class, $serviceLocatorDef->getClass()); $this->assertEquals( - array( + [ 'a' => new ServiceClosureArgument(new Reference('a')), 'c' => new ServiceClosureArgument(new Reference('c')), - ), + ], $serviceLocatorDef->getArgument(0) ); } @@ -190,42 +190,42 @@ public function testProcessEventSubscribersWithMultipleConnections() $container ->register('a', 'stdClass') - ->addTag('doctrine.event_subscriber', array( + ->addTag('doctrine.event_subscriber', [ 'event' => 'onFlush', - )) + ]) ; $container ->register('b', 'stdClass') - ->addTag('doctrine.event_subscriber', array( + ->addTag('doctrine.event_subscriber', [ 'event' => 'onFlush', 'connection' => 'default', - )) + ]) ; $container ->register('c', 'stdClass') - ->addTag('doctrine.event_subscriber', array( + ->addTag('doctrine.event_subscriber', [ 'event' => 'onFlush', 'connection' => 'second', - )) + ]) ; $this->process($container); $this->assertEquals( - array( - array('addEventSubscriber', array(new Reference('a'))), - array('addEventSubscriber', array(new Reference('b'))), - ), + [ + ['addEventSubscriber', [new Reference('a')]], + ['addEventSubscriber', [new Reference('b')]], + ], $container->getDefinition('doctrine.dbal.default_connection.event_manager')->getMethodCalls() ); $this->assertEquals( - array( - array('addEventSubscriber', array(new Reference('a'))), - array('addEventSubscriber', array(new Reference('c'))), - ), + [ + ['addEventSubscriber', [new Reference('a')]], + ['addEventSubscriber', [new Reference('c')]], + ], $container->getDefinition('doctrine.dbal.second_connection.event_manager')->getMethodCalls() ); } @@ -240,39 +240,39 @@ public function testProcessEventSubscribersWithPriorities() ; $container ->register('b', 'stdClass') - ->addTag('doctrine.event_subscriber', array( + ->addTag('doctrine.event_subscriber', [ 'priority' => 5, - )) + ]) ; $container ->register('c', 'stdClass') - ->addTag('doctrine.event_subscriber', array( + ->addTag('doctrine.event_subscriber', [ 'priority' => 10, - )) + ]) ; $container ->register('d', 'stdClass') - ->addTag('doctrine.event_subscriber', array( + ->addTag('doctrine.event_subscriber', [ 'priority' => 10, - )) + ]) ; $container ->register('e', 'stdClass') - ->addTag('doctrine.event_subscriber', array( + ->addTag('doctrine.event_subscriber', [ 'priority' => 10, - )) + ]) ; $this->process($container); $this->assertEquals( - array( - array('addEventSubscriber', array(new Reference('c'))), - array('addEventSubscriber', array(new Reference('d'))), - array('addEventSubscriber', array(new Reference('e'))), - array('addEventSubscriber', array(new Reference('b'))), - array('addEventSubscriber', array(new Reference('a'))), - ), + [ + ['addEventSubscriber', [new Reference('c')]], + ['addEventSubscriber', [new Reference('d')]], + ['addEventSubscriber', [new Reference('e')]], + ['addEventSubscriber', [new Reference('b')]], + ['addEventSubscriber', [new Reference('a')]], + ], $container->getDefinition('doctrine.dbal.default_connection.event_manager')->getMethodCalls() ); } @@ -283,9 +283,9 @@ public function testProcessNoTaggedServices() $this->process($container); - $this->assertEquals(array(), $container->getDefinition('doctrine.dbal.default_connection.event_manager')->getMethodCalls()); + $this->assertEquals([], $container->getDefinition('doctrine.dbal.default_connection.event_manager')->getMethodCalls()); - $this->assertEquals(array(), $container->getDefinition('doctrine.dbal.second_connection.event_manager')->getMethodCalls()); + $this->assertEquals([], $container->getDefinition('doctrine.dbal.second_connection.event_manager')->getMethodCalls()); } private function process(ContainerBuilder $container) @@ -298,7 +298,7 @@ private function createBuilder($multipleConnections = false) { $container = new ContainerBuilder(); - $connections = array('default' => 'doctrine.dbal.default_connection'); + $connections = ['default' => 'doctrine.dbal.default_connection']; $container->register('doctrine.dbal.default_connection.event_manager', 'stdClass') ->addArgument(new Reference('service_container')); diff --git a/src/Symfony/Bridge/Doctrine/Tests/DependencyInjection/CompilerPass/RegisterMappingsPassTest.php b/src/Symfony/Bridge/Doctrine/Tests/DependencyInjection/CompilerPass/RegisterMappingsPassTest.php index 90dd1455921b2..0bb2642a7696e 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/DependencyInjection/CompilerPass/RegisterMappingsPassTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/DependencyInjection/CompilerPass/RegisterMappingsPassTest.php @@ -16,17 +16,17 @@ class RegisterMappingsPassTest extends TestCase public function testNoDriverParmeterException() { $container = $this->createBuilder(); - $this->process($container, array( + $this->process($container, [ 'manager.param.one', 'manager.param.two', - )); + ]); } private function process(ContainerBuilder $container, array $managerParamNames) { $pass = new ConcreteMappingsPass( new Definition('\stdClass'), - array(), + [], $managerParamNames, 'some.%s.metadata_driver' ); diff --git a/src/Symfony/Bridge/Doctrine/Tests/DependencyInjection/DoctrineExtensionTest.php b/src/Symfony/Bridge/Doctrine/Tests/DependencyInjection/DoctrineExtensionTest.php index 5361bab661c10..638e47ef3dffa 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/DependencyInjection/DoctrineExtensionTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/DependencyInjection/DoctrineExtensionTest.php @@ -32,13 +32,13 @@ protected function setUp() $this->extension = $this ->getMockBuilder('Symfony\Bridge\Doctrine\DependencyInjection\AbstractDoctrineExtension') - ->setMethods(array( + ->setMethods([ 'getMappingResourceConfigDirectory', 'getObjectManagerElementName', 'getMappingObjectDefaultName', 'getMappingResourceExtension', 'load', - )) + ]) ->getMock() ; @@ -54,19 +54,19 @@ protected function setUp() */ public function testFixManagersAutoMappingsWithTwoAutomappings() { - $emConfigs = array( - 'em1' => array( + $emConfigs = [ + 'em1' => [ 'auto_mapping' => true, - ), - 'em2' => array( + ], + 'em2' => [ 'auto_mapping' => true, - ), - ); + ], + ]; - $bundles = array( - 'FristBundle' => 'My\FristBundle', + $bundles = [ + 'FirstBundle' => 'My\FirstBundle', 'SecondBundle' => 'My\SecondBundle', - ); + ]; $reflection = new \ReflectionClass(\get_class($this->extension)); $method = $reflection->getMethod('fixManagersAutoMappings'); @@ -77,69 +77,69 @@ public function testFixManagersAutoMappingsWithTwoAutomappings() public function getAutomappingData() { - return array( - array( - array( // no auto mapping on em1 + return [ + [ + [ // no auto mapping on em1 'auto_mapping' => false, - ), - array( // no auto mapping on em2 + ], + [ // no auto mapping on em2 'auto_mapping' => false, - ), - array(), - array(), - ), - array( - array( // no auto mapping on em1 + ], + [], + [], + ], + [ + [ // no auto mapping on em1 'auto_mapping' => false, - ), - array( // auto mapping enabled on em2 + ], + [ // auto mapping enabled on em2 'auto_mapping' => true, - ), - array(), - array( - 'mappings' => array( - 'FristBundle' => array( + ], + [], + [ + 'mappings' => [ + 'FirstBundle' => [ 'mapping' => true, 'is_bundle' => true, - ), - 'SecondBundle' => array( + ], + 'SecondBundle' => [ 'mapping' => true, 'is_bundle' => true, - ), - ), - ), - ), - array( - array( // no auto mapping on em1, but it defines SecondBundle as own + ], + ], + ], + ], + [ + [ // no auto mapping on em1, but it defines SecondBundle as own 'auto_mapping' => false, - 'mappings' => array( - 'SecondBundle' => array( + 'mappings' => [ + 'SecondBundle' => [ 'mapping' => true, 'is_bundle' => true, - ), - ), - ), - array( // auto mapping enabled on em2 + ], + ], + ], + [ // auto mapping enabled on em2 'auto_mapping' => true, - ), - array( - 'mappings' => array( - 'SecondBundle' => array( + ], + [ + 'mappings' => [ + 'SecondBundle' => [ 'mapping' => true, 'is_bundle' => true, - ), - ), - ), - array( - 'mappings' => array( - 'FristBundle' => array( + ], + ], + ], + [ + 'mappings' => [ + 'FirstBundle' => [ 'mapping' => true, 'is_bundle' => true, - ), - ), - ), - ), - ); + ], + ], + ], + ], + ]; } /** @@ -147,15 +147,15 @@ public function getAutomappingData() */ public function testFixManagersAutoMappings(array $originalEm1, array $originalEm2, array $expectedEm1, array $expectedEm2) { - $emConfigs = array( + $emConfigs = [ 'em1' => $originalEm1, 'em2' => $originalEm2, - ); + ]; - $bundles = array( - 'FristBundle' => 'My\FristBundle', + $bundles = [ + 'FirstBundle' => 'My\FirstBundle', 'SecondBundle' => 'My\SecondBundle', - ); + ]; $reflection = new \ReflectionClass(\get_class($this->extension)); $method = $reflection->getMethod('fixManagersAutoMappings'); @@ -163,39 +163,39 @@ public function testFixManagersAutoMappings(array $originalEm1, array $originalE $newEmConfigs = $method->invoke($this->extension, $emConfigs, $bundles); - $this->assertEquals($newEmConfigs['em1'], array_merge(array( + $this->assertEquals($newEmConfigs['em1'], array_merge([ 'auto_mapping' => false, - ), $expectedEm1)); - $this->assertEquals($newEmConfigs['em2'], array_merge(array( + ], $expectedEm1)); + $this->assertEquals($newEmConfigs['em2'], array_merge([ 'auto_mapping' => false, - ), $expectedEm2)); + ], $expectedEm2)); } public function providerBasicDrivers() { - return array( - array('doctrine.orm.cache.apc.class', array('type' => 'apc')), - array('doctrine.orm.cache.apcu.class', array('type' => 'apcu')), - array('doctrine.orm.cache.array.class', array('type' => 'array')), - array('doctrine.orm.cache.xcache.class', array('type' => 'xcache')), - array('doctrine.orm.cache.wincache.class', array('type' => 'wincache')), - array('doctrine.orm.cache.zenddata.class', array('type' => 'zenddata')), - array('doctrine.orm.cache.redis.class', array('type' => 'redis'), array('setRedis')), - array('doctrine.orm.cache.memcached.class', array('type' => 'memcached'), array('setMemcached')), - ); + return [ + ['doctrine.orm.cache.apc.class', ['type' => 'apc']], + ['doctrine.orm.cache.apcu.class', ['type' => 'apcu']], + ['doctrine.orm.cache.array.class', ['type' => 'array']], + ['doctrine.orm.cache.xcache.class', ['type' => 'xcache']], + ['doctrine.orm.cache.wincache.class', ['type' => 'wincache']], + ['doctrine.orm.cache.zenddata.class', ['type' => 'zenddata']], + ['doctrine.orm.cache.redis.class', ['type' => 'redis'], ['setRedis']], + ['doctrine.orm.cache.memcached.class', ['type' => 'memcached'], ['setMemcached']], + ]; } /** * @dataProvider providerBasicDrivers */ - public function testLoadBasicCacheDriver(string $class, array $config, array $expectedCalls = array()) + public function testLoadBasicCacheDriver(string $class, array $config, array $expectedCalls = []) { $container = $this->createContainer(); $cacheName = 'metadata_cache'; - $objectManager = array( + $objectManager = [ 'name' => 'default', 'metadata_cache_driver' => $config, - ); + ]; $this->invokeLoadCacheDriver($objectManager, $container, $cacheName); @@ -219,13 +219,13 @@ public function testServiceCacheDriver() $cacheName = 'metadata_cache'; $container = $this->createContainer(); $definition = new Definition('%doctrine.orm.cache.apc.class%'); - $objectManager = array( + $objectManager = [ 'name' => 'default', - 'metadata_cache_driver' => array( + 'metadata_cache_driver' => [ 'type' => 'service', 'id' => 'service_driver', - ), - ); + ], + ]; $container->setDefinition('service_driver', $definition); @@ -242,12 +242,12 @@ public function testUnrecognizedCacheDriverException() { $cacheName = 'metadata_cache'; $container = $this->createContainer(); - $objectManager = array( + $objectManager = [ 'name' => 'default', - 'metadata_cache_driver' => array( + 'metadata_cache_driver' => [ 'type' => 'unrecognized_type', - ), - ); + ], + ]; $this->invokeLoadCacheDriver($objectManager, $container, $cacheName); } @@ -258,19 +258,19 @@ protected function invokeLoadCacheDriver(array $objectManager, ContainerBuilder $method->setAccessible(true); - $method->invokeArgs($this->extension, array($objectManager, $container, $cacheName)); + $method->invokeArgs($this->extension, [$objectManager, $container, $cacheName]); } /** * @return \Symfony\Component\DependencyInjection\ContainerBuilder */ - protected function createContainer(array $data = array()) + protected function createContainer(array $data = []) { - return new ContainerBuilder(new ParameterBag(array_merge(array( - 'kernel.bundles' => array('FrameworkBundle' => 'Symfony\\Bundle\\FrameworkBundle\\FrameworkBundle'), + return new ContainerBuilder(new ParameterBag(array_merge([ + 'kernel.bundles' => ['FrameworkBundle' => 'Symfony\\Bundle\\FrameworkBundle\\FrameworkBundle'], 'kernel.cache_dir' => __DIR__, 'kernel.container_class' => 'kernel', 'kernel.project_dir' => __DIR__, - ), $data))); + ], $data))); } } diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/BaseUser.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/BaseUser.php new file mode 100644 index 0000000000000..abf8819a4cfc4 --- /dev/null +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/BaseUser.php @@ -0,0 +1,49 @@ +id = $id; + $this->username = $username; + } + + /** + * @return int + */ + public function getId() + { + return $this->id; + } + + /** + * @return string + */ + public function getUsername() + { + return $this->username; + } +} diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoctrineLoaderEntity.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoctrineLoaderEntity.php new file mode 100644 index 0000000000000..4a92edec8fa14 --- /dev/null +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoctrineLoaderEntity.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\Bridge\Doctrine\Tests\Fixtures; + +use Doctrine\ORM\Mapping as ORM; +use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; +use Symfony\Component\Validator\Constraints as Assert; + +/** + * @ORM\Entity + * @UniqueEntity(fields={"alreadyMappedUnique"}) + * + * @author Kévin Dunglas + */ +class DoctrineLoaderEntity +{ + /** + * @ORM\Id + * @ORM\Column + */ + public $id; + + /** + * @ORM\Column(length=20) + */ + public $maxLength; + + /** + * @ORM\Column(length=20) + * @Assert\Length(min=5) + */ + public $mergedMaxLength; + + /** + * @ORM\Column(length=20) + * @Assert\Length(min=1, max=10) + */ + public $alreadyMappedMaxLength; + + /** + * @ORM\Column(unique=true) + */ + public $unique; + + /** + * @ORM\Column(unique=true) + */ + public $alreadyMappedUnique; +} diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleIntIdEntity.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleIntIdEntity.php index d98b0ef93a60d..ff29145e3353f 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleIntIdEntity.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleIntIdEntity.php @@ -25,7 +25,7 @@ class SingleIntIdEntity public $name; /** @Column(type="array", nullable=true) */ - public $phoneNumbers = array(); + public $phoneNumbers = []; public function __construct($id, $name) { diff --git a/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/DoctrineChoiceLoaderTest.php b/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/DoctrineChoiceLoaderTest.php index b24a374fedf59..5a5fba5afaf57 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/DoctrineChoiceLoaderTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/DoctrineChoiceLoaderTest.php @@ -18,6 +18,7 @@ use Symfony\Bridge\Doctrine\Form\ChoiceList\DoctrineChoiceLoader; use Symfony\Bridge\Doctrine\Form\ChoiceList\EntityLoaderInterface; use Symfony\Bridge\Doctrine\Form\ChoiceList\IdReader; +use Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdEntity; use Symfony\Component\Form\ChoiceList\ArrayChoiceList; use Symfony\Component\Form\ChoiceList\Factory\ChoiceListFactoryInterface; @@ -80,10 +81,15 @@ protected function setUp() $this->idReader = $this->getMockBuilder('Symfony\Bridge\Doctrine\Form\ChoiceList\IdReader') ->disableOriginalConstructor() ->getMock(); + $this->idReader->expects($this->any()) + ->method('isSingleId') + ->willReturn(true) + ; + $this->objectLoader = $this->getMockBuilder('Symfony\Bridge\Doctrine\Form\ChoiceList\EntityLoaderInterface')->getMock(); - $this->obj1 = (object) array('name' => 'A'); - $this->obj2 = (object) array('name' => 'B'); - $this->obj3 = (object) array('name' => 'C'); + $this->obj1 = (object) ['name' => 'A']; + $this->obj2 = (object) ['name' => 'B']; + $this->obj3 = (object) ['name' => 'C']; $this->om->expects($this->any()) ->method('getRepository') @@ -104,7 +110,7 @@ public function testLoadChoiceList() $this->idReader ); - $choices = array($this->obj1, $this->obj2, $this->obj3); + $choices = [$this->obj1, $this->obj2, $this->obj3]; $value = function () {}; $choiceList = new ArrayChoiceList($choices, $value); @@ -128,7 +134,7 @@ public function testLoadChoiceListUsesObjectLoaderIfAvailable() $this->objectLoader ); - $choices = array($this->obj1, $this->obj2, $this->obj3); + $choices = [$this->obj1, $this->obj2, $this->obj3]; $choiceList = new ArrayChoiceList($choices); $this->repository->expects($this->never()) @@ -150,20 +156,20 @@ public function testLoadValuesForChoices() $loader = new DoctrineChoiceLoader( $this->om, $this->class, - $this->idReader + null ); - $choices = array($this->obj1, $this->obj2, $this->obj3); + $choices = [$this->obj1, $this->obj2, $this->obj3]; $this->repository->expects($this->once()) ->method('findAll') ->willReturn($choices); - $this->assertSame(array('1', '2'), $loader->loadValuesForChoices(array($this->obj2, $this->obj3))); + $this->assertSame(['1', '2'], $loader->loadValuesForChoices([$this->obj2, $this->obj3])); // no further loads on subsequent calls - $this->assertSame(array('1', '2'), $loader->loadValuesForChoices(array($this->obj2, $this->obj3))); + $this->assertSame(['1', '2'], $loader->loadValuesForChoices([$this->obj2, $this->obj3])); } public function testLoadValuesForChoicesDoesNotLoadIfEmptyChoices() @@ -177,7 +183,7 @@ public function testLoadValuesForChoicesDoesNotLoadIfEmptyChoices() $this->repository->expects($this->never()) ->method('findAll'); - $this->assertSame(array(), $loader->loadValuesForChoices(array())); + $this->assertSame([], $loader->loadValuesForChoices([])); } public function testLoadValuesForChoicesDoesNotLoadIfSingleIntId() @@ -188,10 +194,6 @@ public function testLoadValuesForChoicesDoesNotLoadIfSingleIntId() $this->idReader ); - $this->idReader->expects($this->any()) - ->method('isSingleId') - ->willReturn(true); - $this->repository->expects($this->never()) ->method('findAll'); @@ -200,7 +202,7 @@ public function testLoadValuesForChoicesDoesNotLoadIfSingleIntId() ->with($this->obj2) ->willReturn('2'); - $this->assertSame(array('2'), $loader->loadValuesForChoices(array($this->obj2))); + $this->assertSame(['2'], $loader->loadValuesForChoices([$this->obj2])); } public function testLoadValuesForChoicesLoadsIfSingleIntIdAndValueGiven() @@ -211,19 +213,15 @@ public function testLoadValuesForChoicesLoadsIfSingleIntIdAndValueGiven() $this->idReader ); - $choices = array($this->obj1, $this->obj2, $this->obj3); + $choices = [$this->obj1, $this->obj2, $this->obj3]; $value = function (\stdClass $object) { return $object->name; }; - $this->idReader->expects($this->any()) - ->method('isSingleId') - ->willReturn(true); - $this->repository->expects($this->once()) ->method('findAll') ->willReturn($choices); - $this->assertSame(array('B'), $loader->loadValuesForChoices( - array($this->obj2), + $this->assertSame(['B'], $loader->loadValuesForChoices( + [$this->obj2], $value )); } @@ -236,11 +234,7 @@ public function testLoadValuesForChoicesDoesNotLoadIfValueIsIdReader() $this->idReader ); - $value = array($this->idReader, 'getIdValue'); - - $this->idReader->expects($this->any()) - ->method('isSingleId') - ->willReturn(true); + $value = [$this->idReader, 'getIdValue']; $this->repository->expects($this->never()) ->method('findAll'); @@ -250,8 +244,8 @@ public function testLoadValuesForChoicesDoesNotLoadIfValueIsIdReader() ->with($this->obj2) ->willReturn('2'); - $this->assertSame(array('2'), $loader->loadValuesForChoices( - array($this->obj2), + $this->assertSame(['2'], $loader->loadValuesForChoices( + [$this->obj2], $value )); } @@ -264,17 +258,17 @@ public function testLoadChoicesForValues() $this->idReader ); - $choices = array($this->obj1, $this->obj2, $this->obj3); + $choices = [$this->obj1, $this->obj2, $this->obj3]; $this->repository->expects($this->once()) ->method('findAll') ->willReturn($choices); - $this->assertSame(array($this->obj2, $this->obj3), $loader->loadChoicesForValues(array('1', '2'))); + $this->assertSame([$this->obj2, $this->obj3], $loader->loadChoicesForValues(['1', '2'])); // no further loads on subsequent calls - $this->assertSame(array($this->obj2, $this->obj3), $loader->loadChoicesForValues(array('1', '2'))); + $this->assertSame([$this->obj2, $this->obj3], $loader->loadChoicesForValues(['1', '2'])); } public function testLoadChoicesForValuesDoesNotLoadIfEmptyValues() @@ -288,7 +282,7 @@ public function testLoadChoicesForValuesDoesNotLoadIfEmptyValues() $this->repository->expects($this->never()) ->method('findAll'); - $this->assertSame(array(), $loader->loadChoicesForValues(array())); + $this->assertSame([], $loader->loadChoicesForValues([])); } public function testLoadChoicesForValuesLoadsOnlyChoicesIfSingleIntId() @@ -300,11 +294,7 @@ public function testLoadChoicesForValuesLoadsOnlyChoicesIfSingleIntId() $this->objectLoader ); - $choices = array($this->obj2, $this->obj3); - - $this->idReader->expects($this->any()) - ->method('isSingleId') - ->willReturn(true); + $choices = [$this->obj2, $this->obj3]; $this->idReader->expects($this->any()) ->method('getIdField') @@ -315,19 +305,19 @@ public function testLoadChoicesForValuesLoadsOnlyChoicesIfSingleIntId() $this->objectLoader->expects($this->once()) ->method('getEntitiesByIds') - ->with('idField', array(4 => '3', 7 => '2')) + ->with('idField', [4 => '3', 7 => '2']) ->willReturn($choices); $this->idReader->expects($this->any()) ->method('getIdValue') - ->willReturnMap(array( - array($this->obj2, '2'), - array($this->obj3, '3'), - )); + ->willReturnMap([ + [$this->obj2, '2'], + [$this->obj3, '3'], + ]); $this->assertSame( - array(4 => $this->obj3, 7 => $this->obj2), - $loader->loadChoicesForValues(array(4 => '3', 7 => '2') + [4 => $this->obj3, 7 => $this->obj2], + $loader->loadChoicesForValues([4 => '3', 7 => '2'] )); } @@ -339,19 +329,15 @@ public function testLoadChoicesForValuesLoadsAllIfSingleIntIdAndValueGiven() $this->idReader ); - $choices = array($this->obj1, $this->obj2, $this->obj3); + $choices = [$this->obj1, $this->obj2, $this->obj3]; $value = function (\stdClass $object) { return $object->name; }; - $this->idReader->expects($this->any()) - ->method('isSingleId') - ->willReturn(true); - 8000 $this->repository->expects($this->once()) ->method('findAll') ->willReturn($choices); - $this->assertSame(array($this->obj2), $loader->loadChoicesForValues( - array('B'), + $this->assertSame([$this->obj2], $loader->loadChoicesForValues( + ['B'], $value )); } @@ -365,12 +351,8 @@ public function testLoadChoicesForValuesLoadsOnlyChoicesIfValueIsIdReader() $this->objectLoader ); - $choices = array($this->obj2, $this->obj3); - $value = array($this->idReader, 'getIdValue'); - - $this->idReader->expects($this->any()) - ->method('isSingleId') - ->willReturn(true); + $choices = [$this->obj2, $this->obj3]; + $value = [$this->idReader, 'getIdValue']; $this->idReader->expects($this->any()) ->method('getIdField') @@ -381,16 +363,100 @@ public function testLoadChoicesForValuesLoadsOnlyChoicesIfValueIsIdReader() $this->objectLoader->expects($this->once()) ->method('getEntitiesByIds') - ->with('idField', array('2')) + ->with('idField', ['2']) ->willReturn($choices); $this->idReader->expects($this->any()) ->method('getIdValue') - ->willReturnMap(array( - array($this->obj2, '2'), - array($this->obj3, '3'), - )); + ->willReturnMap([ + [$this->obj2, '2'], + [$this->obj3, '3'], + ]); + + $this->assertSame([$this->obj2], $loader->loadChoicesForValues(['2'], $value)); + } + + /** + * @group legacy + * + * @expectedDeprecation Not explicitly passing an instance of "Symfony\Bridge\Doctrine\Form\ChoiceList\IdReader" to "Symfony\Bridge\Doctrine\Form\ChoiceList\DoctrineChoiceLoader" when it can optimize single id entity "%s" has been deprecated in 4.3 and will not apply any optimization in 5.0. + */ + public function testLoaderWithoutIdReaderCanBeOptimized() + { + $obj1 = new SingleIntIdEntity('1', 'one'); + $obj2 = new SingleIntIdEntity('2', 'two'); + + $metadata = $this->createMock(ClassMetadata::class); + $metadata->expects($this->once()) + ->method('getIdentifierFieldNames') + ->willReturn(['idField']) + ; + $metadata->expects($this->any()) + ->method('getIdentifierValues') + ->willReturnCallback(function ($obj) use ($obj1, $obj2) { + if ($obj === $obj1) { + return ['idField' => '1']; + } + if ($obj === $obj2) { + return ['idField' => '2']; + } + + return null; + }) + ; + + $this->om = $this->createMock(ObjectManager::class); + $this->om->expects($this->once()) + ->method('getClassMetadata') + ->with(SingleIntIdEntity::class) + ->willReturn($metadata) + ; + $this->om->expects($this->any()) + ->method('contains') + ->with($this->isInstanceOf(SingleIntIdEntity::class)) + ->willReturn(true) + ; + + $loader = new DoctrineChoiceLoader( + $this->om, + SingleIntIdEntity::class, + null, + $this->objectLoader + ); + + $choices = [$obj1, $obj2]; + + $this->repository->expects($this->never()) + ->method('findAll'); + + $this->objectLoader->expects($this->once()) + ->method('getEntitiesByIds') + ->with('idField', ['1']) + ->willReturn($choices); + + $this->assertSame([$obj1], $loader->loadChoicesForValues(['1'])); + } + + /** + * @group legacy + * + * @deprecationMessage Passing an instance of "Symfony\Bridge\Doctrine\Form\ChoiceList\IdReader" to "Symfony\Bridge\Doctrine\Form\ChoiceList\DoctrineChoiceLoader" with an entity class "stdClass" that has a composite id is deprecated since Symfony 4.3 and will throw an exception in 5.0. + */ + public function testPassingIdReaderWithoutSingleIdEntity() + { + $idReader = $this->createMock(IdReader::class); + $idReader->expects($this->once()) + ->method('isSingleId') + ->willReturn(false) + ; + + $loader = new DoctrineChoiceLoader( + $this->om, + $this->class, + $idReader, + $this->objectLoader + ); - $this->assertSame(array($this->obj2), $loader->loadChoicesForValues(array('2'), $value)); + $this->assertInstanceOf(DoctrineChoiceLoader::class, $loader); } } diff --git a/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/ORMQueryBuilderLoaderTest.php b/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/ORMQueryBuilderLoaderTest.php index 211cb12e4df2c..3abdb3578aaf9 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/ORMQueryBuilderLoaderTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/ORMQueryBuilderLoaderTest.php @@ -34,17 +34,17 @@ protected function checkIdentifierType($classname, $expectedType) $em = DoctrineTestHelper::createTestEntityManager(); $query = $this->getMockBuilder('QueryMock') - ->setMethods(array('setParameter', 'getResult', 'getSql', '_doExecute')) + ->setMethods(['setParameter', 'getResult', 'getSql', '_doExecute']) ->getMock(); $query->expects($this->once()) ->method('setParameter') - ->with('ORMQueryBuilderLoader_getEntitiesByIds_id', array(1, 2), $expectedType) + ->with('ORMQueryBuilderLoader_getEntitiesByIds_id', [1, 2], $expectedType) ->willReturn($query); $qb = $this->getMockBuilder('Doctrine\ORM\QueryBuilder') - ->setConstructorArgs(array($em)) - ->setMethods(array('getQuery')) + ->setConstructorArgs([$em]) + ->setMethods(['getQuery']) ->getMock(); $qb->expects($this->once()) @@ -55,7 +55,7 @@ protected function checkIdentifierType($classname, $expectedType) ->from($classname, 'e'); $loader = new ORMQueryBuilderLoader($qb); - $loader->getEntitiesByIds('id', array(1, 2)); + $loader->getEntitiesByIds('id', [1, 2]); } public function testFilterNonIntegerValues() @@ -63,17 +63,17 @@ public function testFilterNonIntegerValues() $em = DoctrineTestHelper::createTestEntityManager(); $query = $this->getMockBuilder('QueryMock') - ->setMethods(array('setParameter', 'getResult', 'getSql', '_doExecute')) + ->setMethods(['setParameter', 'getResult', 'getSql', '_doExecute']) ->getMock(); $query->expects($this->once()) ->method('setParameter') - ->with('ORMQueryBuilderLoader_getEntitiesByIds_id', array(1, 2, 3, '9223372036854775808'), Connection::PARAM_INT_ARRAY) + ->with('ORMQueryBuilderLoader_getEntitiesByIds_id', [1, 2, 3, '9223372036854775808'], Connection::PARAM_INT_ARRAY) ->willReturn($query); $qb = $this->getMockBuilder('Doctrine\ORM\QueryBuilder') - ->setConstructorArgs(array($em)) - ->setMethods(array('getQuery')) + ->setConstructorArgs([$em]) + ->setMethods(['getQuery']) ->getMock(); $qb->expects($this->once()) @@ -84,7 +84,7 @@ public function testFilterNonIntegerValues() ->from('Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdEntity', 'e'); $loader = new ORMQueryBuilderLoader($qb); - $loader->getEntitiesByIds('id', array(1, '', 2, 3, 'foo', '9223372036854775808')); + $loader->getEntitiesByIds('id', [1, '', 2, 3, 'foo', '9223372036854775808']); } /** @@ -95,17 +95,17 @@ public function testFilterEmptyUuids($entityClass) $em = DoctrineTestHelper::createTestEntityManager(); $query = $this->getMockBuilder('QueryMock') - ->setMethods(array('setParameter', 'getResult', 'getSql', '_doExecute')) + ->setMethods(['setParameter', 'getResult', 'getSql', '_doExecute']) ->getMock(); $query->expects($this->once()) ->method('setParameter') - ->with('ORMQueryBuilderLoader_getEntitiesByIds_id', array('71c5fd46-3f16-4abb-bad7-90ac1e654a2d', 'b98e8e11-2897-44df-ad24-d2627eb7f499'), Connection::PARAM_STR_ARRAY) + ->with('ORMQueryBuilderLoader_getEntitiesByIds_id', ['71c5fd46-3f16-4abb-bad7-90ac1e654a2d', 'b98e8e11-2897-44df-ad24-d2627eb7f499'], Connection::PARAM_STR_ARRAY) ->willReturn($query); $qb = $this->getMockBuilder('Doctrine\ORM\QueryBuilder') - ->setConstructorArgs(array($em)) - ->setMethods(array('getQuery')) + ->setConstructorArgs([$em]) + ->setMethods(['getQuery']) ->getMock(); $qb->expects($this->once()) @@ -116,7 +116,7 @@ public function testFilterEmptyUuids($entityClass) ->from($entityClass, 'e'); $loader = new ORMQueryBuilderLoader($qb); - $loader->getEntitiesByIds('id', array('71c5fd46-3f16-4abb-bad7-90ac1e654a2d', '', 'b98e8e11-2897-44df-ad24-d2627eb7f499')); + $loader->getEntitiesByIds('id', ['71c5fd46-3f16-4abb-bad7-90ac1e654a2d', '', 'b98e8e11-2897-44df-ad24-d2627eb7f499']); } public function testEmbeddedIdentifierName() @@ -130,17 +130,17 @@ public function testEmbeddedIdentifierName() $em = DoctrineTestHelper::createTestEntityManager(); $query = $this->getMockBuilder('QueryMock') - ->setMethods(array('setParameter', 'getResult', 'getSql', '_doExecute')) + ->setMethods(['setParameter', 'getResult', 'getSql', '_doExecute']) ->getMock(); $query->expects($this->once()) ->method('setParameter') - ->with('ORMQueryBuilderLoader_getEntitiesByIds_id_value', array(1, 2, 3), Connection::PARAM_INT_ARRAY) + ->with('ORMQueryBuilderLoader_getEntitiesByIds_id_value', [1, 2, 3], Connection::PARAM_INT_ARRAY) ->willReturn($query); $qb = $this->getMockBuilder('Doctrine\ORM\QueryBuilder') - ->setConstructorArgs(array($em)) - ->setMethods(array('getQuery')) + ->setConstructorArgs([$em]) + ->setMethods(['getQuery']) ->getMock(); $qb->expects($this->once()) ->method('getQuery') @@ -150,14 +150,14 @@ public function testEmbeddedIdentifierName() ->from('Symfony\Bridge\Doctrine\Tests\Fixtures\EmbeddedIdentifierEntity', 'e'); $loader = new ORMQueryBuilderLoader($qb); - $loader->getEntitiesByIds('id.value', array(1, '', 2, 3, 'foo')); + $loader->getEntitiesByIds('id.value', [1, '', 2, 3, 'foo']); } public function provideGuidEntityClasses() { - return array( - array('Symfony\Bridge\Doctrine\Tests\Fixtures\GuidIdEntity'), - array('Symfony\Bridge\Doctrine\Tests\Fixtures\UuidIdEntity'), - ); + return [ + ['Symfony\Bridge\Doctrine\Tests\Fixtures\GuidIdEntity'], + ['Symfony\Bridge\Doctrine\Tests\Fixtures\UuidIdEntity'], + ]; } } diff --git a/src/Symfony/Bridge/Doctrine/Tests/Form/DataTransformer/CollectionToArrayTransformerTest.php b/src/Symfony/Bridge/Doctrine/Tests/Form/DataTransformer/CollectionToArrayTransformerTest.php index fa3ff911ad355..e6e85f4d3f7df 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Form/DataTransformer/CollectionToArrayTransformerTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Form/DataTransformer/CollectionToArrayTransformerTest.php @@ -32,10 +32,10 @@ protected function setUp() public function testTransform() { - $array = array( + $array = [ 2 => 'foo', 3 => 'bar', - ); + ]; $this->assertSame($array, $this->transformer->transform(new ArrayCollection($array))); } @@ -49,17 +49,17 @@ public function testTransform() */ public function testTransformArray() { - $array = array( + $array = [ 2 => 'foo', 3 => 'bar', - ); + ]; $this->assertSame($array, $this->transformer->transform($array)); } public function testTransformNull() { - $this->assertSame(array(), $this->transformer->transform(null)); + $this->assertSame([], $this->transformer->transform(null)); } /** @@ -72,10 +72,10 @@ public function testTransformExpectsArrayOrCollection() public function testReverseTransform() { - $array = array( + $array = [ 2 => 'foo', 3 => 'bar', - ); + ]; $this->assertEquals(new ArrayCollection($array), $this->transformer->reverseTransform($array)); } diff --git a/src/Symfony/Bridge/Doctrine/Tests/Form/DoctrineOrmTypeGuesserTest.php b/src/Symfony/Bridge/Doctrine/Tests/Form/DoctrineOrmTypeGuesserTest.php index 0eda4a3ba6f0a..c323385ff1929 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Form/DoctrineOrmTypeGuesserTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Form/DoctrineOrmTypeGuesserTest.php @@ -29,54 +29,54 @@ public function testRequiredGuesser($classMetadata, $expected) public function requiredProvider() { - $return = array(); + $return = []; // Simple field, not nullable $classMetadata = $this->getMockBuilder('Doctrine\ORM\Mapping\ClassMetadata')->disableOriginalConstructor()->getMock(); $classMetadata->fieldMappings['field'] = true; $classMetadata->expects($this->once())->method('isNullable')->with('field')->will($this->returnValue(false)); - $return[] = array($classMetadata, new ValueGuess(true, Guess::HIGH_CONFIDENCE)); + $return[] = [$classMetadata, new ValueGuess(true, Guess::HIGH_CONFIDENCE)]; // Simple field, nullable $classMetadata = $this->getMockBuilder('Doctrine\ORM\Mapping\ClassMetadata')->disableOriginalConstructor()->getMock(); $classMetadata->fieldMappings['field'] = true; $classMetadata->expects($this->once())->method('isNullable')->with('field')->will($this->returnValue(true)); - $return[] = array($classMetadata, new ValueGuess(false, Guess::MEDIUM_CONFIDENCE)); + $return[] = [$classMetadata, new ValueGuess(false, Guess::MEDIUM_CONFIDENCE)]; // One-to-one, nullable (by default) $classMetadata = $this->getMockBuilder('Doctrine\ORM\Mapping\ClassMetadata')->disableOriginalConstructor()->getMock(); $classMetadata->expects($this->once())->method('isAssociationWithSingleJoinColumn')->with('field')->will($this->returnValue(true)); - $mapping = array('joinColumns' => array(array())); + $mapping = ['joinColumns' => [[]]]; $classMetadata->expects($this->once())->method('getAssociationMapping')->with('field')->will($this->returnValue($mapping)); - $return[] = array($classMetadata, new ValueGuess(false, Guess::HIGH_CONFIDENCE)); + $return[] = [$classMetadata, new ValueGuess(false, Guess::HIGH_CONFIDENCE)]; // One-to-one, nullable (explicit) $classMetadata = $this->getMockBuilder('Doctrine\ORM\Mapping\ClassMetadata')->disableOriginalConstructor()->getMock(); $classMetadata->expects($this->once())->method('isAssociationWithSingleJoinColumn')->with('field')->will($this->returnValue(true)); - $mapping = array('joinColumns' => array(array('nullable' => true))); + $mapping = ['joinColumns' => [['nullable' => true]]]; $classMetadata->expects($this->once())->method('getAssociationMapping')->with('field')->will($this->returnValue($mapping)); - $return[] = array($classMetadata, new ValueGuess(false, Guess::HIGH_CONFIDENCE)); + $return[] = [$classMetadata, new ValueGuess(false, Guess::HIGH_CONFIDENCE)]; // One-to-one, not nullable $classMetadata = $this->getMockBuilder('Doctrine\ORM\Mapping\ClassMetadata')->disableOriginalConstructor()->getMock(); $classMetadata->expects($this->once())->method('isAssociationWithSingleJoinColumn')->with('field')->will($this->returnValue(true)); - $mapping = array('joinColumns' => array(array('nullable' => false))); + $mapping = ['joinColumns' => [['nullable' => false]]]; $classMetadata->expects($this->once())->method('getAssociationMapping')->with('field')->will($this->returnValue($mapping)); - $return[] = array($classMetadata, new ValueGuess(true, Guess::HIGH_CONFIDENCE)); + $return[] = [$classMetadata, new ValueGuess(true, Guess::HIGH_CONFIDENCE)]; // One-to-many, no clue $classMetadata = $this->getMockBuilder('Doctrine\ORM\Mapping\ClassMetadata')->disableOriginalConstructor()->getMock(); $classMetadata->expects($this->once())->method('isAssociationWithSingleJoinColumn')->with('field')->will($this->returnValue(false)); - $return[] = array($classMetadata, null); + $return[] = [$classMetadata, null]; return $return; } @@ -87,7 +87,7 @@ private function getGuesser(ClassMetadata $classMetadata) $em->expects($this->once())->method('getClassMetaData')->with('TestEntity')->will($this->returnValue($classMetadata)); $registry = $this->getMockBuilder('Doctrine\Common\Persistence\ManagerRegistry')->getMock(); - $registry->expects($this->once())->method('getManagers')->will($this->returnValue(array($em))); + $registry->expects($this->once())->method('getManagers')->will($this->returnValue([$em])); return new DoctrineOrmTypeGuesser($registry); } diff --git a/src/Symfony/Bridge/Doctrine/Tests/Form/EventListener/MergeDoctrineCollectionListenerTest.php b/src/Symfony/Bridge/Doctrine/Tests/Form/EventListener/MergeDoctrineCollectionListenerTest.php index 52ea54dfefbc5..757cdc3934c99 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Form/EventListener/MergeDoctrineCollectionListenerTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Form/EventListener/MergeDoctrineCollectionListenerTest.php @@ -30,7 +30,7 @@ class MergeDoctrineCollectionListenerTest extends TestCase protected function setUp() { - $this->collection = new ArrayCollection(array('test')); + $this->collection = new ArrayCollection(['test']); $this->dispatcher = new EventDispatcher(); $this->factory = $this->getMockBuilder('Symfony\Component\Form\FormFactoryInterface')->getMock(); $this->form = $this->getBuilder() @@ -45,14 +45,14 @@ protected function tearDown() $this->form = null; } - protected function getBuilder($name = 'name') + protected function getBuilder() { - return new FormBuilder($name, null, $this->dispatcher, $this->factory); + return new FormBuilder('name', null, $this->dispatcher, $this->factory); } - protected function getForm($name = 'name') + protected function getForm() { - return $this->getBuilder($name) + return $this->getBuilder() ->setData($this->collection) ->addEventSubscriber(new MergeDoctrineCollectionListener()) ->getForm(); @@ -60,10 +60,10 @@ protected function getForm($name = 'name') public function testOnSubmitDoNothing() { - $submittedData = array('test'); + $submittedData = ['test']; $event = new FormEvent($this->getForm(), $submittedData); - $this->dispatcher->dispatch(FormEvents::SUBMIT, $event); + $this->dispatcher->dispatch($event, FormEvents::SUBMIT); $this->assertTrue($this->collection->contains('test')); $this->assertSame(1, $this->collection->count()); @@ -71,10 +71,10 @@ public function testOnSubmitDoNothing() public function testOnSubmitNullClearCollection() { - $submittedData = array(); + $submittedData = []; $event = new FormEvent($this->getForm(), $submittedData); - $this->dispatcher->dispatch(FormEvents::SUBMIT, $event); + $this->dispatcher->dispatch($event, FormEvents::SUBMIT); $this->assertTrue($this->collection->isEmpty()); } diff --git a/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypePerformanceTest.php b/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypePerformanceTest.php index f4f7effa61c20..5012171542f7b 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypePerformanceTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypePerformanceTest.php @@ -30,6 +30,8 @@ class EntityTypePerformanceTest extends FormPerformanceTestCase */ private $em; + protected static $supportedFeatureSetVersion = 304; + protected function getExtensions() { $manager = $this->getMockBuilder('Doctrine\Common\Persistence\ManagerRegistry')->getMock(); @@ -42,10 +44,10 @@ protected function getExtensions() ->method('getManagerForClass') ->will($this->returnValue($this->em)); - return array( + return [ new CoreExtension(), new DoctrineOrmExtension($manager), - ); + ]; } protected function setUp() @@ -55,9 +57,9 @@ protected function setUp() parent::setUp(); $schemaTool = new SchemaTool($this->em); - $classes = array( + $classes = [ $this->em->getClassMetadata(self::ENTITY_CLASS), - ); + ]; try { $schemaTool->dropSchema($classes); @@ -90,9 +92,9 @@ public function testCollapsedEntityField() $this->setMaxRunningTime(1); for ($i = 0; $i < 40; ++$i) { - $form = $this->factory->create('Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + $form = $this->factory->create('Symfony\Bridge\Doctrine\Form\Type\EntityType', null, [ 'class' => self::ENTITY_CLASS, - )); + ]); // force loading of the choice list $form->createView(); @@ -108,10 +110,10 @@ public function testCollapsedEntityFieldWithChoices() $this->setMaxRunningTime(1); for ($i = 0; $i < 40; ++$i) { - $form = $this->factory->create('Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + $form = $this->factory->create('Symfony\Bridge\Doctrine\Form\Type\EntityType', null, [ 'class' => self::ENTITY_CLASS, 'choices' => $choices, - )); + ]); // force loading of the choice list $form->createView(); @@ -127,10 +129,10 @@ public function testCollapsedEntityFieldWithPreferredChoices() $this->setMaxRunningTime(1); for ($i = 0; $i < 40; ++$i) { - $form = $this->factory->create('Symfony\Bridge\Doctrine\Form\Type\EntityType', null, array( + $form = $this->factory->create('Symfony\Bridge\Doctrine\Form\Type\EntityType', null, [ 'class' => self::ENTITY_CLASS, 'preferred_choices' => $choices, - )); + ]); // force loading of the choice list $form->createView(); diff --git a/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php b/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php index d60992fcf119a..3fe86b19a0149 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php @@ -57,6 +57,8 @@ class EntityTypeTest extends BaseTypeTest */ private $emRegistry; + protected static $supportedFeatureSetVersion = 304; + protected function setUp() { $this->em = DoctrineTestHelper::createTestEntityManager(); @@ -65,7 +67,7 @@ protected function setUp() parent::setUp(); $schemaTool = new SchemaTool($this->em); - $classes = array( + $classes = [ $this->em->getClassMetadata(self::ITEM_GROUP_CLASS), $this->em->getClassMetadata(self::SINGLE_IDENT_CLASS), $this->em->getClassMetadata(self::SINGLE_IDENT_NO_TO_STRING_CLASS), @@ -74,7 +76,7 @@ protected function setUp() $this->em->getClassMetadata(self::SINGLE_STRING_CASTABLE_IDENT_CLASS), $this->em->getClassMetadata(self::COMPOSITE_IDENT_CLASS), $this->em->getClassMetadata(self::COMPOSITE_STRING_IDENT_CLASS), - ); + ]; try { $schemaTool->dropSchema($classes); @@ -97,9 +99,9 @@ protected function tearDown() protected function getExtensions() { - return array_merge(parent::getExtensions(), array( + return array_merge(parent::getExtensions(), [ new DoctrineOrmExtension($this->emRegistry), - )); + ]); } protected function persist(array $entities) @@ -126,9 +128,9 @@ public function testClassOptionIsRequired() */ public function testInvalidClassOption() { - $this->factory->createNamed('name', static::TESTED_TYPE, null, array( + $this->factory->createNamed('name', static::TESTED_TYPE, null, [ 'class' => 'foo', - )); + ]); } public function testSetDataToUninitializedEntityWithNonRequired() @@ -136,16 +138,16 @@ public function testSetDataToUninitializedEntityWithNonRequired() $entity1 = new SingleIntIdEntity(1, 'Foo'); $entity2 = new SingleIntIdEntity(2, 'Bar'); - $this->persist(array($entity1, $entity2)); + $this->persist([$entity1, $entity2]); - $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, [ 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, 'required' => false, 'choice_label' => 'name', - )); + ]); - $this->assertEquals(array(1 => new ChoiceView($entity1, '1', 'Foo'), 2 => new ChoiceView($entity2, '2', 'Bar')), $field->createView()->vars['choices']); + $this->assertEquals([1 => new ChoiceView($entity1, '1', 'Foo'), 2 => new ChoiceView($entity2, '2', 'Bar')], $field->createView()->vars['choices']); } public function testSetDataToUninitializedEntityWithNonRequiredToString() @@ -153,16 +155,16 @@ public function testSetDataToUninitializedEntityWithNonRequiredToString() $entity1 = new SingleIntIdEntity(1, 'Foo'); $entity2 = new SingleIntIdEntity(2, 'Bar'); - $this->persist(array($entity1, $entity2)); + $this->persist([$entity1, $entity2]); - $view = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( + $view = $this->factory->createNamed('name', static::TESTED_TYPE, null, [ 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, 'required' => false, - )) + ]) ->createView(); - $this->assertEquals(array(1 => new ChoiceView($entity1, '1', 'Foo'), 2 => new ChoiceView($entity2, '2', 'Bar')), $view->vars['choices']); + $this->assertEquals([1 => new ChoiceView($entity1, '1', 'Foo'), 2 => new ChoiceView($entity2, '2', 'Bar')], $view->vars['choices']); } public function testSetDataToUninitializedEntityWithNonRequiredQueryBuilder() @@ -170,19 +172,19 @@ public function testSetDataToUninitializedEntityWithNonRequiredQueryBuilder() $entity1 = new SingleIntIdEntity(1, 'Foo'); $entity2 = new SingleIntIdEntity(2, 'Bar'); - $this->persist(array($entity1, $entity2)); + $this->persist([$entity1, $entity2]); $qb = $this->em->createQueryBuilder()->select('e')->from(self::SINGLE_IDENT_CLASS, 'e'); - $view = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( + $view = $this->factory->createNamed('name', static::TESTED_TYPE, null, [ 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, 'required' => false, 'choice_label' => 'name', 'query_builder' => $qb, - )) + ]) ->createView(); - $this->assertEquals(array(1 => new ChoiceView($entity1, '1', 'Foo'), 2 => new ChoiceView($entity2, '2', 'Bar')), $view->vars['choices']); + $this->assertEquals([1 => new ChoiceView($entity1, '1', 'Foo'), 2 => new ChoiceView($entity2, '2', 'Bar')], $view->vars['choices']); } /** @@ -190,11 +192,11 @@ public function testSetDataToUninitializedEntityWithNonRequiredQueryBuilder() */ public function testConfigureQueryBuilderWithNonQueryBuilderAndNonClosure() { - $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, [ 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, 'query_builder' => new \stdClass(), - )); + ]); } /** @@ -202,13 +204,13 @@ public function testConfigureQueryBuilderWithNonQueryBuilderAndNonClosure() */ public function testConfigureQueryBuilderWithClosureReturningNonQueryBuilder() { - $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, [ 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, 'query_builder' => function () { return new \stdClass(); }, - )); + ]); $field->submit('2'); } @@ -218,26 +220,26 @@ public function testConfigureQueryBuilderWithClosureReturningNullUseDefault() $entity1 = new SingleIntIdEntity(1, 'Foo'); $entity2 = new SingleIntIdEntity(2, 'Bar'); - $this->persist(array($entity1, $entity2)); + $this->persist([$entity1, $entity2]); - $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, [ 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, 'query_builder' => function () { return; }, - )); + ]); - $this->assertEquals(array(1 => new ChoiceView($entity1, '1', 'Foo'), 2 => new ChoiceView($entity2, '2', 'Bar')), $field->createView()->vars['choices']); + $this->assertEquals([1 => new ChoiceView($entity1, '1', 'Foo'), 2 => new ChoiceView($entity2, '2', 'Bar')], $field->createView()->vars['choices']); } public function testSetDataSingleNull() { - $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, [ 'multiple' => false, 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, - )); + ]); $field->setData(null); $this->assertNull($field->getData()); @@ -246,30 +248,30 @@ public function testSetDataSingleNull() public function testSetDataMultipleExpandedNull() { - $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, [ 'multiple' => true, 'expanded' => true, 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, - )); + ]); $field->setData(null); $this->assertNull($field->getData()); - $this->assertSame(array(), $field->getViewData()); + $this->assertSame([], $field->getViewData()); } public function testSetDataMultipleNonExpandedNull() { - $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, [ 'multiple' => true, 'expanded' => false, 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, - )); + ]); $field->setData(null); $this->assertNull($field->getData()); - $this->assertSame(array(), $field->getViewData()); + $this->assertSame([], $field->getViewData()); } public function testSubmitSingleNonExpandedSingleIdentifier() @@ -277, 8000 15 +279,15 @@ public function testSubmitSingleNonExpandedSingleIdentifier() $entity1 = new SingleIntIdEntity(1, 'Foo'); $entity2 = new SingleIntIdEntity(2, 'Bar'); - $this->persist(array($entity1, $entity2)); + $this->persist([$entity1, $entity2]); - $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, [ 'multiple' => false, 'expanded' => false, 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, 'choice_label' => 'name', - )); + ]); $field->submit('2'); @@ -302,15 +304,15 @@ public function testSubmitSingleNonExpandedSingleAssocIdentifier() $entity1 = new SingleAssociationToIntIdEntity($innerEntity1, 'Foo'); $entity2 = new SingleAssociationToIntIdEntity($innerEntity2, 'Bar'); - $this->persist(array($innerEntity1, $innerEntity2, $entity1, $entity2)); + $this->persist([$innerEntity1, $innerEntity2, $entity1, $entity2]); - $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, [ 'multiple' => false, 'expanded' => false, 'em' => 'default', 'class' => self::SINGLE_ASSOC_IDENT_CLASS, 'choice_label' => 'name', - )); + ]); $field->submit('2'); @@ -324,15 +326,15 @@ public function testSubmitSingleNonExpandedCompositeIdentifier() $entity1 = new CompositeIntIdEntity(10, 20, 'Foo'); $entity2 = new CompositeIntIdEntity(30, 40, 'Bar'); - $this->persist(array($entity1, $entity2)); + $this->persist([$entity1, $entity2]); - $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, [ 'multiple' => false, 'expanded' => false, 'em' => 'default', 'class' => self::COMPOSITE_IDENT_CLASS, 'choice_label' => 'name', - )); + ]); // the collection key is used here $field->submit('1'); @@ -348,23 +350,23 @@ public function testSubmitMultipleNonExpandedSingleIdentifier() $entity2 = new SingleIntIdEntity(2, 'Bar'); $entity3 = new SingleIntIdEntity(3, 'Baz'); - $this->persist(array($entity1, $entity2, $entity3)); + $this->persist([$entity1, $entity2, $entity3]); - $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, [ 'multiple' => true, 'expanded' => false, 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, 'choice_label' => 'name', - )); + ]); - $field->submit(array('1', '3')); + $field->submit(['1', '3']); - $expected = new ArrayCollection(array($entity1, $entity3)); + $expected = new ArrayCollection([$entity1, $entity3]); $this->assertTrue($field->isSynchronized()); $this->assertEquals($expected, $field->getData()); - $this->assertSame(array('1', '3'), $field->getViewData()); + $this->assertSame(['1', '3'], $field->getViewData()); } public function testSubmitMultipleNonExpandedSingleAssocIdentifier() @@ -377,23 +379,23 @@ public function testSubmitMultipleNonExpandedSingleAssocIdentifier() $entity2 = new SingleAssociationToIntIdEntity($innerEntity2, 'Bar'); $entity3 = new SingleAssociationToIntIdEntity($innerEntity3, 'Baz'); - $this->persist(array($innerEntity1, $innerEntity2, $innerEntity3, $entity1, $entity2, $entity3)); + $this->persist([$innerEntity1, $innerEntity2, $innerEntity3, $entity1, $entity2, $entity3]); - $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, [ 'multiple' => true, 'expanded' => false, 'em' => 'default', 'class' => self::SINGLE_ASSOC_IDENT_CLASS, 'choice_label' => 'name', - )); + ]); - $field->submit(array('1', '3')); + $field->submit(['1', '3']); - $expected = new ArrayCollection(array($entity1, $entity3)); + $expected = new ArrayCollection([$entity1, $entity3]); $this->assertTrue($field->isSynchronized()); $this->assertEquals($expected, $field->getData()); - $this->assertSame(array('1', '3'), $field->getViewData()); + $this->assertSame(['1', '3'], $field->getViewData()); } public function testSubmitMultipleNonExpandedSingleIdentifierForExistingData() @@ -402,29 +404,29 @@ public function testSubmitMultipleNonExpandedSingleIdentifierForExistingData() $entity2 = new SingleIntIdEntity(2, 'Bar'); $entity3 = new SingleIntIdEntity(3, 'Baz'); - $this->persist(array($entity1, $entity2, $entity3)); + $this->persist([$entity1, $entity2, $entity3]); - $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, [ 'multiple' => true, 'expanded' => false, 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, 'choice_label' => 'name', - )); + ]); - $existing = new ArrayCollection(array(0 => $entity2)); + $existing = new ArrayCollection([0 => $entity2]); $field->setData($existing); - $field->submit(array('1', '3')); + $field->submit(['1', '3']); // entry with index 0 ($entity2) was replaced - $expected = new ArrayCollection(array(0 => $entity1, 1 => $entity3)); + $expected = new ArrayCollection([0 => $entity1, 1 => $entity3]); $this->assertTrue($field->isSynchronized()); $this->assertEquals($expected, $field->getData()); // same object still, useful if it is a PersistentCollection $this->assertSame($existing, $field->getData()); - $this->assertSame(array('1', '3'), $field->getViewData()); + $this->assertSame(['1', '3'], $field->getViewData()); } public function testSubmitMultipleNonExpandedCompositeIdentifier() @@ -433,24 +435,24 @@ public function testSubmitMultipleNonExpandedCompositeIdentifier() $entity2 = new CompositeIntIdEntity(30, 40, 'Bar'); $entity3 = new CompositeIntIdEntity(50, 60, 'Baz'); - $this->persist(array($entity1, $entity2, $entity3)); + $this->persist([$entity1, $entity2, $entity3]); - $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, [ 'multiple' => true, 'expanded' => false, 'em' => 'default', 'class' => self::COMPOSITE_IDENT_CLASS, 'choice_label' => 'name', - )); + ]); // because of the composite key collection keys are used - $field->submit(array('0', '2')); + $field->submit(['0', '2']); - $expected = new ArrayCollection(array($entity1, $entity3)); + $expected = new ArrayCollection([$entity1, $entity3]); $this->assertTrue($field->isSynchronized()); $this->assertEquals($expected, $field->getData()); - $this->assertSame(array('0', '2'), $field->getViewData()); + $this->assertSame(['0', '2'], $field->getViewData()); } public function testSubmitMultipleNonExpandedCompositeIdentifierExistingData() @@ -459,29 +461,29 @@ public function testSubmitMultipleNonExpandedCompositeIdentifierExistingData() $entity2 = new CompositeIntIdEntity(30, 40, 'Bar'); $entity3 = new CompositeIntIdEntity(50, 60, 'Baz'); - $this->persist(array($entity1, $entity2, $entity3)); + $this->persist([$entity1, $entity2, $entity3]); - $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, [ 'multiple' => true, 'expanded' => false, 'em' => 'default', 'class' => self::COMPOSITE_IDENT_CLASS, 'choice_label' => 'name', - )); + ]); - $existing = new ArrayCollection(array(0 => $entity2)); + $existing = new ArrayCollection([0 => $entity2]); $field->setData($existing); - $field->submit(array('0', '2')); + $field->submit(['0', '2']); // entry with index 0 ($entity2) was replaced - $expected = new ArrayCollection(array(0 => $entity1, 1 => $entity3)); + $expected = new ArrayCollection([0 => $entity1, 1 => $entity3]); $this->assertTrue($field->isSynchronized()); $this->assertEquals($expected, $field->getData()); // same object still, useful if it is a PersistentCollection $this->assertSame($existing, $field->getData()); - $this->assertSame(array('0', '2'), $field->getViewData()); + $this->assertSame(['0', '2'], $field->getViewData()); } public function testSubmitSingleExpanded() @@ -489,15 +491,15 @@ public function testSubmitSingleExpanded() $entity1 = new SingleIntIdEntity(1, 'Foo'); $entity2 = new SingleIntIdEntity(2, 'Bar'); - $this->persist(array($entity1, $entity2)); + $this->persist([$entity1, $entity2]); - $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, [ 'multiple' => false, 'expanded' => true, 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, 'choice_label' => 'name', - )); + ]); $field->submit('2'); @@ -515,19 +517,19 @@ public function testSubmitMultipleExpanded() $entity2 = new SingleIntIdEntity(2, 'Bar'); $entity3 = new SingleIntIdEntity(3, 'Bar'); - $this->persist(array($entity1, $entity2, $entity3)); + $this->persist([$entity1, $entity2, $entity3]); - $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, [ 'multiple' => true, 'expanded' => true, 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, 'choice_label' => 'name', - )); + ]); - $field->submit(array('1', '3')); + $field->submit(['1', '3']); - $expected = new ArrayCollection(array($entity1, $entity3)); + $expected = new ArrayCollection([$entity1, $entity3]); $this->assertTrue($field->isSynchronized()); $this->assertEquals($expected, $field->getData()); @@ -544,19 +546,19 @@ public function testSubmitMultipleExpandedWithNegativeIntegerId() $entity1 = new SingleIntIdEntity(-1, 'Foo'); $entity2 = new SingleIntIdEntity(2, 'Bar'); - $this->persist(array($entity1, $entity2)); + $this->persist([$entity1, $entity2]); - $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, [ 'multiple' => true, 'expanded' => true, 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, 'choice_label' => 'name', - )); + ]); - $field->submit(array('-1')); + $field->submit(['-1']); - $expected = new ArrayCollection(array($entity1)); + $expected = new ArrayCollection([$entity1]); $this->assertTrue($field->isSynchronized()); $this->assertEquals($expected, $field->getData()); @@ -569,15 +571,15 @@ public function testSubmitSingleNonExpandedStringCastableIdentifier() $entity1 = new SingleStringCastableIdEntity(1, 'Foo'); $entity2 = new SingleStringCastableIdEntity(2, 'Bar'); - $this->persist(array($entity1, $entity2)); + $this->persist([$entity1, $entity2]); - $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, [ 'multiple' => false, 'expanded' => false, 'em' => 'default', 'class' => self::SINGLE_STRING_CASTABLE_IDENT_CLASS, 'choice_label' => 'name', - )); + ]); $field->submit('2'); @@ -591,15 +593,15 @@ public function testSubmitSingleStringCastableIdentifierExpanded() $entity1 = new SingleStringCastableIdEntity(1, 'Foo'); $entity2 = new SingleStringCastableIdEntity(2, 'Bar'); - $this->persist(array($entity1, $entity2)); + $this->persist([$entity1, $entity2]); - $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, [ 'multiple' => false, 'expanded' => true, 'em' => 'default', 'class' => self::SINGLE_STRING_CASTABLE_IDENT_CLASS, 'choice_label' => 'name', - )); + ]); $field->submit('2'); @@ -617,29 +619,29 @@ public function testSubmitMultipleNonExpandedStringCastableIdentifierForExisting $entity2 = new SingleStringCastableIdEntity(2, 'Bar'); $entity3 = new SingleStringCastableIdEntity(3, 'Baz'); - $this->persist(array($entity1, $entity2, $entity3)); + $this->persist([$entity1, $entity2, $entity3]); - $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, [ 'multiple' => true, 'expanded' => false, 'em' => 'default', 'class' => self::SINGLE_STRING_CASTABLE_IDENT_CLASS, 'choice_label' => 'name', - )); + ]); - $existing = new ArrayCollection(array(0 => $entity2)); + $existing = new ArrayCollection([0 => $entity2]); $field->setData($existing); - $field->submit(array('1', '3')); + $field->submit(['1', '3']); // entry with index 0 ($entity2) was replaced - $expected = new ArrayCollection(array(0 => $entity1, 1 => $entity3)); + $expected = new ArrayCollection([0 => $entity1, 1 => $entity3]); $this->assertTrue($field->isSynchronized()); $this->assertEquals($expected, $field->getData()); // same object still, useful if it is a PersistentCollection $this->assertSame($existing, $field->getData()); - $this->assertSame(array('1', '3'), $field->getViewData()); + $this->assertSame(['1', '3'], $field->getViewData()); } public function testSubmitMultipleNonExpandedStringCastableIdentifier() @@ -648,23 +650,23 @@ public function testSubmitMultipleNonExpandedStringCastableIdentifier() $entity2 = new SingleStringCastableIdEntity(2, 'Bar'); $entity3 = new SingleStringCastableIdEntity(3, 'Baz'); - $this->persist(array($entity1, $entity2, $entity3)); + $this->persist([$entity1, $entity2, $entity3]); - $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, [ 'multiple' => true, 'expanded' => false, 'em' => 'default', 'class' => self::SINGLE_STRING_CASTABLE_IDENT_CLASS, 'choice_label' => 'name', - )); + ]); - $field->submit(array('1', '3')); + $field->submit(['1', '3']); - $expected = new ArrayCollection(array($entity1, $entity3)); + $expected = new ArrayCollection([$entity1, $entity3]); $this->assertTrue($field->isSynchronized()); $this->assertEquals($expected, $field->getData()); - $this->assertSame(array('1', '3'), $field->getViewData()); + $this->assertSame(['1', '3'], $field->getViewData()); } public function testSubmitMultipleStringCastableIdentifierExpanded() @@ -673,19 +675,19 @@ public function testSubmitMultipleStringCastableIdentifierExpanded() $entity2 = new SingleStringCastableIdEntity(2, 'Bar'); $entity3 = new SingleStringCastableIdEntity(3, 'Bar'); - $this->persist(array($entity1, $entity2, $entity3)); + $this->persist([$entity1, $entity2, $entity3]); - $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, [ 'multiple' => true, 'expanded' => true, 'em' => 'default', 'class' => self::SINGLE_STRING_CASTABLE_IDENT_CLASS, 'choice_label' => 'name', - )); + ]); - $field->submit(array('1', '3')); + $field->submit(['1', '3']); - $expected = new ArrayCollection(array($entity1, $entity3)); + $expected = new ArrayCollection([$entity1, $entity3]); $this->assertTrue($field->isSynchronized()); $this->assertEquals($expected, $field->getData()); @@ -703,19 +705,19 @@ public function testOverrideChoices() $entity2 = new SingleIntIdEntity(2, 'Bar'); $entity3 = new SingleIntIdEntity(3, 'Baz'); - $this->persist(array($entity1, $entity2, $entity3)); + $this->persist([$entity1, $entity2, $entity3]); - $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, [ 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, // not all persisted entities should be displayed - 'choices' => array($entity1, $entity2), + 'choices' => [$entity1, $entity2], 'choice_label' => 'name', - )); + ]); $field->submit('2'); - $this->assertEquals(array(1 => new ChoiceView($entity1, '1', 'Foo'), 2 => new ChoiceView($entity2, '2', 'Bar')), $field->createView()->vars['choices']); + $this->assertEquals([1 => new ChoiceView($entity1, '1', 'Foo'), 2 => new ChoiceView($entity2, '2', 'Bar')], $field->createView()->vars['choices']); $this->assertTrue($field->isSynchronized()); $this->assertSame($entity2, $field->getData()); $this->assertSame('2', $field->getViewData()); @@ -726,18 +728,18 @@ public function testOverrideChoicesValues() $entity1 = new SingleIntIdEntity(1, 'Foo'); $entity2 = new SingleIntIdEntity(2, 'Bar'); - $this->persist(array($entity1, $entity2)); + $this->persist([$entity1, $entity2]); - $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, [ 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, 'choice_label' => 'name', 'choice_value' => 'name', - )); + ]); $field->submit('Bar'); - $this->assertEquals(array('Foo' => new ChoiceView($entity1, 'Foo', 'Foo'), 'Bar' => new ChoiceView($entity2, 'Bar', 'Bar')), $field->createView()->vars['choices']); + $this->assertEquals(['Foo' => new ChoiceView($entity1, 'Foo', 'Foo'), 'Bar' => new ChoiceView($entity2, 'Bar', 'Bar')], $field->createView()->vars['choices']); $this->assertTrue($field->isSynchronized(), 'Field should be synchronized.'); $this->assertSame($entity2, $field->getData(), 'Entity should be loaded by custom value.'); $this->assertSame('Bar', $field->getViewData()); @@ -748,9 +750,9 @@ public function testOverrideChoicesValuesWithCallable() $entity1 = new GroupableEntity(1, 'Foo', 'BazGroup'); $entity2 = new GroupableEntity(2, 'Bar', 'BooGroup'); - $this->persist(array($entity1, $entity2)); + $this->persist([$entity1, $entity2]); - $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, [ 'em' => 'default', 'class' => self::ITEM_GROUP_CLASS, 'choice_label' => 'name', @@ -761,14 +763,14 @@ public function testOverrideChoicesValuesWithCallable() return $entity->groupName.'/'.$entity->name; }, - )); + ]); $field->submit('BooGroup/Bar'); - $this->assertEquals(array( + $this->assertEquals([ 'BazGroup/Foo' => new ChoiceView($entity1, 'BazGroup/Foo', 'Foo'), 'BooGroup/Bar' => new ChoiceView($entity2, 'BooGroup/Bar', 'Bar'), - ), $field->createView()->vars['choices']); + ], $field->createView()->vars['choices']); $this->assertTrue($field->isSynchronized(), 'Field should be synchronized.'); $this->assertSame($entity2, $field->getData(), 'Entity should be loaded by custom value.'); $this->assertSame('BooGroup/Bar', $field->getViewData()); @@ -779,13 +781,13 @@ public function testChoicesForValuesOptimization() $entity1 = new SingleIntIdEntity(1, 'Foo'); $entity2 = new SingleIntIdEntity(2, 'Bar'); - $this->persist(array($entity1, $entity2)); + $this->persist([$entity1, $entity2]); - $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, [ 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, 'choice_label' => 'name', - )); + ]); $this->em->clear(); @@ -805,29 +807,29 @@ public function testGroupByChoices() $item3 = new GroupableEntity(3, 'Baz', 'Group2'); $item4 = new GroupableEntity(4, 'Boo!', null); - $this->persist(array($item1, $item2, $item3, $item4)); + $this->persist([$item1, $item2, $item3, $item4]); - $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, [ 'em' => 'default', 'class' => self::ITEM_GROUP_CLASS, - 'choices' => array($item1, $item2, $item3, $item4), + 'choices' => [$item1, $item2, $item3, $item4], 'choice_label' => 'name', 'group_by' => 'groupName', - )); + ]); $field->submit('2'); $this->assertSame('2', $field->getViewData()); - $this->assertEquals(array( - 'Group1' => new ChoiceGroupView('Group1', array( + $this->assertEquals([ + 'Group1' => new ChoiceGroupView('Group1', [ 1 => new ChoiceView($item1, '1', 'Foo'), 2 => new ChoiceView($item2, '2', 'Bar'), - )), - 'Group2' => new ChoiceGroupView('Group2', array( + ]), + 'Group2' => new ChoiceGroupView('Group2', [ 3 => new ChoiceView($item3, '3', 'Baz'), - )), + ]), 4 => new ChoiceView($item4, '4', 'Boo!'), - ), $field->createView()->vars['choices']); + ], $field->createView()->vars['choices']); } public function testPreferredChoices() @@ -836,17 +838,17 @@ public function testPreferredChoices() $entity2 = new SingleIntIdEntity(2, 'Bar'); $entity3 = new SingleIntIdEntity(3, 'Baz'); - $this->persist(array($entity1, $entity2, $entity3)); + $this->persist([$entity1, $entity2, $entity3]); - $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, [ 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, - 'preferred_choices' => array($entity3, $entity2), + 'preferred_choices' => [$entity3, $entity2], 'choice_label' => 'name', - )); + ]); - $this->assertEquals(array(3 => new ChoiceView($entity3, '3', 'Baz'), 2 => new ChoiceView($entity2, '2', 'Bar')), $field->createView()->vars['preferred_choices']); - $this->assertEquals(array(1 => new ChoiceView($entity1, '1', 'Foo')), $field->createView()->vars['choices']); + $this->assertEquals([3 => new ChoiceView($entity3, '3', 'Baz'), 2 => new ChoiceView($entity2, '2', 'Bar')], $field->createView()->vars['preferred_choices']); + $this->assertEquals([1 => new ChoiceView($entity1, '1', 'Foo')], $field->createView()->vars['choices']); } public function testOverrideChoicesWithPreferredChoices() @@ -855,18 +857,18 @@ public function testOverrideChoicesWithPreferredChoices() $entity2 = new SingleIntIdEntity(2, 'Bar'); $entity3 = new SingleIntIdEntity(3, 'Baz'); - $this->persist(array($entity1, $entity2, $entity3)); + $this->persist([$entity1, $entity2, $entity3]); - $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, [ 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, - 'choices' => array($entity2, $entity3), - 'preferred_choices' => array($entity3), + 'choices' => [$entity2, $entity3], + 'preferred_choices' => [$entity3], 'choice_label' => 'name', - )); + ]); - $this->assertEquals(array(3 => new ChoiceView($entity3, '3', 'Baz')), $field->createView()->vars['preferred_choices']); - $this->assertEquals(array(2 => new ChoiceView($entity2, '2', 'Bar')), $field->createView()->vars['choices']); + $this->assertEquals([3 => new ChoiceView($entity3, '3', 'Baz')], $field->createView()->vars['preferred_choices']); + $this->assertEquals([2 => new ChoiceView($entity2, '2', 'Bar')], $field->createView()->vars['choices']); } public function testDisallowChoicesThatAreNotIncludedChoicesSingleIdentifier() @@ -875,14 +877,14 @@ public function testDisallowChoicesThatAreNotIncludedChoicesSingleIdentifier() $entity2 = new SingleIntIdEntity(2, 'Bar'); $entity3 = new SingleIntIdEntity(3, 'Baz'); - $this->persist(array($entity1, $entity2, $entity3)); + $this->persist([$entity1, $entity2, $entity3]); - $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, [ 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, - 'choices' => array($entity1, $entity2), + 'choices' => [$entity1, $entity2], 'choice_label' => 'name', - )); + ]); $field->submit('3'); @@ -898,14 +900,14 @@ public function testDisallowChoicesThatAreNotIncludedChoicesSingleAssocIdentifie $entity1 = new SingleAssociationToIntIdEntity($innerEntity1, 'Foo'); $entity2 = new SingleAssociationToIntIdEntity($innerEntity2, 'Bar'); - $this->persist(array($innerEntity1, $innerEntity2, $entity1, $entity2)); + $this->persist([$innerEntity1, $innerEntity2, $entity1, $entity2]); - $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, [ 'em' => 'default', 'class' => self::SINGLE_ASSOC_IDENT_CLASS, - 'choices' => array($entity1, $entity2), + 'choices' => [$entity1, $entity2], 'choice_label' => 'name', - )); + ]); $field->submit('3'); @@ -919,14 +921,14 @@ public function testDisallowChoicesThatAreNotIncludedChoicesCompositeIdentifier( $entity2 = new CompositeIntIdEntity(30, 40, 'Bar'); $entity3 = new CompositeIntIdEntity(50, 60, 'Baz'); - $this->persist(array($entity1, $entity2, $entity3)); + $this->persist([$entity1, $entity2, $entity3]); - $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, [ 'em' => 'default', 'class' => self::COMPOSITE_IDENT_CLASS, - 'choices' => array($entity1, $entity2), + 'choices' => [$entity1, $entity2], 'choice_label' => 'name', - )); + ]); $field->submit('2'); @@ -940,17 +942,17 @@ public function testDisallowChoicesThatAreNotIncludedQueryBuilderSingleIdentifie $entity2 = new SingleIntIdEntity(2, 'Bar'); $entity3 = new SingleIntIdEntity(3, 'Baz'); - $this->persist(array($entity1, $entity2, $entity3)); + $this->persist([$entity1, $entity2, $entity3]); $repository = $this->em->getRepository(self::SINGLE_IDENT_CLASS); - $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, [ 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, 'query_builder' => $repository->createQueryBuilder('e') ->where('e.id IN (1, 2)'), 'choice_label' => 'name', - )); + ]); $field->submit('3'); @@ -968,17 +970,17 @@ public function testDisallowChoicesThatAreNotIncludedQueryBuilderSingleAssocIden $entity2 = new SingleAssociationToIntIdEntity($innerEntity2, 'Bar'); $entity3 = new SingleAssociationToIntIdEntity($innerEntity3, 'Baz'); - $this->persist(array($innerEntity1, $innerEntity2, $innerEntity3, $entity1, $entity2, $entity3)); + $this->persist([$innerEntity1, $innerEntity2, $innerEntity3, $entity1, $entity2, $entity3]); $repository = $this->em->getRepository(self::SINGLE_ASSOC_IDENT_CLASS); - $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, [ 'em' => 'default', 'class' => self::SINGLE_ASSOC_IDENT_CLASS, 'query_builder' => $repository->createQueryBuilder('e') ->where('e.entity IN (1, 2)'), 'choice_label' => 'name', - )); + ]); $field->submit('3'); @@ -992,9 +994,9 @@ public function testDisallowChoicesThatAreNotIncludedQueryBuilderAsClosureSingle $entity2 = new SingleIntIdEntity(2, 'Bar'); $entity3 = new SingleIntIdEntity(3, 'Baz'); - $this->persist(array($entity1, $entity2, $entity3)); + $this->persist([$entity1, $entity2, $entity3]); - $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, [ 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, 'query_builder' => function (EntityRepository $repository) { @@ -1002,7 +1004,7 @@ public function testDisallowChoicesThatAreNotIncludedQueryBuilderAsClosureSingle ->where('e.id IN (1, 2)'); }, 'choice_label' => 'name', - )); + ]); $field->submit('3'); @@ -1016,9 +1018,9 @@ public function testDisallowChoicesThatAreNotIncludedQueryBuilderAsClosureCompos $entity2 = new CompositeIntIdEntity(30, 40, 'Bar'); $entity3 = new CompositeIntIdEntity(50, 60, 'Baz'); - $this->persist(array($entity1, $entity2, $entity3)); + $this->persist([$entity1, $entity2, $entity3]); - $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, [ 'em' => 'default', 'class' => self::COMPOSITE_IDENT_CLASS, 'query_builder' => function (EntityRepository 8000 $repository) { @@ -1026,7 +1028,7 @@ public function testDisallowChoicesThatAreNotIncludedQueryBuilderAsClosureCompos ->where('e.id1 IN (10, 50)'); }, 'choice_label' => 'name', - )); + ]); $field->submit('2'); @@ -1038,15 +1040,15 @@ public function testSubmitSingleStringIdentifier() { $entity1 = new SingleStringIdEntity('foo', 'Foo'); - $this->persist(array($entity1)); + $this->persist([$entity1]); - $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, [ 'multiple' => false, 'expanded' => false, 'em' => 'default', 'class' => self::SINGLE_STRING_IDENT_CLASS, 'choice_label' => 'name', - )); + ]); $field->submit('foo'); @@ -1059,15 +1061,15 @@ public function testSubmitCompositeStringIdentifier() { $entity1 = new CompositeStringIdEntity('foo1', 'foo2', 'Foo'); - $this->persist(array($entity1)); + $this->persist([$entity1]); - $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( + $field = $this->factory->createNamed('name', static::TESTED_TYPE, null, [ 'multiple' => false, 'expanded' => false, 'em' => 'default', 'class' => self::COMPOSITE_STRING_IDENT_CLASS, 'choice_label' => 'name', - )); + ]); // the collection key is used here $field->submit('0'); @@ -1087,11 +1089,11 @@ public function testGetManagerForClassIfNoEm() ->with(self::SINGLE_IDENT_CLASS) ->will($this->returnValue($this->em)); - $this->factory->createNamed('name', static::TESTED_TYPE, null, array( + $this->factory->createNamed('name', static::TESTED_TYPE, null, [ 'class' => self::SINGLE_IDENT_CLASS, 'required' => false, 'choice_label' => 'name', - )); + ]); } public function testExplicitEm() @@ -1102,11 +1104,11 @@ public function testExplicitEm() $this->emRegistry->expects($this->never()) ->method('getManagerForClass'); - $this->factory->createNamed('name', static::TESTED_TYPE, null, array( + $this->factory->createNamed('name', static::TESTED_TYPE, null, [ 'em' => $this->em, 'class' => self::SINGLE_IDENT_CLASS, 'choice_label' => 'name', - )); + ]); } public function testLoaderCaching() @@ -1115,7 +1117,7 @@ public function testLoaderCaching() $entity2 = new SingleIntIdEntity(2, 'Bar'); $entity3 = new SingleIntIdEntity(3, 'Baz'); - $this->persist(array($entity1, $entity2, $entity3)); + $this->persist([$entity1, $entity2, $entity3]); $repo = $this->em->getRepository(self::SINGLE_IDENT_CLASS); @@ -1130,35 +1132,35 @@ public function testLoaderCaching() $formBuilder = $factory->createNamedBuilder('form', FormTypeTest::TESTED_TYPE); - $formBuilder->add('property1', static::TESTED_TYPE, array( + $formBuilder->add('property1', static::TESTED_TYPE, [ 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, 'query_builder' => $repo->createQueryBuilder('e')->where('e.id IN (1, 2)'), - )); + ]); - $formBuilder->add('property2', static::TESTED_TYPE, array( + $formBuilder->add('property2', static::TESTED_TYPE, [ 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, 'query_builder' => function (EntityRepository $repo) { return $repo->createQueryBuilder('e')->where('e.id IN (1, 2)'); }, - )); + ]); - $formBuilder->add('property3', static::TESTED_TYPE, array( + $formBuilder->add('property3', static::TESTED_TYPE, [ 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, 'query_builder' => function (EntityRepository $repo) { return $repo->createQueryBuilder('e')->where('e.id IN (1, 2)'); }, - )); + ]); $form = $formBuilder->getForm(); - $form->submit(array( + $form->submit([ 'property1' => 1, 'property2' => 1, 'property3' => 2, - )); + ]); $choiceLoader1 = $form->get('property1')->getConfig()->getOption('choice_loader'); $choiceLoader2 = $form->get('property2')->getConfig()->getOption('choice_loader'); @@ -1175,7 +1177,7 @@ public function testLoaderCachingWithParameters() $entity2 = new SingleIntIdEntity(2, 'Bar'); $entity3 = new SingleIntIdEntity(3, 'Baz'); - $this->persist(array($entity1, $entity2, $entity3)); + $this->persist([$entity1, $entity2, $entity3]); $repo = $this->em->getRepository(self::SINGLE_IDENT_CLASS); @@ -1190,35 +1192,35 @@ public function testLoaderCachingWithParameters() $formBuilder = $factory->createNamedBuilder('form', FormTypeTest::TESTED_TYPE); - $formBuilder->add('property1', static::TESTED_TYPE, array( + $formBuilder->add('property1', static::TESTED_TYPE, [ 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, 'query_builder' => $repo->createQueryBuilder('e')->where('e.id = :id')->setParameter('id', 1), - )); + ]); - $formBuilder->add('property2', static::TESTED_TYPE, array( + $formBuilder->add('property2', static::TESTED_TYPE, [ 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, 'query_builder' => function (EntityRepository $repo) { return $repo->createQueryBuilder('e')->where('e.id = :id')->setParameter('id', 1); }, - )); + ]); - $formBuilder->add('property3', static::TESTED_TYPE, array( + $formBuilder->add('property3', static::TESTED_TYPE, [ 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, 'query_builder' => function (EntityRepository $repo) { return $repo->createQueryBuilder('e')->where('e.id = :id')->setParameter('id', 1); }, - )); + ]); $form = $formBuilder->getForm(); - $form->submit(array( + $form->submit([ 'property1' => 1, 'property2' => 1, 'property3' => 2, - )); + ]); $choiceLoader1 = $form->get('property1')->getConfig()->getOption('choice_loader'); $choiceLoader2 = $form->get('property2')->getConfig()->getOption('choice_loader'); @@ -1242,21 +1244,21 @@ protected function createRegistryMock($name, $em) public function testPassDisabledAsOption() { - $form = $this->factory->create(static::TESTED_TYPE, null, array( + $form = $this->factory->create(static::TESTED_TYPE, null, [ 'em' => 'default', 'disabled' => true, 'class' => self::SINGLE_IDENT_CLASS, - )); + ]); $this->assertTrue($form->isDisabled()); } public function testPassIdAndNameToView() { - $view = $this->factory->createNamed('name', static::TESTED_TYPE, null, array( + $view = $this->factory->createNamed('name', static::TESTED_TYPE, null, [ 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, - )) + ]) ->createView(); $this->assertEquals('name', $view->vars['id']); @@ -1266,10 +1268,10 @@ public function testPassIdAndNameToView() public function testStripLeadingUnderscoresAndDigitsFromId() { - $view = $this->factory->createNamed('_09name', static::TESTED_TYPE, null, array( + $view = $this->factory->createNamed('_09name', static::TESTED_TYPE, null, [ 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, - )) + ]) ->createView(); $this->assertEquals('name', $view->vars['id']); @@ -1280,10 +1282,10 @@ public function testStripLeadingUnderscoresAndDigitsFromId() public function testPassIdAndNameToViewWithParent() { $view = $this->factory->createNamedBuilder('parent', FormTypeTest::TESTED_TYPE) - ->add('child', static::TESTED_TYPE, array( + ->add('child', static::TESTED_TYPE, [ 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, - )) + ]) ->getForm() ->createView(); @@ -1296,10 +1298,10 @@ public function testPassIdAndNameToViewWithGrandParent() { $builder = $this->factory->createNamedBuilder('parent', FormTypeTest::TESTED_TYPE) ->add('child', FormTypeTest::TESTED_TYPE); - $builder->get('child')->add('grand_child', static::TESTED_TYPE, array( + $builder->get('child')->add('grand_child', static::TESTED_TYPE, [ 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, - )); + ]); $view = $builder->getForm()->createView(); $this->assertEquals('parent_child_grand_child', $view['child']['grand_child']->vars['id']); @@ -1309,11 +1311,11 @@ public function testPassIdAndNameToViewWithGrandParent() public function testPassTranslationDomainToView() { - $view = $this->factory->create(static::TESTED_TYPE, null, array( + $view = $this->factory->create(static::TESTED_TYPE, null, [ 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, 'translation_domain' => 'domain', - )) + ]) ->createView(); $this->assertSame('domain', $view->vars['translation_domain']); @@ -1322,13 +1324,13 @@ public function testPassTranslationDomainToView() public function testInheritTranslationDomainFromParent() { $view = $this->factory - ->createNamedBuilder('parent', FormTypeTest::TESTED_TYPE, null, array( + ->createNamedBuilder('parent', FormTypeTest::TESTED_TYPE, null, [ 'translation_domain' => 'domain', - )) - ->add('child', static::TESTED_TYPE, array( + ]) + ->add('child', static::TESTED_TYPE, [ 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, - )) + ]) ->getForm() ->createView(); @@ -1338,14 +1340,14 @@ public function testInheritTranslationDomainFromParent() public function testPreferOwnTranslationDomain() { $view = $this->factory - ->createNamedBuilder('parent', FormTypeTest::TESTED_TYPE, null, array( + ->createNamedBuilder('parent', FormTypeTest::TESTED_TYPE, null, [ 'translation_domain' => 'parent_domain', - )) - ->add('child', static::TESTED_TYPE, array( + ]) + ->add('child', static::TESTED_TYPE, [ 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, 'translation_domain' => 'domain', - )) + ]) ->getForm() ->createView(); @@ -1356,23 +1358,197 @@ public function testDefaultTranslationDomain() { $view = $this->factory ->createNamedBuilder('parent', FormTypeTest::TESTED_TYPE) - ->add('child', static::TESTED_TYPE, array( + ->add('child', static::TESTED_TYPE, [ 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, - )) + ]) ->getForm() ->createView(); $this->assertNull($view['child']->vars['translation_domain']); } + public function testPassLabelTranslationParametersToView() + { + $view = $this->factory->create(static::TESTED_TYPE, null, [ + 'label_translation_parameters' => ['%param%' => 'value'], + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + ]) + ->createView(); + + $this->assertSame(['%param%' => 'value'], $view->vars['label_translation_parameters']); + } + + public function testPassHelpTranslationParametersToView() + { + $view = $this->factory->create(static::TESTED_TYPE, null, [ + 'help_translation_parameters' => ['%param%' => 'value'], + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + ]) + ->createView(); + + $this->assertSame(['%param%' => 'value'], $view->vars['help_translation_parameters']); + } + + public function testPassAttrTranslationParametersToView() + { + $view = $this->factory->create(static::TESTED_TYPE, null, [ + 'attr_translation_parameters' => ['%param%' => 'value'], + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + ]) + ->createView(); + + $this->assertSame(['%param%' => 'value'], $view->vars['attr_translation_parameters']); + } + + public function testInheritLabelTranslationParametersFromParent() + { + $view = $this->factory + ->createNamedBuilder('parent', FormTypeTest::TESTED_TYPE, null, [ + 'label_translation_parameters' => ['%param%' => 'value'], + ]) + ->add('child', static::TESTED_TYPE, [ + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + ]) + ->getForm() + ->createView(); + + $this->assertEquals(['%param%' => 'value'], $view['child']->vars['label_translation_parameters']); + } + + public function testInheritHelpTranslationParametersFromParent() + { + $view = $this->factory + ->createNamedBuilder('parent', FormTypeTest::TESTED_TYPE, null, [ + 'help_translation_parameters' => ['%param%' => 'value'], + ]) + ->add('child', static::TESTED_TYPE, [ + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + ]) + ->getForm() + ->createView(); + + $this->assertEquals(['%param%' => 'value'], $view['child']->vars['help_translation_parameters']); + } + + public function testInheritAttrTranslationParametersFromParent() + { + $view = $this->factory + ->createNamedBuilder('parent', FormTypeTest::TESTED_TYPE, null, [ + 'attr_translation_parameters' => ['%param%' => 'value'], + ]) + ->add('child', static::TESTED_TYPE, [ + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + ]) + ->getForm() + ->createView(); + + $this->assertEquals(['%param%' => 'value'], $view['child']->vars['attr_translation_parameters']); + } + + public function testPreferOwnLabelTranslationParameters() + { + $view = $this->factory + ->createNamedBuilder('parent', FormTypeTest::TESTED_TYPE, null, [ + 'label_translation_parameters' => ['%parent_param%' => 'parent_value', '%override_param%' => 'parent_override_value'], + ]) + ->add('child', static::TESTED_TYPE, [ + 'label_translation_parameters' => ['%override_param%' => 'child_value'], + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + ]) + ->getForm() + ->createView(); + + $this->assertEquals(['%parent_param%' => 'parent_value', '%override_param%' => 'child_value'], $view['child']->vars['label_translation_parameters']); + } + + public function testPreferOwnHelpTranslationParameters() + { + $view = $this->factory + ->createNamedBuilder('parent', FormTypeTest::TESTED_TYPE, null, [ + 'help_translation_parameters' => ['%parent_param%' => 'parent_value', '%override_param%' => 'parent_override_value'], + ]) + ->add('child', static::TESTED_TYPE, [ + 'help_translation_parameters' => ['%override_param%' => 'child_value'], + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + ]) + ->getForm() + ->createView(); + + $this->assertEquals(['%parent_param%' => 'parent_value', '%override_param%' => 'child_value'], $view['child']->vars['help_translation_parameters']); + } + + public function testPreferOwnAttrTranslationParameters() + { + $view = $this->factory + ->createNamedBuilder('parent', FormTypeTest::TESTED_TYPE, null, [ + 'attr_translation_parameters' => ['%parent_param%' => 'parent_value', '%override_param%' => 'parent_override_value'], + ]) + ->add('child', static::TESTED_TYPE, [ + 'attr_translation_parameters' => ['%override_param%' => 'child_value'], + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + ]) + ->getForm() + ->createView(); + + $this->assertEquals(['%parent_param%' => 'parent_value', '%override_param%' => 'child_value'], $view['child']->vars['attr_translation_parameters']); + } + + public function testDefaultLabelTranslationParameters() + { + $view = $this->factory->createNamedBuilder('parent', FormTypeTest::TESTED_TYPE) + ->add('child', static::TESTED_TYPE, [ + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + ]) + ->getForm() + ->createView(); + + $this->assertEquals([], $view['child']->vars['label_translation_parameters']); + } + + public function testDefaultHelpTranslationParameters() + { + $view = $this->factory->createNamedBuilder('parent', FormTypeTest::TESTED_TYPE) + ->add('child', static::TESTED_TYPE, [ + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + ]) + ->getForm() + ->createView(); + + $this->assertEquals([], $view['child']->vars['help_translation_parameters']); + } + + public function testDefaultAttrTranslationParameters() + { + $view = $this->factory->createNamedBuilder('parent', FormTypeTest::TESTED_TYPE) + ->add('child', static::TESTED_TYPE, [ + 'em' => 'default', + 'class' => self::SINGLE_IDENT_CLASS, + ]) + ->getForm() + ->createView(); + + $this->assertEquals([], $view['child']->vars['attr_translation_parameters']); + } + public function testPassLabelToView() { - $view = $this->factory->createNamed('__test___field', static::TESTED_TYPE, null, array( + $view = $this->factory->createNamed('__test___field', static::TESTED_TYPE, null, [ 'label' => 'My label', 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, - )) + ]) ->createView(); $this->assertSame('My label', $view->vars['label']); @@ -1380,10 +1556,10 @@ public function testPassLabelToView() public function testPassMultipartFalseToView() { - $view = $this->factory->create(static::TESTED_TYPE, null, array( + $view = $this->factory->create(static::TESTED_TYPE, null, [ 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, - )) + ]) ->createView(); $this->assertFalse($view->vars['multipart']); @@ -1391,10 +1567,10 @@ public function testPassMultipartFalseToView() public function testSubmitNull($expected = null, $norm = null, $view = null) { - $form = $this->factory->create(static::TESTED_TYPE, null, array( + $form = $this->factory->create(static::TESTED_TYPE, null, [ 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, - )); + ]); $form->submit(null); $this->assertNull($form->getData()); @@ -1404,11 +1580,11 @@ public function testSubmitNull($expected = null, $norm = null, $view = null) public function testSubmitNullExpanded() { - $form = $this->factory->create(static::TESTED_TYPE, null, array( + $form = $this->factory->create(static::TESTED_TYPE, null, [ 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, 'expanded' => true, - )); + ]); $form->submit(null); $this->assertNull($form->getData()); @@ -1418,82 +1594,82 @@ public function testSubmitNullExpanded() public function testSubmitNullMultiple() { - $form = $this->factory->create(static::TESTED_TYPE, null, array( + $form = $this->factory->create(static::TESTED_TYPE, null, [ 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, 'multiple' => true, - )); + ]); $form->submit(null); $collection = new ArrayCollection(); $this->assertEquals($collection, $form->getData()); $this->assertEquals($collection, $form->getNormData()); - $this->assertSame(array(), $form->getViewData(), 'View data is always an array'); + $this->assertSame([], $form->getViewData(), 'View data is always an array'); } public function testSubmitNullExpandedMultiple() { - $form = $this->factory->create(static::TESTED_TYPE, null, array( + $form = $this->factory->create(static::TESTED_TYPE, null, [ 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, 'expanded' => true, 'multiple' => true, - )); + ]); $form->submit(null); $collection = new ArrayCollection(); $this->assertEquals($collection, $form->getData()); $this->assertEquals($collection, $form->getNormData()); - $this->assertSame(array(), $form->getViewData(), 'View data is always an array'); + $this->assertSame([], $form->getViewData(), 'View data is always an array'); } public function testSetDataEmptyArraySubmitNullMultiple() { - $emptyArray = array(); - $form = $this->factory->create(static::TESTED_TYPE, null, array( + $emptyArray = []; + $form = $this->factory->create(static::TESTED_TYPE, null, [ 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, 'multiple' => true, - )); + ]); $form->setData($emptyArray); $form->submit(null); $this->assertInternalType('array', $form->getData()); - $this->assertEquals(array(), $form->getData()); - $this->assertEquals(array(), $form->getNormData()); - $this->assertSame(array(), $form->getViewData(), 'View data is always an array'); + $this->assertEquals([], $form->getData()); + $this->assertEquals([], $form->getNormData()); + $this->assertSame([], $form->getViewData(), 'View data is always an array'); } public function testSetDataNonEmptyArraySubmitNullMultiple() { $entity1 = new SingleIntIdEntity(1, 'Foo'); - $this->persist(array($entity1)); - $form = $this->factory->create(static::TESTED_TYPE, null, array( + $this->persist([$entity1]); + $form = $this->factory->create(static::TESTED_TYPE, null, [ 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, 'multiple' => true, - )); - $existing = array(0 => $entity1); + ]); + $existing = [0 => $entity1]; $form->setData($existing); $form->submit(null); $this->assertInternalType('array', $form->getData()); - $this->assertEquals(array(), $form->getData()); - $this->assertEquals(array(), $form->getNormData()); - $this->assertSame(array(), $form->getViewData(), 'View data is always an array'); + $this->assertEquals([], $form->getData()); + $this->assertEquals([], $form->getNormData()); + $this->assertSame([], $form->getViewData(), 'View data is always an array'); } public function testSubmitNullUsesDefaultEmptyData($emptyData = 'empty', $expectedData = null) { $emptyData = '1'; $entity1 = new SingleIntIdEntity(1, 'Foo'); - $this->persist(array($entity1)); + $this->persist([$entity1]); - $form = $this->factory->create(static::TESTED_TYPE, null, array( + $form = $this->factory->create(static::TESTED_TYPE, null, [ 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, 'empty_data' => $emptyData, - )); + ]); $form->submit(null); $this->assertSame($emptyData, $form->getViewData()); @@ -1503,19 +1679,19 @@ public function testSubmitNullUsesDefaultEmptyData($emptyData = 'empty', $expect public function testSubmitNullMultipleUsesDefaultEmptyData() { - $emptyData = array('1'); + $emptyData = ['1']; $entity1 = new SingleIntIdEntity(1, 'Foo'); - $this->persist(array($entity1)); + $this->persist([$entity1]); - $form = $this->factory->create(static::TESTED_TYPE, null, array( + $form = $this->factory->create(static::TESTED_TYPE, null, [ 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, 'multiple' => true, 'empty_data' => $emptyData, - )); + ]); $form->submit(null); - $collection = new ArrayCollection(array($entity1)); + $collection = new ArrayCollection([$entity1]); $this->assertSame($emptyData, $form->getViewData()); $this->assertEquals($collection, $form->getNormData()); diff --git a/src/Symfony/Bridge/Doctrine/Tests/Logger/DbalLoggerTest.php b/src/Symfony/Bridge/Doctrine/Tests/Logger/DbalLoggerTest.php index 38bbed12945fe..10c403f461688 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Logger/DbalLoggerTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Logger/DbalLoggerTest.php @@ -25,8 +25,8 @@ public function testLog($sql, $params, $logParams) $dbalLogger = $this ->getMockBuilder('Symfony\\Bridge\\Doctrine\\Logger\\DbalLogger') - ->setConstructorArgs(array($logger, null)) - ->setMethods(array('log')) + ->setConstructorArgs([$logger, null]) + ->setMethods(['log']) ->getMock() ; @@ -41,14 +41,14 @@ public function testLog($sql, $params, $logParams) public function getLogFixtures() { - return array( - array('SQL', null, array()), - array('SQL', array(), array()), - array('SQL', array('foo' => 'bar'), array('foo' => 'bar')), - array('SQL', array('foo' => "\x7F\xFF"), array('foo' => DbalLogger::BINARY_DATA_VALUE)), - array('SQL', array('foo' => "bar\x7F\xFF"), array('foo' => DbalLogger::BINARY_DATA_VALUE)), - array('SQL', array('foo' => ''), array('foo' => '')), - ); + return [ + ['SQL', null, []], + ['SQL', [], []], + ['SQL', ['foo' => 'bar'], ['foo' => 'bar']], + ['SQL', ['foo' => "\x7F\xFF"], ['foo' => DbalLogger::BINARY_DATA_VALUE]], + ['SQL', ['foo' => "bar\x7F\xFF"], ['foo' => DbalLogger::BINARY_DATA_VALUE]], + ['SQL', ['foo' => ''], ['foo' => '']], + ]; } public function testLogNonUtf8() @@ -57,21 +57,21 @@ public function testLogNonUtf8() $dbalLogger = $this ->getMockBuilder('Symfony\\Bridge\\Doctrine\\Logger\\DbalLogger') - ->setConstructorArgs(array($logger, null)) - ->setMethods(array('log')) + ->setConstructorArgs([$logger, null]) + ->setMethods(['log']) ->getMock() ; $dbalLogger ->expects($this->once()) ->method('log') - ->with('SQL', array('utf8' => 'foo', 'nonutf8' => DbalLogger::BINARY_DATA_VALUE)) + ->with('SQL', ['utf8' => 'foo', 'nonutf8' => DbalLogger::BINARY_DATA_VALUE]) ; - $dbalLogger->startQuery('SQL', array( + $dbalLogger->startQuery('SQL', [ 'utf8' => 'foo', 'nonutf8' => "\x7F\xFF", - )); + ]); } public function testLogNonUtf8Array() @@ -80,29 +80,29 @@ public function testLogNonUtf8Array() $dbalLogger = $this ->getMockBuilder('Symfony\\Bridge\\Doctrine\\Logger\\DbalLogger') - ->setConstructorArgs(array($logger, null)) - ->setMethods(array('log')) + ->setConstructorArgs([$logger, null]) + ->setMethods(['log']) ->getMock() ; $dbalLogger ->expects($this->once()) ->method('log') - ->with('SQL', array( + ->with('SQL', [ 'utf8' => 'foo', - array( + [ 'nonutf8' => DbalLogger::BINARY_DATA_VALUE, - ), - ) + ], + ] ) ; - $dbalLogger->startQuery('SQL', array( + $dbalLogger->startQuery('SQL', [ 'utf8' => 'foo', - array( + [ 'nonutf8' => "\x7F\xFF", - ), - )); + ], + ]); } public function testLogLongString() @@ -111,8 +111,8 @@ public function testLogLongString() $dbalLogger = $this ->getMockBuilder('Symfony\\Bridge\\Doctrine\\Logger\\DbalLogger') - ->setConstructorArgs(array($logger, null)) - ->setMethods(array('log')) + ->setConstructorArgs([$logger, null]) + ->setMethods(['log']) ->getMock() ; @@ -124,13 +124,13 @@ public function testLogLongString() $dbalLogger ->expects($this->once()) ->method('log') - ->with('SQL', array('short' => $shortString, 'long' => substr($longString, 0, DbalLogger::MAX_STRING_LENGTH - 6).' [...]')) + ->with('SQL', ['short' => $shortString, 'long' => substr($longString, 0, DbalLogger::MAX_STRING_LENGTH - 6).' [...]']) ; - $dbalLogger->startQuery('SQL', array( + $dbalLogger->startQuery('SQL', [ 'short' => $shortString, 'long' => $longString, - )); + ]); } public function testLogUTF8LongString() @@ -139,12 +139,12 @@ public function testLogUTF8LongString() $dbalLogger = $this ->getMockBuilder('Symfony\\Bridge\\Doctrine\\Logger\\DbalLogger') - ->setConstructorArgs(array($logger, null)) - ->setMethods(array('log')) + ->setConstructorArgs([$logger, null]) + ->setMethods(['log']) ->getMock() ; - $testStringArray = array('é', 'á', 'ű', 'ő', 'ú', 'ö', 'ü', 'ó', 'í'); + $testStringArray = ['é', 'á', 'ű', 'ő', 'ú', 'ö', 'ü', 'ó', 'í']; $testStringCount = \count($testStringArray); $shortString = ''; @@ -158,12 +158,12 @@ public function testLogUTF8LongString() $dbalLogger ->expects($this->once()) ->method('log') - ->with('SQL', array('short' => $shortString, 'long' => mb_substr($longString, 0, DbalLogger::MAX_STRING_LENGTH - 6, 'UTF-8').' [...]')) + ->with('SQL', ['short' => $shortString, 'long' => mb_substr($longString, 0, DbalLogger::MAX_STRING_LENGTH - 6, 'UTF-8').' [...]']) ; - $dbalLogger->startQuery('SQL', array( + $dbalLogger->startQuery('SQL', [ 'short' => $shortString, 'long' => $longString, - )); + ]); } } diff --git a/src/Symfony/Bridge/Doctrine/Tests/ManagerRegistryTest.php b/src/Symfony/Bridge/Doctrine/Tests/ManagerRegistryTest.php index 490a48cfe6c6e..e5ebeeacf813a 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/ManagerRegistryTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/ManagerRegistryTest.php @@ -30,7 +30,7 @@ public function testResetService() { $container = new \LazyServiceProjectServiceContainer(); - $registry = new TestManagerRegistry('name', 8000 array(), array('defaultManager' => 'foo'), 'defaultConnection', 'defaultManager', 'proxyInterfaceName'); + $registry = new TestManagerRegistry('name', [], ['defaultManager' => 'foo'], 'defaultConnection', 'defaultManager', 'proxyInterfaceName'); $registry->setTestContainer($container); $foo = $container->get('foo'); diff --git a/src/Symfony/Bridge/Doctrine/Tests/Messenger/DoctrineCloseConnectionMiddlewareTest.php b/src/Symfony/Bridge/Doctrine/Tests/Messenger/DoctrineCloseConnectionMiddlewareTest.php new file mode 100644 index 0000000000000..3036b42593401 --- /dev/null +++ b/src/Symfony/Bridge/Doctrine/Tests/Messenger/DoctrineCloseConnectionMiddlewareTest.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\Bridge\Doctrine\Tests\Messenger; + +use Doctrine\Common\Persistence\ManagerRegistry; +use Doctrine\DBAL\Connection; +use Doctrine\ORM\EntityManagerInterface; +use Symfony\Bridge\Doctrine\Messenger\DoctrineCloseConnectionMiddleware; +use Symfony\Component\Messenger\Envelope; +use Symfony\Component\Messenger\Test\Middleware\MiddlewareTestCase; + +class DoctrineCloseConnectionMiddlewareTest extends MiddlewareTestCase +{ + private $connection; + private $entityManager; + private $managerRegistry; + private $middleware; + private $entityManagerName = 'default'; + + protected function setUp() + { + $this->connection = $this->createMock(Connection::class); + + $this->entityManager = $this->createMock(EntityManagerInterface::class); + $this->entityManager->method('getConnection')->willReturn($this->connection); + + $this->managerRegistry = $this->createMock(ManagerRegistry::class); + $this->managerRegistry->method('getManager')->willReturn($this->entityManager); + + $this->middleware = new DoctrineCloseConnectionMiddleware( + $this->managerRegistry, + $this->entityManagerName + ); + } + + public function testMiddlewareCloseConnection() + { + $this->connection->expects($this->once()) + ->method('close') + ; + + $this->middleware->handle(new Envelope(new \stdClass()), $this->getStackMock()); + } +} diff --git a/src/Symfony/Bridge/Doctrine/Tests/Messenger/DoctrinePingConnectionMiddlewareTest.php b/src/Symfony/Bridge/Doctrine/Tests/Messenger/DoctrinePingConnectionMiddlewareTest.php new file mode 100644 index 0000000000000..cc15625227983 --- /dev/null +++ b/src/Symfony/Bridge/Doctrine/Tests/Messenger/DoctrinePingConnectionMiddlewareTest.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\Bridge\Doctrine\Tests\Messenger; + +use Doctrine\Common\Persistence\ManagerRegistry; +use Doctrine\DBAL\Connection; +use Doctrine\ORM\EntityManagerInterface; +use Symfony\Bridge\Doctrine\Messenger\DoctrinePingConnectionMiddleware; +use Symfony\Component\Messenger\Envelope; +use Symfony\Component\Messenger\Test\Middleware\MiddlewareTestCase; + +class DoctrinePingConnectionMiddlewareTest extends MiddlewareTestCase +{ + private $connection; + private $entityManager; + private $managerRegistry; + private $middleware; + private $entityManagerName = 'default'; + + protected function setUp() + { + $this->connection = $this->createMock(Connection::class); + + $this->entityManager = $this->createMock(EntityManagerInterface::class); + $this->entityManager->method('getConnection')->willReturn($this->connection); + + $this->managerRegistry = $this->createMock(ManagerRegistry::class); + $this->managerRegistry->method('getManager')->willReturn($this->entityManager); + + $this->middleware = new DoctrinePingConnectionMiddleware( + $this->managerRegistry, + $this->entityManagerName + ); + } + + public function testMiddlewarePingOk() + { + $this->connection->expects($this->once()) + ->method('ping') + ->willReturn(false); + + $this->connection->expects($this->once()) + ->method('close') + ; + $this->connection->expects($this->once()) + ->method('connect') + ; + + $this->middleware->handle(new Envelope(new \stdClass()), $this->getStackMock()); + } + + public function testMiddlewarePingResetEntityManager() + { + $this->entityManager->expects($this->once()) + ->method('isOpen') + ->willReturn(false) + ; + $this->managerRegistry->expects($this->once()) + ->method('resetManager') + ->with($this->entityManagerName) + ; + + $this->middleware->handle(new Envelope(new \stdClass()), $this->getStackMock()); + } +} diff --git a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/DoctrineExtractorTest.php b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/DoctrineExtractorTest.php index 498c5ecbc95cb..ace1d447c2b61 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/DoctrineExtractorTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/DoctrineExtractorTest.php @@ -25,8 +25,8 @@ class DoctrineExtractorTest extends TestCase { private function createExtractor(bool $legacy = false) { - $config = Setup::createAnnotationMetadataConfiguration(array(__DIR__.\DIRECTORY_SEPARATOR.'Fixtures'), true); - $entityManager = EntityManager::create(array('driver' => 'pdo_sqlite'), $config); + $config = Setup::createAnnotationMetadataConfiguration([__DIR__.\DIRECTORY_SEPARATOR.'Fixtures'], true); + $entityManager = EntityManager::create(['driver' => 'pdo_sqlite'], $config); if (!DBALType::hasType('foo')) { DBALType::addType('foo', 'Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\DoctrineFooType'); @@ -49,7 +49,7 @@ public function testLegacyGetProperties() private function doTestGetProperties(bool $legacy) { $this->assertEquals( - array( + [ 'id', 'guid', 'time', @@ -67,7 +67,7 @@ private function doTestGetProperties(bool $legacy) 'bar', 'indexedBar', 'indexedFoo', - ), + ], $this->createExtractor($legacy)->getProperties('Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\DoctrineDummy') ); } @@ -89,10 +89,10 @@ private function doTestGetPropertiesWithEmbedded(bool $legacy) } $this->assertEquals( - array( + [ 'id', 'embedded', - ), + ], $this->createExtractor($legacy)->getProperties('Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\DoctrineWithEmbedded') ); } @@ -115,7 +115,7 @@ public function testLegacyExtract($property, array $type = null) private function doTestExtract(bool $legacy, $property, array $type = null) { - $this->assertEquals($type, $this->createExtractor($legacy)->getTypes('Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\DoctrineDummy', $property, array())); + $this->assertEquals($type, $this->createExtractor($legacy)->getTypes('Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\DoctrineDummy', $property, [])); } public function testExtractWithEmbedded() @@ -134,16 +134,16 @@ private function doTestExtractWithEmbedded(bool $legacy) $this->markTestSkipped('@Embedded is not available in Doctrine ORM lower than 2.5.'); } - $expectedTypes = array(new Type( + $expectedTypes = [new Type( Type::BUILTIN_TYPE_OBJECT, false, 'Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\DoctrineEmbeddable' - )); + )]; $actualTypes = $this->createExtractor($legacy)->getTypes( 'Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\DoctrineWithEmbedded', 'embedded', - array() + [] ); $this->assertEquals($expectedTypes, $actualTypes); @@ -151,47 +151,47 @@ private function doTestExtractWithEmbedded(bool $legacy) public function typesProvider() { - return array( - array('id', array(new Type(Type::BUILTIN_TYPE_INT))), - array('guid', array(new Type(Type::BUILTIN_TYPE_STRING))), - array('bigint', array(new Type(Type::BUILTIN_TYPE_STRING))), - array('time', array(new Type(Type::BUILTIN_TYPE_OBJECT, false, 'DateTime'))), - array('timeImmutable', array(new Type(Type::BUILTIN_TYPE_OBJECT, false, 'DateTimeImmutable'))), - array('dateInterval', array(new Type(Type::BUILTIN_TYPE_OBJECT, false, 'DateInterval'))), - array('float', array(new Type(Type::BUILTIN_TYPE_FLOAT))), - array('decimal', array(new Type(Type::BUILTIN_TYPE_STRING))), - array('bool', array(new Type(Type::BUILTIN_TYPE_BOOL))), - array('binary', array(new Type(Type::BUILTIN_TYPE_RESOURCE))), - array('json', array(new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true))), - array('foo', array(new Type(Type::BUILTIN_TYPE_OBJECT, true, 'Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\DoctrineRelation'))), - array('bar', array(new Type( + return [ + ['id', [new Type(Type::BUILTIN_TYPE_INT)]], + ['guid', [new Type(Type::BUILTIN_TYPE_STRING)]], + ['bigint', [new Type(Type::BUILTIN_TYPE_STRING)]], + ['time', [new Type(Type::BUILTIN_TYPE_OBJECT, false, 'DateTime')]], + ['timeImmutable', [new Type(Type::BUILTIN_TYPE_OBJECT, false, 'DateTimeImmutable')]], + ['dateInterval', [new Type(Type::BUILTIN_TYPE_OBJECT, false, 'DateInterval')]], + ['float', [new Type(Type::BUILTIN_TYPE_FLOAT)]], + ['decimal', [new Type(Type::BUILTIN_TYPE_STRING)]], + ['bool', [new Type(Type::BUILTIN_TYPE_BOOL)]], + ['binary', [new Type(Type::BUILTIN_TYPE_RESOURCE)]], + ['json', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true)]], + ['foo', [new Type(Type::BUILTIN_TYPE_OBJECT, true, 'Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\DoctrineRelation')]], + ['bar', [new Type( Type::BUILTIN_TYPE_OBJECT, false, 'Doctrine\Common\Collections\Collection', true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_OBJECT, false, 'Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\DoctrineRelation') - ))), - array('indexedBar', array(new Type( + )]], + ['indexedBar', [new Type( Type::BUILTIN_TYPE_OBJECT, false, 'Doctrine\Common\Collections\Collection', true, new Type(Type::BUILTIN_TYPE_STRING), new Type(Type::BUILTIN_TYPE_OBJECT, false, 'Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\DoctrineRelation') - ))), - array('indexedFoo', array(new Type( + )]], + ['indexedFoo', [new Type( Type::BUILTIN_TYPE_OBJECT, false, 'Doctrine\Common\Collections\Collection', true, new Type(Type::BUILTIN_TYPE_STRING), new Type(Type::BUILTIN_TYPE_OBJECT, false, 'Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\DoctrineRelation') - ))), - array('simpleArray', array(new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_STRING)))), - array('customFoo', null), - array('notMapped', null), - ); + )]], + ['simpleArray', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_STRING))]], + ['customFoo', null], + ['notMapped', null], + ]; } public function testGetPropertiesCatchException() diff --git a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineFooType.php b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineFooType.php index 8d0a9381143df..1b8cba50f3ece 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineFooType.php +++ b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineFooType.php @@ -38,7 +38,7 @@ public function getName() */ public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) { - return $platform->getClobTypeDeclarationSQL(array()); + return $platform->getClobTypeDeclarationSQL([]); } /** diff --git a/src/Symfony/Bridge/Doctrine/Tests/Resources/orm/BaseUser.orm.xml b/src/Symfony/Bridge/Doctrine/Tests/Resources/orm/BaseUser.orm.xml new file mode 100644 index 0000000000000..c38982990eda0 --- /dev/null +++ b/src/Symfony/Bridge/Doctrine/Tests/Resources/orm/BaseUser.orm.xml @@ -0,0 +1,12 @@ + + + + + + + + + diff --git a/src/Symfony/Bridge/Doctrine/Tests/Resources/validator/BaseUser.xml b/src/Symfony/Bridge/Doctrine/Tests/Resources/validator/BaseUser.xml new file mode 100644 index 0000000000000..bf64b92ca484d --- /dev/null +++ b/src/Symfony/Bridge/Doctrine/Tests/Resources/validator/BaseUser.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + diff --git a/src/Symfony/Bridge/Doctrine/Tests/Security/User/EntityUserProviderTest.php b/src/Symfony/Bridge/Doctrine/Tests/Security/User/EntityUserProviderTest.php index 9d0f79948ba6e..eaa86b39f8f5f 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Security/User/EntityUserProviderTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Security/User/EntityUserProviderTest.php @@ -105,7 +105,7 @@ public function testRefreshUserRequiresId() $user1 = new User(null, null, 'user1'); $provider = new EntityUserProvider($this->getManager($em), 'Symfony\Bridge\Doctrine\Tests\Fixtures\User', 'name'); - $this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}( + $this->expectException( 'InvalidArgumentException', 'You cannot refresh a user from the EntityUserProvider that does not contain an identifier. The user object has to be serialized with its own identifier mapped by Doctrine' ); @@ -125,7 +125,7 @@ public function testRefreshInvalidUser() $provider = new EntityUserProvider($this->getManager($em), 'Symfony\Bridge\Doctrine\Tests\Fixtures\User', 'name'); $user2 = new User(1, 2, 'user2'); - $this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}( + $this->expectException( 'Symfony\Component\Security\Core\Exception\UsernameNotFoundException', 'User with id {"id1":1,"id2":2} not found' ); @@ -145,7 +145,7 @@ public function testSupportProxy() $provider = new EntityUserProvider($this->getManager($em), 'Symfony\Bridge\Doctrine\Tests\Fixtures\User', 'name'); - $user2 = $em->getReference('Symfony\Bridge\Doctrine\Tests\Fixtures\User', array('id1' => 1, 'id2' => 1)); + $user2 = $em->getReference('Symfony\Bridge\Doctrine\Tests\Fixtures\User', ['id1' => 1, 'id2' => 1]); $this->assertTrue($provider->supportsClass(\get_class($user2))); } @@ -196,7 +196,7 @@ private function getManager($em, $name = null) private function getObjectManager($repository) { $em = $this->getMockBuilder('\Doctrine\Common\Persistence\ObjectManager') - ->setMethods(array('getClassMetadata', 'getRepository')) + ->setMethods(['getClassMetadata', 'getRepository']) ->getMockForAbstractClass(); $em->expects($this->any()) ->method('getRepository') @@ -208,8 +208,8 @@ private function getObjectManager($repository) private function createSchema($em) { $schemaTool = new SchemaTool($em); - $schemaTool->createSchema(array( + $schemaTool->createSchema([ $em->getClassMetadata('Symfony\Bridge\Doctrine\Tests\Fixtures\User'), - )); + ]); } } diff --git a/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php b/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php index d8b55eb808fc2..60007eb8a3ba8 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php @@ -90,7 +90,7 @@ protected function createRegistryMock(ObjectManager $em = null) protected function createRepositoryMock() { $repository = $this->getMockBuilder('Doctrine\Common\Persistence\ObjectRepository') - ->setMethods(array('findByCustom', 'find', 'findAll', 'findOneBy', 'findBy', 'getClassName')) + ->setMethods(['findByCustom', 'find', 'findAll', 'findOneBy', 'findBy', 'getClassName']) ->getMock() ; @@ -118,8 +118,8 @@ protected function createEntityManagerMock($repositoryMock) ->getMock() ; $refl = $this->getMockBuilder('Doctrine\Common\Reflection\StaticReflectionProperty') - ->setConstructorArgs(array($reflParser, 'property-name')) - ->setMethods(array('getValue')) + ->setConstructorArgs([$reflParser, 'property-name']) + ->setMethods(['getValue']) ->getMock() ; $refl @@ -127,7 +127,7 @@ protected function createEntityManagerMock($repositoryMock) ->method('getValue') ->will($this->returnValue(true)) ; - $classMetadata->reflFields = array('name' => $refl); + $classMetadata->reflFields = ['name' => $refl]; $em->expects($this->any()) ->method('getClassMetadata') ->will($this->returnValue($classMetadata)) @@ -144,7 +144,7 @@ protected function createValidator() private function createSchema(ObjectManager $em) { $schemaTool = new SchemaTool($em); - $schemaTool->createSchema(array( + $schemaTool->createSchema([ $em->getClassMetadata('Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdEntity'), $em->getClassMetadata('Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdNoToStringEntity'), $em->getClassMetadata('Symfony\Bridge\Doctrine\Tests\Fixtures\DoubleNameEntity'), @@ -156,7 +156,7 @@ private function createSchema(ObjectManager $em) $em->getClassMetadata('Symfony\Bridge\Doctrine\Tests\Fixtures\Employee'), $em->getClassMetadata('Symfony\Bridge\Doctrine\Tests\Fixtures\CompositeObjectNoToStringIdEntity'), $em->getClassMetadata('Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdStringWrapperNameEntity'), - )); + ]); } /** @@ -164,11 +164,11 @@ private function createSchema(ObjectManager $em) */ public function testValidateUniqueness() { - $constraint = new UniqueEntity(array( + $constraint = new UniqueEntity([ 'message' => 'myMessage', - 'fields' => array('name'), + 'fields' => ['name'], 'em' => self::EM_NAME, - )); + ]); $entity1 = new SingleIntIdEntity(1, 'Foo'); $entity2 = new SingleIntIdEntity(2, 'Foo'); @@ -190,19 +190,19 @@ public function testValidateUniqueness() ->atPath('property.path.name') ->setParameter('{{ value }}', '"Foo"') ->setInvalidValue($entity2) - ->setCause(array($entity1)) + ->setCause([$entity1]) ->setCode(UniqueEntity::NOT_UNIQUE_ERROR) ->assertRaised(); } public function testValidateCustomErrorPath() { - $constraint = new UniqueEntity(array( + $constraint = new UniqueEntity([ 'message' => 'myMessage', - 'fields' => array('name'), + 'fields' => ['name'], 'em' => self::EM_NAME, 'errorPath' => 'bar', - )); + ]); $entity1 = new SingleIntIdEntity(1, 'Foo'); $entity2 = new SingleIntIdEntity(2, 'Foo'); @@ -216,18 +216,18 @@ public function testValidateCustomErrorPath() ->atPath('property.path.bar') ->setParameter('{{ value }}', '"Foo"') ->setInvalidValue($entity2) - ->setCause(array($entity1)) + ->setCause([$entity1]) ->setCode(UniqueEntity::NOT_UNIQUE_ERROR) ->assertRaised(); } public function testValidateUniquenessWithNull() { - $constraint = new UniqueEntity(array( + $constraint = new UniqueEntity([ 'message' => 'myMessage', - 'fields' => array('name'), + 'fields' => ['name'], 'em' => self::EM_NAME, - )); + ]); $entity1 = new SingleIntIdEntity(1, null); $entity2 = new SingleIntIdEntity(2, null); @@ -243,12 +243,12 @@ public function testValidateUniquenessWithNull() public function testValidateUniquenessWithIgnoreNullDisabled() { - $constraint = new UniqueEntity(array( + $constraint = new UniqueEntity([ 'message' => 'myMessage', - 'fields' => array('name', 'name2'), + 'fields' => ['name', 'name2'], 'em' => self::EM_NAME, 'ignoreNull' => false, - )); + ]); $entity1 = new DoubleNameEntity(1, 'Foo', null); $entity2 = new DoubleNameEntity(2, 'Foo', null); @@ -270,7 +270,7 @@ public function testValidateUniquenessWithIgnoreNullDisabled() ->atPath('property.path.name') ->setParameter('{{ value }}', '"Foo"') ->setInvalidValue('Foo') - ->setCause(array($entity1)) + ->setCause([$entity1]) ->setCode(UniqueEntity::NOT_UNIQUE_ERROR) ->assertRaised(); } @@ -280,12 +280,12 @@ public function testValidateUniquenessWithIgnoreNullDisabled() */ public function testAllConfiguredFieldsAreCheckedOfBeingMappedByDoctrineWithIgnoreNullEnabled() { - $constraint = new UniqueEntity(array( + $constraint = new UniqueEntity([ 'message' => 'myMessage', - 'fields' => array('name', 'name2'), + 'fields' => ['name', 'name2'], 'em' => self::EM_NAME, 'ignoreNull' => true, - )); + ]); $entity1 = new SingleIntIdEntity(1, null); @@ -294,12 +294,12 @@ public function testAllConfiguredFieldsAreCheckedOfBeingMappedByDoctrineWithIgno public function testNoValidationIfFirstFieldIsNullAndNullValuesAreIgnored() { - $constraint = new UniqueEntity(array( + $constraint = new UniqueEntity([ 'message' => 'myMessage', - 'fields' => array('name', 'name2'), + 'fields' => ['name', 'name2'], 'em' => self::EM_NAME, 'ignoreNull' => true, - )); + ]); $entity1 = new DoubleNullableNameEntity(1, null, 'Foo'); $entity2 = new DoubleNullableNameEntity(2, null, 'Foo'); @@ -322,12 +322,12 @@ public function testNoValidationIfFirstFieldIsNullAndNullValuesAreIgnored() public function testValidateUniquenessWithValidCustomErrorPath() { - $constraint = new UniqueEntity(array( + $constraint = new UniqueEntity([ 'message' => 'myMessage', - 'fields' => array('name', 'name2'), + 'fields' => ['name', 'name2'], 'em' => self::EM_NAME, 'errorPath' => 'name2', - )); + ]); $entity1 = new DoubleNameEntity(1, 'Foo', 'Bar'); $entity2 = new DoubleNameEntity(2, 'Foo', 'Bar'); @@ -349,24 +349,24 @@ public function testValidateUniquenessWithValidCustomErrorPath() ->atPath('property.path.name2') ->setParameter('{{ value }}', '"Bar"') ->setInvalidValue('Bar') - ->setCause(array($entity1)) + ->setCause([$entity1]) ->setCode(UniqueEntity::NOT_UNIQUE_ERROR) ->assertRaised(); } public function testValidateUniquenessUsingCustomRepositoryMethod() { - $constraint = new UniqueEntity(array( + $constraint = new UniqueEntity([ 'message' => 'myMessage', - 'fields' => array('name'), + 'fields' => ['name'], 'em' => self::EM_NAME, 'repositoryMethod' => 'findByCustom', - )); + ]); $repository = $this->createRepositoryMock(); $repository->expects($this->once()) ->method('findByCustom') - ->will($this->returnValue(array())) + ->will($this->returnValue([])) ; $this->em = $this->createEntityManagerMock($repository); $this->registry = $this->createRegistryMock($this->em); @@ -382,12 +382,12 @@ public function testValidateUniquenessUsingCustomRepositoryMethod() public function testValidateUniquenessWithUnrewoundArray() { - $constraint = new UniqueEntity(array( + $constraint = new UniqueEntity([ 'message' => 'myMessage', - 'fields' => array('name'), + 'fields' => ['name'], 'em' => self::EM_NAME, 'repositoryMethod' => 'findByCustom', - )); + ]); $entity = new SingleIntIdEntity(1, 'foo'); @@ -396,9 +396,9 @@ public function testValidateUniquenessWithUnrewoundArray() ->method('findByCustom') ->will( $this->returnCallback(function () use ($entity) { - $returnValue = array( + $returnValue = [ $entity, - ); + ]; next($returnValue); return $returnValue; @@ -420,12 +420,12 @@ public function testValidateUniquenessWithUnrewoundArray() */ public function testValidateResultTypes($entity1, $result) { - $constraint = new UniqueEntity(array( + $constraint = new UniqueEntity([ 'message' => 'myMessage', - 'fields' => array('name'), + 'fields' => ['name'], 'em' => self::EM_NAME, 'repositoryMethod' => 'findByCustom', - )); + ]); $repository = $this->createRepositoryMock(); $repository->expects($this->once()) @@ -446,20 +446,20 @@ public function resultTypesProvider() { $entity = new SingleIntIdEntity(1, 'foo'); - return array( - array($entity, array($entity)), - array($entity, new \ArrayIterator(array($entity))), - array($entity, new ArrayCollection(array($entity))), - ); + return [ + [$entity, [$entity]], + [$entity, new \ArrayIterator([$entity])], + [$entity, new ArrayCollection([$entity])], + ]; } public function testAssociatedEntity() { - $constraint = new UniqueEntity(array( + $constraint = new UniqueEntity([ 'message' => 'myMessage', - 'fields' => array('single'), + 'fields' => ['single'], 'em' => self::EM_NAME, - )); + ]); $entity1 = new SingleIntIdEntity(1, 'foo'); $associated = new AssociationEntity(); @@ -485,17 +485,17 @@ public function testAssociatedEntity() ->setParameter('{{ value }}', 'foo') ->setInvalidValue($entity1) ->setCode(UniqueEntity::NOT_UNIQUE_ERROR) - ->setCause(array($associated, $associated2)) + ->setCause([$associated, $associated2]) ->assertRaised(); } public function testValidateUniquenessNotToStringEntityWithAssociatedEntity() { - $constraint = new UniqueEntity(array( + $constraint = new UniqueEntity([ 'message' => 'myMessage', - 'fields' => array('single'), + 'fields' => ['single'], 'em' => self::EM_NAME, - )); + ]); $entity1 = new SingleIntIdNoToStringEntity(1, 'foo'); $associated = new AssociationEntity2(); @@ -522,19 +522,19 @@ public function testValidateUniquenessNotToStringEntityWithAssociatedEntity() ->atPath('property.path.single') ->setParameter('{{ value }}', $expectedValue) ->setInvalidValue($entity1) - ->setCause(array($associated, $associated2)) + ->setCause([$associated, $associated2]) ->setCode(UniqueEntity::NOT_UNIQUE_ERROR) ->assertRaised(); } public function testAssociatedEntityWithNull() { - $constraint = new UniqueEntity(array( + $constraint = new UniqueEntity([ 'message' => 'myMessage', - 'fields' => array('single'), + 'fields' => ['single'], 'em' => self::EM_NAME, 'ignoreNull' => false, - )); + ]); $associated = new AssociationEntity(); $associated->single = null; @@ -552,19 +552,19 @@ public function testValidateUniquenessWithArrayValue() $repository = $this->createRepositoryMock(); $this->repositoryFactory->setRepository($this->em, 'Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdEntity', $repository); - $constraint = new UniqueEntity(array( + $constraint = new UniqueEntity([ 'message' => 'myMessage', - 'fields' => array('phoneNumbers'), + 'fields' => ['phoneNumbers'], 'em' => self::EM_NAME, 'repositoryMethod' => 'findByCustom', - )); + ]); $entity1 = new SingleIntIdEntity(1, 'foo'); $entity1->phoneNumbers[] = 123; $repository->expects($this->once()) ->method('findByCustom') - ->will($this->returnValue(array($entity1))) + ->will($this->returnValue([$entity1])) ; $this->em->persist($entity1); @@ -580,8 +580,8 @@ public function testValidateUniquenessWithArrayValue() $this->buildViolation('myMessage') ->atPath('property.path.phoneNumbers') ->setParameter('{{ value }}', 'array') - ->setInvalidValue(array(123)) - ->setCause(array($entity1)) + ->setInvalidValue([123]) + ->setCause([$entity1]) ->setCode(UniqueEntity::NOT_UNIQUE_ERROR) ->assertRaised(); } @@ -592,11 +592,11 @@ public function testValidateUniquenessWithArrayValue() 8000 */ public function testDedicatedEntityManagerNullObject() { - $constraint = new UniqueEntity(array( + $constraint = new UniqueEntity([ 'message' => 'myMessage', - 'fields' => array('name'), + 'fields' => ['name'], 'em' => self::EM_NAME, - )); + ]); $this->em = null; $this->registry = $this->createRegistryMock($this->em); @@ -614,11 +614,11 @@ public function testDedicatedEntityManagerNullObject() */ public function testEntityManagerNullObject() { - $constraint = new UniqueEntity(array( + $constraint = new UniqueEntity([ 'message' => 'myMessage', - 'fields' => array('name'), + 'fields' => ['name'], // no "em" option set - )); + ]); $this->em = null; $this->registry = $this->createRegistryMock($this->em); @@ -643,11 +643,11 @@ public function testValidateUniquenessOnNullResult() $this->validator = $this->createValidator(); $this->validator->initialize($this->context); - $constraint = new UniqueEntity(array( + $constraint = new UniqueEntity([ 'message' => 'myMessage', - 'fields' => array('name'), + 'fields' => ['name'], 'em' => self::EM_NAME, - )); + ]); $entity = new SingleIntIdEntity(1, null); @@ -660,12 +660,12 @@ public function testValidateUniquenessOnNullResult() public function testValidateInheritanceUniqueness() { - $constraint = new UniqueEntity(array( + $constraint = new UniqueEntity([ 'message' => 'myMessage', - 'fields' => array('name'), + 'fields' => ['name'], 'em' => self::EM_NAME, 'entityClass' => 'Symfony\Bridge\Doctrine\Tests\Fixtures\Person', - )); + ]); $entity1 = new Person(1, 'Foo'); $entity2 = new Employee(2, 'Foo'); @@ -687,8 +687,8 @@ public function testValidateInheritanceUniqueness() ->atPath('property.path.name') ->setInvalidValue('Foo') ->setCode('23bd9dbf-6b9b-41cd-a99e-4844bcf3077f') - ->setCause(array($entity1)) - ->setParameters(array('{{ value }}' => '"Foo"')) + ->setCause([$entity1]) + ->setParameters(['{{ value }}' => '"Foo"']) ->assertRaised(); } @@ -698,12 +698,12 @@ public function testValidateInheritanceUniqueness() */ public function testInvalidateRepositoryForInheritance() { - $constraint = new UniqueEntity(array( + $constraint = new UniqueEntity([ 'message' => 'myMessage', - 'fields' => array('name'), + 'fields' => ['name'], 'em' => self::EM_NAME, 'entityClass' => 'Symfony\Bridge\Doctrine\Tests\Fixtures\SingleStringIdEntity', - )); + ]); $entity = new Person(1, 'Foo'); $this->validator->validate($entity, $constraint); @@ -711,11 +711,11 @@ public function testInvalidateRepositoryForInheritance() public function testValidateUniquenessWithCompositeObjectNoToStringIdEntity() { - $constraint = new UniqueEntity(array( + $constraint = new UniqueEntity([ 'message' => 'myMessage', - 'fields' => array('objectOne', 'objectTwo'), + 'fields' => ['objectOne', 'objectTwo'], 'em' => self::EM_NAME, - )); + ]); $objectOne = new SingleIntIdNoToStringEntity(1, 'foo'); $objectTwo = new SingleIntIdNoToStringEntity(2, 'bar'); @@ -739,18 +739,18 @@ public function testValidateUniquenessWithCompositeObjectNoToStringIdEntity() ->atPath('property.path.objectOne') ->setParameter('{{ value }}', $expectedValue) ->setInvalidValue($objectOne) - ->setCause(array($entity)) + ->setCause([$entity]) ->setCode(UniqueEntity::NOT_UNIQUE_ERROR) ->assertRaised(); } public function testValidateUniquenessWithCustomDoctrineTypeValue() { - $constraint = new UniqueEntity(array( + $constraint = new UniqueEntity([ 'message' => 'myMessage', - 'fields' => array('name'), + 'fields' => ['name'], 'em' => self::EM_NAME, - )); + ]); $existingEntity = new SingleIntIdStringWrapperNameEntity(1, new StringWrapper('foo')); @@ -767,7 +767,7 @@ public function testValidateUniquenessWithCustomDoctrineTypeValue() ->atPath('property.path.name') ->setParameter('{{ value }}', $expectedValue) ->setInvalidValue($existingEntity->name) - ->setCause(array($existingEntity)) + ->setCause([$existingEntity]) ->setCode(UniqueEntity::NOT_UNIQUE_ERROR) ->assertRaised(); } @@ -777,11 +777,11 @@ public function testValidateUniquenessWithCustomDoctrineTypeValue() */ public function testValidateUniquenessCause() { - $constraint = new UniqueEntity(array( + $constraint = new UniqueEntity([ 'message' => 'myMessage', - 'fields' => array('name'), + 'fields' => ['name'], 'em' => self::EM_NAME, - )); + ]); $entity1 = new SingleIntIdEntity(1, 'Foo'); $entity2 = new SingleIntIdEntity(2, 'Foo'); @@ -803,7 +803,7 @@ public function testValidateUniquenessCause() ->atPath('property.path.name') ->setParameter('{{ value }}', '"Foo"') ->setInvalidValue($entity2) - ->setCause(array($entity1)) + ->setCause([$entity1]) ->setCode(UniqueEntity::NOT_UNIQUE_ERROR) ->assertRaised(); } diff --git a/src/Symfony/Bridge/Doctrine/Tests/Validator/DoctrineLoaderTest.php b/src/Symfony/Bridge/Doctrine/Tests/Validator/DoctrineLoaderTest.php new file mode 100644 index 0000000000000..cde956eed3493 --- /dev/null +++ b/src/Symfony/Bridge/Doctrine/Tests/Validator/DoctrineLoaderTest.php @@ -0,0 +1,119 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Tests\Validator; + +use PHPUnit\Framework\TestCase; +use Symfony\Bridge\Doctrine\Test\DoctrineTestHelper; +use Symfony\Bridge\Doctrine\Tests\Fixtures\BaseUser; +use Symfony\Bridge\Doctrine\Tests\Fixtures\DoctrineLoaderEntity; +use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; +use Symfony\Bridge\Doctrine\Validator\DoctrineLoader; +use Symfony\Component\Validator\Constraints\Length; +use Symfony\Component\Validator\Mapping\ClassMetadata; +use Symfony\Component\Validator\Tests\Fixtures\Entity; +use Symfony\Component\Validator\Validation; +use Symfony\Component\Validator\ValidatorBuilder; + +/** + * @author Kévin Dunglas + */ +class DoctrineLoaderTest extends TestCase +{ + public function testLoadClassMetadata() + { + if (!method_exists(ValidatorBuilder::class, 'addLoader')) { + $this->markTestSkipped('Auto-mapping requires symfony/validation 4.2+'); + } + + $validator = Validation::createValidatorBuilder() + ->enableAnnotationMapping() + ->addLoader(new DoctrineLoader(DoctrineTestHelper::createTestEntityManager())) + ->getValidator() + ; + + $classMetadata = $validator->getMetadataFor(new DoctrineLoaderEntity()); + + $classConstraints = $classMetadata->getConstraints(); + $this->assertCount(2, $classConstraints); + $this->assertInstanceOf(UniqueEntity::class, $classConstraints[0]); + $this->assertInstanceOf(UniqueEntity::class, $classConstraints[1]); + $this->assertSame(['alreadyMappedUnique'], $classConstraints[0]->fields); + $this->assertSame('unique', $classConstraints[1]->fields); + + $maxLengthMetadata = $classMetadata->getPropertyMetadata('maxLength'); + $this->assertCount(1, $maxLengthMetadata); + $maxLengthConstraints = $maxLengthMetadata[0]->getConstraints(); + $this->assertCount(1, $maxLengthConstraints); + $this->assertInstanceOf(Length::class, $maxLengthConstraints[0]); + $this->assertSame(20, $maxLengthConstraints[0]->max); + + $mergedMaxLengthMetadata = $classMetadata->getPropertyMetadata('mergedMaxLength'); + $this->assertCount(1, $mergedMaxLengthMetadata); + $mergedMaxLengthConstraints = $mergedMaxLengthMetadata[0]->getConstraints(); + $this->assertCount(1, $mergedMaxLengthConstraints); + $this->assertInstanceOf(Length::class, $mergedMaxLengthConstraints[0]); + $this->assertSame(20, $mergedMaxLengthConstraints[0]->max); + $this->assertSame(5, $mergedMaxLengthConstraints[0]->min); + + $alreadyMappedMaxLengthMetadata = $classMetadata->getPropertyMetadata('alreadyMappedMaxLength'); + $this->assertCount(1, $alreadyMappedMaxLengthMetadata); + $alreadyMappedMaxLengthConstraints = $alreadyMappedMaxLengthMetadata[0]->getConstraints(); + $this->assertCount(1, $alreadyMappedMaxLengthConstraints); + $this->assertInstanceOf(Length::class, $alreadyMappedMaxLengthConstraints[0]); + $this->assertSame(10, $alreadyMappedMaxLengthConstraints[0]->max); + $this->assertSame(1, $alreadyMappedMaxLengthConstraints[0]->min); + } + + public function testFieldMappingsConfiguration() + { + if (!method_exists(ValidatorBuilder::class, 'addLoader')) { + $this->markTestSkipped('Auto-mapping requires symfony/validation 4.2+'); + } + + $validator = Validation::createValidatorBuilder() + ->enableAnnotationMapping() + ->addXmlMappings([__DIR__.'/../Resources/validator/BaseUser.xml']) + ->addLoader( + new DoctrineLoader( + DoctrineTestHelper::createTestEntityManager( + DoctrineTestHelper::createTestConfigurationWithXmlLoader() + ), '{}' + ) + ) + ->getValidator(); + + $classMetadata = $validator->getMetadataFor(new BaseUser(1, 'DemoUser')); + + $constraints = $classMetadata->getConstraints(); + $this->assertCount(0, $constraints); + } + + /** + * @dataProvider regexpProvider + */ + public function testClassValidator(bool $expected, string $classValidatorRegexp = null) + { + $doctrineLoader = new DoctrineLoader(DoctrineTestHelper::createTestEntityManager(), $classValidatorRegexp); + + $classMetadata = new ClassMetadata(DoctrineLoaderEntity::class); + $this->assertSame($expected, $doctrineLoader->loadClassMetadata($classMetadata)); + } + + public function regexpProvider() + { + return [ + [true, null], + [true, '{^'.preg_quote(DoctrineLoaderEntity::class).'$|^'.preg_quote(Entity::class).'$}'], + [false, '{^'.preg_quote(Entity::class).'$}'], + ]; + } +} diff --git a/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntity.php b/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntity.php index c9b13dcf59960..2c319709ebc9d 100644 --- a/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntity.php +++ b/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntity.php @@ -30,17 +30,17 @@ class UniqueEntity extends Constraint public $em = null; public $entityClass = null; public $repositoryMethod = 'findBy'; - public $fields = array(); + public $fields = []; public $errorPath = null; public $ignoreNull = true; - protected static $errorNames = array( + protected static $errorNames = [ self::NOT_UNIQUE_ERROR => 'NOT_UNIQUE_ERROR', - ); + ]; public function getRequiredOptions() { - return array('fields'); + return ['fields']; } /** diff --git a/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php b/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php index 161a187ff98ab..47cb2bd730317 100644 --- a/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php +++ b/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php @@ -81,7 +81,7 @@ public function validate($entity, Constraint $constraint) $class = $em->getClassMetadata(\get_class($entity)); /* @var $class \Doctrine\Common\Persistence\Mapping\ClassMetadata */ - $criteria = array(); + $criteria = []; $hasNullValue = false; foreach ($fields as $fieldName) { @@ -149,15 +149,15 @@ public function validate($entity, Constraint $constraint) if ($result instanceof \Iterator) { $result->rewind(); if ($result instanceof \Countable && 1 < \count($result)) { - $result = array($result->current(), $result->current()); + $result = [$result->current(), $result->current()]; } else { $result = $result->current(); - $result = null === $result ? array() : array($result); + $result = null === $result ? [] : [$result]; } } elseif (\is_array($result)) { reset($result); } else { - $result = null === $result ? array() : array($result); + $result = null === $result ? [] : [$result]; } /* If no entity matched the query criteria or a single entity matched, @@ -197,7 +197,7 @@ private function formatWithIdentifiers(ObjectManager $em, ClassMetadata $class, } else { // this case might happen if the non unique column has a custom doctrine type and its value is an object // in which case we cannot get any identifiers for it - $identifiers = array(); + $identifiers = []; } } else { $identifiers = $class->getIdentifierValues($value); diff --git a/src/Symfony/Bridge/Doctrine/Validator/DoctrineLoader.php b/src/Symfony/Bridge/Doctrine/Validator/DoctrineLoader.php new file mode 100644 index 0000000000000..376670e8e405a --- /dev/null +++ b/src/Symfony/Bridge/Doctrine/Validator/DoctrineLoader.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\Bridge\Doctrine\Validator; + +use Doctrine\Common\Persistence\Mapping\MappingException; +use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\Mapping\ClassMetadataInfo; +use Doctrine\ORM\Mapping\MappingException as OrmMappingException; +use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; +use Symfony\Component\Validator\Constraints\Length; +use Symfony\Component\Validator\Mapping\ClassMetadata; +use Symfony\Component\Validator\Mapping\Loader\LoaderInterface; + +/** + * Guesses and loads the appropriate constraints using Doctrine's metadata. + * + * @author Kévin Dunglas + */ +final class DoctrineLoader implements LoaderInterface +{ + private $entityManager; + private $classValidatorRegexp; + + public function __construct(EntityManagerInterface $entityManager, string $classValidatorRegexp = null) + { + $this->entityManager = $entityManager; + $this->classValidatorRegexp = $classValidatorRegexp; + } + + /** + * {@inheritdoc} + */ + public function loadClassMetadata(ClassMetadata $metadata): bool + { + $className = $metadata->getClassName(); + if (null !== $this->classValidatorRegexp && !preg_match($this->classValidatorRegexp, $className)) { + return false; + } + + try { + $doctrineMetadata = $this->entityManager->getClassMetadata($className); + } catch (MappingException | OrmMappingException $exception) { + return false; + } + + if (!$doctrineMetadata instanceof ClassMetadataInfo) { + return false; + } + + /* Available keys: + - type + - scale + - length + - unique + - nullable + - precision + */ + $existingUniqueFields = $this->getExistingUniqueFields($metadata); + + // Type and nullable aren't handled here, use the PropertyInfo Loader instead. + foreach ($doctrineMetadata->fieldMappings as $mapping) { + if (true === ($mapping['unique'] ?? false) && !isset($existingUniqueFields[$mapping['fieldName']])) { + $metadata->addConstraint(new UniqueEntity(['fields' => $mapping['fieldName']])); + } + + if (null === ($mapping['length'] ?? null)) { + continue; + } + + $constraint = $this->getLengthConstraint($metadata, $mapping['fieldName']); + if (null === $constraint) { + $metadata->addPropertyConstraint($mapping['fieldName'], new Length(['max' => $mapping['length']])); + } elseif (null === $constraint->max) { + // If a Length constraint exists and no max length has been explicitly defined, set it + $constraint->max = $mapping['length']; + } + } + + return true; + } + + private function getLengthConstraint(ClassMetadata $metadata, string $fieldName): ?Length + { + foreach ($metadata->getPropertyMetadata($fieldName) as $propertyMetadata) { + foreach ($propertyMetadata->getConstraints() as $constraint) { + if ($constraint instanceof Length) { + return $constraint; + } + } + } + + return null; + } + + private function getExistingUniqueFields(ClassMetadata $metadata): array + { + $fields = []; + foreach ($metadata->getConstraints() as $constraint) { + if (!$constraint instanceof UniqueEntity) { + continue; + } + + if (\is_string($constraint->fields)) { + $fields[$constraint->fields] = true; + } elseif (\is_array($constraint->fields) && 1 === \count($constraint->fields)) { + $fields[$constraint->fields[0]] = true; + } + } + + return $fields; + } +} diff --git a/src/Symfony/Bridge/Doctrine/composer.json b/src/Symfony/Bridge/Doctrine/composer.json index bc828b43e21b6..819af8522046f 100644 --- a/src/Symfony/Bridge/Doctrine/composer.json +++ b/src/Symfony/Bridge/Doctrine/composer.json @@ -17,7 +17,6 @@ ], "require": { "php": "^7.1.3", - "doctrine/collections": "~1.0", "doctrine/event-manager": "~1.0", "doctrine/persistence": "~1.0", "symfony/contracts": "^1.0", @@ -27,7 +26,7 @@ "require-dev": { "symfony/stopwatch": "~3.4|~4.0", "symfony/dependency-injection": "~3.4|~4.0", - "symfony/form": "~3.4|~4.0", + "symfony/form": "~4.3", "symfony/http-kernel": "~3.4|~4.0", "symfony/messenger": "~4.2", "symfony/property-access": "~3.4|~4.0", @@ -39,6 +38,7 @@ "symfony/translation": "~3.4|~4.0", "doctrine/annotations": "~1.0", "doctrine/cache": "~1.6", + "doctrine/collections": "~1.0", "doctrine/data-fixtures": "1.0.*", "doctrine/dbal": "~2.4", "doctrine/orm": "^2.4.5", @@ -46,7 +46,9 @@ }, "conflict": { "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0", - "symfony/dependency-injection": "<3.4" + "symfony/dependency-injection": "<3.4", + "symfony/form": "<4.3", + "symfony/messenger": "<4.2" }, "suggest": { "symfony/form": "", @@ -65,7 +67,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "4.2-dev" + "dev-master": "4.3-dev" } } } diff --git a/src/Symfony/Bridge/Monolog/CHANGELOG.md b/src/Symfony/Bridge/Monolog/CHANGELOG.md index 2cb6c3f39d897..8b519c9f31104 100644 --- a/src/Symfony/Bridge/Monolog/CHANGELOG.md +++ b/src/Symfony/Bridge/Monolog/CHANGELOG.md @@ -1,6 +1,12 @@ CHANGELOG ========= +4.3.0 +----- + + * added `ConsoleCommandProcessor`: monolog processor that adds command name and arguments + * added `RouteProcessor`: monolog processor that adds route name, controller::action and route params + 4.2.0 ----- diff --git a/src/Symfony/Bridge/Monolog/Formatter/ConsoleFormatter.php b/src/Symfony/Bridge/Monolog/Formatter/ConsoleFormatter.php index feb735e8ac740..aafe9e45e07be 100644 --- a/src/Symfony/Bridge/Monolog/Formatter/ConsoleFormatter.php +++ b/src/Symfony/Bridge/Monolog/Formatter/ConsoleFormatter.php @@ -30,7 +30,7 @@ class ConsoleFormatter implements FormatterInterface const SIMPLE_FORMAT = "%datetime% %start_tag%%level_name%%end_tag% [%channel%] %message%%context%%extra%\n"; const SIMPLE_DATE = 'H:i:s'; - private static $levelColorMap = array( + private static $levelColorMap = [ Logger::DEBUG => 'fg=white', Logger::INFO => 'fg=green', Logger::NOTICE => 'fg=blue', @@ -39,7 +39,7 @@ class ConsoleFormatter implements FormatterInterface Logger::CRITICAL => 'fg=red', Logger::ALERT => 'fg=red', Logger::EMERGENCY => 'fg=white;bg=red', - ); + ]; private $options; private $cloner; @@ -53,28 +53,28 @@ class ConsoleFormatter implements FormatterInterface * * colors: If true, the log string contains ANSI code to add color; * * multiline: If false, "context" and "extra" are dumped on one line. */ - public function __construct(array $options = array()) + public function __construct(array $options = []) { - $this->options = array_replace(array( + $this->options = array_replace([ 'format' => self::SIMPLE_FORMAT, 'date_format' => self::SIMPLE_DATE, 'colors' => true, 'multiline' => false, 'level_name_format' => '%-9s', 'ignore_empty_context_and_extra' => true, - ), $options); + ], $options); if (class_exists(VarCloner::class)) { $this->cloner = new VarCloner(); - $this->cloner->addCasters(array( - '*' => array($this, 'castObject'), - )); + $this->cloner->addCasters([ + '*' => [$this, 'castObject'], + ]); $this->outputBuffer = fopen('php://memory', 'r+b'); if ($this->options['multiline']) { $output = $this->outputBuffer; } else { - $output = array($this, 'echoLine'); + $output = [$this, 'echoLine']; } $this->dumper = new CliDumper($output, null, CliDumper::DUMP_LIGHT_ARRAY | CliDumper::DUMP_COMMA_SEPARATOR); @@ -114,8 +114,10 @@ public function format(array $record) $extra = ''; } - $formatted = strtr($this->options['format'], array( - '%datetime%' => $record['datetime']->format($this->options['date_format']), + $formatted = strtr($this->options['format'], [ + '%datetime%' => $record['datetime'] instanceof \DateTimeInterface + ? $record['datetime']->format($this->options['date_format']) + : $record['datetime'], '%start_tag%' => sprintf('<%s>', $levelColor), '%level_name%' => sprintf($this->options['level_name_format'], $record['level_name']), '%end_tag%' => '', @@ -123,7 +125,7 @@ public function format(array $record) '%message%' => $this->replacePlaceHolder($record)['message'], '%context%' => $context, '%extra%' => $extra, - )); + ]); return $formatted; } @@ -149,7 +151,7 @@ public function castObject($v, array $a, Stub $s, $isNested) if ($isNested && !$v instanceof \DateTimeInterface) { $s->cut = -1; - $a = array(); + $a = []; } return $a; @@ -165,7 +167,7 @@ private function replacePlaceHolder(array $record) $context = $record['context']; - $replacements = array(); + $replacements = []; foreach ($context as $k => $v) { // Remove quotes added by the dumper around string. $v = trim($this->dumpData($v, false), '"'); diff --git a/src/Symfony/Bridge/Monolog/Handler/ChromePhpHandler.php b/src/Symfony/Bridge/Monolog/Handler/ChromePhpHandler.php index 65e99f6f87b75..4f98d58b1ffbc 100644 --- a/src/Symfony/Bridge/Monolog/Handler/ChromePhpHandler.php +++ b/src/Symfony/Bridge/Monolog/Handler/ChromePhpHandler.php @@ -19,10 +19,12 @@ * ChromePhpHandler. * * @author Christophe Coevoet + * + * @final since Symfony 4.3 */ class ChromePhpHandler extends BaseChromePhpHandler { - private $headers = array(); + private $headers = []; /** * @var Response @@ -40,7 +42,7 @@ public function onKernelResponse(FilterResponseEvent $event) if (!preg_match(static::USER_AGENT_REGEX, $event->getRequest()->headers->get('User-Agent'))) { $this->sendHeaders = false; - $this->headers = array(); + $this->headers = []; return; } @@ -49,7 +51,7 @@ public function onKernelResponse(FilterResponseEvent $event) foreach ($this->headers as $header => $content) { $this->response->headers->set($header, $content); } - $this->headers = array(); + $this->headers = []; } /** diff --git a/src/Symfony/Bridge/Monolog/Handler/ConsoleHandler.php b/src/Symfony/Bridge/Monolog/Handler/ConsoleHandler.php index a23dc327c48f6..1ec91e43f29a2 100644 --- a/src/Symfony/Bridge/Monolog/Handler/ConsoleHandler.php +++ b/src/Symfony/Bridge/Monolog/Handler/ConsoleHandler.php @@ -43,13 +43,14 @@ class ConsoleHandler extends AbstractProcessingHandler implements EventSubscriberInterface { private $output; - private $verbosityLevelMap = array( + private $verbosityLevelMap = [ OutputInterface::VERBOSITY_QUIET => Logger::ERROR, OutputInterface::VERBOSITY_NORMAL => Logger::WARNING, OutputInterface::VERBOSITY_VERBOSE => Logger::NOTICE, OutputInterface::VERBOSITY_VERY_VERBOSE => Logger::INFO, OutputInterface::VERBOSITY_DEBUG => Logger::DEBUG, - ); + ]; + private $consoleFormaterOptions; /** * @param OutputInterface|null $output The console output to use (the handler remains disabled when passing null @@ -58,7 +59,7 @@ class ConsoleHandler extends AbstractProcessingHandler implements EventSubscribe * @param array $verbosityLevelMap Array that maps the OutputInterface verbosity to a minimum logging * level (leave empty to use the default mapping) */ - public function __construct(OutputInterface $output = null, bool $bubble = true, array $verbosityLevelMap = array()) + public function __construct(OutputInterface $output = null, bool $bubble = true, array $verbosityLevelMap = [], array $consoleFormaterOptions = []) { parent::__construct(Logger::DEBUG, $bubble); $this->output = $output; @@ -66,6 +67,8 @@ public function __construct(OutputInterface $output = null, bool $bubble = true, if ($verbosityLevelMap) { $this->verbosityLevelMap = $verbosityLevelMap; } + + $this->consoleFormaterOptions = $consoleFormaterOptions; } /** @@ -131,10 +134,10 @@ public function onTerminate(ConsoleTerminateEvent $event) */ public static function getSubscribedEvents() { - return array( - ConsoleEvents::COMMAND => array('onCommand', 255), - ConsoleEvents::TERMINATE => array('onTerminate', -255), - ); + return [ + ConsoleEvents::COMMAND => ['onCommand', 255], + ConsoleEvents::TERMINATE => ['onTerminate', -255], + ]; } /** @@ -155,13 +158,13 @@ protected function getDefaultFormatter() return new LineFormatter(); } if (!$this->output) { - return new ConsoleFormatter(); + return new ConsoleFormatter($this->consoleFormaterOptions); } - return new ConsoleFormatter(array( + return new ConsoleFormatter(array_replace([ 'colors' => $this->output->isDecorated(), 'multiline' => OutputInterface::VERBOSITY_DEBUG <= $this->output->getVerbosity(), - )); + ], $this->consoleFormaterOptions)); } /** diff --git a/src/Symfony/Bridge/Monolog/Handler/FingersCrossed/HttpCodeActivationStrategy.php b/src/Symfony/Bridge/Monolog/Handler/FingersCrossed/HttpCodeActivationStrategy.php index 2a1ae70a1a091..ae8fd3650a36b 100644 --- a/src/Symfony/Bridge/Monolog/Handler/FingersCrossed/HttpCodeActivationStrategy.php +++ b/src/Symfony/Bridge/Monolog/Handler/FingersCrossed/HttpCodeActivationStrategy.php @@ -31,10 +31,10 @@ class HttpCodeActivationStrategy extends ErrorLevelActivationStrategy public function __construct(RequestStack $requestStack, array $exclusions, $actionLevel) { foreach ($exclusions as $exclusion) { - if (!array_key_exists('code', $exclusion)) { + if (!\array_key_exists('code', $exclusion)) { throw new \LogicException(sprintf('An exclusion must have a "code" key')); } - if (!array_key_exists('urls', $exclusion)) { + if (!\array_key_exists('urls', $exclusion)) { throw new \LogicException(sprintf('An exclusion must have a "urls" key')); } } @@ -60,7 +60,6 @@ public function isHandlerActivated(array $record) continue; } - $urlBlacklist = null; if (\count($exclusion['urls'])) { return !preg_match('{('.implode('|', $exclusion['urls']).')}i', $request->getPathInfo()); } diff --git a/src/Symfony/Bridge/Monolog/Handler/FirePHPHandler.php b/src/Symfony/Bridge/Monolog/Handler/FirePHPHandler.php index 9956edad386d1..b235fc101ea73 100644 --- a/src/Symfony/Bridge/Monolog/Handler/FirePHPHandler.php +++ b/src/Symfony/Bridge/Monolog/Handler/FirePHPHandler.php @@ -19,10 +19,12 @@ * FirePHPHandler. * * @author Jordi Boggiano + * + * @final since Symfony 4.3 */ class FirePHPHandler extends BaseFirePHPHandler { - private $headers = array(); + private $headers = []; /** * @var Response @@ -42,7 +44,7 @@ public function onKernelResponse(FilterResponseEvent $event) if (!preg_match('{\bFirePHP/\d+\.\d+\b}', $request->headers->get('User-Agent')) && !$request->headers->has('X-FirePHP-Version')) { self::$sendHeaders = false; - $this->headers = array(); + $this->headers = []; return; } @@ -51,7 +53,7 @@ public function onKernelResponse(FilterResponseEvent $event) foreach ($this->headers as $header => $content) { $this->response->headers->set($header, $content); } - $this->headers = array(); + $this->headers = []; } /** diff --git a/src/Symfony/Bridge/Monolog/Handler/ServerLogHandler.php b/src/Symfony/Bridge/Monolog/Handler/ServerLogHandler.php index 6c21a335f0856..22c035731dc5b 100644 --- a/src/Symfony/Bridge/Monolog/Handler/ServerLogHandler.php +++ b/src/Symfony/Bridge/Monolog/Handler/ServerLogHandler.php @@ -24,7 +24,7 @@ class ServerLogHandler extends AbstractHandler private $context; private $socket; - public function __construct(string $host, int $level = Logger::DEBUG, bool $bubble = true, array $context = array()) + public function __construct(string $host, int $level = Logger::DEBUG, bool $bubble = true, array $context = []) { parent::__construct($level, $bubble); @@ -102,13 +102,13 @@ private function formatRecord(array $record) { if ($this->processors) { foreach ($this->processors as $processor) { - $record = \call_user_func($processor, $record); 6D40 + $record = $processor($record); } } $recordFormatted = $this->getFormatter()->format($record); - foreach (array('log_uuid', 'uuid', 'uid') as $key) { + foreach (['log_uuid', 'uuid', 'uid'] as $key) { if (isset($record['extra'][$key])) { $recordFormatted['log_id'] = $record['extra'][$key]; break; diff --git a/src/Symfony/Bridge/Monolog/Handler/SwiftMailerHandler.php b/src/Symfony/Bridge/Monolog/Handler/SwiftMailerHandler.php index fcbd98ac7dc64..93f2f72e6457a 100644 --- a/src/Symfony/Bridge/Monolog/Handler/SwiftMailerHandler.php +++ b/src/Symfony/Bridge/Monolog/Handler/SwiftMailerHandler.php @@ -19,6 +19,8 @@ * Extended SwiftMailerHandler that flushes mail queue if necessary. * * @author Philipp Kräutli + * + * @final since Symfony 4.3 */ class SwiftMailerHandler extends BaseSwiftMailerHandler { @@ -59,6 +61,14 @@ protected function send($content, array $records) } } + /** + * {@inheritdoc} + */ + public function reset() + { + $this->flushMemorySpool(); + } + /** * Flushes the mail queue if a memory spool is used. */ diff --git a/src/Symfony/Bridge/Monolog/LICENSE b/src/Symfony/Bridge/Monolog/LICENSE index 21d7fb9e2f29b..a677f43763ca4 100644 --- a/src/Symfony/Bridge/Monolog/LICENSE +++ b/src/Symfony/Bridge/Monolog/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2018 Fabien Potencier +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 diff --git a/src/Symfony/Bridge/Monolog/Logger.php b/src/Symfony/Bridge/Monolog/Logger.php index 1d896f4ac4f2f..5141ac955f44d 100644 --- a/src/Symfony/Bridge/Monolog/Logger.php +++ b/src/Symfony/Bridge/Monolog/Logger.php @@ -12,6 +12,7 @@ namespace Symfony\Bridge\Monolog; use Monolog\Logger as BaseLogger; +use Monolog\ResettableInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Log\DebugLoggerInterface; use Symfony\Contracts\Service\ResetInterface; @@ -36,7 +37,7 @@ public function getLogs(/* Request $request = null */) return $logger->getLogs(...\func_get_args()); } - return array(); + return []; } /** @@ -73,6 +74,25 @@ public function clear() public function reset() { $this->clear(); + + if ($this instanceof ResettableInterface) { + parent::reset(); + } + } + + public function removeDebugLogger() + { + foreach ($this->processors as $k => $processor) { + if ($processor instanceof DebugLoggerInterface) { + unset($this->processors[$k]); + } + } + + foreach ($this->handlers as $k => $handler) { + if ($handler instanceof DebugLoggerInterface) { + unset($this->handlers[$k]); + } + } } /** diff --git a/src/Symfony/Bridge/Monolog/Processor/ConsoleCommandProcessor.php b/src/Symfony/Bridge/Monolog/Processor/ConsoleCommandProcessor.php new file mode 100644 index 0000000000000..2891f4f2f4916 --- /dev/null +++ b/src/Symfony/Bridge/Monolog/Processor/ConsoleCommandProcessor.php @@ -0,0 +1,69 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Monolog\Processor; + +use Symfony\Component\Console\ConsoleEvents; +use Symfony\Component\Console\Event\ConsoleEvent; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Contracts\Service\ResetInterface; + +/** + * Adds the current console command information to the log entry. + * + * @author Piotr Stankowski + */ +class ConsoleCommandProcessor implements EventSubscriberInterface, ResetInterface +{ + private $commandData; + private $includeArguments; + private $includeOptions; + + public function __construct(bool $includeArguments = true, bool $includeOptions = false) + { + $this->includeArguments = $includeArguments; + $this->includeOptions = $includeOptions; + } + + public function __invoke(array $records) + { + if (null !== $this->commandData && !isset($records['extra']['command'])) { + $records['extra']['command'] = $this->commandData; + } + + return $records; + } + + public function reset() + { + $this->commandData = null; + } + + public function addCommandData(ConsoleEvent $event) + { + $this->commandData = [ + 'name' => $event->getCommand()->getName(), + ]; + if ($this->includeArguments) { + $this->commandData['arguments'] = $event->getInput()->getArguments(); + } + if ($this->includeOptions) { + $this->commandData['options'] = $event->getInput()->getOptions(); + } + } + + public static function getSubscribedEvents() + { + return [ + ConsoleEvents::COMMAND => ['addCommandData', 1], + ]; + } +} diff --git a/src/Symfony/Bridge/Monolog/Processor/DebugProcessor.php b/src/Symfony/Bridge/Monolog/Processor/DebugProcessor.php index 2abd9d06a19c2..24a670de79630 100644 --- a/src/Symfony/Bridge/Monolog/Processor/DebugProcessor.php +++ b/src/Symfony/Bridge/Monolog/Processor/DebugProcessor.php @@ -19,8 +19,8 @@ class DebugProcessor implements DebugLoggerInterface, ResetInterface { - private $records = array(); - private $errorCount = array(); + private $records = []; + private $errorCount = []; private $requestStack; public function __construct(RequestStack $requestStack = null) @@ -32,14 +32,14 @@ public function __invoke(array $record) { $hash = $this->requestStack && ($request = $this->requestStack->getCurrentRequest()) ? spl_object_hash($request) : ''; - $this->records[$hash][] = array( + $this->records[$hash][] = [ 'timestamp' => $record['datetime']->getTimestamp(), 'message' => $record['message'], 'priority' => $record['level'], 'priorityName' => $record['level_name'], 'context' => $record['context'], 'channel' => isset($record['channel']) ? $record['channel'] : '', - ); + ]; if (!isset($this->errorCount[$hash])) { $this->errorCount[$hash] = 0; @@ -68,11 +68,11 @@ public function getLogs(/* Request $request = null */) } if (1 <= \func_num_args() && null !== $request = \func_get_arg(0)) { - return $this->records[spl_object_hash($request)] ?? array(); + return $this->records[spl_object_hash($request)] ?? []; } if (0 === \count($this->records)) { - return array(); + return []; } return array_merge(...array_values($this->records)); @@ -101,8 +101,8 @@ public function countErrors(/* Request $request = null */) */ public function clear() { - $this->records = array(); - $this->errorCount = array(); + $this->records = []; + $this->errorCount = []; } /** diff --git a/src/Symfony/Bridge/Monolog/Processor/RouteProcessor.php b/src/Symfony/Bridge/Monolog/Processor/RouteProcessor.php new file mode 100644 index 0000000000000..0160754ad9575 --- /dev/null +++ b/src/Symfony/Bridge/Monolog/Processor/RouteProcessor.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\Bridge\Monolog\Processor; + +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\HttpKernel\Event\FinishRequestEvent; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Contracts\Service\ResetInterface; + +/** + * Adds the current route information to the log entry. + * + * @author Piotr Stankowski + */ +class RouteProcessor implements EventSubscriberInterface, ResetInterface +{ + private $routeData; + private $includeParams; + + public function __construct(bool $includeParams = true) + { + $this->includeParams = $includeParams; + $this->reset(); + } + + public function __invoke(array $records) + { + if ($this->routeData && !isset($records['extra']['requests'])) { + $records['extra']['requests'] = array_values($this->routeData); + } + + return $records; + } + + public function reset() + { + $this->routeData = []; + } + + public function addRouteData(GetResponseEvent $event) + { + if ($event->isMasterRequest()) { + $this->reset(); + } + + $request = $event->getRequest(); + if (!$request->attributes->has('_controller')) { + return; + } + + $currentRequestData = [ + 'controller' => $request->attributes->get('_controller'), + 'route' => $request->attributes->get('_route'), + ]; + + if ($this->includeParams) { + $currentRequestData['route_params'] = $request->attributes->get('_route_params'); + } + + $this->routeData[spl_object_id($request)] = $currentRequestData; + } + + public function removeRouteData(FinishRequestEvent $event) + { + $requestId = spl_object_id($event->getRequest()); + unset($this->routeData[$requestId]); + } + + public static function getSubscribedEvents() + { + return [ + KernelEvents::REQUEST => ['addRouteData', 1], + KernelEvents::FINISH_REQUEST => ['removeRouteData', 1], + ]; + } +} diff --git a/src/Symfony/Bridge/Monolog/Processor/TokenProcessor.php b/src/Symfony/Bridge/Monolog/Processor/TokenProcessor.php index 11547be22b2ee..7613d01361962 100644 --- a/src/Symfony/Bridge/Monolog/Processor/TokenProcessor.php +++ b/src/Symfony/Bridge/Monolog/Processor/TokenProcessor.php @@ -31,11 +31,17 @@ public function __invoke(array $records) { $records['extra']['token'] = null; if (null !== $token = $this->tokenStorage->getToken()) { - $records['extra']['token'] = array( + if (method_exists($token, 'getRoleNames')) { + $roles = $token->getRoleNames(); + } else { + $roles = array_map(function ($role) { return $role->getRole(); }, $token->getRoles(false)); + } + + $records['extra']['token'] = [ 'username' => $token->getUsername(), 'authenticated' => $token->isAuthenticated(), - 'roles' => array_map(function ($role) { return $role->getRole(); }, $token->getRoles()), - ); + 'roles' => $roles, + ]; } return $records; diff --git a/src/Symfony/Bridge/Monolog/Processor/WebProcessor.php b/src/Symfony/Bridge/Monolog/Processor/WebProcessor.php index 9c32e756c514e..71bf71a816327 100644 --- a/src/Symfony/Bridge/Monolog/Processor/WebProcessor.php +++ b/src/Symfony/Bridge/Monolog/Processor/WebProcessor.php @@ -20,13 +20,15 @@ * WebProcessor override to read from the HttpFoundation's Request. * * @author Jordi Boggiano + * + * @final since Symfony 4.3 */ class WebProcessor extends BaseWebProcessor implements EventSubscriberInterface { public function __construct(array $extraFields = null) { // Pass an empty array as the default null value would access $_SERVER - parent::__construct(array(), $extraFields); + parent::__construct([], $extraFields); } public function onKernelRequest(GetResponseEvent $event) @@ -39,8 +41,8 @@ public function onKernelRequest(GetResponseEvent $event) public static function getSubscribedEvents() { - return array( - KernelEvents::REQUEST => array('onKernelRequest', 4096), - ); + return [ + KernelEvents::REQUEST => ['onKernelRequest', 4096], + ]; } } diff --git a/src/Symfony/Bridge/Monolog/Tests/Formatter/ConsoleFormatterTest.php b/src/Symfony/Bridge/Monolog/Tests/Formatter/ConsoleFormatterTest.php new file mode 100644 index 0000000000000..c09597e916cfc --- /dev/null +++ b/src/Symfony/Bridge/Monolog/Tests/Formatter/ConsoleFormatterTest.php @@ -0,0 +1,66 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Monolog\Tests\Formatter; + +use Monolog\Logger; +use PHPUnit\Framework\TestCase; +use Symfony\Bridge\Monolog\Formatter\ConsoleFormatter; + +class ConsoleFormatterTest extends TestCase +{ + /** + * @dataProvider providerFormatTests + */ + public function testFormat(array $record, $expectedMessage) + { + $formatter = new ConsoleFormatter(); + self::assertSame($expectedMessage, $formatter->format($record)); + } + + /** + * @return array + */ + public function providerFormatTests() + { + $currentDateTime = new \DateTime(); + + return [ + 'record with DateTime object in datetime field' => [ + 'record' => [ + 'message' => 'test', + 'context' => [], + 'level' => Logger::WARNING, + 'level_name' => Logger::getLevelName(Logger::WARNING), + 'channel' => 'test', + 'datetime' => $currentDateTime, + 'extra' => [], + ], + 'expectedMessage' => sprintf( + "%s WARNING [test] test\n", + $currentDateTime->format(ConsoleFormatter::SIMPLE_DATE) + ), + ], + 'record with string in datetime field' => [ + 'record' => [ + 'message' => 'test', + 'context' => [], + 'level' => Logger::WARNING, + 'level_name' => Logger::getLevelName(Logger::WARNING), + 'channel' => 'test', + 'datetime' => '2019-01-01T00:42:00+00:00', + 'extra' => [], + ], + 'expectedMessage' => "2019-01-01T00:42:00+00:00 WARNING [test] test\n", + ], + ]; + } +} diff --git a/src/Symfony/Bridge/Monolog/Tests/Handler/ConsoleHandlerTest.php b/src/Symfony/Bridge/Monolog/Tests/Handler/ConsoleHandlerTest.php index 03bba1b3e2858..192238c839340 100644 --- a/src/Symfony/Bridge/Monolog/Tests/Handler/ConsoleHandlerTest.php +++ b/src/Symfony/Bridge/Monolog/Tests/Handler/ConsoleHandlerTest.php @@ -38,13 +38,13 @@ public function testConstructor() public function testIsHandling() { $handler = new ConsoleHandler(); - $this->assertFalse($handler->isHandling(array()), '->isHandling returns false when no output is set'); + $this->assertFalse($handler->isHandling([]), '->isHandling returns false when no output is set'); } /** * @dataProvider provideVerbosityMappingTests */ - public function testVerbosityMapping($verbosity, $level, $isHandling, array $map = array()) + public function testVerbosityMapping($verbosity, $level, $isHandling, array $map = []) { $output = $this->getMockBuilder('Symfony\Component\Console\Output\OutputInterface')->getMock(); $output @@ -53,7 +53,7 @@ public function testVerbosityMapping($verbosity, $level, $isHandling, array $map ->will($this->returnValue($verbosity)) ; $handler = new ConsoleHandler($output, true, $map); - $this->assertSame($isHandling, $handler->isHandling(array('level' => $level)), + $this->assertSame($isHandling, $handler->isHandling(['level' => $level]), '->isHandling returns correct value depending on console verbosity and log level' ); @@ -61,7 +61,7 @@ public function testVerbosityMapping($verbosity, $level, $isHandling, array $map $levelName = Logger::getLevelName($level); $levelName = sprintf('%-9s', $levelName); - $realOutput = $this->getMockBuilder('Symfony\Component\Console\Output\Output')->setMethods(array('doWrite'))->getMock(); + $realOutput = $this->getMockBuilder('Symfony\Component\Console\Output\Output')->setMethods(['doWrite'])->getMock(); $realOutput->setVerbosity($verbosity); if ($realOutput->isDebug()) { $log = "16:21:54 $levelName [app] My info message\n"; @@ -74,38 +74,38 @@ public function testVerbosityMapping($verbosity, $level, $isHandling, array $map ->with($log, false); $handler = new ConsoleHandler($realOutput, true, $map); - $infoRecord = array( + $infoRecord = [ 'message' => 'My info message', - 'context' => array(), + 'context' => [], 'level' => $level, 'level_name' => Logger::getLevelName($level), 'channel' => 'app', 'datetime' => new \DateTime('2013-05-29 16:21:54'), - 'extra' => array(), - ); + 'extra' => [], + ]; $this->assertFalse($handler->handle($infoRecord), 'The handler finished handling the log.'); } public function provideVerbosityMappingTests() { - return array( - array(OutputInterface::VERBOSITY_QUIET, Logger::ERROR, true), - array(OutputInterface::VERBOSITY_QUIET, Logger::WARNING, false), - array(OutputInterface::VERBOSITY_NORMAL, Logger::WARNING, true), - array(OutputInterface::VERBOSITY_NORMAL, Logger::NOTICE, false), - array(OutputInterface::VERBOSITY_VERBOSE, Logger::NOTICE, true), - array(OutputInterface::VERBOSITY_VERBOSE, Logger::INFO, false), - array(OutputInterface::VERBOSITY_VERY_VERBOSE, Logger::INFO, true), - array(OutputInterface::VERBOSITY_VERY_VERBOSE, Logger::DEBUG, false), - array(OutputInterface::VERBOSITY_DEBUG, Logger::DEBUG, true), - array(OutputInterface::VERBOSITY_DEBUG, Logger::EMERGENCY, true), - array(OutputInterface::VERBOSITY_NORMAL, Logger::NOTICE, true, array( + return [ + [OutputInterface::VERBOSITY_QUIET, Logger::ERROR, true], + [OutputInterface::VERBOSITY_QUIET, Logger::WARNING, false], + [OutputInterface::VERBOSITY_NORMAL, Logger::WARNING, true], + [OutputInterface::VERBOSITY_NORMAL, Logger::NOTICE, false], + [OutputInterface::VERBOSITY_VERBOSE, Logger::NOTICE, true], + [OutputInterface::VERBOSITY_VERBOSE, Logger::INFO, false], + [OutputInterface::VERBOSITY_VERY_VERBOSE, Logger::INFO, true], + [OutputInterface::VERBOSITY_VERY_VERBOSE, Logger::DEBUG, false], + [OutputInterface::VERBOSITY_DEBUG, Logger::DEBUG, true], + [OutputInterface::VERBOSITY_DEBUG, Logger::EMERGENCY, true], + [OutputInterface::VERBOSITY_NORMAL, Logger::NOTICE, true, [ OutputInterface::VERBOSITY_NORMAL => Logger::NOTICE, - )), - array(OutputInterface::VERBOSITY_DEBUG, Logger::NOTICE, true, array( + ]], + [OutputInterface::VERBOSITY_DEBUG, Logger::NOTICE, true, [ OutputInterface::VERBOSITY_NORMAL => Logger::NOTICE, - )), - ); + ]], + ]; } public function testVerbosityChanged() @@ -122,10 +122,10 @@ public function testVerbosityChanged() ->will($this->returnValue(OutputInterface::VERBOSITY_DEBUG)) ; $handler = new ConsoleHandler($output); - $this->assertFalse($handler->isHandling(array('level' => Logger::NOTICE)), + $this->assertFalse($handler->isHandling(['level' => Logger::NOTICE]), 'when verbosity is set to quiet, the handler does not handle the log' ); - $this->assertTrue($handler->isHandling(array('level' => Logger::NOTICE)), + $this->assertTrue($handler->isHandling(['level' => Logger::NOTICE]), 'since the verbosity of the output increased externally, the handler is now handling the log' ); } @@ -155,15 +155,15 @@ public function testWritingAndFormatting() $handler = new ConsoleHandler(null, false); $handler->setOutput($output); - $infoRecord = array( + $infoRecord = [ 'message' => 'My info message', - 'context' => array(), + 'context' => [], 'level' => Logger::INFO, 'level_name' => Logger::getLevelName(Logger::INFO), 'channel' => 'app', 'datetime' => new \DateTime('2013-05-29 16:21:54'), - 'extra' => array(), - ); + 'extra' => [], + ]; $this->assertTrue($handler->handle($infoRecord), 'The handler finished handling the log as bubble is false.'); } @@ -196,12 +196,12 @@ public function testLogsFromListeners() }); $event = new ConsoleCommandEvent(new Command('foo'), $this->getMockBuilder('Symfony\Component\Console\Input\InputInterface')->getMock(), $output); - $dispatcher->dispatch(ConsoleEvents::COMMAND, $event); + $dispatcher->dispatch($event, ConsoleEvents::COMMAND); $this->assertContains('Before command message.', $out = $output->fetch()); $this->assertContains('After command message.', $out); $event = new ConsoleTerminateEvent(new Command('foo'), $this->getMockBuilder('Symfony\Component\Console\Input\InputInterface')->getMock(), $output, 0); - $dispatcher->dispatch(ConsoleEvents::TERMINATE, $event); + $dispatcher->dispatch($event, ConsoleEvents::TERMINATE); $this->assertContains('Before terminate message.', $out = $output->fetch()); $this->assertContains('After terminate message.', $out); } diff --git a/src/Symfony/Bridge/Monolog/Tests/Handler/FingersCrossed/HttpCodeActivationStrategyTest.php b/src/Symfony/Bridge/Monolog/Tests/Handler/FingersCrossed/HttpCodeActivationStrategyTest.php index 9f0b0b3735e44..0fce18d3861be 100644 --- a/src/Symfony/Bridge/Monolog/Tests/Handler/FingersCrossed/HttpCodeActivationStrategyTest.php +++ b/src/Symfony/Bridge/Monolog/Tests/Handler/FingersCrossed/HttpCodeActivationStrategyTest.php @@ -25,7 +25,7 @@ class HttpCodeActivationStrategyTest extends TestCase */ public function testExclusionsWithoutCode() { - new HttpCodeActivationStrategy(new RequestStack(), array(array('urls' => array())), Logger::WARNING); + new HttpCodeActivationStrategy(new RequestStack(), [['urls' => []]], Logger::WARNING); } /** @@ -33,7 +33,7 @@ public function testExclusionsWithoutCode() */ public function testExclusionsWithoutUrls() { - new HttpCodeActivationStrategy(new RequestStack(), array(array('code' => 404)), Logger::WARNING); + new HttpCodeActivationStrategy(new RequestStack(), [['code' => 404]], Logger::WARNING); } /** @@ -46,12 +46,12 @@ public function testIsActivated($url, $record, $expected) $strategy = new HttpCodeActivationStrategy( $requestStack, - array( - array('code' => 403, 'urls' => array()), - array('code' => 404, 'urls' => array()), - array('code' => 405, 'urls' => array()), - array('code' => 400, 'urls' => array('^/400/a', '^/400/b')), - ), + [ + ['code' => 403, 'urls' => []], + ['code' => 404, 'urls' => []], + ['code' => 405, 'urls' => []], + ['code' => 400, 'urls' => ['^/400/a', '^/400/b']], + ], Logger::WARNING ); @@ -60,22 +60,22 @@ public function testIsActivated($url, $record, $expected) public function isActivatedProvider() { - return array( - array('/test', array('level' => Logger::ERROR), true), - array('/400', array('level' => Logger::ERROR, 'context' => $this->getContextException(400)), true), - array('/400/a', array('level' => Logger::ERROR, 'context' => $this->getContextException(400)), false), - array('/400/b', array('level' => Logger::ERROR, 'context' => $this->getContextException(400)), false), - array('/400/c', array('level' => Logger::ERROR, 'context' => $this->getContextException(400)), true), - array('/401', array('level' => Logger::ERROR, 'context' => $this->getContextException(401)), true), - array('/403', array('level' => Logger::ERROR, 'context' => $this->getContextException(403)), false), - array('/404', array('level' => Logger::ERROR, 'context' => $this->getContextException(404)), false), - array('/405', array('level' => Logger::ERROR, 'context' => $this->getContextException(405)), false), - array('/500', array('level' => Logger::ERROR, 'context' => $this->getContextException(500)), true), - ); + return [ + ['/test', ['level' => Logger::ERROR], true], + ['/400', ['level' => Logger::ERROR, 'context' => $this->getContextException(400)], true], + ['/400/a', ['level' => Logger::ERROR, 'context' => $this->getContextException(400)], false], + ['/400/b', ['level' => Logger::ERROR, 'context' => $this->getContextException(400)], false], + ['/400/c', ['level' => Logger::ERROR, 'context' => $this->getContextException(400)], true], + ['/401', ['level' => Logger::ERROR, 'context' => $this->getContextException(401)], true], + ['/403', ['level' => Logger::ERROR, 'context' => $this->getContextException(403)], false], + ['/404', ['level' => Logger::ERROR, 'context' => $this->getContextException(404)], false], + ['/405', ['level' => Logger::ERROR, 'context' => $this->getContextException(405)], false], + ['/500', ['level' => Logger::ERROR, 'context' => $this->getContextException(500)], true], + ]; } protected function getContextException($code) { - return array('exception' => new HttpException($code)); + return ['exception' => new HttpException($code)]; } } diff --git a/src/Symfony/Bridge/Monolog/Tests/Handler/FingersCrossed/NotFoundActivationStrategyTest.php b/src/Symfony/Bridge/Monolog/Tests/Handler/FingersCrossed/NotFoundActivationStrategyTest.php index 3c34f065eb038..b04678106c96e 100644 --- a/src/Symfony/Bridge/Monolog/Tests/Handler/FingersCrossed/NotFoundActivationStrategyTest.php +++ b/src/Symfony/Bridge/Monolog/Tests/Handler/FingersCrossed/NotFoundActivationStrategyTest.php @@ -28,28 +28,28 @@ public function testIsActivated($url, $record, $expected) $requestStack = new RequestStack(); $requestStack->push(Request::create($url)); - $strategy = new NotFoundActivationStrategy($requestStack, array('^/foo', 'bar'), Logger::WARNING); + $strategy = new NotFoundActivationStrategy($requestStack, ['^/foo', 'bar'], Logger::WARNING); $this->assertEquals($expected, $strategy->isHandlerActivated($record)); } public function isActivatedProvider() { - return array( - array('/tes 9E88 t', array('level' => Logger::DEBUG), false), - array('/foo', array('level' => Logger::DEBUG, 'context' => $this->getContextException(404)), false), - array('/baz/bar', array('level' => Logger::ERROR, 'context' => $this->getContextException(404)), false), - array('/foo', array('level' => Logger::ERROR, 'context' => $this->getContextException(404)), false), - array('/foo', array('level' => Logger::ERROR, 'context' => $this->getContextException(500)), true), - - array('/test', array('level' => Logger::ERROR), true), - array('/baz', array('level' => Logger::ERROR, 'context' => $this->getContextException(404)), true), - array('/baz', array('level' => Logger::ERROR, 'context' => $this->getContextException(500)), true), - ); + return [ + ['/test', ['level' => Logger::DEBUG], false], + ['/foo', ['level' => Logger::DEBUG, 'context' => $this->getContextException(404)], false], + ['/baz/bar', ['level' => Logger::ERROR, 'context' => $this->getContextException(404)], false], + ['/foo', ['level' => Logger::ERROR, 'context' => $this->getContextException(404)], false], + ['/foo', ['level' => Logger::ERROR, 'context' => $this->getContextException(500)], true], + + ['/test', ['level' => Logger::ERROR], true], + ['/baz', ['level' => Logger::ERROR, 'context' => $this->getContextException(404)], true], + ['/baz', ['level' => Logger::ERROR, 'context' => $this->getContextException(500)], true], + ]; } protected function getContextException($code) { - return array('exception' => new HttpException($code)); + return ['exception' => new HttpException($code)]; } } diff --git a/src/Symfony/Bridge/Monolog/Tests/LoggerTest.php b/src/Symfony/Bridge/Monolog/Tests/LoggerTest.php index 6ffade80731bf..143200f6c5718 100644 --- a/src/Symfony/Bridge/Monolog/Tests/LoggerTest.php +++ b/src/Symfony/Bridge/Monolog/Tests/LoggerTest.php @@ -12,6 +12,7 @@ namespace Symfony\Bridge\Monolog\Tests; use Monolog\Handler\TestHandler; +use Monolog\ResettableInterface; use PHPUnit\Framework\TestCase; use Symfony\Bridge\Monolog\Logger; use Symfony\Bridge\Monolog\Processor\DebugProcessor; @@ -22,16 +23,16 @@ class LoggerTest extends TestCase public function testGetLogsWithoutDebugProcessor() { $handler = new TestHandler(); - $logger = new Logger(__METHOD__, array($handler)); + $logger = new Logger(__METHOD__, [$handler]); $logger->error('error message'); - $this->assertSame(array(), $logger->getLogs()); + $this->assertSame([], $logger->getLogs()); } public function testCountErrorsWithoutDebugProcessor() { $handler = new TestHandler(); - $logger = new Logger(__METHOD__, array($handler)); + $logger = new Logger(__METHOD__, [$handler]); $logger->error('error message'); $this->assertSame(0, $logger->countErrors()); @@ -41,7 +42,7 @@ public function testGetLogsWithDebugProcessor() { $handler = new TestHandler(); $processor = new DebugProcessor(); - $logger = new Logger(__METHOD__, array($handler), array($processor)); + $logger = new Logger(__METHOD__, [$handler], [$processor]); $logger->error('error message'); $this->assertCount(1, $logger->getLogs()); @@ -51,7 +52,7 @@ public function testCountErrorsWithDebugProcessor() { $handler = new TestHandler(); $processor = new DebugProcessor(); - $logger = new Logger(__METHOD__, array($handler), array($processor)); + $logger = new Logger(__METHOD__, [$handler], [$processor]); $logger->debug('test message'); $logger->info('test message'); @@ -69,7 +70,7 @@ public function testCountErrorsWithDebugProcessor() public function testGetLogsWithDebugProcessor2() { $handler = new TestHandler(); - $logger = new Logger('test', array($handler)); + $logger = new Logger('test', [$handler]); $logger->pushProcessor(new DebugProcessor()); $logger->info('test'); @@ -88,7 +89,7 @@ public function testGetLogsWithDebugProcessor3() $processor->expects($this->once())->method('countErrors')->with($request); $handler = new TestHandler(); - $logger = new Logger('test', array($handler)); + $logger = new Logger('test', [$handler]); $logger->pushProcessor($processor); $logger->getLogs($request); @@ -98,7 +99,7 @@ public function testGetLogsWithDebugProcessor3() public function testClear() { $handler = new TestHandler(); - $logger = new Logger('test', array($handler)); + $logger = new Logger('test', [$handler]); $logger->pushProcessor(new DebugProcessor()); $logger->info('test'); @@ -108,6 +109,22 @@ public function testClear() $this->assertSame(0, $logger->countErrors()); } + public function testReset() + { + $handler = new TestHandler(); + $logger = new Logger('test', [$handler]); + $logger->pushProcessor(new DebugProcessor()); + + $logger->info('test'); + $logger->reset(); + + $this->assertEmpty($logger->getLogs()); + $this->assertSame(0, $logger->countErrors()); + if (class_exists(ResettableInterface::class)) { + $this->assertEmpty($handler->getRecords()); + } + } + /** * @group legacy * @expectedDeprecation The "Symfony\Bridge\Monolog\Logger::getLogs()" method will have a new "Request $request = null" argument in version 5.0, not defining it is deprecated since Symfony 4.2. diff --git a/src/Symfony/Bridge/Monolog/Tests/Processor/ConsoleCommandProcessorTest.php b/src/Symfony/Bridge/Monolog/Tests/Processor/ConsoleCommandProcessorTest.php new file mode 100644 index 0000000000000..4c9774b4a4385 --- /dev/null +++ b/src/Symfony/Bridge/Monolog/Tests/Processor/ConsoleCommandProcessorTest.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\Bridge\Monolog\Tests\Processor; + +use PHPUnit\Framework\TestCase; +use Symfony\Bridge\Monolog\Processor\ConsoleCommandProcessor; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Event\ConsoleEvent; +use Symfony\Component\Console\Input\InputInterface; + +class ConsoleCommandProcessorTest extends TestCase +{ + private const TEST_ARGUMENTS = ['test' => 'argument']; + private const TEST_OPTIONS = ['test' => 'option']; + private const TEST_NAME = 'some:test'; + + public function testProcessor() + { + $processor = new ConsoleCommandProcessor(); + $processor->addCommandData($this->getConsoleEvent()); + + $record = $processor(['extra' => []]); + + $this->assertArrayHasKey('command', $record['extra']); + $this->assertEquals( + ['name' => self::TEST_NAME, 'arguments' => self::TEST_ARGUMENTS], + $record['extra']['command'] + ); + } + + public function testProcessorWithOptions() + { + $processor = new ConsoleCommandProcessor(true, true); + $processor->addCommandData($this->getConsoleEvent()); + + $record = $processor(['extra' => []]); + + $this->assertArrayHasKey('command', $record['extra']); + $this->assertEquals( + ['name' => self::TEST_NAME, 'arguments' => self::TEST_ARGUMENTS, 'options' => self::TEST_OPTIONS], + $record['extra']['command'] + ); + } + + public function testProcessorDoesNothingWhenNotInConsole() + { + $processor = new ConsoleCommandProcessor(true, true); + + $record = $processor(['extra' => []]); + $this->assertEquals(['extra' => []], $record); + } + + private function getConsoleEvent(): ConsoleEvent + { + $input = $this->getMockBuilder(InputInterface::class)->getMock(); + $input->method('getArguments')->willReturn(self::TEST_ARGUMENTS); + $input->method('getOptions')->willReturn(self::TEST_OPTIONS); + $command = $this->getMockBuilder(Command::class)->disableOriginalConstructor()->getMock(); + $command->method('getName')->willReturn(self::TEST_NAME); + $consoleEvent = $this->getMockBuilder(ConsoleEvent::class)->disableOriginalConstructor()->getMock(); + $consoleEvent->method('getCommand')->willReturn($command); + $consoleEvent->method('getInput')->willReturn($input); + + return $consoleEvent; + } +} diff --git a/src/Symfony/Bridge/Monolog/Tests/Processor/DebugProcessorTest.php b/src/Symfony/Bridge/Monolog/Tests/Processor/DebugProcessorTest.php index 8712632ce6c93..9aceb9337e403 100644 --- a/src/Symfony/Bridge/Monolog/Tests/Processor/DebugProcessorTest.php +++ b/src/Symfony/Bridge/Monolog/Tests/Processor/DebugProcessorTest.php @@ -85,15 +85,15 @@ public function testInheritedClassCallCountErrorsWithoutArgument() private function getRecord($level = Logger::WARNING, $message = 'test') { - return array( + return [ 'message' => $message, - 'context' => array(), + 'context' => [], 'level' => $level, 'level_name' => Logger::getLevelName($level), 'channel' => 'test', 'datetime' => new \DateTime(), - 'extra' => array(), - ); + 'extra' => [], + ]; } } diff --git a/src/Symfony/Bridge/Monolog/Tests/Processor/RouteProcessorTest.php b/src/Symfony/Bridge/Monolog/Tests/Processor/RouteProcessorTest.php new file mode 100644 index 0000000000000..5534240ba278a --- /dev/null +++ b/src/Symfony/Bridge/Monolog/Tests/Processor/RouteProcessorTest.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\Bridge\Monolog\Tests\Processor; + +use PHPUnit\Framework\TestCase; +use Symfony\Bridge\Monolog\Processor\RouteProcessor; +use Symfony\Component\HttpFoundation\ParameterBag; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Event\FinishRequestEvent; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; + +class RouteProcessorTest extends TestCase +{ + private const TEST_CONTROLLER = 'App\Controller\SomeController::someMethod'; + private const TEST_ROUTE = 'someRouteName'; + private const TEST_PARAMS = ['param1' => 'value1']; + + public function testProcessor() + { + $request = $this->mockFilledRequest(); + $processor = new RouteProcessor(); + $processor->addRouteData($this->mockGetResponseEvent($request)); + + $record = $processor(['extra' => []]); + + $this->assertArrayHasKey('requests', $record['extra']); + $this->assertCount(1, $record['extra']['requests']); + $this->assertEquals( + ['controller' => self::TEST_CONTROLLER, 'route' => self::TEST_ROUTE, 'route_params' => self::TEST_PARAMS], + $record['extra']['requests'][0] + ); + } + + public function testProcessorWithoutParams() + { + $request = $this->mockFilledRequest(); + $processor = new RouteProcessor(false); + $processor->addRouteData($this->mockGetResponseEvent($request)); + + $record = $processor(['extra' => []]); + + $this->assertArrayHasKey('requests', $record['extra']); + $this->assertCount(1, $record['extra']['requests']); + $this->assertEquals( + ['controller' => self::TEST_CONTROLLER, 'route' => self::TEST_ROUTE], + $record['extra']['requests'][0] + ); + } + + public function testProcessorWithSubRequests() + { + $controllerFromSubRequest = 'OtherController::otherMethod'; + $mainRequest = $this->mockFilledRequest(); + $subRequest = $this->mockFilledRequest($controllerFromSubRequest); + + $processor = new RouteProcessor(false); + $processor->addRouteData($this->mockGetResponseEvent($mainRequest)); + $processor->addRouteData($this->mockGetResponseEvent($subRequest)); + + $record = $processor(['extra' => []]); + + $this->assertArrayHasKey('requests', $record['extra']); + $this->assertCount(2, $record['extra']['requests']); + $this->assertEquals( + ['controller' => self::TEST_CONTROLLER, 'route' => self::TEST_ROUTE], + $record['extra']['requests'][0] + ); + $this->assertEquals( + ['controller' => $controllerFromSubRequest, 'route' => self::TEST_ROUTE], + $record['extra']['requests'][1] + ); + } + + public function testFinishRequestRemovesRelatedEntry() + { + $mainRequest = $this->mockFilledRequest(); + $subRequest = $this->mockFilledRequest('OtherController::otherMethod'); + + $processor = new RouteProcessor(false); + $processor->addRouteData($this->mockGetResponseEvent($mainRequest)); + $processor->addRouteData($this->mockGetResponseEvent($subRequest)); + $processor->removeRouteData($this->mockFinishRequestEvent($subRequest)); + $record = $processor(['extra' => []]); + + $this->assertArrayHasKey('requests', $record['extra']); + $this->assertCount(1, $record['extra']['requests']); + $this->assertEquals( + ['controller' => self::TEST_CONTROLLER, 'route' => self::TEST_ROUTE], + $record['extra']['requests'][0] + ); + + $processor->removeRouteData($this->mockFinishRequestEvent($mainRequest)); + $record = $processor(['extra' => []]); + + $this->assertArrayNotHasKey('requests', $record['extra']); + } + + public function testProcessorWithEmptyRequest() + { + $request = $this->mockEmptyRequest(); + $processor = new RouteProcessor(); + $processor->addRouteData($this->mockGetResponseEvent($request)); + + $record = $processor(['extra' => []]); + $this->assertEquals(['extra' => []], $record); + } + + public function testProcessorDoesNothingWhenNoRequest() + { + $processor = new RouteProcessor(); + + $record = $processor(['extra' => []]); + $this->assertEquals(['extra' => []], $record); + } + + private function mockGetResponseEvent(Request $request): GetResponseEvent + { + $event = $this->getMockBuilder(GetResponseEvent::class)->disableOriginalConstructor()->getMock(); + $event->method('getRequest')->willReturn($request); + + return $event; + } + + private function mockFinishRequestEvent(Request $request): FinishRequestEvent + { + $event = $this->getMockBuilder(FinishRequestEvent::class)->disableOriginalConstructor()->getMock(); + $event->method('getRequest')->willReturn($request); + + return $event; + } + + private function mockEmptyRequest(): Request + { + return $this->mockRequest([]); + } + + private function mockFilledRequest(string $controller = self::TEST_CONTROLLER): Request + { + return $this->mockRequest([ + '_controller' => $controller, + '_route' => self::TEST_ROUTE, + '_route_params' => self::TEST_PARAMS, + ]); + } + + private function mockRequest(array $attributes): Request + { + $request = $this->getMockBuilder(Request::class)->disableOriginalConstructor()->getMock(); + $request->attributes = new ParameterBag($attributes); + + return $request; + } +} diff --git a/src/Symfony/Bridge/Monolog/Tests/Processor/TokenProcessorTest.php b/src/Symfony/Bridge/Monolog/Tests/Processor/TokenProcessorTest.php index e78acf47b54c4..ef3f6cc9f5c6a 100644 --- a/src/Symfony/Bridge/Monolog/Tests/Processor/TokenProcessorTest.php +++ b/src/Symfony/Bridge/Monolog/Tests/Processor/TokenProcessorTest.php @@ -25,18 +25,17 @@ class TokenProcessorTest extends TestCase { public function testProcessor() { - $token = new UsernamePasswordToken('user', 'password', 'provider', array('ROLE_USER')); + $token = new UsernamePasswordToken('user', 'password', 'provider', ['ROLE_USER']); $tokenStorage = $this->getMockBuilder(TokenStorageInterface::class)->getMock(); $tokenStorage->method('getToken')->willReturn($token); $processor = new TokenProcessor($tokenStorage); - $record = array('extra' => array()); + $record = ['extra' => []]; $record = $processor($record); $this->assertArrayHasKey('token', $record['extra']); $this->assertEquals($token->getUsername(), $record['extra']['token']['username']); $this->assertEquals($token->isAuthenticated(), $record['extra']['token']['authenticated']); - $roles = array_map(function ($role) { return $role->getRole(); }, $token->getRoles()); - $this->assertEquals($roles, $record['extra']['token']['roles']); + $this->assertEquals(['ROLE_USER'], $record['extra']['token']['roles']); } } diff --git a/src/Symfony/Bridge/Monolog/Tests/Processor/WebProcessorTest.php b/src/Symfony/Bridge/Monolog/Tests/Processor/WebProcessorTest.php index c18f857285c9d..0f682e842cad5 100644 --- a/src/Symfony/Bridge/Monolog/Tests/Processor/WebProcessorTest.php +++ b/src/Symfony/Bridge/Monolog/Tests/Processor/WebProcessorTest.php @@ -15,6 +15,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Bridge\Monolog\Processor\WebProcessor; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Event\RequestEvent; class WebProcessorTest extends TestCase { @@ -36,8 +37,8 @@ public function testUsesRequestServerData() public function testUseRequestClientIp() { - Request::setTrustedProxies(array('192.168.0.1'), Request::HEADER_X_FORWARDED_ALL); - list($event, $server) = $this->createRequestEvent(array('X_FORWARDED_FOR' => '192.168.0.2')); + Request::setTrustedProxies(['192.168.0.1'], Request::HEADER_X_FORWARDED_ALL); + list($event, $server) = $this->createRequestEvent(['X_FORWARDED_FOR' => '192.168.0.2']); $processor = new WebProcessor(); $processor->onKernelRequest($event); @@ -50,7 +51,7 @@ public function testUseRequestClientIp() $this->assertEquals($server['SERVER_NAME'], $record['extra']['server']); $this->assertEquals($server['HTTP_REFERER'], $record['extra']['referrer']); - Request::setTrustedProxies(array(), -1); + Request::setTrustedProxies([], -1); } public function testCanBeConstructedWithExtraFields() @@ -61,7 +62,7 @@ public function testCanBeConstructedWithExtraFields() list($event, $server) = $this->createRequestEvent(); - $processor = new WebProcessor(array('url', 'referrer')); + $processor = new WebProcessor(['url', 'referrer']); $processor->onKernelRequest($event); $record = $processor($this->getRecord()); @@ -70,16 +71,16 @@ public function testCanBeConstructedWithExtraFields() $this->assertEquals($server['HTTP_REFERER'], $record['extra']['referrer']); } - private function createRequestEvent($additionalServerParameters = array()): array + private function createRequestEvent($additionalServerParameters = []): array { $server = array_merge( - array( + [ 'REQUEST_URI' => 'A', 'REMOTE_ADDR' => '192.168.0.1', 'REQUEST_METHOD' => 'C', 'SERVER_NAME' => 'D', 'HTTP_REFERER' => 'E', - ), + ], $additionalServerParameters ); @@ -87,7 +88,7 @@ private function createRequestEvent($additionalServerParameters = array()): arra $request->server->replace($server); $request->headers->replace($server); - $event = $this->getMockBuilder('Symfony\Component\HttpKernel\Event\GetResponseEvent') + $event = $this->getMockBuilder(RequestEvent::class) ->disableOriginalConstructor() ->getMock(); $event->expects($this->any()) @@ -97,20 +98,20 @@ private function createRequestEvent($additionalServerParameters = array()): arra ->method('getRequest') ->will($this->returnValue($request)); - return array($event, $server); + return [$event, $server]; } private function getRecord(int $level = Logger::WARNING, string $message = 'test'): array { - return array( + return [ 'message' => $message, - 'context' => array(), + 'context' => [], 'level' => $level, 'level_name' => Logger::getLevelName($level), 'channel' => 'test', 'datetime' => new \DateTime(), - 'extra' => array(), - ); + 'extra' => [], + ]; } private function isExtraFieldsSupported() diff --git a/src/Symfony/Bridge/Monolog/composer.json b/src/Symfony/Bridge/Monolog/composer.json index ce7fee470b247..81e7b15cd0e15 100644 --- a/src/Symfony/Bridge/Monolog/composer.json +++ b/src/Symfony/Bridge/Monolog/composer.json @@ -19,11 +19,10 @@ "php": "^7.1.3", "monolog/monolog": "~1.19", "symfony/contracts": "^1.0", - "symfony/http-kernel": "~3.4|~4.0" + "symfony/http-kernel": "^4.3" }, "require-dev": { "symfony/console": "~3.4|~4.0", - "symfony/event-dispatcher": "~3.4|~4.0", "symfony/security-core": "~3.4|~4.0", "symfony/var-dumper": "~3.4|~4.0" }, @@ -34,7 +33,6 @@ "suggest": { "symfony/http-kernel": "For using the debugging handlers together with the response life cycle of the HTTP kernel.", "symfony/console": "For the possibility to show log messages in console commands depending on verbosity settings.", - "symfony/event-dispatcher": "Needed when using log messages in console commands.", "symfony/var-dumper": "For using the debugging handlers like the console handler or the log server handler." }, "autoload": { @@ -46,7 +44,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "4.2-dev" + "dev-master": "4.3-dev" } } } diff --git a/src/Symfony/Bridge/PhpUnit/CHANGELOG.md b/src/Symfony/Bridge/PhpUnit/CHANGELOG.md index 9c5ef00f1992a..8cd3c372f2298 100644 --- a/src/Symfony/Bridge/PhpUnit/CHANGELOG.md +++ b/src/Symfony/Bridge/PhpUnit/CHANGELOG.md @@ -1,6 +1,13 @@ CHANGELOG ========= +4.3.0 +----- + + * added `ClassExistsMock` + * bumped PHP version from 5.3.3 to 5.5.9 + * split simple-phpunit bin into php file with code and a shell script + 4.1.0 ----- diff --git a/src/Symfony/Bridge/PhpUnit/ClassExistsMock.php b/src/Symfony/Bridge/PhpUnit/ClassExistsMock.php new file mode 100644 index 0000000000000..e8ca4ac9402a8 --- /dev/null +++ b/src/Symfony/Bridge/PhpUnit/ClassExistsMock.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\Bridge\PhpUnit; + +/** + * @author Roland Franssen + */ +class ClassExistsMock +{ + private static $classes = []; + + /** + * Configures the classes to be checked upon existence. + * + * @param array $classes Mocked class names as keys (case sensitive, without leading root namespace slash) and booleans as values + */ + public static function withMockedClasses(array $classes) + { + self::$classes = $classes; + } + + public static function class_exists($name, $autoload = true) + { + return (bool) (self::$classes[ltrim($name, '\\')] ?? \class_exists($name, $autoload)); + } + + public static function interface_exists($name, $autoload = true) + { + return (bool) (self::$classes[ltrim($name, '\\')] ?? \interface_exists($name, $autoload)); + } + + public static function trait_exists($name, $autoload = true) + { + return (bool) (self::$classes[ltrim($name, '\\')] ?? \trait_exists($name, $autoload)); + } + + public static function register($class) + { + $self = \get_called_class(); + + $mockedNs = [substr($class, 0, strrpos($class, '\\'))]; + if (0 < strpos($class, '\\Tests\\')) { + $ns = str_replace('\\Tests\\', '\\', $class); + $mockedNs[] = substr($ns, 0, strrpos($ns, '\\')); + } elseif (0 === strpos($class, 'Tests\\')) { + $mockedNs[] = substr($class, 6, strrpos($class, '\\') - 6); + } + foreach ($mockedNs as $ns) { + foreach (['class', 'interface', 'trait'] as $type) { + if (\function_exists($ns.'\\'.$type.'_exists')) { + continue; + } + eval(<< 0, + 'remaining selfCount' => 0, + 'legacyCount' => 0, + 'otherCount' => 0, + 'remaining directCount' => 0, + 'remaining indirectCount' => 0, + 'unsilenced' => [], + 'remaining self' => [], + 'legacy' => [], + 'other' => [], + 'remaining direct' => [], + 'remaining indirect' => [], + ]; private static $isRegistered = false; + private static $utilPrefix; /** * Registers and configures the deprecation handler. * - * The following reporting modes are supported: - * - use "weak" to hide the deprecation report but keep a global count; - * - use "weak_vendors" to fail only on deprecations triggered in your own code; - * - use "/some-regexp/" to stop the test suite whenever a deprecation - * message matches the given regular expression; - * - use a number to define the upper bound of allowed deprecations, - * making the test suite fail whenever more notices are triggered. + * The mode is a query string with options: + * - "disabled" to disable the deprecation handler + * - "verbose" to enable/disable displaying the deprecation report + * - "max" to configure the number of deprecations to allow before exiting with a non-zero + * status code; it's an array with keys "total", "self", "direct" and "indirect" + * + * The default mode is "max[total]=0&verbose=1". + * + * The mode can alternatively be "/some-regexp/" to stop the test suite whenever + * a deprecation message matches the given regular expression. * * @param int|string|false $mode The reporting mode, defaults to not allowing any deprecations */ @@ -43,262 +72,239 @@ public static function register($mode = 0) return; } - $UtilPrefix = class_exists('PHPUnit_Util_ErrorHandler') ? 'PHPUnit_Util_' : 'PHPUnit\Util\\'; + self::$utilPrefix = class_exists('PHPUnit_Util_ErrorHandler') ? 'PHPUnit_Util_' : 'PHPUnit\Util\\'; - $getMode = function () use ($mode) { - static $memoizedMode = false; + $handler = new self(); + $oldErrorHandler = set_error_handler([$handler, 'handleError']); - if (false !== $memoizedMode) { - return $memoizedMode; - } - if (false === $mode) { - $mode = getenv('SYMFONY_DEPRECATIONS_HELPER'); - } - if (DeprecationErrorHandler::MODE_DISABLED !== $mode - && DeprecationErrorHandler::MODE_WEAK !== $mode - && DeprecationErrorHandler::MODE_WEAK_VENDORS !== $mode - && (!isset($mode[0]) || '/' !== $mode[0]) - ) { - $mode = preg_match('/^[1-9][0-9]*$/', $mode) ? (int) $mode : 0; - } - - return $memoizedMode = $mode; - }; + if (null !== $oldErrorHandler) { + restore_error_handler(); - $inVendors = function ($path) { - /** @var string[] absolute paths to vendor directories */ - static $vendors; - if (null === $vendors) { - 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')) { - $vendors[] = $v; - } - } - } - } - $realPath = realpath($path); - if (false === $realPath && '-' !== $path && 'Standard input code' !== $path) { - return true; + if ([self::$utilPrefix.'ErrorHandler', 'handleError'] === $oldErrorHandler) { + restore_error_handler(); + self::register($mode); } - foreach ($vendors as $vendor) { - if (0 === strpos($realPath, $vendor) && false !== strpbrk(substr($realPath, \strlen($vendor), 1), '/'.\DIRECTORY_SEPARATOR)) { - return true; + } else { + $handler->mode = $mode; + self::$isRegistered = true; + register_shutdown_function([$handler, 'shutdown']); + } + } + + public static function collectDeprecations($outputFile) + { + $deprecations = []; + $previousErrorHandler = set_error_handler(function ($type, $msg, $file, $line, $context = []) use (&$deprecations, &$previousErrorHandler) { + if (E_USER_DEPRECATED !== $type && E_DEPRECATED !== $type) { + if ($previousErrorHandler) { + return $previousErrorHandler($type, $msg, $file, $line, $context); } - } - return false; - }; + static $autoload = true; - $deprecations = array( - 'unsilencedCount' => 0, - 'remainingCount' => 0, - 'legacyCount' => 0, - 'otherCount' => 0, - 'remaining vendorCount' => 0, - 'unsilenced' => array(), - 'remaining' => array(), - 'legacy' => array(), - 'other' => array(), - 'remaining vendor' => array(), - ); - $deprecationHandler = function ($type, $msg, $file, $line, $context = array()) use (&$deprecations, $getMode, $UtilPrefix, $inVendors) { - $mode = $getMode(); - if ((E_USER_DEPRECATED !== $type && E_DEPRECATED !== $type) || DeprecationErrorHandler::MODE_DISABLED === $mode) { - $ErrorHandler = $UtilPrefix.'ErrorHandler'; + $ErrorHandler = class_exists('PHPUnit_Util_ErrorHandler', $autoload) ? 'PHPUnit_Util_ErrorHandler' : 'PHPUnit\Util\ErrorHandler'; + $autoload = false; return $ErrorHandler::handleError($type, $msg, $file, $line, $context); } - $trace = debug_backtrace(); - $group = 'other'; - $isVendor = DeprecationErrorHandler::MODE_WEAK_VENDORS === $mode && $inVendors($file); + $deprecations[] = [error_reporting(), $msg, $file]; + }); - $i = \count($trace); - while (1 < $i && (!isset($trace[--$i]['class']) || ('ReflectionMethod' === $trace[$i]['class'] || 0 === strpos($trace[$i]['class'], 'PHPUnit_') || 0 === strpos($trace[$i]['class'], 'PHPUnit\\')))) { - // No-op - } + register_shutdown_function(function () use ($outputFile, &$deprecations) { + file_put_contents($outputFile, serialize($deprecations)); + }); + } - if (isset($trace[$i]['object']) || isset($trace[$i]['class'])) { - if (isset($trace[$i]['class']) && 0 === strpos($trace[$i]['class'], 'Symfony\Bridge\PhpUnit\Legacy\SymfonyTestsListenerFor')) { - $parsedMsg = unserialize($msg); - $msg = $parsedMsg['deprecation']; - $class = $parsedMsg['class']; - $method = $parsedMsg['method']; - // If the deprecation has been triggered via - // \Symfony\Bridge\PhpUnit\Legacy\SymfonyTestsListenerTrait::endTest() - // then we need to use the serialized information to determine - // if the error has been triggered from vendor code. - $isVendor = DeprecationErrorHandler::MODE_WEAK_VENDORS === $mode && isset($parsedMsg['triggering_file']) && $inVendors($parsedMsg['triggering_file']); - } else { - $class = isset($trace[$i]['object']) ? \get_class($trace[$i]['object']) : $trace[$i]['class']; - $method = $trace[$i]['function']; - } + /** + * @internal + */ + public function handleError($type, $msg, $file, $line, $context = []) + { + if ((E_USER_DEPRECATED !== $type && E_DEPRECATED !== $type) || !$this->getConfiguration()->isEnabled()) { + $ErrorHandler = self::$utilPrefix.'ErrorHandler'; - $Test = $UtilPrefix.'Test'; - - if (0 !== error_reporting()) { - $group = 'unsilenced'; - } elseif (0 === strpos($method, 'testLegacy') - || 0 === strpos($method, 'provideLegacy') - || 0 === strpos($method, 'getLegacy') - || strpos($class, '\Legacy') - || \in_array('legacy', $Test::getGroups($class, $method), true) - ) { - $group = 'legacy'; - } elseif ($isVendor) { - $group = 'remaining vendor'; - } else { - $group = 'remaining'; - } + return $ErrorHandler::handleError($type, $msg, $file, $line, $context); + } - if (isset($mode[0]) && '/' === $mode[0] && preg_match($mode, $msg)) { - $e = new \Exception($msg); - $r = new \ReflectionProperty($e, 'trace'); - $r->setAccessible(true); - $r->setValue($e, \array_slice($trace, 1, $i)); + $deprecation = new Deprecation($msg, debug_backtrace(), $file); + $group = 'other'; - echo "\n".ucfirst($group).' deprecation triggered by '.$class.'::'.$method.':'; - echo "\n".$msg; - echo "\nStack trace:"; - echo "\n".str_replace(' '.getcwd().\DIRECTORY_SEPARATOR, ' ', $e->getTraceAsString()); - echo "\n"; + if ($deprecation->originatesFromAnObject()) { + $class = $deprecation->originatingClass(); + $method = $deprecation->originatingMethod(); + $msg = $deprecation->getMessage(); - exit(1); - } - if ('legacy' !== $group && DeprecationErrorHandler::MODE_WEAK !== $mode) { - $ref = &$deprecations[$group][$msg]['count']; - ++$ref; - $ref = &$deprecations[$group][$msg][$class.'::'.$method]; - ++$ref; - } - } elseif (DeprecationErrorHandler::MODE_WEAK !== $mode) { - $ref = &$deprecations[$group][$msg]['count']; - ++$ref; + if (0 !== error_reporting()) { + $group = 'unsilenced'; + } elseif ($deprecation->isLegacy(self::$utilPrefix)) { + $group = 'legacy'; + } elseif (!$deprecation->isSelf()) { + $group = $deprecation->isIndirect() ? 'remaining indirect' : 'remaining direct'; + } else { + $group = 'remaining self'; } - ++$deprecations[$group.'Count']; - }; - $oldErrorHandler = set_error_handler($deprecationHandler); - if (null !== $oldErrorHandler) { - restore_error_handler(); - if (array($UtilPrefix.'ErrorHandler', 'handleError') === $oldErrorHandler) { - restore_error_handler(); - self::register($mode); + if ($this->getConfiguration()->shouldDisplayStackTrace($msg)) { + echo "\n".ucfirst($group).' '.$deprecation->toString(); + + exit(1); + } + if ('legacy' !== $group) { + $ref = &$this->deprecations[$group][$msg]['count']; + ++$ref; + $ref = &$this->deprecations[$group][$msg][$class.'::'.$method]; + ++$ref; } } else { - self::$isRegistered = true; - if (self::hasColorSupport()) { - $colorize = function ($str, $red) { - $color = $red ? '41;37' : '43;30'; + $ref = &$this->deprecations[$group][$msg]['count']; + ++$ref; + } - return "\x1B[{$color}m{$str}\x1B[0m"; - }; - } else { - $colorize = function ($str) { return $str; }; - } - register_shutdown_function(function () use ($getMode, &$deprecations, $deprecationHandler, $colorize) { - $mode = $getMode(); - if (isset($mode[0]) && '/' === $mode[0]) { - return; - } - $currErrorHandler = set_error_handler('var_dump'); - restore_error_handler(); + ++$this->deprecations[$group.'Count']; + } - if (DeprecationErrorHandler::MODE_WEAK === $mode) { - $colorize = function ($str) { return $str; }; - } - if ($currErrorHandler !== $deprecationHandler) { - echo "\n", $colorize('THE ERROR HANDLER HAS CHANGED!', true), "\n"; - F438 } + /** + * @internal + */ + public function shutdown() + { + $configuration = $this->getConfiguration(); - $cmp = function ($a, $b) { - return $b['count'] - $a['count']; - }; + if ($configuration->isInRegexMode()) { + return; + } - $groups = array('unsilenced', 'remaining'); - if (DeprecationErrorHandler::MODE_WEAK_VENDORS === $mode) { - $groups[] = 'remaining vendor'; - } - array_push($groups, 'legacy', 'other'); + $currErrorHandler = set_error_handler('var_dump'); + restore_error_handler(); - $displayDeprecations = function ($deprecations) use ($colorize, $cmp, $groups) { - foreach ($groups as $group) { - if ($deprecations[$group.'Count']) { - echo "\n", $colorize( - sprintf('%s deprecation notices (%d)', ucfirst($group), $deprecations[$group.'Count']), - 'legacy' !== $group && 'remaining vendor' !== $group - ), "\n"; + if ($currErrorHandler !== [$this, 'handleError']) { + echo "\n", self::colorize('THE ERROR HANDLER HAS CHANGED!', true), "\n"; + } - uasort($deprecations[$group], $cmp); + $groups = ['unsilenced', 'remaining self', 'remaining direct', 'remaining indirect', 'legacy', 'other']; - foreach ($deprecations[$group] as $msg => $notices) { - echo "\n ", $notices['count'], 'x: ', $msg, "\n"; + $this->displayDeprecations($groups, $configuration); - arsort($notices); + // store failing status + $isFailing = !$configuration->tolerates($this->deprecations); - foreach ($notices as $method => $count) { - if ('count' !== $method) { - echo ' ', $count, 'x in ', preg_replace('/(.*)\\\\(.*?::.*?)$/', '$2 from $1', $method), "\n"; - } - } - } - } - } - if (!empty($notices)) { - echo "\n"; - } - }; + // reset deprecations array + foreach ($this->deprecations as $group => $arrayOrInt) { + $this->deprecations[$group] = \is_int($arrayOrInt) ? 0 : []; + } - $displayDeprecations($deprecations); + register_shutdown_function(function () use ($isFailing, $groups, $configuration) { + foreach ($this->deprecations as $group => $arrayOrInt) { + if (0 < (\is_int($arrayOrInt) ? $arrayOrInt : \count($arrayOrInt))) { + echo "Shutdown-time deprecations:\n"; + break; + } + } - // store failing status - $isFailing = DeprecationErrorHandler::MODE_WEAK !== $mode && $mode < $deprecations['unsilencedCount'] + $deprecations['remainingCount'] + $deprecations['otherCount']; + $this->displayDeprecations($groups, $configuration); - // reset deprecations array - foreach ($deprecations as $group => $arrayOrInt) { - $deprecations[$group] = \is_int($arrayOrInt) ? 0 : array(); - } + if ($isFailing || !$configuration->tolerates($this->deprecations)) { + exit(1); + } + }); + } - register_shutdown_function(function () use (&$deprecations, $isFailing, $displayDeprecations, $mode) { - foreach ($deprecations as $group => $arrayOrInt) { - if (0 < (\is_int($arrayOrInt) ? $arrayOrInt : \count($arrayOrInt))) { - echo "Shutdown-time deprecations:\n"; - break; - } - } - $displayDeprecations($deprecations); - if ($isFailing || DeprecationErrorHandler::MODE_WEAK !== $mode && $mode < $deprecations['unsilencedCount'] + $deprecations['remainingCount'] + $deprecations['otherCount']) { - exit(1); - } - }); - }); + private function getConfiguration() + { + if (null !== $this->configuration) { + return $this->configuration; + } + if (false === $mode = $this->mode) { + $mode = getenv('SYMFONY_DEPRECATIONS_HELPER'); + } + if ('strict' === $mode) { + return $this->configuration = Configuration::inStrictMode(); + } + if (self::MODE_DISABLED === $mode) { + return $this->configuration = Configuration::inDisabledMode(); } + if ('weak' === $mode) { + return $this->configuration = Configuration::inWeakMode(); + } + if (self::MODE_WEAK_VENDORS === $mode) { + ++$this->deprecations['remaining selfCount']; + $msg = sprintf('Setting SYMFONY_DEPRECATIONS_HELPER to "%s" is deprecated in favor of "max[self]=0"', $mode); + $ref = &$this->deprecations['remaining self'][$msg]['count']; + ++$ref; + $mode = 'max[self]=0'; + } + if (isset($mode[0]) && '/' === $mode[0]) { + return $this->configuration = Configuration::fromRegex($mode); + } + + if (preg_match('/^[1-9][0-9]*$/', (string) $mode)) { + return $this->configuration = Configuration::fromNumber($mode); + } + + if (!$mode) { + return $this->configuration = Configuration::fromNumber(0); + } + + return $this->configuration = Configuration::fromUrlEncodedString((string) $mode); } - public static function collectDeprecations($outputFile) + /** + * @param string $str + * @param bool $red + * + * @return string + */ + private static function colorize($str, $red) { - $deprecations = array(); - $previousErrorHandler = set_error_handler(function ($type, $msg, $file, $line, $context = array()) use (&$deprecations, &$previousErrorHandler) { - if (E_USER_DEPRECATED !== $type && E_DEPRECATED !== $type) { - if ($previousErrorHandler) { - return $previousErrorHandler($type, $msg, $file, $line, $context); + if (!self::hasColorSupport()) { + return $str; + } + + $color = $red ? '41;37' : '43;30'; + + return "\x1B[{$color}m{$str}\x1B[0m"; + } + + /** + * @param string[] $groups + * @param Configuration $configuration + */ + private function displayDeprecations($groups, $configuration) + { + $cmp = function ($a, $b) { + return $b['count'] - $a['count']; + }; + + foreach ($groups as $group) { + if ($this->deprecations[$group.'Count']) { + echo "\n", self::colorize( + sprintf('%s deprecation notices (%d)', ucfirst($group), $this->deprecations[$group.'Count']), + 'legacy' !== $group && 'remaining indirect' !== $group + ), "\n"; + + if (!$configuration->verboseOutput()) { + continue; } - static $autoload = true; + uasort($this->deprecations[$group], $cmp); - $ErrorHandler = class_exists('PHPUnit_Util_ErrorHandler', $autoload) ? 'PHPUnit_Util_ErrorHandler' : 'PHPUnit\Util\ErrorHandler'; - $autoload = false; + foreach ($this->deprecations[$group] as $msg => $notices) { + echo "\n ", $notices['count'], 'x: ', $msg, "\n"; - return $ErrorHandler::handleError($type, $msg, $file, $line, $context); + arsort($notices); + + foreach ($notices as $method => $count) { + if ('count' !== $method) { + echo ' ', $count, 'x in ', preg_replace('/(.*)\\\\(.*?::.*?)$/', '$2 from $1', $method), "\n"; + } + } + } } - $deprecations[] = array(error_reporting(), $msg, $file); - }); + } - register_shutdown_function(function () use ($outputFile, &$deprecations) { - file_put_contents($outputFile, serialize($deprecations)); - }); + if (!empty($notices)) { + echo "\n"; + } } /** @@ -336,6 +342,7 @@ private static function hasColorSupport() } $stat = fstat(STDOUT); + // Check if formatted mode is S_IFCHR return $stat ? 0020000 === ($stat['mode'] & 0170000) : false; } diff --git a/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler/Configuration.php b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler/Configuration.php new file mode 100644 index 0000000000000..44f0341205aa7 --- /dev/null +++ b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler/Configuration.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\Bridge\PhpUnit\DeprecationErrorHandler; + +/** + * @internal + */ +class Configuration +{ + /** + * @var int[] + */ + private $thresholds; + + /** + * @var string + */ + private $regex; + + /** + * @var bool + */ + private $enabled = true; + + /** + * @var bool + */ + private $verboseOutput = true; + + /** + * @param int[] $thresholds A hash associating groups to thresholds + * @param string $regex Will be matched against messages, to decide + * whether to display a stack trace + * @param bool $verboseOutput + */ + private function __construct(array $thresholds = [], $regex = '', $verboseOutput = true) + { + $groups = ['total', 'indirect', 'direct', 'self']; + + foreach ($thresholds as $group => $threshold) { + if (!\in_array($group, $groups, true)) { + throw new \InvalidArgumentException(sprintf('Unrecognized threshold "%s", expected one of "%s"', $group, implode('", "', $groups))); + } + if (!is_numeric($threshold)) { + throw new \InvalidArgumentException(sprintf('Threshold for group "%s" has invalid value "%s"', $group, $threshold)); + } + $this->thresholds[$group] = (int) $threshold; + } + if (isset($this->thresholds['direct'])) { + $this->thresholds += [ + 'self' => $this->thresholds['direct'], + ]; + } + if (isset($this->thresholds['indirect'])) { + $this->thresholds += [ + 'direct' => $this->thresholds['indirect'], + 'self' => $this->thresholds['indirect'], + ]; + } + foreach ($groups as $group) { + if (!isset($this->thresholds[$group])) { + $this->thresholds[$group] = 999999; + } + } + $this->regex = $regex; + $this->verboseOutput = $verboseOutput; + } + + /** + * @return bool + */ + public function isEnabled() + { + return $this->enabled; + } + + /** + * @param mixed[] $deprecations + * + * @return bool + */ + public function tolerates(array $deprecations) + { + $deprecationCounts = array_filter($deprecations, function ($key) { + return false !== strpos($key, 'Count') && false === strpos($key, 'legacy'); + }, ARRAY_FILTER_USE_KEY); + + if (array_sum($deprecationCounts) > $this->thresholds['total']) { + return false; + } + foreach (['self', 'direct', 'indirect'] as $deprecationType) { + if ($deprecationCounts['remaining '.$deprecationType.'Count'] > $this->thresholds[$deprecationType]) { + return false; + } + } + + return true; + } + + /** + * @param string $message + * + * @return bool + */ + public function shouldDisplayStackTrace($message) + { + return '' !== $this->regex && preg_match($this->regex, $message); + } + + /** + * @return bool + */ + public function isInRegexMode() + { + return '' !== $this->regex; + } + + /** + * @return bool + */ + public function verboseOutput() + { + return $this->verboseOutput; + } + + /** + * @param string $serializedConfiguration an encoded string, for instance + * max[total]=1234&max[indirect]=42 + * + * @return self + */ + public static function fromUrlEncodedString($serializedConfiguration) + { + parse_str($serializedConfiguration, $normalizedConfiguration); + foreach (array_keys($normalizedConfiguration) as $key) { + if (!\in_array($key, ['max', 'disabled', 'verbose'], true)) { + throw new \InvalidArgumentException(sprintf('Unknown configuration option "%s"', $key)); + } + } + + if (isset($normalizedConfiguration['disabled'])) { + return self::inDisabledMode(); + } + + $verboseOutput = true; + if (isset($normalizedConfiguration['verbose'])) { + $verboseOutput = (bool) $normalizedConfiguration['verbose']; + } + + return new self( + isset($normalizedConfiguration['max']) ? $normalizedConfiguration['max'] : [], + '', + $verboseOutput + ); + } + + /** + * @return self + */ + public static function inDisabledMode() + { + $configuration = new self(); + $configuration->enabled = false; + + return $configuration; + } + + /** + * @return self + */ + public static function inStrictMode() + { + return new self(['total' => 0]); + } + + /** + * @return self + */ + public static function inWeakMode() + { + return new self([], '', false); + } + + /** + * @return self + */ + public static function fromNumber($upperBound) + { + return new self(['total' => $upperBound]); + } + + /** + * @return self + */ + public static function fromRegex($regex) + { + return new self([], $regex); + } +} diff --git a/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler/Deprecation.php b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler/Deprecation.php new file mode 100644 index 0000000000000..ba9e753c7b1d1 --- /dev/null +++ b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler/Deprecation.php @@ -0,0 +1,299 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\PhpUnit\DeprecationErrorHandler; + +use Symfony\Bridge\PhpUnit\Legacy\SymfonyTestsListenerFor; + +/** + * @internal + */ +class Deprecation +{ + /** + * @var array + */ + private $trace; + + /** + * @var string + */ + private $message; + + /** + * @var ?string + */ + private $originClass; + + /** + * @var ?string + */ + private $originMethod; + + /** + * @var bool + */ + private $self; + + /** @var string[] absolute paths to vendor directories */ + private static $vendors; + + /** + * @param string $message + * @param string $file + */ + public function __construct($message, array $trace, $file) + { + $this->trace = $trace; + $this->message = $message; + $i = \count($trace); + while (1 < $i && $this->lineShouldBeSkipped($trace[--$i])) { + // No-op + } + $line = $trace[$i]; + $this->self = !$this->pathOriginatesFromVendor($file); + if (isset($line['object']) || isset($line['class'])) { + if (isset($line['class']) && 0 === strpos($line['class'], SymfonyTestsListenerFor::class)) { + $parsedMsg = unserialize($this->message); + $this->message = $parsedMsg['deprecation']; + $this->originClass = $parsedMsg['class']; + $this->originMethod = $parsedMsg['method']; + // If the deprecation has been triggered via + // \Symfony\Bridge\PhpUnit\Legacy\SymfonyTestsListenerTrait::endTest() + // then we need to use the serialized information to determine + // if the error has been triggered from vendor code. + $this->self = isset($parsedMsg['triggering_file']) + && $this->pathOriginatesFromVendor($parsedMsg['triggering_file']); + + return; + } + $this->originClass = isset($line['object']) ? \get_class($line['object']) : $line['class']; + $this->originMethod = $line['function']; + } + } + + /** + * @return bool + */ + private function lineShouldBeSkipped(array $line) + { + if (!isset($line['class'])) { + return true; + } + $class = $line['class']; + + return 'ReflectionMethod' === $class || 0 === strpos($class, 'PHPUnit_') || 0 === strpos($class, 'PHPUnit\\'); + } + + /** + * @return bool + */ + public function originatesFromAnObject() + { + return isset($this->originClass); + } + + /** + * @return bool + */ + public function isSelf() + { + return $this->self; + } + + /** + * @return string + */ + public function originatingClass() + { + if (null === $this->originClass) { + throw new \LogicException('Check with originatesFromAnObject() before calling this method'); + } + + return $this->originClass; + } + + /** + * @return string + */ + public function originatingMethod() + { + if (null === $this->originMethod) { + throw new \LogicException('Check with originatesFromAnObject() before calling this method'); + } + + return $this->originMethod; + } + + /** + * @return string + */ + public function getMessage() + { + return $this->message; + } + + /** + * @param string $utilPrefix + * + * @return bool + */ + public function isLegacy($utilPrefix) + { + $test = $utilPrefix.'Test'; + $class = $this->originatingClass(); + $method = $this->originatingMethod(); + + return 0 === strpos($method, 'testLegacy') + || 0 === strpos($method, 'provideLegacy') + || 0 === strpos($method, 'getLegacy') + || strpos($class, '\Legacy') + || \in_array('legacy', $test::getGroups($class, $method), true); + } + + /** + * Tells whether both the calling package and the called package are vendor + * packages. + * + * @return bool + */ + public function isIndirect() + { + $erroringFile = $erroringPackage = null; + foreach ($this->trace as $line) { + if (\in_array($line['function'], ['require', 'require_once', 'include', 'include_once'], true)) { + continue; + } + if (!isset($line['file'])) { + continue; + } + $file = $line['file']; + if ('-' === $file || 'Standard input code' === $file || !realpath($file)) { + continue; + } + if (!$this->pathOriginatesFromVendor($file)) { + return false; + } + if (null !== $erroringFile && null !== $erroringPackage) { + $package = $this->getPackage($file); + if ('composer' !== $package && $package !== $erroringPackage) { + return true; + } + continue; + } + $erroringFile = $file; + $erroringPackage = $this->getPackage($file); + } + + return false; + } + + /** + * pathOriginatesFromVendor() should always be called prior to calling this method. + * + * @param string $path + * + * @return string + */ + private function getPackage($path) + { + $path = realpath($path) ?: $path; + foreach (self::getVendors() as $vendorRoot) { + if (0 === strpos($path, $vendorRoot)) { + $relativePath = substr($path, \strlen($vendorRoot) + 1); + $vendor = strstr($relativePath, \DIRECTORY_SEPARATOR, true); + if (false === $vendor) { + throw new \RuntimeException(sprintf('Could not find directory separator "%s" in path "%s"', \DIRECTORY_SEPARATOR, $relativePath)); + } + + return rtrim($vendor.'/'.strstr(substr( + $relativePath, + \strlen($vendor) + 1 + ), \DIRECTORY_SEPARATOR, true), '/'); + } + } + + throw new \RuntimeException(sprintf('No vendors found for path "%s"', $path)); + } + + /** + * @return string[] an array of paths + */ + private static function getVendors() + { + if (null === self::$vendors) { + self::$vendors = []; + 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::$vendors[] = $v; + } + } + } + } + + return self::$vendors; + } + + /** + * @param string $path + * + * @return bool + */ + private function pathOriginatesFromVendor($path) + { + $realPath = realpath($path); + if (false === $realPath && '-' !== $path && 'Standard input code' !== $path) { + return true; + } + foreach (self::getVendors() as $vendor) { + if (0 === strpos($realPath, $vendor) && false !== strpbrk(substr($realPath, \strlen($vendor), 1), '/'.\DIRECTORY_SEPARATOR)) { + return true; + } + } + + return false; + } + + /** + * @return string + */ + public function toString() + { + $exception = new \Exception($this->message); + $reflection = new \ReflectionProperty($exception, 'trace'); + $reflection->setAccessible(true); + $reflection->setValue($exception, $this->trace); + + return 'deprecation triggered by '.$this->originatingClass().'::'.$this->originatingMethod().':'. + "\n".$this->message. + "\nStack trace:". + "\n".str_replace(' '.getcwd().\DIRECTORY_SEPARATOR, ' ', $exception->getTraceAsString()). + "\n"; + } + + private function getPackageFromLine(array $line) + { + if (!isset($line['file'])) { + return 'internal function'; + } + if (!$this->pathOriginatesFromVendor($line['file'])) { + return 'source code'; + } + try { + return $this->getPackage($line['file']); + } catch (\RuntimeException $e) { + return 'unknown'; + } + } +} diff --git a/src/Symfony/Bridge/PhpUnit/LICENSE b/src/Symfony/Bridge/PhpUnit/LICENSE index 15fc1c88d330b..cf8b3ebe87145 100644 --- a/src/Symfony/Bridge/PhpUnit/LICENSE +++ b/src/Symfony/Bridge/PhpUnit/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2014-2018 Fabien Potencier +Copyright (c) 2014-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 diff --git a/src/Symfony/Bridge/PhpUnit/Legacy/CommandForV5.php b/src/Symfony/Bridge/PhpUnit/Legacy/CommandForV5.php index d4b5ea26d8cd8..95dcb1e5541fc 100644 --- a/src/Symfony/Bridge/PhpUnit/Legacy/CommandForV5.php +++ b/src/Symfony/Bridge/PhpUnit/Legacy/CommandForV5.php @@ -23,6 +23,24 @@ class CommandForV5 extends \PHPUnit_TextUI_Command */ protected function createRunner() { - return new TestRunnerForV5($this->arguments['loader']); + $listener = new SymfonyTestsListenerForV5(); + + $this->arguments['listeners'] = isset($this->arguments['listeners']) ? $this->arguments['listeners'] : array(); + + $registeredLocally = false; + + foreach ($this->arguments['listeners'] as $registeredListener) { + if ($registeredListener instanceof SymfonyTestsListenerForV5) { + $registeredListener->globalListenerDisabled(); + $registeredLocally = true; + break; + } + } + + if (!$registeredLocally) { + $this->arguments['listeners'][] = $listener; + } + + return parent::createRunner(); } } diff --git a/src/Symfony/Bridge/PhpUnit/Legacy/CommandForV6.php b/src/Symfony/Bridge/PhpUnit/Legacy/CommandForV6.php index fc717ef415cb3..f8f75bb09a93b 100644 --- a/src/Symfony/Bridge/PhpUnit/Legacy/CommandForV6.php +++ b/src/Symfony/Bridge/PhpUnit/Legacy/CommandForV6.php @@ -13,7 +13,7 @@ use PHPUnit\TextUI\Command as BaseCommand; use PHPUnit\TextUI\TestRunner as BaseRunner; -use Symfony\Bridge\PhpUnit\TextUI\TestRunner; +use Symfony\Bridge\PhpUnit\SymfonyTestsListener; /** * {@inheritdoc} @@ -27,6 +27,24 @@ class CommandForV6 extends BaseCommand */ protected function createRunner(): BaseRunner { - return new TestRunner($this->arguments['loader']); + $listener = new SymfonyTestsListener(); + + $this->arguments['listeners'] = isset($this->arguments['listeners']) ? $this->arguments['listeners'] : []; + + $registeredLocally = false; + + foreach ($this->arguments['listeners'] as $registeredListener) { + if ($registeredListener instanceof SymfonyTestsListener) { + $registeredListener->globalListenerDisabled(); + $registeredLocally = true; + break; + } + } + + if (!$registeredLocally) { + $this->arguments['listeners'][] = $listener; + } + + return parent::createRunner(); } } diff --git a/src/Symfony/Bridge/PhpUnit/Legacy/CoverageListenerTrait.php b/src/Symfony/Bridge/PhpUnit/Legacy/CoverageListenerTrait.php index 8e9bdbe92ed4d..d68aa2888218b 100644 --- a/src/Symfony/Bridge/PhpUnit/Legacy/CoverageListenerTrait.php +++ b/src/Symfony/Bridge/PhpUnit/Legacy/CoverageListenerTrait.php @@ -102,6 +102,16 @@ private function findSutFqcn($test) return $sutFqcn; } + public function __sleep() + { + throw new \BadMethodCallException('Cannot serialize '.__CLASS__); + } + + public function __wakeup() + { + throw new \BadMethodCallException('Cannot unserialize '.__CLASS__); + } + public function __destruct() { if (!$this->warnings) { diff --git a/src/Symfony/Bridge/PhpUnit/Legacy/SymfonyTestsListenerTrait.php b/src/Symfony/Bridge/PhpUnit/Legacy/SymfonyTestsListenerTrait.php index b527e039662bb..cf2723796106e 100644 --- a/src/Symfony/Bridge/PhpUnit/Legacy/SymfonyTestsListenerTrait.php +++ b/src/Symfony/Bridge/PhpUnit/Legacy/SymfonyTestsListenerTrait.php @@ -83,6 +83,16 @@ public function __construct(array $mockedNamespaces = array()) } } + public function __sleep() + { + throw new \BadMethodCallException('Cannot serialize '.__CLASS__); + } + + public function __wakeup() + { + throw new \BadMethodCallException('Cannot unserialize '.__CLASS__); + } + public function __destruct() { if (0 < $this->state) { diff --git a/src/Symfony/Bridge/PhpUnit/Legacy/TestRunnerForV5.php b/src/Symfony/Bridge/PhpUnit/Legacy/TestRunnerForV5.php deleted file mode 100644 index 7897861cf52f7..0000000000000 --- a/src/Symfony/Bridge/PhpUnit/Legacy/TestRunnerForV5.php +++ /dev/null @@ -1,48 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bridge\PhpUnit\Legacy; - -/** - * {@inheritdoc} - * - * @internal - */ -class TestRunnerForV5 extends \PHPUnit_TextUI_TestRunner -{ - /** - * {@inheritdoc} - */ - protected function handleConfiguration(array &$arguments) - { - $listener = new SymfonyTestsListenerForV5(); - - $result = parent::handleConfiguration($arguments); - - $arguments['listeners'] = isset($arguments['listeners']) ? $arguments['listeners'] : array(); - - $registeredLocally = false; - - foreach ($arguments['listeners'] as $registeredListener) { - if ($registeredListener instanceof SymfonyTestsListenerForV5) { - $registeredListener->globalListenerDisabled(); - $registeredLocally = true; - break; - } - } - - if (!$registeredLocally) { - $arguments['listeners'][] = $listener; - } - - return $result; - } -} diff --git a/src/Symfony/Bridge/PhpUnit/Legacy/TestRunnerForV6.php b/src/Symfony/Bridge/PhpUnit/Legacy/TestRunnerForV6.php deleted file mode 100644 index 6da7c65448532..0000000000000 --- a/src/Symfony/Bridge/PhpUnit/Legacy/TestRunnerForV6.php +++ /dev/null @@ -1,49 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bridge\PhpUnit\Legacy; - -use PHPUnit\TextUI\TestRunner as BaseRunner; -use Symfony\Bridge\PhpUnit\SymfonyTestsListener; - -/** - * {@inheritdoc} - * - * @internal - */ -class TestRunnerForV6 extends BaseRunner -{ - /** - * {@inheritdoc} - */ - protected function handleConfiguration(array &$arguments) - { - $listener = new SymfonyTestsListener(); - - parent::handleConfiguration($arguments); - - $arguments['listeners'] = isset($arguments['listeners']) ? $arguments['listeners'] : array(); - - $registeredLocally = false; - - foreach ($arguments['listeners'] as $registeredListener) { - if ($registeredListener instanceof SymfonyTestsListener) { - $registeredListener->globalListenerDisabled(); - $registeredLocally = true; - break; - } - } - - if (!$registeredLocally) { - $arguments['listeners'][] = $listener; - } - } -} diff --git a/src/Symfony/Bridge/PhpUnit/Legacy/TestRunnerForV7.php b/src/Symfony/Bridge/PhpUnit/Legacy/TestRunnerForV7.php deleted file mode 100644 index a175fb65d7f5a..0000000000000 --- a/src/Symfony/Bridge/PhpUnit/Legacy/TestRunnerForV7.php +++ /dev/null @@ -1,49 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bridge\PhpUnit\Legacy; - -use PHPUnit\TextUI\TestRunner as BaseRunner; -use Symfony\Bridge\PhpUnit\SymfonyTestsListener; - -/** - * {@inheritdoc} - * - * @internal - */ -class TestRunnerForV7 extends BaseRunner -{ - /** - * {@inheritdoc} - */ - protected function handleConfiguration(array &$arguments): void - { - $listener = new SymfonyTestsListener(); - - parent::handleConfiguration($arguments); - - $arguments['listeners'] = isset($arguments['listeners']) ? $arguments['listeners'] : array(); - - $registeredLocally = false; - - foreach ($arguments['listeners'] as $registeredListener) { - if ($registeredListener instanceof SymfonyTestsListener) { - $registeredListener->globalListenerDisabled(); - $registeredLocally = true; - break; - } - } - - if (!$registeredLocally) { - $arguments['listeners'][] = $listener; - } - } -} diff --git a/src/Symfony/Bridge/PhpUnit/Tests/ClassExistsMockTest.php b/src/Symfony/Bridge/PhpUnit/Tests/ClassExistsMockTest.php new file mode 100644 index 0000000000000..002d313a6fa01 --- /dev/null +++ b/src/Symfony/Bridge/PhpUnit/Tests/ClassExistsMockTest.php @@ -0,0 +1,119 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\PhpUnit\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Bridge\PhpUnit\ClassExistsMock; + +class ClassExistsMockTest extends TestCase +{ + public static function setUpBeforeClass() + { + ClassExistsMock::register(__CLASS__); + } + + protected function setUp() + { + ClassExistsMock::withMockedClasses([ + ExistingClass::class => false, + 'NonExistingClass' => true, + ExistingInterface::class => false, + 'NonExistingInterface' => true, + ExistingTrait::class => false, + 'NonExistingTrait' => true, + ]); + } + + public function testClassExists() + { + $this->assertFalse(class_exists(ExistingClass::class)); + $this->assertFalse(class_exists(ExistingClass::class, false)); + $this->assertFalse(class_exists('\\'.ExistingClass::class)); + $this->assertFalse(class_exists('\\'.ExistingClass::class, false)); + $this->assertTrue(class_exists('NonExistingClass')); + $this->assertTrue(class_exists('NonExistingClass', false)); + $this->assertTrue(class_exists('\\NonExistingClass')); + $this->assertTrue(class_exists('\\NonExistingClass', false)); + $this->assertTrue(class_exists(ExistingClassReal::class)); + $this->assertTrue(class_exists(ExistingClassReal::class, false)); + $this->assertTrue(class_exists('\\'.ExistingClassReal::class)); + $this->assertTrue(class_exists('\\'.ExistingClassReal::class, false)); + $this->assertFalse(class_exists('NonExistingClassReal')); + $this->assertFalse(class_exists('NonExistingClassReal', false)); + $this->assertFalse(class_exists('\\NonExistingClassReal')); + $this->assertFalse(class_exists('\\NonExistingClassReal', false)); + } + + public function testInterfaceExists() + { + $this->assertFalse(interface_exists(ExistingInterface::class)); + $this->assertFalse(interface_exists(ExistingInterface::class, false)); + $this->assertFalse(interface_exists('\\'.ExistingInterface::class)); + $this->assertFalse(interface_exists('\\'.ExistingInterface::class, false)); + $this->assertTrue(interface_exists('NonExistingInterface')); + $this->assertTrue(interface_exists('NonExistingInterface', false)); + $this->assertTrue(interface_exists('\\NonExistingInterface')); + $this->assertTrue(interface_exists('\\NonExistingInterface', false)); + $this->assertTrue(interface_exists(ExistingInterfaceReal::class)); + $this->assertTrue(interface_exists(ExistingInterfaceReal::class, false)); + $this->assertTrue(interface_exists('\\'.ExistingInterfaceReal::class)); + $this->assertTrue(interface_exists('\\'.ExistingInterfaceReal::class, false)); + $this->assertFalse(interface_exists('NonExistingClassReal')); + $this->assertFalse(interface_exists('NonExistingClassReal', false)); + $this->assertFalse(interface_exists('\\NonExistingInterfaceReal')); + $this->assertFalse(interface_exists('\\NonExistingInterfaceReal', false)); + } + + public function testTraitExists() + { + $this->assertFalse(trait_exists(ExistingTrait::class)); + $this->assertFalse(trait_exists(ExistingTrait::class, false)); + $this->assertFalse(trait_exists('\\'.ExistingTrait::class)); + $this->assertFalse(trait_exists('\\'.ExistingTrait::class, false)); + $this->assertTrue(trait_exists('NonExistingTrait')); + $this->assertTrue(trait_exists('NonExistingTrait', false)); + $this->assertTrue(trait_exists('\\NonExistingTrait')); + $this->assertTrue(trait_exists('\\NonExistingTrait', false)); + $this->assertTrue(trait_exists(ExistingTraitReal::class)); + $this->assertTrue(trait_exists(ExistingTraitReal::class, false)); + $this->assertTrue(trait_exists('\\'.ExistingTraitReal::class)); + $this->assertTrue(trait_exists('\\'.ExistingTraitReal::class, false)); + $this->assertFalse(trait_exists('NonExistingClassReal')); + $this->assertFalse(trait_exists('NonExistingClassReal', false)); + $this->assertFalse(trait_exists('\\NonExistingTraitReal')); + $this->assertFalse(trait_exists('\\NonExistingTraitReal', false)); + } +} + +class ExistingClass +{ +} + +class ExistingClassReal +{ +} + +interface ExistingInterface +{ +} + +interface ExistingInterfaceReal +{ +} + +trait ExistingTrait +{ +} + +trait ExistingTraitReal +{ +} diff --git a/src/Symfony/Bridge/PhpUnit/Tests/ClockMockTest.php b/src/Symfony/Bridge/PhpUnit/Tests/ClockMockTest.php index 4c77e99f5e20f..5b92ccd8507e4 100644 --- a/src/Symfony/Bridge/PhpUnit/Tests/ClockMockTest.php +++ b/src/Symfony/Bridge/PhpUnit/Tests/ClockMockTest.php @@ -62,4 +62,11 @@ public function testDate() { $this->assertSame('1234567890', date('U')); } + + public function testGmDate() + { + ClockMock::withClockMock(1555075769); + + $this->assertSame('1555075769', gmdate('U')); + } } diff --git a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/ConfigurationTest.php b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/ConfigurationTest.php new file mode 100644 index 0000000000000..39e792cd3a2cb --- /dev/null +++ b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/ConfigurationTest.php @@ -0,0 +1,195 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\PhpUnit\Tests\DeprecationErrorHandler; + +use PHPUnit\Framework\TestCase; +use Symfony\Bridge\PhpUnit\DeprecationErrorHandler\Configuration; + +class ConfigurationTest extends TestCase +{ + public function testItThrowsOnStringishValue() + { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('hi'); + Configuration::fromUrlEncodedString('hi'); + } + + public function testItThrowsOnUnknownConfigurationOption() + { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('min'); + Configuration::fromUrlEncodedString('min[total]=42'); + } + + public function testItThrowsOnUnknownThreshold() + { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('deep'); + Configuration::fromUrlEncodedString('max[deep]=42'); + } + + public function testItThrowsOnStringishThreshold() + { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('forty-two'); + Configuration::fromUrlEncodedString('max[total]=forty-two'); + } + + public function testItNoticesExceededTotalThreshold() + { + $configuration = Configuration::fromUrlEncodedString('max[total]=3'); + $this->assertTrue($configuration->tolerates([ + 'unsilencedCount' => 1, + 'remaining selfCount' => 0, + 'legacyCount' => 1, + 'otherCount' => 0, + 'remaining directCount' => 1, + 'remaining indirectCount' => 1, + ])); + $this->assertFalse($configuration->tolerates([ + 'unsilencedCount' => 1, + 'remaining selfCount' => 1, + 'legacyCount' => 1, + 'otherCount' => 0, + 'remaining directCount' => 1, + 'remaining indirectCount' => 1, + ])); + } + + public function testItNoticesExceededSelfThreshold() + { + $configuration = Configuration::fromUrlEncodedString('max[self]=1'); + $this->assertTrue($configuration->tolerates([ + 'unsilencedCount' => 1234, + 'remaining selfCount' => 1, + 'legacyCount' => 23, + 'otherCount' => 13, + 'remaining directCount' => 124, + 'remaining indirectCount' => 3244, + ])); + $this->assertFalse($configuration->tolerates([ + 'unsilencedCount' => 1234, + 'remaining selfCount' => 2, + 'legacyCount' => 23, + 'otherCount' => 13, + 'remaining directCount' => 124, + 'remaining indirectCount' => 3244, + ])); + } + + public function testItNoticesExceededDirectThreshold() + { + $configuration = Configuration::fromUrlEncodedString('max[direct]=1&max[self]=999999'); + $this->assertTrue($configuration->tolerates([ + 'unsilencedCount' => 1234, + 'remaining selfCount' => 123, + 'legacyCount' => 23, + 'otherCount' => 13, + 'remaining directCount' => 1, + 'remaining indirectCount' => 3244, + ])); + $this->assertFalse($configuration->tolerates([ + 'unsilencedCount' => 1234, + 'remaining selfCount' => 124, + 'legacyCount' => 23, + 'otherCount' => 13, + 'remaining directCount' => 2, + 'remaining indirectCount' => 3244, + ])); + } + + public function testItNoticesExceededIndirectThreshold() + { + $configuration = Configuration::fromUrlEncodedString('max[indirect]=1&max[direct]=999999&max[self]=999999'); + $this->assertTrue($configuration->tolerates([ + 'unsilencedCount' => 1234, + 'remaining selfCount' => 123, + 'legacyCount' => 23, + 'otherCount' => 13, + 'remaining directCount' => 1234, + 'remaining indirectCount' => 1, + ])); + $this->assertFalse($configuration->tolerates([ + 'unsilencedCount' => 1234, + 'remaining selfCount' => 124, + 'legacyCount' => 23, + 'otherCount' => 13, + 'remaining directCount' => 2324, + 'remaining indirectCount' => 2, + ])); + } + + public function testIndirectThresholdIsUsedAsADefaultForDirectAndSelfThreshold() + { + $configuration = Configuration::fromUrlEncodedString('max[indirect]=1'); + $this->assertTrue($configuration->tolerates([ + 'unsilencedCount' => 0, + 'remaining selfCount' => 1, + 'legacyCount' => 0, + 'otherCount' => 0, + 'remaining directCount' => 0, + 'remaining indirectCount' => 0, + ])); + $this->assertFalse($configuration->tolerates([ + 'unsilencedCount' => 0, + 'remaining selfCount' => 2, + 'legacyCount' => 0, + 'otherCount' => 0, + 'remaining directCount' => 0, + 'remaining indirectCount' => 0, + ])); + $this->assertTrue($configuration->tolerates([ + 'unsilencedCount' => 0, + 'remaining selfCount' => 0, + 'legacyCount' => 0, + 'otherCount' => 0, + 'remaining directCount' => 1, + 'remaining indirectCount' => 0, + ])); + $this->assertFalse($configuration->tolerates([ + 'unsilencedCount' => 0, + 'remaining selfCount' => 0, + 'legacyCount' => 0, + 'otherCount' => 0, + 'remaining directCount' => 2, + 'remaining indirectCount' => 0, + ])); + } + + public function testItCanTellWhetherToDisplayAStackTrace() + { + $configuration = Configuration::fromUrlEncodedString(''); + $this->assertFalse($configuration->shouldDisplayStackTrace('interesting')); + + $configuration = Configuration::fromRegex('/^interesting/'); + $this->assertFalse($configuration->shouldDisplayStackTrace('uninteresting')); + $this->assertTrue($configuration->shouldDisplayStackTrace('interesting')); + } + + public function testItCanBeDisabled() + { + $configuration = Configuration::fromUrlEncodedString('disabled'); + $this->assertFalse($configuration->isEnabled()); + } + + public function testItCanBeShushed() + { + $configuration = Configuration::fromUrlEncodedString('verbose'); + $this->assertFalse($configuration->verboseOutput()); + } + + public function testOutputIsNotVerboseInWeakMode() + { + $configuration = Configuration::inWeakMode(); + $this->assertFalse($configuration->verboseOutput()); + } +} diff --git a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/DeprecationTest.php b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/DeprecationTest.php new file mode 100644 index 0000000000000..92bad71e08498 --- /dev/null +++ b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/DeprecationTest.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\Bridge\PhpUnit\Tests\DeprecationErrorHandler; + +use PHPUnit\Framework\TestCase; +use Symfony\Bridge\PhpUnit\DeprecationErrorHandler\Deprecation; + +class DeprecationTest extends TestCase +{ + public function testItCanDetermineTheClassWhereTheDeprecationHappened() + { + $deprecation = new Deprecation('💩', $this->debugBacktrace(), __FILE__); + $this->assertTrue($deprecation->originatesFromAnObject()); + $this->assertSame(self::class, $deprecation->originatingClass()); + $this->assertSame(__FUNCTION__, $deprecation->originatingMethod()); + } + + public function testItCanTellWhetherItIsInternal() + { + $deprecation = new Deprecation('💩', $this->debugBacktrace(), __FILE__); + $this->assertTrue($deprecation->isSelf()); + } + + public function testLegacyTestMethodIsDetectedAsSuch() + { + $deprecation = new Deprecation('💩', $this->debugBacktrace(), __FILE__); + $this->assertTrue($deprecation->isLegacy('whatever')); + } + + public function testItCanBeConvertedToAString() + { + $deprecation = new Deprecation('💩', $this->debugBacktrace(), __FILE__); + $this->assertContains('💩', $deprecation->toString()); + $this->assertContains(__FUNCTION__, $deprecation->toString()); + } + + public function testItRulesOutFilesOutsideVendorsAsIndirect() + { + $deprecation = new Deprecation('💩', $this->debugBacktrace(), __FILE__); + $this->assertFalse($deprecation->isIndirect()); + } + + /** + * This method is here to simulate the extra level from the piece of code + * triggering an error to the error handler + */ + public function debugBacktrace(): array + { + return debug_backtrace(); + } +} diff --git a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/default.phpt b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/default.phpt index 7a0595a7ddebc..e9f7bec9664c6 100644 --- a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/default.phpt +++ b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/default.phpt @@ -73,7 +73,7 @@ Unsilenced deprecation notices (3) 1x: unsilenced bar deprecation 1x in FooTestCase::testNonLegacyBar -Remaining deprecation notices (1) +Remaining self deprecation notices (1) 1x: silenced bar deprecation 1x in FooTestCase::testNonLegacyBar diff --git a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/regexp.phpt b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/deprecated_regexp.phpt similarity index 100% rename from src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/regexp.phpt rename to src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/deprecated_regexp.phpt diff --git a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/eval_not_self.phpt b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/eval_not_self.phpt new file mode 100644 index 0000000000000..8d823feb2c97e --- /dev/null +++ b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/eval_not_self.phpt @@ -0,0 +1,24 @@ +--TEST-- +Test eval()'d deprecation is not considered as self +--FILE-- + +--EXPECTF-- +Other deprecation notices (1) + + 1x: who knows where I come from? diff --git a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/fake_vendor/acme/lib/SomeService.php b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/fake_vendor/acme/lib/SomeService.php new file mode 100644 index 0000000000000..6a354103ff3ce --- /dev/null +++ b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/fake_vendor/acme/lib/SomeService.php @@ -0,0 +1,14 @@ +deprecatedApi(); diff --git a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/lagging_vendor.phpt b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/lagging_vendor.phpt new file mode 100644 index 0000000000000..37488e1d160ec --- /dev/null +++ b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/lagging_vendor.phpt @@ -0,0 +1,38 @@ +--TEST-- +Test DeprecationErrorHandler in weak vendors mode on vendor file +--FILE-- + +--EXPECTF-- +Remaining indirect deprecation notices (1) + + 1x: deprecatedApi is deprecated! You should stop relying on it! + 1x in SomeService::deprecatedApi from acme\lib diff --git a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/quiet.phpt b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/quiet.phpt new file mode 100644 index 0000000000000..b7e22a711df20 --- /dev/null +++ b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/quiet.phpt @@ -0,0 +1,39 @@ +--TEST-- +Test DeprecationErrorHandler with quiet output +--FILE-- +testLegacyFoo(); + +?> +--EXPECTF-- +Unsilenced deprecation notices (1) + +Legacy deprecation notices (1) + +Other deprecation notices (1) diff --git a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/self_on_non_vendor.phpt b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/self_on_non_vendor.phpt new file mode 100644 index 0000000000000..cb21ea8c21bd1 --- /dev/null +++ b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/self_on_non_vendor.phpt @@ -0,0 +1,74 @@ +--TEST-- +Test DeprecationErrorHandler with no self deprecations on self deprecation +--FILE-- +testLegacyFoo(); +$foo->testNonLegacyBar(); + +?> +--EXPECTF-- +Unsilenced deprecation notices (3) + + 2x: unsilenced foo deprecation + 2x in FooTestCase::testLegacyFoo + + 1x: unsilenced bar deprecation + 1x in FooTestCase::testNonLegacyBar + +Remaining self deprecation notices (1) + + 1x: silenced bar deprecation + 1x in FooTestCase::testNonLegacyBar + +Legacy deprecation notices (1) + +Other deprecation notices (1) + + 1x: root deprecation + diff --git a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/shutdown_deprecations.phpt b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/shutdown_deprecations.phpt index fddeed6085dea..46e9691085aac 100644 --- a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/shutdown_deprecations.phpt +++ b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/shutdown_deprecations.phpt @@ -73,7 +73,7 @@ Unsilenced deprecation notices (3) 1x: unsilenced bar deprecation 1x in FooTestCase::testNonLegacyBar -Remaining deprecation notices (1) +Remaining self deprecation notices (1) 1x: silenced bar deprecation 1x in FooTestCase::testNonLegacyBar diff --git a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/weak_vendors_on_eval_d_deprecation.phpt b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/weak_vendors_on_eval_d_deprecation.phpt index 8390d16332fa1..ab513b646c15d 100644 --- a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/weak_vendors_on_eval_d_deprecation.phpt +++ b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/weak_vendors_on_eval_d_deprecation.phpt @@ -3,7 +3,7 @@ Test DeprecationErrorHandler in weak vendors mode on eval()'d deprecation --FILE-- --EXPECTF-- - Other deprecation notices (1) 1x: who knows where I come from? diff --git a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/weak_vendors_on_non_vendor.phpt b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/weak_vendors_on_non_vendor.phpt index e20c7adf6ba1f..4068a392b2c9f 100644 --- a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/weak_vendors_on_non_vendor.phpt +++ b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/weak_vendors_on_non_vendor.phpt @@ -3,7 +3,7 @@ Test DeprecationErrorHandler in weak vendors mode on a non vendor file --FILE-- --EXPECTF-- - Other deprecation notices (1) 1x: I come from… afar! :D diff --git a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/weak_vendors_on_vendor.phpt b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/weak_vendors_on_vendor.phpt index 68e233df7d0d9..cb707610574eb 100644 --- a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/weak_vendors_on_vendor.phpt +++ b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/weak_vendors_on_vendor.phpt @@ -1,9 +1,9 @@ --TEST-- -Test DeprecationErrorHandler in weak vendors mode on vendor file +Test DeprecationErrorHandler with no self deprecations on vendor deprecation --FILE-- - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bridge\PhpUnit\TextUI; - -if (class_exists('PHPUnit_Runner_Version') && version_compare(\PHPUnit_Runner_Version::id(), '6.0.0', '<')) { - class_alias('Symfony\Bridge\PhpUnit\Legacy\TestRunnerForV5', 'Symfony\Bridge\PhpUnit\TextUI\TestRunner'); -} elseif (version_compare(\PHPUnit\Runner\Version::id(), '7.0.0', '<')) { - class_alias('Symfony\Bridge\PhpUnit\Legacy\TestRunnerForV6', 'Symfony\Bridge\PhpUnit\TextUI\TestRunner'); -} else { - class_alias('Symfony\Bridge\PhpUnit\Legacy\TestRunnerForV7', 'Symfony\Bridge\PhpUnit\TextUI\TestRunner'); -} - -if (false) { - class TestRunner - { - } -} diff --git a/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit b/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit index c94fa31abd937..53305e5ee8d2c 100755 --- a/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit +++ b/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit @@ -10,236 +10,4 @@ * file that was distributed with this source code. */ -// Please update when phpunit needs to be reinstalled with fresh deps: -// Cache-Id-Version: 2018-11-20 15:30 UTC - -error_reporting(-1); - -$getEnvVar = function ($name, $default = false) { - if (false !== $value = getenv($name 10000 )) { - return $value; - } - - static $phpunitConfig = null; - if (null === $phpunitConfig) { - $phpunitConfigFilename = null; - if (file_exists('phpunit.xml')) { - $phpunitConfigFilename = 'phpunit.xml'; - } elseif (file_exists('phpunit.xml.dist')) { - $phpunitConfigFilename = 'phpunit.xml.dist'; - } - if ($phpunitConfigFilename) { - $phpunitConfig = new DomDocument(); - $phpunitConfig->load($phpunitConfigFilename); - } else { - $phpunitConfig = false; - } - } - if (false !== $phpunitConfig) { - $var = new DOMXpath($phpunitConfig); - foreach ($var->query('//php/env[@name="'.$name.'"]') as $var) { - return $var->getAttribute('value'); - } - } - - return $default; -}; - -if (PHP_VERSION_ID >= 70200) { - // PHPUnit 6 is required for PHP 7.2+ - $PHPUNIT_VERSION = $getEnvVar('SYMFONY_PHPUNIT_VERSION', '6.5'); -} elseif (PHP_VERSION_ID >= 50600) { - // PHPUnit 4 does not support PHP 7 - $PHPUNIT_VERSION = $getEnvVar('SYMFONY_PHPUNIT_VERSION', '5.7'); -} else { - // PHPUnit 5.1 requires PHP 5.6+ - $PHPUNIT_VERSION = '4.8'; -} - -if ('composer.json' !== $COMPOSER_JSON = getenv('COMPOSER') ?: 'composer.json') { - putenv('COMPOSER=composer.json'); - $_SERVER['COMPOSER'] = $_ENV['COMPOSER'] = 'composer.json'; -} - -$root = __DIR__; -while (!file_exists($root.'/'.$COMPOSER_JSON) || file_exists($root.'/DeprecationErrorHandler.php')) { - if ($root === dirname($root)) { - break; - } - $root = dirname($root); -} - -$oldPwd = getcwd(); -$PHPUNIT_DIR = $getEnvVar('SYMFONY_PHPUNIT_DIR', $root.'/vendor/bin/.phpunit'); -$PHP = defined('PHP_BINARY') ? PHP_BINARY : 'php'; -$PHP = escapeshellarg($PHP); -if ('phpdbg' === PHP_SAPI) { - $PHP .= ' -qrr'; -} - -$COMPOSER = file_exists($COMPOSER = $oldPwd.'/composer.phar') || ($COMPOSER = rtrim('\\' === DIRECTORY_SEPARATOR ? preg_replace('/[\r\n].*/', '', `where.exe composer.phar`) : `which composer.phar 2> /dev/null`)) - ? $PHP.' '.escapeshellarg($COMPOSER) - : 'composer'; - - -$SYMFONY_PHPUNIT_REMOVE = $getEnvVar('SYMFONY_PHPUNIT_REMOVE', 'phpspec/prophecy symfony/yaml'); - -if (!file_exists("$PHPUNIT_DIR/phpunit-$PHPUNIT_VERSION/phpunit") || md5_file(__FILE__)."\n".$SYMFONY_PHPUNIT_REMOVE !== @file_get_contents("$PHPUNIT_DIR/.$PHPUNIT_VERSION.md5")) { - // Build a standalone phpunit without symfony/yaml nor prophecy by default - - @mkdir($PHPUNIT_DIR, 0777, true); - chdir($PHPUNIT_DIR); - if (file_exists("phpunit-$PHPUNIT_VERSION")) { - passthru(sprintf('\\' === DIRECTORY_SEPARATOR ? '(del /S /F /Q %s & rmdir %1$s) >nul': 'rm -rf %s', "phpunit-$PHPUNIT_VERSION")); - } - passthru("$COMPOSER create-project --no-install --prefer-dist --no-scripts --no-plugins --no-progress --ansi phpunit/phpunit phpunit-$PHPUNIT_VERSION \"$PHPUNIT_VERSION.*\""); - chdir("phpunit-$PHPUNIT_VERSION"); - if ($SYMFONY_PHPUNIT_REMOVE) { - passthru("$COMPOSER remove --no-update ".$SYMFONY_PHPUNIT_REMOVE); - } - if (5.1 <= $PHPUNIT_VERSION && $PHPUNIT_VERSION < 5.4) { - passthru("$COMPOSER require --no-update phpunit/phpunit-mock-objects \"~3.1.0\""); - } - if (file_exists($path = $root.'/vendor/symfony/phpunit-bridge')) { - passthru("$COMPOSER require --no-update symfony/phpunit-bridge \"*@dev\""); - passthru("$COMPOSER config repositories.phpunit-bridge path ".escapeshellarg(str_replace('/', DIRECTORY_SEPARATOR, $path))); - if ('\\' === DIRECTORY_SEPARATOR) { - file_put_contents('composer.json', preg_replace('/^( {8})"phpunit-bridge": \{$/m', "$0\n$1 ".'"options": {"symlink": false},', file_get_contents('composer.json'))); - } - } else { - passthru("$COMPOSER require --no-update symfony/phpunit-bridge \"*\""); - } - $prevRoot = getenv('COMPOSER_ROOT_VERSION'); - putenv("COMPOSER_ROOT_VERSION=$PHPUNIT_VERSION.99"); - // --no-suggest is not in the list to keep compat with composer 1.0, which is shipped with Ubuntu 16.04LTS - $exit = proc_close(proc_open("$COMPOSER install --no-dev --prefer-dist --no-progress --ansi", array(), $p, getcwd(), null, array('bypass_shell' => true))); - putenv('COMPOSER_ROOT_VERSION'.(false !== $prevRoot ? '='.$prevRoot : '')); - if ($exit) { - exit($exit); - } - file_put_contents('phpunit', <<<'EOPHP' -setMaxDepth(getenv('SYMFONY_PHPUNIT_MAX_DEPTH') ?: 3); - - foreach ($finder as $file => $fileInfo) { - if ('phpunit.xml.dist' === $file) { - $components[] = dirname($fileInfo->getPathname()); - } - } - if ($components) { - array_shift($cmd); - } -} - -$cmd[0] = sprintf('%s %s --colors=always', $PHP, escapeshellarg("$PHPUNIT_DIR/phpunit-$PHPUNIT_VERSION/phpunit")); -$cmd = str_replace('%', '%%', implode(' ', $cmd)).' %1$s'; - -if ('\\' === DIRECTORY_SEPARATOR) { - $cmd = 'cmd /v:on /d /c "('.$cmd.')%2$s"'; -} else { - $cmd .= '%2$s'; -} - -if ($components) { - $skippedTests = isset($_SERVER['SYMFONY_PHPUNIT_SKIPPED_TESTS']) ? $_SERVER['SYMFONY_PHPUNIT_SKIPPED_TESTS'] : false; - $runningProcs = array(); - - foreach ($components as $component) { - // Run phpunit tests in parallel - - if ($skippedTests) { - putenv("SYMFONY_PHPUNIT_SKIPPED_TESTS=$component/$skippedTests"); - } - - $c = escapeshellarg($component); - - if ($proc = proc_open(sprintf($cmd, $c, " > $c/phpunit.stdout 2> $c/phpunit.stderr"), array(), $pipes)) { - $runningProcs[$component] = $proc; - } else { - $exit = 1; - echo "\033[41mKO\033[0m $component\n\n"; - } - } - - while ($runningProcs) { - usleep(300000); - $terminatedProcs = array(); - foreach ($runningProcs as $component => $proc) { - $procStatus = proc_get_status($proc); - if (!$procStatus['running']) { - $terminatedProcs[$component] = $procStatus['exitcode']; - unset($runningProcs[$component]); - proc_close($proc); - } - } - - foreach ($terminatedProcs as $component => $procStatus) { - foreach (array('out', 'err') as $file) { - $file = "$component/phpunit.std$file"; - readfile($file); - unlink($file); - } - - // Fail on any individual component failures but ignore some error codes on Windows when APCu is enabled: - // STATUS_STACK_BUFFER_OVERRUN (-1073740791/0xC0000409) - // STATUS_ACCESS_VIOLATION (-1073741819/0xC0000005) - // STATUS_HEAP_CORRUPTION (-1073740940/0xC0000374) - if ($procStatus && ('\\' !== DIRECTORY_SEPARATOR || !extension_loaded('apcu') || !filter_var(ini_get('apc.enable_cli'), FILTER_VALIDATE_BOOLEAN) || !in_array($procStatus, array(-1073740791, -1073741819, -1073740940)))) { - $exit = $procStatus; - echo "\033[41mKO\033[0m $component\n\n"; - } else { - echo "\033[32mOK\033[0m $component\n\n"; - } - } - } -} elseif (!isset($argv[1]) || 'install' !== $argv[1] || file_exists('install')) { - if (!class_exists('SymfonyBlacklistSimplePhpunit', false)) { - class SymfonyBlacklistSimplePhpunit {} - } - array_splice($argv, 1, 0, array('--colors=always')); - $_SERVER['argv'] = $argv; - $_SERVER['argc'] = ++$argc; - include "$PHPUNIT_DIR/phpunit-$PHPUNIT_VERSION/phpunit"; -} - -exit($exit); +require __DIR__.DIRECTORY_SEPARATOR.'simple-phpunit.php'; diff --git a/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit.php b/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit.php new file mode 100644 index 0000000000000..da9e11c9853cf --- /dev/null +++ b/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit.php @@ -0,0 +1,261 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +// Please update when phpunit needs to be reinstalled with fresh deps: +// Cache-Id-Version: 2018-11-20 15:30 UTC + +error_reporting(-1); + +$getEnvVar = function ($name, $default = false) { + if (false !== $value = getenv($name)) { + return $value; + } + + static $phpunitConfig = null; + if (null === $phpunitConfig) { + $phpunitConfigFilename = null; + if (file_exists('phpunit.xml')) { + $phpunitConfigFilename = 'phpunit.xml'; + } elseif (file_exists('phpunit.xml.dist')) { + $phpunitConfigFilename = 'phpunit.xml.dist'; + } + if ($phpunitConfigFilename) { + $phpunitConfig = new DomDocument(); + $phpunitConfig->load($phpunitConfigFilename); + } else { + $phpunitConfig = false; + } + } + if (false !== $phpunitConfig) { + $var = new DOMXpath($phpunitConfig); + foreach ($var->query('//php/server[@name="'.$name.'"]') as $var) { + return $var->getAttribute('value'); + } + foreach ($var->query('//php/env[@name="'.$name.'"]') as $var) { + return $var->getAttribute('value'); + } + } + + return $default; +}; + +if (PHP_VERSION_ID >= 70100) { + // PHPUnit 7 requires PHP 7.1+ + $PHPUNIT_VERSION = $getEnvVar('SYMFONY_PHPUNIT_VERSION', '7.4'); +} elseif (PHP_VERSION_ID >= 70000) { + // PHPUnit 6 requires PHP 7.0+ + $PHPUNIT_VERSION = $getEnvVar('SYMFONY_PHPUNIT_VERSION', '6.5'); +} elseif (PHP_VERSION_ID >= 50600) { + // PHPUnit 5 requires PHP 5.6+ + $PHPUNIT_VERSION = $getEnvVar('SYMFONY_PHPUNIT_VERSION', '5.7'); +} else { + $PHPUNIT_VERSION = '4.8'; +} + +$COMPOSER_JSON = getenv('COMPOSER') ?: 'composer.json'; + +$root = __DIR__; +while (!file_exists($root.'/'.$COMPOSER_JSON) || file_exists($root.'/DeprecationErrorHandler.php')) { + if ($root === dirname($root)) { + break; + } + $root = dirname($root); +} + +$oldPwd = getcwd(); +$PHPUNIT_DIR = $getEnvVar('SYMFONY_PHPUNIT_DIR', $root.'/vendor/bin/.phpunit'); +$PHP = defined('PHP_BINARY') ? PHP_BINARY : 'php'; +$PHP = escapeshellarg($PHP); +if ('phpdbg' === PHP_SAPI) { + $PHP .= ' -qrr'; +} + +$defaultEnvs = array( + 'COMPOSER' => 'composer.json', + 'COMPOSER_VENDOR_DIR' => 'vendor', + 'COMPOSER_BIN_DIR' => 'bin', +); + +foreach ($defaultEnvs as $envName => $envValue) { + if ($envValue !== getenv($envName)) { + putenv("$envName=$envValue"); + $_SERVER[$envName] = $_ENV[$envName] = $envValue; + } +} + +$COMPOSER = file_exists($COMPOSER = $oldPwd.'/composer.phar') || ($COMPOSER = rtrim('\\' === DIRECTORY_SEPARATOR ? preg_replace('/[\r\n].*/', '', `where.exe composer.phar`) : `which composer.phar 2> /dev/null`)) + ? $PHP.' '.escapeshellarg($COMPOSER) + : 'composer'; + + +$SYMFONY_PHPUNIT_REMOVE = $getEnvVar('SYMFONY_PHPUNIT_REMOVE', 'phpspec/prophecy'.($PHPUNIT_VERSION < 6.0 ? ' symfony/yaml': '')); + +if (!file_exists("$PHPUNIT_DIR/phpunit-$PHPUNIT_VERSION/phpunit") || md5_file(__FILE__)."\n".$SYMFONY_PHPUNIT_REMOVE !== @file_get_contents("$PHPUNIT_DIR/.$PHPUNIT_VERSION.md5")) { + // Build a standalone phpunit without symfony/yaml nor prophecy by default + + @mkdir($PHPUNIT_DIR, 0777, true); + chdir($PHPUNIT_DIR); + if (file_exists("phpunit-$PHPUNIT_VERSION")) { + passthru(sprintf('\\' === DIRECTORY_SEPARATOR ? 'rmdir /S /Q %s > NUL': 'rm -rf %s', "phpunit-$PHPUNIT_VERSION.old")); + rename("phpunit-$PHPUNIT_VERSION", "phpunit-$PHPUNIT_VERSION.old"); + passthru(sprintf('\\' === DIRECTORY_SEPARATOR ? 'rmdir /S /Q %s': 'rm -rf %s', "phpunit-$PHPUNIT_VERSION.old")); + } + passthru("$COMPOSER create-project --no-install --prefer-dist --no-scripts --no-plugins --no-progress --ansi phpunit/phpunit phpunit-$PHPUNIT_VERSION \"$PHPUNIT_VERSION.*\""); + chdir("phpunit-$PHPUNIT_VERSION"); + if ($SYMFONY_PHPUNIT_REMOVE) { + passthru("$COMPOSER remove --no-update ".$SYMFONY_PHPUNIT_REMOVE); + } + if (5.1 <= $PHPUNIT_VERSION && $PHPUNIT_VERSION < 5.4) { + passthru("$COMPOSER require --no-update phpunit/phpunit-mock-objects \"~3.1.0\""); + } + if (file_exists($path = $root.'/vendor/symfony/phpunit-bridge')) { + passthru("$COMPOSER require --no-update symfony/phpunit-bridge \"*@dev\""); + passthru("$COMPOSER config repositories.phpunit-bridge path ".escapeshellarg(str_replace('/', DIRECTORY_SEPARATOR, $path))); + if ('\\' === DIRECTORY_SEPARATOR) { + file_put_contents('composer.json', preg_replace('/^( {8})"phpunit-bridge": \{$/m', "$0\n$1 ".'"options": {"symlink": false},', file_get_contents('composer.json'))); + } + } else { + passthru("$COMPOSER require --no-update symfony/phpunit-bridge \"*\""); + } + $prevRoot = getenv('COMPOSER_ROOT_VERSION'); + putenv("COMPOSER_ROOT_VERSION=$PHPUNIT_VERSION.99"); + // --no-suggest is not in the list to keep compat with composer 1.0, which is shipped with Ubuntu 16.04LTS + $exit = proc_close(proc_open("$COMPOSER install --no-dev --prefer-dist --no-progress --ansi", array(), $p, getcwd(), null, array('bypass_shell' => true))); + putenv('COMPOSER_ROOT_VERSION'.(false !== $prevRoot ? '='.$prevRoot : '')); + if ($exit) { + exit($exit); + } + file_put_contents('phpunit', <<<'EOPHP' +setMaxDepth(getenv('SYMFONY_PHPUNIT_MAX_DEPTH') ?: 3); + + foreach ($finder as $file => $fileInfo) { + if ('phpunit.xml.dist' === $file) { + $components[] = dirname($fileInfo->getPathname()); + } + } + if ($components) { + array_shift($cmd); + } +} + +$cmd[0] = sprintf('%s %s --colors=always', $PHP, escapeshellarg("$PHPUNIT_DIR/phpunit-$PHPUNIT_VERSION/phpunit")); +$cmd = str_replace('%', '%%', implode(' ', $cmd)).' %1$s'; + +if ('\\' === DIRECTORY_SEPARATOR) { + $cmd = 'cmd /v:on /d /c "('.$cmd.')%2$s"'; +} else { + $cmd .= '%2$s'; +} + +if ($components) { + $skippedTests = isset($_SERVER['SYMFONY_PHPUNIT_SKIPPED_TESTS']) ? $_SERVER['SYMFONY_PHPUNIT_SKIPPED_TESTS'] : false; + $runningProcs = array(); + + foreach ($components as $component) { + // Run phpunit tests in parallel + + if ($skippedTests) { + putenv("SYMFONY_PHPUNIT_SKIPPED_TESTS=$component/$skippedTests"); + } + + $c = escapeshellarg($component); + + if ($proc = proc_open(sprintf($cmd, $c, " > $c/phpunit.stdout 2> $c/phpunit.stderr"), array(), $pipes)) { + $runningProcs[$component] = $proc; + } else { + $exit = 1; + echo "\033[41mKO\033[0m $component\n\n"; + } + } + + while ($runningProcs) { + usleep(300000); + $terminatedProcs = array(); + foreach ($runningProcs as $component => $proc) { + $procStatus = proc_get_status($proc); + if (!$procStatus['running']) { + $terminatedProcs[$component] = $procStatus['exitcode']; + unset($runningProcs[$component]); + proc_close($proc); + } + } + + foreach ($terminatedProcs as $component => $procStatus) { + foreach (array('out', 'err') as $file) { + $file = "$component/phpunit.std$file"; + readfile($file); + unlink($file); + } + + // Fail on any individual component failures but ignore some error codes on Windows when APCu is enabled: + // STATUS_STACK_BUFFER_OVERRUN (-1073740791/0xC0000409) + // STATUS_ACCESS_VIOLATION (-1073741819/0xC0000005) + // STATUS_HEAP_CORRUPTION (-1073740940/0xC0000374) + if ($procStatus && ('\\' !== DIRECTORY_SEPARATOR || !extension_loaded('apcu') || !filter_var(ini_get('apc.enable_cli'), FILTER_VALIDATE_BOOLEAN) || !in_array($procStatus, array(-1073740791, -1073741819, -1073740940)))) { + $exit = $procStatus; + echo "\033[41mKO\033[0m $component\n\n"; + } else { + echo "\033[32mOK\033[0m $component\n\n"; + } + } + } +} elseif (!isset($argv[1]) || 'install' !== $argv[1] || file_exists('install')) { + if (!class_exists('SymfonyBlacklistSimplePhpunit', false)) { + class SymfonyBlacklistSimplePhpunit {} + } + array_splice($argv, 1, 0, array('--colors=always')); + $_SERVER['argv'] = $argv; + $_SERVER['argc'] = ++$argc; + include "$PHPUNIT_DIR/phpunit-$PHPUNIT_VERSION/phpunit"; +} + +exit($exit); diff --git a/src/Symfony/Bridge/PhpUnit/composer.json b/src/Symfony/Bridge/PhpUnit/composer.json index 4d72603eb452f..496bb8a385039 100644 --- a/src/Symfony/Bridge/PhpUnit/composer.json +++ b/src/Symfony/Bridge/PhpUnit/composer.json @@ -16,13 +16,12 @@ } ], "require": { - "php": ">=5.3.3 EVEN ON LATEST SYMFONY VERSIONS TO ALLOW USING", + "php": ">=5.5.9 EVEN ON LATEST SYMFONY VERSIONS TO ALLOW USING", "php": "THIS BRIDGE WHEN TESTING LOWEST SYMFONY VERSIONS.", - "php": ">=5.3.3" + "php": ">=5.5.9" }, "suggest": { - "symfony/debug": "For tracking deprecated interfaces usages at runtime with DebugClassLoader", - "ext-zip": "Zip support is required when using bin/simple-phpunit" + "symfony/debug": "For tracking deprecated interfaces usages at runtime with DebugClassLoader" }, "conflict": { "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0" @@ -40,7 +39,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "4.2-dev" + "dev-master": "4.3-dev" }, "thanks": { "name": "phpunit/phpunit", diff --git a/src/Symfony/Bridge/ProxyManager/LICENSE b/src/Symfony/Bridge/ProxyManager/LICENSE index 21d7fb9e2f29b..a677f43763ca4 100644 --- a/src/Symfony/Bridge/ProxyManager/LICENSE +++ b/src/Symfony/Bridge/ProxyManager/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2018 Fabien Potencier +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 diff --git a/src/Symfony/Bridge/ProxyManager/LazyProxy/Instantiator/RuntimeInstantiator.php b/src/Symfony/Bridge/ProxyManager/LazyProxy/Instantiator/RuntimeInstantiator.php index cc68d65058839..7b6ce56b0fbe9 100644 --- a/src/Symfony/Bridge/ProxyManager/LazyProxy/Instantiator/RuntimeInstantiator.php +++ b/src/Symfony/Bridge/ProxyManager/LazyProxy/Instantiator/RuntimeInstantiator.php @@ -43,7 +43,7 @@ public function instantiateProxy(ContainerInterface $container, Definition $defi return $this->factory->createProxy( $this->factory->getGenerator()->getProxifiedClass($definition), function (&$wrappedInstance, LazyLoadingInterface $proxy) use ($realInstantiator) { - $wrappedInstance = \call_user_func($realInstantiator); + $wrappedInstance = $realInstantiator(); $proxy->setProxyInitializer(null); diff --git a/src/Symfony/Bridge/ProxyManager/LazyProxy/PhpDumper/ProxyDumper.php b/src/Symfony/Bridge/ProxyManager/LazyProxy/PhpDumper/ProxyDumper.php index 208639a4b1ec3..70ca302affeb4 100644 --- a/src/Symfony/Bridge/ProxyManager/LazyProxy/PhpDumper/ProxyDumper.php +++ b/src/Symfony/Bridge/ProxyManager/LazyProxy/PhpDumper/ProxyDumper.php @@ -53,7 +53,7 @@ public function getProxyFactoryCode(Definition $definition, $id, $factoryCode = $instantiation = 'return'; if ($definition->isShared()) { - $instantiation .= sprintf(' $this->%s[\'%s\'] =', $definition->isPublic() && !$definition->isPrivate() ? 'services' : 'privates', $id); + $instantiation .= sprintf(' $this->%s[%s] =', $definition->isPublic() && !$definition->isPrivate() ? 'services' : 'privates', var_export($id, true)); } if (null === $factoryCode) { diff --git a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Dumper/PhpDumperTest.php b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Dumper/PhpDumperTest.php index 7c7464133315a..647d1667c3ea6 100644 --- a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Dumper/PhpDumperTest.php +++ b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Dumper/PhpDumperTest.php @@ -69,6 +69,6 @@ private function dumpLazyServiceProjectServiceContainer() $dumper->setProxyDumper(new ProxyDumper()); - return $dumper->dump(array('class' => 'LazyServiceProjectServiceContainer')); + return $dumper->dump(['class' => 'LazyServiceProjectServiceContainer']); } } diff --git a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Fixtures/includes/foo.php b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Fixtures/includes/foo.php index 8ffc5be9af40a..435e9a4d77bff 100644 --- a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Fixtures/includes/foo.php +++ b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Fixtures/includes/foo.php @@ -11,14 +11,14 @@ class ProxyManagerBridgeFooClass public $initialized = false; public $configured = false; public $called = false; - public $arguments = array(); + public $arguments = []; - public function __construct($arguments = array()) + public function __construct($arguments = []) { $this->arguments = $arguments; } - public static function getInstance($arguments = array()) + public static function getInstance($arguments = []) { $obj = new self($arguments); $obj->called = true; diff --git a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/Fixtures/proxy-factory.php b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/Fixtures/proxy-factory.php index 648eb36d4b598..af3b972cdfdcf 100644 --- a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/Fixtures/proxy-factory.php +++ b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/Fixtures/proxy-factory.php @@ -3,7 +3,7 @@ return new class { public $proxyClass; - private $privates = array(); + private $privates = []; public function getFooService($lazyLoad = true) { diff --git a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/ProxyDumperTest.php b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/ProxyDumperTest.php index b1dc1cf66a2ea..984e2e2a1b598 100644 --- a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/ProxyDumperTest.php +++ b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/ProxyDumperTest.php @@ -94,18 +94,18 @@ public function testCorrectAssigning(Definition $definition, $access) public function getPrivatePublicDefinitions() { - return array( - array( + return [ + [ (new Definition(__CLASS__)) ->setPublic(false), 'privates', - ), - array( + ], + [ (new Definition(__CLASS__)) ->setPublic(true), 'services', - ), - ); + ], + ]; } /** @@ -125,8 +125,8 @@ public function testGetProxyFactoryCodeForInterface() $definition = new Definition($class); $definition->setLazy(true); - $definition->addTag('proxy', array('interface' => DummyInterface::class)); - $definition->addTag('proxy', array('interface' => SunnyInterface::class)); + $definition->addTag('proxy', ['interface' => DummyInterface::class]); + $definition->addTag('proxy', ['interface' => SunnyInterface::class]); $implem = "dumper->getProxyCode($definition); $factory = $this->dumper->getProxyFactoryCode($definition, 'foo', '$this->getFooService(false)'); @@ -136,7 +136,7 @@ public function testGetProxyFactoryCodeForInterface() return new class { public \$proxyClass; - private \$privates = array(); + private \$privates = []; public function getFooService(\$lazyLoad = true) { @@ -179,12 +179,12 @@ protected function createProxy(\$class, \Closure \$factory) */ public function getProxyCandidates() { - $definitions = array( - array(new Definition(__CLASS__), true), - array(new Definition('stdClass'), true), - array(new Definition(uniqid('foo', true)), false), - array(new Definition(), false), - ); + $definitions = [ + [new Definition(__CLASS__), true], + [new Definition('stdClass'), true], + [new Definition(uniqid('foo', true)), false], + [new Definition(), false], + ]; array_map( function ($definition) { diff --git a/src/Symfony/Bridge/ProxyManager/composer.json b/src/Symfony/Bridge/ProxyManager/composer.json index 648bf8990fb64..d5ce7a3e3989f 100644 --- a/src/Symfony/Bridge/ProxyManager/composer.json +++ b/src/Symfony/Bridge/ProxyManager/composer.json @@ -35,7 +35,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "4.2-dev" + "dev-master": "4.3-dev" } } } diff --git a/src/Symfony/Bridge/Twig/AppVariable.php b/src/Symfony/Bridge/Twig/AppVariable.php index 702edcbceaa7e..21020270a06e4 100644 --- a/src/Symfony/Bridge/Twig/AppVariable.php +++ b/src/Symfony/Bridge/Twig/AppVariable.php @@ -150,7 +150,7 @@ public function getDebug() * Returns some or all the existing flash messages: * * getFlashes() returns all the flash messages * * getFlashes('notice') returns a simple array with flash messages of that type - * * getFlashes(array('notice', 'error')) returns a nested array of type => messages. + * * getFlashes(['notice', 'error']) returns a nested array of type => messages. * * @return array */ @@ -159,13 +159,13 @@ public function getFlashes($types = null) try { $session = $this->getSession(); if (null === $session) { - return array(); + return []; } } catch (\RuntimeException $e) { - return array(); + return []; } - if (null === $types || '' === $types || array() === $types) { + if (null === $types || '' === $types || [] === $types) { return $session->getFlashBag()->all(); } @@ -173,7 +173,7 @@ public function getFlashes($types = null) return $session->getFlashBag()->get($types); } - $result = array(); + $result = []; foreach ($types as $type) { $result[$type] = $session->getFlashBag()->get($type); } diff --git a/src/Symfony/Bridge/Twig/CHANGELOG.md b/src/Symfony/Bridge/Twig/CHANGELOG.md index 341bcfa538fdb..905d242217c3f 100644 --- a/src/Symfony/Bridge/Twig/CHANGELOG.md +++ b/src/Symfony/Bridge/Twig/CHANGELOG.md @@ -1,6 +1,15 @@ CHANGELOG ========= +4.3.0 +----- + + * added the `form_parent()` function that allows to reliably retrieve the parent form in Twig templates + * added the `workflow_transition_blockers()` function + * deprecated the `$requestStack` and `$requestContext` arguments of the + `HttpFoundationExtension`, pass a `Symfony\Component\HttpFoundation\UrlHelper` + instance as the only argument instead + 4.2.0 ----- @@ -45,7 +54,7 @@ CHANGELOG use Symfony\Bridge\Twig\Form\TwigRendererEngine; // ... - $rendererEngine = new TwigRendererEngine(array('form_div_layout.html.twig')); + $rendererEngine = new TwigRendererEngine(['form_div_layout.html.twig']); $rendererEngine->setEnvironment($twig); $twig->addExtension(new FormExtension(new TwigRenderer($rendererEngine, $csrfTokenManager))); ``` @@ -54,13 +63,13 @@ CHANGELOG ```php // ... - $rendererEngine = new TwigRendererEngine(array('form_div_layout.html.twig'), $twig); + $rendererEngine = new TwigRendererEngine(['form_div_layout.html.twig'], $twig); // require Twig 1.30+ - $twig->addRuntimeLoader(new \Twig\RuntimeLoader\FactoryRuntimeLoader(array( + $twig->addRuntimeLoader(new \Twig\RuntimeLoader\FactoryRuntimeLoader([ TwigRenderer::class => function () use ($rendererEngine, $csrfTokenManager) { return new TwigRenderer($rendererEngine, $csrfTokenManager); }, - ))); + ])); $twig->addExtension(new FormExtension()); ``` * Deprecated the `TwigRendererEngineInterface` interface. diff --git a/src/Symfony/Bridge/Twig/Command/DebugCommand.php b/src/Symfony/Bridge/Twig/Command/DebugCommand.php index 072022c0a417e..5533a2d98ffa2 100644 --- a/src/Symfony/Bridge/Twig/Command/DebugCommand.php +++ b/src/Symfony/Bridge/Twig/Command/DebugCommand.php @@ -13,13 +13,16 @@ use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Formatter\OutputFormatter; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\Finder\Finder; +use Symfony\Component\HttpKernel\Debug\FileLinkFormatter; use Twig\Environment; +use Twig\Loader\ChainLoader; use Twig\Loader\FilesystemLoader; /** @@ -36,8 +39,10 @@ class DebugCommand extends Command private $bundlesMetadata; private $twigDefaultPath; private $rootDir; + private $filesystemLoaders; + private $fileLinkFormatter; - public function __construct(Environment $twig, string $projectDir = null, array $bundlesMetadata = array(), string $twigDefaultPath = null, string $rootDir = null) + public function __construct(Environment $twig, string $projectDir = null, array $bundlesMetadata = [], string $twigDefaultPath = null, string $rootDir = null, FileLinkFormatter $fileLinkFormatter = null) { parent::__construct(); @@ -46,16 +51,17 @@ public function __construct(Environment $twig, string $projectDir = null, array $this->bundlesMetadata = $bundlesMetadata; $this->twigDefaultPath = $twigDefaultPath; $this->rootDir = $rootDir; + $this->fileLinkFormatter = $fileLinkFormatter; } protected function configure() { $this - ->setDefinition(array( + ->setDefinition([ new InputArgument('name', InputArgument::OPTIONAL, 'The template name'), new InputOption('filter', null, InputOption::VALUE_REQUIRED, 'Show details for all entries matching this filter'), new InputOption('format', null, InputOption::VALUE_REQUIRED, 'The output format (text or json)', 'text'), - )) + ]) ->setDescription('Shows a list of twig functions, filters, globals and tests') ->setHelp(<<<'EOF' The %command.name% command outputs a list of twig functions, @@ -87,7 +93,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $name = $input->getArgument('name'); $filter = $input->getOption('filter'); - if (null !== $name && !$this->twig->getLoader() instanceof FilesystemLoader) { + if (null !== $name && [] === $this->getFilesystemLoaders()) { throw new InvalidArgumentException(sprintf('Argument "name" not supported, it requires the Twig loader "%s"', FilesystemLoader::class)); } @@ -103,23 +109,35 @@ protected function execute(InputInterface $input, OutputInterface $output) private function displayPathsText(SymfonyStyle $io, string $name) { - $files = $this->findTemplateFiles($name); + $file = new \ArrayIterator($this->findTemplateFiles($name)); $paths = $this->getLoaderPaths($name); $io->section('Matched File'); - if ($files) { - $io->success(array_shift($files)); + if ($file->valid()) { + if ($fileLink = $this->getFileLink($file->key())) { + $io->block($file->current(), 'OK', sprintf('fg=black;bg=green;href=%s', $fileLink), ' ', true); + } else { + $io->success($file->current()); + } + $file->next(); - if ($files) { + if ($file->valid()) { $io->section('Overridden Files'); - $io->listing($files); + do { + if ($fileLink = $this->getFileLink($file->key())) { + $io->text(sprintf('* %s', $fileLink, $file->current())); + } else { + $io->text(sprintf('* %s', $file->current())); + } + $file->next(); + } while ($file->valid()); } } else { - $alternatives = array(); + $alternatives = []; if ($paths) { - $shortnames = array(); - $dirs = array(); + $shortnames = []; + $dirs = []; foreach (current($paths) as $path) { $dirs[] = $this->isAbsolutePath($path) ? $path : $this->projectDir.'/'.$path; } @@ -141,25 +159,27 @@ private function displayPathsText(SymfonyStyle $io, string $name) $io->section('Configured Paths'); if ($paths) { - $io->table(array('Namespace', 'Paths'), $this->buildTableRows($paths)); + $io->table(['Namespace', 'Paths'], $this->buildTableRows($paths)); } else { - $alternatives = array(); + $alternatives = []; $namespace = $this->parseTemplateName($name)[0]; if (FilesystemLoader::MAIN_NAMESPACE === $namespace) { $message = 'No template paths configured for your application'; } else { $message = sprintf('No template paths configured for "@%s" namespace', $namespace); - $namespaces = $this->twig->getLoader()->getNamespaces(); - foreach ($this->findAlternatives($namespace, $namespaces) as $namespace) { - $alternatives[] = '@'.$namespace; + foreach ($this->getFilesystemLoaders() as $loader) { + $namespaces = $loader->getNamespaces(); + foreach ($this->findAlternatives($namespace, $namespaces) as $namespace) { + $alternatives[] = '@'.$namespace; + } } } $this->error($io, $message, $alternatives); if (!$alternatives && $paths = $this->getLoaderPaths()) { - $io->table(array('Namespace', 'Paths'), $this->buildTableRows($paths)); + $io->table(['Namespace', 'Paths'], $this->buildTableRows($paths)); } } } @@ -184,12 +204,13 @@ private function displayPathsJson(SymfonyStyle $io, string $name) private function displayGeneralText(SymfonyStyle $io, string $filter = null) { - $types = array('functions', 'filters', 'tests', 'globals'); + $decorated = $io->isDecorated(); + $types = ['functions', 'filters', 'tests', 'globals']; foreach ($types as $index => $type) { - $items = array(); + $items = []; foreach ($this->twig->{'get'.ucfirst($type)}() as $name => $entity) { if (!$filter || false !== strpos($name, $filter)) { - $items[$name] = $name.$this->getPrettyMetadata($type, $entity); + $items[$name] = $name.$this->getPrettyMetadata($type, $entity, $decorated); } } @@ -205,11 +226,11 @@ private function displayGeneralText(SymfonyStyle $io, string $filter = null) if (!$filter && $paths = $this->getLoaderPaths()) { $io->section('Loader Paths'); - $io->table(array('Namespace', 'Paths'), $this->buildTableRows($paths)); + $io->table(['Namespace', 'Paths'], $this->buildTableRows($paths)); } - if ($wronBundles = $this->findWrongBundleOverrides()) { - foreach ($this->buildWarningMessages($wronBundles) as $message) { + if ($wrongBundles = $this->findWrongBundleOverrides()) { + foreach ($this->buildWarningMessages($wrongBundles) as $message) { $io->warning($message); } } @@ -217,8 +238,9 @@ private function displayGeneralText(SymfonyStyle $io, string $filter = null) private function displayGeneralJson(SymfonyStyle $io, $filter) { - $types = array('functions', 'filters', 'tests', 'globals'); - $data = array(); + $decorated = $io->isDecorated(); + $types = ['functions', 'filters', 'tests', 'globals']; + $data = []; foreach ($types as $type) { foreach ($this->twig->{'get'.ucfirst($type)}() as $name => $entity) { if (!$filter || false !== strpos($name, $filter)) { @@ -234,40 +256,35 @@ private function displayGeneralJson(SymfonyStyle $io, $filter) $data['loader_paths'] = $paths; } - if ($wronBundles = $this->findWrongBundleOverrides()) { - $data['warnings'] = $this->buildWarningMessages($wronBundles); + if ($wrongBundles = $this->findWrongBundleOverrides()) { + $data['warnings'] = $this->buildWarningMessages($wrongBundles); } - $io->writeln(json_encode($data)); + $data = json_encode($data, JSON_PRETTY_PRINT); + $io->writeln($decorated ? OutputFormatter::escape($data) : $data); } private function getLoaderPaths(string $name = null): array { - /** @var FilesystemLoader $loader */ - $loader = $this->twig->getLoader(); - $loaderPaths = array(); - $namespaces = $loader->getNamespaces(); - if (null !== $name) { - $namespace = $this->parseTemplateName($name)[0]; - $namespaces = array_intersect(array($namespace), $namespaces); - } + $loaderPaths = []; + foreach ($this->getFilesystemLoaders() as $loader) { + $namespaces = $loader->getNamespaces(); + if (null !== $name) { + $namespace = $this->parseTemplateName($name)[0]; + $namespaces = array_intersect([$namespace], $namespaces); + } - foreach ($namespaces as $namespace) { - $paths = array_map(function ($path) { - if (null !== $this->projectDir && 0 === strpos($path, $this->projectDir)) { - $path = ltrim(substr($path, \strlen($this->projectDir)), \DIRECTORY_SEPARATOR); - } + foreach ($namespaces as $namespace) { + $paths = array_map([$this, 'getRelativePath'], $loader->getPaths($namespace)); - return $path; - }, $loader->getPaths($namespace)); + if (FilesystemLoader::MAIN_NAMESPACE === $namespace) { + $namespace = '(None)'; + } else { + $namespace = '@'.$namespace; + } - if (FilesystemLoader::MAIN_NAMESPACE === $namespace) { - $namespace = '(None)'; - } else { - $namespace = '@'.$namespace; + $loaderPaths[$namespace] = array_merge($loaderPaths[$namespace] ?? [], $paths); } - - $loaderPaths[$namespace] = $paths; } return $loaderPaths; @@ -329,7 +346,7 @@ private function getMetadata($type, $entity) } } - private function getPrettyMetadata($type, $entity) + private function getPrettyMetadata($type, $entity, $decorated) { if ('tests' === $type) { return ''; @@ -341,7 +358,7 @@ private function getPrettyMetadata($type, $entity) return '(unknown?)'; } } catch (\UnexpectedValueException $e) { - return ' '.$e->getMessage().''; + return sprintf(' %s', $decorated ? OutputFormatter::escape($e->getMessage()) : $e->getMessage()); } if ('globals' === $type) { @@ -349,7 +366,9 @@ private function getPrettyMetadata($type, $entity) return ' = object('.\get_class($meta).')'; } - return ' = '.substr(@json_encode($meta), 0, 50); + $description = substr(@json_encode($meta), 0, 50); + + return sprintf(' = %s', $decorated ? OutputFormatter::escape($description) : $description); } if ('functions' === $type) { @@ -363,58 +382,43 @@ private function getPrettyMetadata($type, $entity) private function findWrongBundleOverrides(): array { - $alternatives = array(); - $bundleNames = array(); + $alternatives = []; + $bundleNames = []; if ($this->rootDir && $this->projectDir) { $folders = glob($this->rootDir.'/Resources/*/views', GLOB_ONLYDIR); - $relativePath = ltrim(substr($this->rootDir.'/Resources/', \strlen($this->projectDir)), \DIRECTORY_SEPARATOR); - $bundleNames = array_reduce( - $folders, - function ($carry, $absolutePath) use ($relativePath) { - if (0 === strpos($absolutePath, $this->projectDir)) { - $name = basename(\dirname($absolutePath)); - $path = $relativePath.$name; - $carry[$name] = $path; - } + $relativePath = ltrim(substr($this->rootDir.\DIRECTORY_SEPARATOR.'Resources/', \strlen($this->projectDir)), \DIRECTORY_SEPARATOR); + $bundleNames = array_reduce($folders, function ($carry, $absolutePath) use ($relativePath) { + if (0 === strpos($absolutePath, $this->projectDir)) { + $name = basename(\dirname($absolutePath)); + $path = ltrim($relativePath.$name, \DIRECTORY_SEPARATOR); + $carry[$name] = $path; + + @trigger_error(sprintf('Loading Twig templates from the "%s" directory is deprecated since Symfony 4.2, use "%s" instead.', $absolutePath, $this->twigDefaultPath.'/bundles/'.$name), E_USER_DEPRECATED); + } - return $carry; - }, - $bundleNames - ); + return $carry; + }, $bundleNames); } if ($this->twigDefaultPath && $this->projectDir) { $folders = glob($this->twigDefaultPath.'/bundles/*', GLOB_ONLYDIR); - $relativePath = ltrim(substr($this->twigDefaultPath.'/bundles', \strlen($this->projectDir)), \DIRECTORY_SEPARATOR); - $bundleNames = array_reduce( - $folders, - function ($carry, $absolutePath) use ($relativePath) { - if (0 === strpos($absolutePath, $this->projectDir)) { - $path = ltrim(substr($absolutePath, \strlen($this->projectDir)), \DIRECTORY_SEPARATOR); - $name = ltrim(substr($path, \strlen($relativePath)), \DIRECTORY_SEPARATOR); - $carry[$name] = $path; - } - - return $carry; - }, - $bundleNames - ); - } - - if (\count($bundleNames)) { - $notFoundBundles = array_diff_key($bundleNames, $this->bundlesMetadata); - if (\count($notFoundBundles)) { - $alternatives = array(); - foreach ($notFoundBundles as $notFoundBundle => $path) { - $alternatives[$path] = array(); - foreach ($this->bundlesMetadata as $name => $bundle) { - $lev = levenshtein($notFoundBundle, $name); - if ($lev <= \strlen($notFoundBundle) / 3 || false !== strpos($name, $notFoundBundle)) { - $alternatives[$path][] = $name; - } - } + $relativePath = ltrim(substr($this->twigDefaultPath.'/bundles/', \strlen($this->projectDir)), \DIRECTORY_SEPARATOR); + $bundleNames = array_reduce($folders, function ($carry, $absolutePath) use ($relativePath) { + if (0 === strpos($absolutePath, $this->projectDir)) { + $name = basename($absolutePath); + $path = ltrim($relativePath.$name, \DIRECTORY_SEPARATOR); + $carry[$name] = $path; } + + return $carry; + }, $bundleNames); + } + + if ($notFoundBundles = array_diff_key($bundleNames, $this->bundlesMetadata)) { + $alternatives = []; + foreach ($notFoundBundles as $notFoundBundle => $path) { + $alternatives[$path] = $this->findAlternatives($notFoundBundle, array_keys($this->bundlesMetadata)); } } @@ -423,7 +427,7 @@ function ($carry, $absolutePath) use ($relativePath) { private function buildWarningMessages(array $wrongBundles): array { - $messages = array(); + $messages = []; foreach ($wrongBundles as $path => $alternatives) { $message = sprintf('Path "%s" not matching any bundle found', $path); if ($alternatives) { @@ -442,7 +446,7 @@ private function buildWarningMessages(array $wrongBundles): array return $messages; } - private function error(SymfonyStyle $io, string $message, array $alternatives = array()): void + private function error(SymfonyStyle $io, string $message, array $alternatives = []): void { if ($alternatives) { if (1 === \count($alternatives)) { @@ -458,22 +462,22 @@ private function error(SymfonyStyle $io, string $message, array $alternatives = private function findTemplateFiles(string $name): array { - /** @var FilesystemLoader $loader */ - $loader = $this->twig->getLoader(); - $files = array(); list($namespace, $shortname) = $this->parseTemplateName($name); - foreach ($loader->getPaths($namespace) as $path) { - if (!$this->isAbsolutePath($path)) { - $path = $this->projectDir.'/'.$path; - } - $filename = $path.'/'.$shortname; + $files = []; + foreach ($this->getFilesystemLoaders() as $loader) { + foreach ($loader->getPaths($namespace) as $path) { + if (!$this->isAbsolutePath($path)) { + $path = $this->projectDir.'/'.$path; + } + $filename = $path.'/'.$shortname; - if (is_file($filename)) { - if (false !== $realpath = realpath($filename)) { - $files[] = $this->getRelativePath($realpath); - } else { - $files[] = $this->getRelativePath($filename); + if (is_file($filename)) { + if (false !== $realpath = realpath($filename)) { + $files[$realpath] = $this->getRelativePath($realpath); + } else { + $files[$filename] = $this->getRelativePath($filename); + } } } } @@ -491,29 +495,29 @@ private function parseTemplateName(string $name, string $default = FilesystemLoa $namespace = substr($name, 1, $pos - 1); $shortname = substr($name, $pos + 1); - return array($namespace, $shortname); + return [$namespace, $shortname]; } - return array($default, $name); + return [$default, $name]; } private function buildTableRows(array $loaderPaths): array { - $rows = array(); + $rows = []; $firstNamespace = true; $prevHasSeparator = false; foreach ($loaderPaths as $namespace => $paths) { if (!$firstNamespace && !$prevHasSeparator && \count($paths) > 1) { - $rows[] = array('', ''); + $rows[] = ['', '']; } $firstNamespace = false; foreach ($paths as $path) { - $rows[] = array($namespace, $path.\DIRECTORY_SEPARATOR); + $rows[] = [$namespace, $path.\DIRECTORY_SEPARATOR]; $namespace = ''; } if (\count($paths) > 1) { - $rows[] = array('', ''); + $rows[] = ['', '']; $prevHasSeparator = true; } else { $prevHasSeparator = false; @@ -528,7 +532,7 @@ private function buildTableRows(array $loaderPaths): array private function findAlternatives(string $name, array $collection): array { - $alternatives = array(); + $alternatives = []; foreach ($collection as $item) { $lev = levenshtein($name, $item); if ($lev <= \strlen($name) / 3 || false !== strpos($item, $name)) { @@ -556,4 +560,37 @@ private function isAbsolutePath(string $file): bool { return strspn($file, '/\\', 0, 1) || (\strlen($file) > 3 && ctype_alpha($file[0]) && ':' === $file[1] && strspn($file, '/\\', 2, 1)) || null !== parse_url($file, PHP_URL_SCHEME); } + + /** + * @return FilesystemLoader[] + */ + private function getFilesystemLoaders(): array + { + if (null !== $this->filesystemLoaders) { + return $this->filesystemLoaders; + } + $this->filesystemLoaders = []; + + $loader = $this->twig->getLoader(); + if ($loader instanceof FilesystemLoader) { + $this->filesystemLoaders[] = $loader; + } elseif ($loader instanceof ChainLoader) { + foreach ($loader->getLoaders() as $l) { + if ($l instanceof FilesystemLoader) { + $this->filesystemLoaders[] = $l; + } + } + } + + return $this->filesystemLoaders; + } + + private function getFileLink(string $absolutePath): string + { + if (null === $this->fileLinkFormatter) { + return ''; + } + + return (string) $this->fileLinkFormatter->format($absolutePath, 1); + } } diff --git a/src/Symfony/Bridge/Twig/Command/LintCommand.php b/src/Symfony/Bridge/Twig/Command/LintCommand.php index 68eebf123cf89..d2f7542af7435 100644 --- a/src/Symfony/Bridge/Twig/Command/LintCommand.php +++ b/src/Symfony/Bridge/Twig/Command/LintCommand.php @@ -87,7 +87,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $template .= fread(STDIN, 1024); } - return $this->display($input, $output, $io, array($this->validate($template, uniqid('sf_', true)))); + return $this->display($input, $output, $io, [$this->validate($template, uniqid('sf_', true))]); } $filesInfo = $this->getFilesInfo($filenames); @@ -97,7 +97,7 @@ protected function execute(InputInterface $input, OutputInterface $output) private function getFilesInfo(array $filenames) { - $filesInfo = array(); + $filesInfo = []; foreach ($filenames as $filename) { foreach ($this->findFiles($filename) as $file) { $filesInfo[] = $this->validate(file_get_contents($file), $file); @@ -110,7 +110,7 @@ private function getFilesInfo(array $filenames) protected function findFiles($filename) { if (is_file($filename)) { - return array($filename); + return [$filename]; } elseif (is_dir($filename)) { return Finder::create()->files()->in($filename)->name('*.twig'); } @@ -122,7 +122,7 @@ private function validate($template, $file) { $realLoader = $this->twig->getLoader(); try { - $temporaryLoader = new ArrayLoader(array((string) $file => $template)); + $temporaryLoader = new ArrayLoader([(string) $file => $template]); $this->twig->setLoader($temporaryLoader); $nodeTree = $this->twig->parse($this->twig->tokenize(new Source($template, (string) $file))); $this->twig->compile($nodeTree); @@ -130,10 +130,10 @@ private function validate($template, $file) } catch (Error $e) { $this->twig->setLoader($realLoader); - return array('template' => $template, 'file' => $file, 'line' => $e->getTemplateLine(), 'valid' => false, 'exception' => $e); + return ['template' => $template, 'file' => $file, 'line' => $e->getTemplateLine(), 'valid' => false, 'exception' => $e]; } - return array('template' => $template, 'file' => $file, 'valid' => true); + return ['template' => $template, 'file' => $file, 'valid' => true]; } private function display(InputInterface $input, OutputInterface $output, SymfonyStyle $io, $files) @@ -219,7 +219,7 @@ private function getContext($template, $line, $context = 3) $position = max(0, $line - $context); $max = min(\count($lines), $line - 1 + $context); - $result = array(); + $result = []; while ($position < $max) { $result[$position + 1] = $lines[$position]; ++$position; diff --git a/src/Symfony/Bridge/Twig/DataCollector/TwigDataCollector.php b/src/Symfony/Bridge/Twig/DataCollector/TwigDataCollector.php index 9e214fc731015..b7d059daea7c7 100644 --- a/src/Symfony/Bridge/Twig/DataCollector/TwigDataCollector.php +++ b/src/Symfony/Bridge/Twig/DataCollector/TwigDataCollector.php @@ -16,6 +16,7 @@ use Symfony\Component\HttpKernel\DataCollector\DataCollector; use Symfony\Component\HttpKernel\DataCollector\LateDataCollectorInterface; use Twig\Environment; +use Twig\Error\LoaderError; use Twig\Markup; use Twig\Profiler\Dumper\HtmlDumper; use Twig\Profiler\Profile; @@ -51,7 +52,7 @@ public function reset() { $this->profile->reset(); $this->computed = null; - $this->data = array(); + $this->data = []; } /** @@ -60,7 +61,7 @@ public function reset() public function lateCollect() { $this->data['profile'] = serialize($this->profile); - $this->data['template_paths'] = array(); + $this->data['template_paths'] = []; if (null === $this->twig) { return; @@ -70,7 +71,7 @@ public function lateCollect() if ($profile->isTemplate()) { try { $template = $this->twig->load($name = $profile->getName()); - } catch (\Twig_Error_Loader $e) { + } catch (LoaderError $e) { $template = null; } @@ -122,15 +123,17 @@ public function getHtmlCallGraph() $dump = $dumper->dump($this->getProfile()); // needed to remove the hardcoded CSS styles - $dump = str_replace(array( + $dump = str_replace([ '', '', '', - ), array( + '', + ], [ '', '', '', - ), $dump); + '', + ], $dump); return new Markup($dump, 'UTF-8'); } @@ -138,7 +141,7 @@ public function getHtmlCallGraph() public function getProfile() { if (null === $this->profile) { - $this->profile = unserialize($this->data['profile'], array('allowed_classes' => array('Twig_Profiler_Profile', 'Twig\Profiler\Profile'))); + $this->profile = unserialize($this->data['profile'], ['allowed_classes' => ['Twig_Profiler_Profile', 'Twig\Profiler\Profile']]); } return $this->profile; @@ -155,13 +158,13 @@ private function getComputedData($index) private function computeData(Profile $profile) { - $data = array( + $data = [ 'template_count' => 0, 'block_count' => 0, 'macro_count' => 0, - ); + ]; - $templates = array(); + $templates = []; foreach ($profile as $p) { $d = $this->computeData($p); diff --git a/src/Symfony/Bridge/Twig/Extension/AssetExtension.php b/src/Symfony/Bridge/Twig/Extension/AssetExtension.php index d5d70fb397f69..cc2cdb268e5b5 100644 --- a/src/Symfony/Bridge/Twig/Extension/AssetExtension.php +++ b/src/Symfony/Bridge/Twig/Extension/AssetExtension.php @@ -34,10 +34,10 @@ public function __construct(Packages $packages) */ public function getFunctions() { - return array( - new TwigFunction('asset', array($this, 'getAssetUrl')), - new TwigFunction('asset_version', array($this, 'getAssetVersion')), - ); + return [ + new TwigFunction('asset', [$this, 'getAssetUrl']), + new TwigFunction('asset_version', [$this, 'getAssetVersion']), + ]; } /** diff --git a/src/Symfony/Bridge/Twig/Extension/CodeExtension.php b/src/Symfony/Bridge/Twig/Extension/CodeExtension.php index 73d9031b90833..56211fe6ec162 100644 --- a/src/Symfony/Bridge/Twig/Extension/CodeExtension.php +++ b/src/Symfony/Bridge/Twig/Extension/CodeExtension.php @@ -43,18 +43,18 @@ public function __construct($fileLinkFormat, string $projectDir, string $charset */ public function getFilters() { - return array( - new TwigFilter('abbr_class', array($this, 'abbrClass'), array('is_safe' => array('html'))), - new TwigFilter('abbr_method', array($this, 'abbrMethod'), array('is_safe' => array('html'))), - new TwigFilter('format_args', array($this, 'formatArgs'), array('is_safe' => array('html'))), - new TwigFilter('format_args_as_text', array($this, 'formatArgsAsText')), - new TwigFilter('file_excerpt', array($this, 'fileExcerpt'), array('is_safe' => array('html'))), - new TwigFilter('format_file', array($this, 'formatFile'), array('is_safe' => array('html'))), - new TwigFilter('format_file_from_text', array($this, 'formatFileFromText'), array('is_safe' => array('html'))), - new TwigFilter('format_log_message', array($this, 'formatLogMessage'), array('is_safe' => array('html'))), - new TwigFilter('file_link', array($this, 'getFileLink')), - new TwigFilter('file_relative', array($this, 'getFileRelative')), - ); + return [ + new TwigFilter('abbr_class', [$this, 'abbrClass'], ['is_safe' => ['html']]), + new TwigFilter('abbr_method', [$this, 'abbrMethod'], ['is_safe' => ['html']]), + new TwigFilter('format_args', [$this, 'formatArgs'], ['is_safe' => ['html']]), + new TwigFilter('format_args_as_text', [$this, 'formatArgsAsText']), + new TwigFilter('file_excerpt', [$this, 'fileExcerpt'], ['is_safe' => ['html']]), + new TwigFilter('format_file', [$this, 'formatFile'], ['is_safe' => ['html']]), + new TwigFilter('format_file_from_text', [$this, 'formatFileFromText'], ['is_safe' => ['html']]), + new TwigFilter('format_log_message', [$this, 'formatLogMessage'], ['is_safe' => ['html']]), + new TwigFilter('file_link', [$this, 'getFileLink']), + new TwigFilter('file_relative', [$this, 'getFileRelative']), + ]; } public function abbrClass($class) @@ -88,7 +88,7 @@ public function abbrMethod($method) */ public function formatArgs($args) { - $result = array(); + $result = []; foreach ($args as $key => $item) { if ('object' === $item[0]) { $parts = explode('\\', $item[1]); @@ -147,7 +147,7 @@ public function fileExcerpt($file, $line, $srcContext = 3) }, $code); $content = explode('
', $code); - $lines = array(); + $lines = []; if (0 > $srcContext) { $srcContext = \count($content); } @@ -205,7 +205,7 @@ public function formatFile($file, $line, $text = null) public function getFileLink($file, $line) { if ($fmt = $this->fileLinkFormat) { - return \is_string($fmt) ? strtr($fmt, array('%f' => $file, '%l' => $line)) : $fmt->format($file, $line); + return \is_string($fmt) ? strtr($fmt, ['%f' => $file, '%l' => $line]) : $fmt->format($file, $line); } return false; @@ -235,7 +235,7 @@ public function formatFileFromText($text) public function formatLogMessage($message, array $context) { if ($context && false !== strpos($message, '{')) { - $replacements = array(); + $replacements = []; foreach ($context as $key => $val) { if (is_scalar($val)) { $replacements['{'.$key.'}'] = $val; diff --git a/src/Symfony/Bridge/Twig/Extension/CsrfExtension.php b/src/Symfony/Bridge/Twig/Extension/CsrfExtension.php index 2f061acfd3ae1..934fe91d7cb5c 100644 --- a/src/Symfony/Bridge/Twig/Extension/CsrfExtension.php +++ b/src/Symfony/Bridge/Twig/Extension/CsrfExtension.php @@ -25,8 +25,8 @@ class CsrfExtension extends AbstractExtension */ public function getFunctions(): array { - return array( - new TwigFunction('csrf_token', array(CsrfRuntime::class, 'getCsrfToken')), - ); + return [ + new TwigFunction('csrf_token', [CsrfRuntime::class, 'getCsrfToken']), + ]; } } diff --git a/src/Symfony/Bridge/Twig/Extension/DumpExtension.php b/src/Symfony/Bridge/Twig/Extension/DumpExtension.php index 3d4c6841f201e..88b75368da203 100644 --- a/src/Symfony/Bridge/Twig/Extension/DumpExtension.php +++ b/src/Symfony/Bridge/Twig/Extension/DumpExtension.php @@ -37,14 +37,14 @@ public function __construct(ClonerI F987 nterface $cloner, HtmlDumper $dumper = null) public function getFunctions() { - return array( - new TwigFunction('dump', array($this, 'dump'), array('is_safe' => array('html'), 'needs_context' => true, 'needs_environment' => true)), - ); + return [ + new TwigFunction('dump', [$this, 'dump'], ['is_safe' => ['html'], 'needs_context' => true, 'needs_environment' => true]), + ]; } public function getTokenParsers() { - return array(new DumpTokenParser()); + return [new DumpTokenParser()]; } public function getName() @@ -59,14 +59,14 @@ public function dump(Environment $env, $context) } if (2 === \func_num_args()) { - $vars = array(); + $vars = []; foreach ($context as $key => $value) { if (!$value instanceof Template) { $vars[$key] = $value; } } - $vars = array($vars); + $vars = [$vars]; } else { $vars = \func_get_args(); unset($vars[0], $vars[1]); diff --git a/src/Symfony/Bridge/Twig/Extension/ExpressionExtension.php b/src/Symfony/Bridge/Twig/Extension/ExpressionExtension.php index fc64fa3e3775d..21f6be4d6ec6d 100644 --- a/src/Symfony/Bridge/Twig/Extension/ExpressionExtension.php +++ b/src/Symfony/Bridge/Twig/Extension/ExpressionExtension.php @@ -27,9 +27,9 @@ class ExpressionExtension extends AbstractExtension */ public function getFunctions() { - return array( - new TwigFunction('expression', array($this, 'createExpression')), - ); + return [ + new TwigFunction('expression', [$this, 'createExpression']), + ]; } public function createExpression($expression) diff --git a/src/Symfony/Bridge/Twig/Extension/FormExtension.php b/src/Symfony/Bridge/Twig/Extension/FormExtension.php index 54f3b70bf1ba5..909e20d58d690 100644 --- a/src/Symfony/Bridge/Twig/Extension/FormExtension.php +++ b/src/Symfony/Bridge/Twig/Extension/FormExtension.php @@ -32,10 +32,10 @@ class FormExtension extends AbstractExtension */ public function getTokenParsers() { - return array( + return [ // {% form_theme form "SomeBundle::widgets.twig" %} new FormThemeTokenParser(), - ); + ]; } /** @@ -43,18 +43,19 @@ public function getTokenParsers() */ public function getFunctions() { - return array( - new TwigFunction('form_widget', null, array('node_class' => 'Symfony\Bridge\Twig\Node\SearchAndRenderBlockNode', 'is_safe' => array('html'))), - new TwigFunction('form_errors', null, array('node_class' => 'Symfony\Bridge\Twig\Node\SearchAndRenderBlockNode', 'is_safe' => array('html'))), - new TwigFunction('form_label', null, array('node_class' => 'Symfony\Bridge\Twig\Node\SearchAndRenderBlockNode', 'is_safe' => array('html'))), - new TwigFunction('form_help', null, array('node_class' => 'Symfony\Bridge\Twig\Node\SearchAndRenderBlockNode', 'is_safe' => array('html'))), - new TwigFunction('form_row', null, array('node_class' => 'Symfony\Bridge\Twig\Node\SearchAndRenderBlockNode', 'is_safe' => array('html'))), - new TwigFunction('form_rest', null, array('node_class' => 'Symfony\Bridge\Twig\Node\SearchAndRenderBlockNode', 'is_safe' => array('html'))), - new TwigFunction('form', null, array('node_class' => 'Symfony\Bridge\Twig\Node\RenderBlockNode', 'is_safe' => array('html'))), - new TwigFunction('form_start', null, array('node_class' => 'Symfony\Bridge\Twig\Node\RenderBlockNode', 'is_safe' => array('html'))), - new TwigFunction('form_end', null, array('node_class' => 'Symfony\Bridge\Twig\Node\RenderBlockNode', 'is_safe' => array('html'))), - new TwigFunction('csrf_token', array('Symfony\Component\Form\FormRenderer', 'renderCsrfToken')), - ); + return [ + new TwigFunction('form_widget', null, ['node_class' => 'Symfony\Bridge\Twig\Node\SearchAndRenderBlockNode', 'is_safe' => ['html']]), + new TwigFunction('form_errors', null, ['node_class' => 'Symfony\Bridge\Twig\Node\SearchAndRenderBlockNode', 'is_safe' => ['html']]), + new TwigFunction('form_label', null, ['node_class' => 'Symfony\Bridge\Twig\Node\SearchAndRenderBlockNode', 'is_safe' => ['html']]), + new TwigFunction('form_help', null, ['node_class' => 'Symfony\Bridge\Twig\Node\SearchAndRenderBlockNode', 'is_safe' => ['html']]), + new TwigFunction('form_row', null, ['node_class' => 'Symfony\Bridge\Twig\Node\SearchAndRenderBlockNode', 'is_safe' => ['html']]), + new TwigFunction('form_rest', null, ['node_class' => 'Symfony\Bridge\Twig\Node\SearchAndRenderBlockNode', 'is_safe' => ['html']]), + new TwigFunction('form', null, ['node_class' => 'Symfony\Bridge\Twig\Node\RenderBlockNode', 'is_safe' => ['html']]), + new TwigFunction('form_start', null, ['node_class' => 'Symfony\Bridge\Twig\Node\RenderBlockNode', 'is_safe' => ['html']]), + new TwigFunction('form_end', null, ['node_class' => 'Symfony\Bridge\Twig\Node\RenderBlockNode', 'is_safe' => ['html']]), + new TwigFunction('csrf_token', ['Symfony\Component\Form\FormRenderer', 'renderCsrfToken']), + new TwigFunction('form_parent', 'Symfony\Bridge\Twig\Extension\twig_get_form_parent'), + ]; } /** @@ -62,10 +63,10 @@ public function getFunctions() */ public function getFilters() { - return array( - new TwigFilter('humanize', array('Symfony\Component\Form\FormRenderer', 'humanize')), - new TwigFilter('form_encode_currency', array('Symfony\Component\Form\FormRenderer', 'encodeCurrency'), array('is_safe' => array('html'), 'needs_environment' => true)), - ); + return [ + new TwigFilter('humanize', ['Symfony\Component\Form\FormRenderer', 'humanize']), + new TwigFilter('form_encode_currency', ['Symfony\Component\Form\FormRenderer', 'encodeCurrency'], ['is_safe' => ['html'], 'needs_environment' => true]), + ]; } /** @@ -73,10 +74,10 @@ public function getFilters() */ public function getTests() { - return array( + return [ new TwigTest('selectedchoice', 'Symfony\Bridge\Twig\Extension\twig_is_selected_choice'), new TwigTest('rootform', 'Symfony\Bridge\Twig\Extension\twig_is_root_form'), - ); + ]; } /** @@ -115,3 +116,11 @@ function twig_is_root_form(FormView $formView) { return null === $formView->parent; } + +/** + * @internal + */ +function twig_get_form_parent(FormView $formView): ?FormView +{ + return $formView->parent; +} diff --git a/src/Symfony/Bridge/Twig/Extension/HttpFoundationExtension.php b/src/Symfony/Bridge/Twig/Extension/HttpFoundationExtension.php index fe2778393cfd2..a72339e1243e1 100644 --- a/src/Symfony/Bridge/Twig/Extension/HttpFoundationExtension.php +++ b/src/Symfony/Bridge/Twig/Extension/HttpFoundationExtension.php @@ -13,6 +13,7 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\HttpFoundation\UrlHelper; use Symfony\Component\Routing\RequestContext; use Twig\Extension\AbstractExtension; use Twig\TwigFunction; @@ -24,13 +25,34 @@ */ class HttpFoundationExtension extends AbstractExtension { - private $requestStack; - private $requestContext; + private $urlHelper; - public function __construct(RequestStack $requestStack, RequestContext $requestContext = null) + /** + * @param UrlHelper $urlHelper + */ + public function __construct($urlHelper) { - $this->requestStack = $requestStack; - $this->requestContext = $requestContext; + if ($urlHelper instanceof UrlHelper) { + $this->urlHelper = $urlHelper; + + return; + } + + if (!$urlHelper instanceof RequestStack) { + throw new \TypeError(sprintf('The first argument must be an instance of "%s" or an instance of "%s".', UrlHelper::class, RequestStack::class)); + } + + @trigger_error(sprintf('Passing a "%s" instance as the first argument to the "%s" constructor is deprecated since Symfony 4.3, pass a "%s" instance instead.', RequestStack::class, __CLASS__, UrlHelper::class), E_USER_DEPRECATED); + + $requestContext = null; + if (2 === \func_num_args()) { + $requestContext = \func_get_arg(1); + if (null !== $requestContext && !$requestContext instanceof RequestContext) { + throw new \TypeError(sprintf('The second argument must be an instance of "%s".', RequestContext::class)); + } + } + + $this->urlHelper = new UrlHelper($urlHelper, $requestContext); } /** @@ -38,10 +60,10 @@ public function __construct(RequestStack $requestStack, RequestContext $requestC */ public function getFunctions() { - return array( - new TwigFunction('absolute_url', array($this, 'generateAbsoluteUrl')), - new TwigFunction('relative_path', array($this, 'generateRelativePath')), - ); + return [ + new TwigFunction('absolute_url', [$this, 'generateAbsoluteUrl']), + new TwigFunction('relative_path', [$this, 'generateRelativePath']), + ]; } /** @@ -57,55 +79,7 @@ public function getFunctions() */ public function generateAbsoluteUrl($path) { - if (false !== strpos($path, '://') || '//' === substr($path, 0, 2)) { - return $path; - } - - if (!$request = $this->requestStack->getMasterRequest()) { - if (null !== $this->requestContext && '' !== $host = $this->requestContext->getHost()) { - $scheme = $this->requestContext->getScheme(); - $port = ''; - - if ('http' === $scheme && 80 != $this->requestContext->getHttpPort()) { - $port = ':'.$this->requestContext->getHttpPort(); - } elseif ('https' === $scheme && 443 != $this->requestContext->getHttpsPort()) { - $port = ':'.$this->requestContext->getHttpsPort(); - } - - if ('#' === $path[0]) { - $queryString = $this->requestContext->getQueryString(); - $path = $this->requestContext->getPathInfo().($queryString ? '?'.$queryString : '').$path; - } elseif ('?' === $path[0]) { - $path = $this->requestContext->getPathInfo().$path; - } - - if ('/' !== $path[0]) { - $path = rtrim($this->requestContext->getBaseUrl(), '/').'/'.$path; - } - - return $scheme.'://'.$host.$port.$path; - } - - return $path; - } - - if ('#' === $path[0]) { - $path = $request->getRequestUri().$path; - } elseif ('?' === $path[0]) { - $path = $request->getPathInfo().$path; - } - - if (!$path || '/' !== $path[0]) { - $prefix = $request->getPathInfo(); - $last = \strlen($prefix) - 1; - if ($last !== $pos = strrpos($prefix, '/')) { - $prefix = substr($prefix, 0, $pos).'/'; - } - - return $request->getUriForPath($prefix.$path); - } - - return $request->getSchemeAndHttpHost().$path; + return $this->urlHelper->getAbsoluteUrl($path); } /** @@ -121,15 +95,7 @@ public function generateAbsoluteUrl($path) */ public function generateRelativePath($path) { - if (false !== strpos($path, '://') || '//' === substr($path, 0, 2)) { - return $path; - } - - if (!$request = $this->requestStack->getMasterRequest()) { - return $path; - } - - return $request->getRelativeUriForPath($path); + return $this->urlHelper->getRelativePath($path); } /** diff --git a/src/Symfony/Bridge/Twig/Extension/HttpKernelExtension.php b/src/Symfony/Bridge/Twig/Extension/HttpKernelExtension.php index 45142e7402a67..f8b93ada15475 100644 --- a/src/Symfony/Bridge/Twig/Extension/HttpKernelExtension.php +++ b/src/Symfony/Bridge/Twig/Extension/HttpKernelExtension.php @@ -24,14 +24,14 @@ class HttpKernelExtension extends AbstractExtension { public function getFunctions() { - return array( - new TwigFunction('render', array(HttpKernelRuntime::class, 'renderFragment'), array('is_safe' => array('html'))), - new TwigFunction('render_*', array(HttpKernelRuntime::class, 'renderFragmentStrategy'), array('is_safe' => array('html'))), + return [ + new TwigFunction('render', [HttpKernelRuntime::class, 'renderFragment'], ['is_safe' => ['html']]), + new TwigFunction('render_*', [HttpKernelRuntime::class, 'renderFragmentStrategy'], ['is_safe' => ['html']]), new TwigFunction('controller', static::class.'::controller'), - ); + ]; } - public static function controller($controller, $attributes = array(), $query = array()) + public static function controller($controller, $attributes = [], $query = []) { return new ControllerReference($controller, $attributes, $query); } diff --git a/src/Symfony/Bridge/Twig/Extension/HttpKernelRuntime.php b/src/Symfony/Bridge/Twig/Extension/HttpKernelRuntime.php index 6eea673e25fa6..fcd7c24416fbe 100644 --- a/src/Symfony/Bridge/Twig/Extension/HttpKernelRuntime.php +++ b/src/Symfony/Bridge/Twig/Extension/HttpKernelRuntime.php @@ -38,7 +38,7 @@ public function __construct(FragmentHandler $handler) * * @see FragmentHandler::render() */ - public function renderFragment($uri, $options = array()) + public function renderFragment($uri, $options = []) { $strategy = isset($options['strategy']) ? $options['strategy'] : 'inline'; unset($options['strategy']); @@ -57,7 +57,7 @@ public function renderFragment($uri, $options = array()) * * @see FragmentHandler::render() */ - public function renderFragmentStrategy($strategy, $uri, $options = array()) + public function renderFragmentStrategy($strategy, $uri, $options = []) { return $this->handler->render($uri, $strategy, $options); } diff --git a/src/Symfony/Bridge/Twig/Extension/LogoutUrlExtension.php b/src/Symfony/Bridge/Twig/Extension/LogoutUrlExtension.php index 17abb779899ac..e8bc6190cd65a 100644 --- a/src/Symfony/Bridge/Twig/Extension/LogoutUrlExtension.php +++ b/src/Symfony/Bridge/Twig/Extension/LogoutUrlExtension.php @@ -34,10 +34,10 @@ public function __construct(LogoutUrlGenerator $generator) */ public function getFunctions() { - return array( - new TwigFunction('logout_url', array($this, 'getLogoutUrl')), - new TwigFunction('logout_path', array($this, 'getLogoutPath')), - ); + return [ + new TwigFunction('logout_url', [$this, 'getLogoutUrl']), + new TwigFunction('logout_path', [$this, 'getLogoutPath']), + ]; } /** diff --git a/src/Symfony/Bridge/Twig/Extension/RoutingExtension.php b/src/Symfony/Bridge/Twig/Extension/RoutingExtension.php index a23a62bb28aed..67fbe8d3910a3 100644 --- a/src/Symfony/Bridge/Twig/Extension/RoutingExtension.php +++ b/src/Symfony/Bridge/Twig/Extension/RoutingExtension.php @@ -39,10 +39,10 @@ public function __construct(UrlGeneratorInterface $generator) */ public function getFunctions() { - return array( - new TwigFunction('url', array($this, 'getUrl'), array('is_safe_callback' => array($this, 'isUrlGenerationSafe'))), - new TwigFunction('path', array($this, 'getPath'), array('is_safe_callback' => array($this, 'isUrlGenerationSafe'))), - ); + return [ + new TwigFunction('url', [$this, 'getUrl'], ['is_safe_callback' => [$this, 'isUrlGenerationSafe']]), + new TwigFunction('path', [$this, 'getPath'], ['is_safe_callback' => [$this, 'isUrlGenerationSafe']]), + ]; } /** @@ -52,7 +52,7 @@ public function getFunctions() * * @return string */ - public function getPath($name, $parameters = array(), $relative = false) + public function getPath($name, $parameters = [], $relative = false) { return $this->generator->generate($name, $parameters, $relative ? UrlGeneratorInterface::RELATIVE_PATH : UrlGeneratorInterface::ABSOLUTE_PATH); } @@ -64,7 +64,7 @@ public function getPath($name, $parameters = array(), $relative = false) * * @return string */ - public function getUrl($name, $parameters = array(), $schemeRelative = false) + public function getUrl($name, $parameters = [], $schemeRelative = false) { return $this->generator->generate($name, $parameters, $schemeRelative ? UrlGeneratorInterface::NETWORK_PATH : UrlGeneratorInterface::ABSOLUTE_URL); } @@ -103,10 +103,10 @@ public function isUrlGenerationSafe(Node $argsNode) if (null === $paramsNode || $paramsNode instanceof ArrayExpression && \count($paramsNode) <= 2 && (!$paramsNode->hasNode(1) || $paramsNode->getNode(1) instanceof ConstantExpression) ) { - return array('html'); + return ['html']; } - return array(); + return []; } /** diff --git a/src/Symfony/Bridge/Twig/Extension/SecurityExtension.php b/src/Symfony/Bridge/Twig/Extension/SecurityExtension.php index 193726a684371..439c31aad3df2 100644 --- a/src/Symfony/Bridge/Twig/Extension/SecurityExtension.php +++ b/src/Symfony/Bridge/Twig/Extension/SecurityExtension.php @@ -53,9 +53,9 @@ public function isGranted($role, $object = null, $field = null) */ public function getFunctions() { - return array( - new TwigFunction('is_granted', array($this, 'isGranted')), - ); + return [ + new TwigFunction('is_granted', [$this, 'isGranted']), + ]; } /** diff --git a/src/Symfony/Bridge/Twig/Extension/StopwatchExtension.php b/src/Symfony/Bridge/Twig/Extension/StopwatchExtension.php index 20b0471d53b23..19dfed23e3bcd 100644 --- a/src/Symfony/Bridge/Twig/Extension/StopwatchExtension.php +++ b/src/Symfony/Bridge/Twig/Extension/StopwatchExtension.php @@ -38,14 +38,14 @@ public function getStopwatch() public function getTokenParsers() { - return array( + return [ /* * {% stopwatch foo %} * Some stuff which will be recorded on the timeline * {% endstopwatch %} */ new StopwatchTokenParser(null !== $this->stopwatch && $this->enabled), - ); + ]; } public function getName() diff --git a/src/Symfony/Bridge/Twig/Extension/TranslationExtension.php b/src/Symfony/Bridge/Twig/Extension/TranslationExtension.php index ec5d452cef460..b8a0eb61546aa 100644 --- a/src/Symfony/Bridge/Twig/Extension/TranslationExtension.php +++ b/src/Symfony/Bridge/Twig/Extension/TranslationExtension.php @@ -69,10 +69,10 @@ public function getTranslator() */ public function getFilters() { - return array( - new TwigFilter('trans', array($this, 'trans')), - new TwigFilter('transchoice', array($this, 'transchoice'), array('deprecated' => '4.2', 'alternative' => 'trans" with parameter "%count%')), - ); + return [ + new TwigFilter('trans', [$this, 'trans']), + new TwigFilter('transchoice', [$this, 'transchoice'], ['deprecated' => '4.2', 'alternative' => 'trans" with parameter "%count%']), + ]; } /** @@ -82,7 +82,7 @@ public function getFilters() */ public function getTokenParsers() { - return array( + return [ // {% trans %}Symfony is great!{% endtrans %} new TransTokenParser(), @@ -93,7 +93,7 @@ public function getTokenParsers() // {% trans_default_domain "foobar" %} new TransDefaultDomainTokenParser(), - ); + ]; } /** @@ -101,7 +101,7 @@ public function getTokenParsers() */ public function getNodeVisitors() { - return array($this->getTranslationNodeVisitor(), new TranslationDefaultDomainNodeVisitor()); + return [$this->getTranslationNodeVisitor(), new TranslationDefaultDomainNodeVisitor()]; } public function getTranslationNodeVisitor() @@ -109,7 +109,7 @@ public function getTranslationNodeVisitor() return $this->translationNodeVisitor ?: $this->translationNodeVisitor = new TranslationNodeVisitor(); } - public function trans($message, array $arguments = array(), $domain = null, $locale = null, $count = null) + public function trans($message, array $arguments = [], $domain = null, $locale = null, $count = null) { if (null !== $count) { $arguments['%count%'] = $count; @@ -124,16 +124,16 @@ public function trans($message, array $arguments = array(), $domain = null, $loc /** * @deprecated since Symfony 4.2, use the trans() method instead with a %count% parameter */ - public function transchoice($message, $count, array $arguments = array(), $domain = null, $locale = null) + public function transchoice($message, $count, array $arguments = [], $domain = null, $locale = null) { if (null === $this->translator) { - return $this->doTrans($message, array_merge(array('%count%' => $count), $arguments), $domain, $locale); + return $this->doTrans($message, array_merge(['%count%' => $count], $arguments), $domain, $locale); } if ($this->translator instanceof TranslatorInterface) { - return $this->translator->trans($message, array_merge(array('%count%' => $count), $arguments), $domain, $locale); + return $this->translator->trans($message, array_merge(['%count%' => $count], $arguments), $domain, $locale); } - return $this->translator->transChoice($message, $count, array_merge(array('%count%' => $count), $arguments), $domain, $locale); + return $this->translator->transChoice($message, $count, array_merge(['%count%' => $count], $arguments), $domain, $locale); } /** diff --git a/src/Symfony/Bridge/Twig/Extension/WebLinkExtension.php b/src/Symfony/Bridge/Twig/Extension/WebLinkExtension.php index 3d90c3bf21c74..0ca519ee72423 100644 --- a/src/Symfony/Bridge/Twig/Extension/WebLinkExtension.php +++ b/src/Symfony/Bridge/Twig/Extension/WebLinkExtension.php @@ -36,14 +36,14 @@ public function __construct(RequestStack $requestStack) */ public function getFunctions() { - return array( - new TwigFunction('link', array($this, 'link')), - new TwigFunction('preload', array($this, 'preload')), - new TwigFunction('dns_prefetch', array($this, 'dnsPrefetch')), - new TwigFunction('preconnect', array($this, 'preconnect')), - new TwigFunction('prefetch', array($this, 'prefetch')), - new TwigFunction('prerender', array($this, 'prerender')), - ); + return [ + new TwigFunction('link', [$this, 'link']), + new TwigFunction('preload', [$this, 'preload']), + new TwigFunction('dns_prefetch', [$this, 'dnsPrefetch']), + new TwigFunction('preconnect', [$this, 'preconnect']), + new TwigFunction('prefetch', [$this, 'prefetch']), + new TwigFunction('prerender', [$this, 'prerender']), + ]; } /** @@ -51,11 +51,11 @@ public function getFunctions() * * @param string $uri The relation URI * @param string $rel The relation type (e.g. "preload", "prefetch", "prerender" or "dns-prefetch") - * @param array $attributes The attributes of this link (e.g. "array('as' => true)", "array('pr' => 0.5)") + * @param array $attributes The attributes of this link (e.g. "['as' => true]", "['pr' => 0.5]") * * @return string The relation URI */ - public function link($uri, $rel, array $attributes = array()) + public function link($uri, $rel, array $attributes = []) { if (!$request = $this->requestStack->getMasterRequest()) { return $uri; @@ -76,11 +76,11 @@ public function link($uri, $rel, array $attributes = array()) * Preloads a resource. * * @param string $uri A public path - * @param array $attributes The attributes of this link (e.g. "array('as' => true)", "array('crossorigin' => 'use-credentials')") + * @param array $attributes The attributes of this link (e.g. "['as' => true]", "['crossorigin' => 'use-credentials']") * * @return string The path of the asset */ - public function preload($uri, array $attributes = array()) + public function preload($uri, array $attributes = []) { return $this->link($uri, 'preload', $attributes); } @@ -89,11 +89,11 @@ public function preload($uri, array $attributes = array()) * Resolves a resource origin as early as possible. * * @param string $uri A public path - * @param array $attributes The attributes of this link (e.g. "array('as' => true)", "array('pr' => 0.5)") + * @param array $attributes The attributes of this link (e.g. "['as' => true]", "['pr' => 0.5]") * * @return string The path of the asset */ - public function dnsPrefetch($uri, array $attributes = array()) + public function dnsPrefetch($uri, array $attributes = []) { return $this->link($uri, 'dns-prefetch', $attributes); } @@ -102,11 +102,11 @@ public function dnsPrefetch($uri, array $attributes = array()) * Initiates a early connection to a resource (DNS resolution, TCP handshake, TLS negotiation). * * @param string $uri A public path - * @param array $attributes The attributes of this link (e.g. "array('as' => true)", "array('pr' => 0.5)") + * @param array $attributes The attributes of this link (e.g. "['as' => true]", "['pr' => 0.5]") * * @return string The path of the asset */ - public function preconnect($uri, array $attributes = array()) + public function preconnect($uri, array $attributes = []) { return $this->link($uri, 'preconnect', $attributes); } @@ -115,11 +115,11 @@ public function preconnect($uri, array $attributes = array()) * Indicates to the client that it should prefetch this resource. * * @param string $uri A public path - * @param array $attributes The attributes of this link (e.g. "array('as' => true)", "array('pr' => 0.5)") + * @param array $attributes The attributes of this link (e.g. "['as' => true]", "['pr' => 0.5]") * * @return string The path of the asset */ - public function prefetch($uri, array $attributes = array()) + public function prefetch($uri, array $attributes = []) { return $this->link($uri, 'prefetch', $attributes); } @@ -128,11 +128,11 @@ public function prefetch($uri, array $attributes = array()) * Indicates to the client that it should prerender this resource . * * @param string $uri A public path - * @param array $attributes The attributes of this link (e.g. "array('as' => true)", "array('pr' => 0.5)") + * @param array $attributes The attributes of this link (e.g. "['as' => true]", "['pr' => 0.5]") * * @return string The path of the asset */ - public function prerender($uri, array $attributes = array()) + public function prerender($uri, array $attributes = []) { return $this->link($uri, 'prerender', $attributes); } diff --git a/src/Symfony/Bridge/Twig/Extension/WorkflowExtension.php b/src/Symfony/Bridge/Twig/Extension/WorkflowExtension.php index cd63e84ea4484..85b4f7a4d73cd 100644 --- a/src/Symfony/Bridge/Twig/Extension/WorkflowExtension.php +++ b/src/Symfony/Bridge/Twig/Extension/WorkflowExtension.php @@ -13,6 +13,7 @@ use Symfony\Component\Workflow\Registry; use Symfony\Component\Workflow\Transition; +use Symfony\Component\Workflow\TransitionBlockerList; use Twig\Extension\AbstractExtension; use Twig\TwigFunction; @@ -32,13 +33,14 @@ public function __construct(Registry $workflowRegistry) public function getFunctions() { - return array( - new TwigFunction('workflow_can', array($this, 'canTransition')), - new TwigFunction('workflow_transitions', array($this, 'getEnabledTransitions')), - new TwigFunction('workflow_has_marked_place', array($this, 'hasMarkedPlace')), - new TwigFunction('workflow_marked_places', array($this, 'getMarkedPlaces')), - new TwigFunction('workflow_metadata', array($this, 'getMetadata')), - ); + return [ + new TwigFunction('workflow_can', [$this, 'canTransition']), + new TwigFunction('workflow_transitions', [$this, 'getEnabledTransitions']), + new TwigFunction('workflow_has_marked_place', [$this, 'hasMarkedPlace']), + new TwigFunction('workflow_marked_places', [$this, 'getMarkedPlaces']), + new TwigFunction('workflow_metadata', [$this, 'getMetadata']), + new TwigFunction('workflow_transition_blockers', [$this, 'buildTransitionBlockerList']), + ]; } /** @@ -120,6 +122,13 @@ public function getMetadata($subject, string $key, $metadataSubject = null, stri ; } + public function buildTransitionBlockerList($subject, string $transitionName, string $name = null): TransitionBlockerList + { + $workflow = $this->workflowRegistry->get($subject, $name); + + return $workflow->buildTransitionBlockerList($subject, $transitionName); + } + public function getName() { return 'workflow'; diff --git a/src/Symfony/Bridge/Twig/Extension/YamlExtension.php b/src/Symfony/Bridge/Twig/Extension/YamlExtension.php index 88b29d7c23124..3284ec5cd3d06 100644 --- a/src/Symfony/Bridge/Twig/Extension/YamlExtension.php +++ b/src/Symfony/Bridge/Twig/Extension/YamlExtension.php @@ -28,10 +28,10 @@ class YamlExtension extends AbstractExtension */ public function getFilters() { - return array( - new TwigFilter('yaml_encode', array($this, 'encode')), - new TwigFilter('yaml_dump', array($this, 'dump')), - ); + return [ + new TwigFilter('yaml_encode', [$this, 'encode']), + new TwigFilter('yaml_dump', [$this, 'dump']), + ]; } public function encode($input, $inline = 0, $dumpObjects = 0) diff --git a/src/Symfony/Bridge/Twig/Form/TwigRendererEngine.php b/src/Symfony/Bridge/Twig/Form/TwigRendererEngine.php index bf97e52ad3468..8dc8998a747d5 100644 --- a/src/Symfony/Bridge/Twig/Form/TwigRendererEngine.php +++ b/src/Symfony/Bridge/Twig/Form/TwigRendererEngine.php @@ -40,7 +40,7 @@ public function __construct(array $defaultThemes, Environment $environment) /** * {@inheritdoc} */ - public function renderBlock(FormView $view, $resource, $blockName, array $variables = array()) + public function renderBlock(FormView $view, $resource, $blockName, array $variables = []) { $cacheKey = $view->vars[self::CACHE_KEY_VAR]; @@ -169,7 +169,7 @@ protected function loadResourcesFromTheme($cacheKey, &$theme) // theme is a reference and we don't want to change it. $currentTheme = $theme; - $context = $this->environment->mergeGlobals(array()); + $context = $this->environment->mergeGlobals([]); // The do loop takes care of template inheritance. // Add blocks from all templates in the inheritance tree, but avoid diff --git a/src/Symfony/Bridge/Twig/LICENSE b/src/Symfony/Bridge/Twig/LICENSE index 21d7fb9e2f29b..a677f43763ca4 100644 --- a/src/Symfony/Bridge/Twig/LICENSE +++ b/src/Symfony/Bridge/Twig/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2018 Fabien Potencier +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 diff --git a/src/Symfony/Bridge/Twig/Mime/BodyRenderer.php b/src/Symfony/Bridge/Twig/Mime/BodyRenderer.php new file mode 100644 index 0000000000000..df2c9f91c3cf2 --- /dev/null +++ b/src/Symfony/Bridge/Twig/Mime/BodyRenderer.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\Bridge\Twig\Mime; + +use League\HTMLToMarkdown\HtmlConverter; +use Symfony\Component\Mime\BodyRendererInterface; +use Symfony\Component\Mime\Message; +use Twig\Environment; + +/** + * @author Fabien Potencier + * + * @experimental in 4.3 + */ +final class BodyRenderer implements BodyRendererInterface +{ + private $twig; + private $context; + private $converter; + + public function __construct(Environment $twig, array $context = []) + { + $this->twig = $twig; + $this->context = $context; + if (class_exists(HtmlConverter::class)) { + $this->converter = new HtmlConverter([ + 'hard_break' => true, + 'strip_tags' => true, + 'remove_nodes' => 'head style', + ]); + } + } + + public function render(Message $message): void + { + if (!$message instanceof TemplatedEmail) { + return; + } + + $vars = array_merge($this->context, $message->getContext(), [ + 'email' => new WrappedTemplatedEmail($this->twig, $message), + ]); + + if ($template = $message->getTextTemplate()) { + $message->text($this->twig->render($template, $vars)); + } + + if ($template = $message->getHtmlTemplate()) { + $message->html($this->twig->render($template, $vars)); + } + + // if text body is empty, compute one from the HTML body + if (!$message->getTextBody() && null !== $html = $message->getHtmlBody()) { + $message->text($this->convertHtmlToText(\is_resource($html) ? stream_get_contents($html) : $html)); + } + } + + private function convertHtmlToText(string $html): string + { + if (null !== $this->converter) { + return $this->converter->convert($html); + } + + return strip_tags($html); + } +} diff --git a/src/Symfony/Bridge/Twig/Mime/TemplatedEmail.php b/src/Symfony/Bridge/Twig/Mime/TemplatedEmail.php new file mode 100644 index 0000000000000..e487055706892 --- /dev/null +++ b/src/Symfony/Bridge/Twig/Mime/TemplatedEmail.php @@ -0,0 +1,89 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\Mime; + +use Symfony\Component\Mime\Email; + +/** + * @author Fabien Potencier + * + * @experimental in 4.3 + */ +class TemplatedEmail extends Email +{ + private $htmlTemplate; + private $textTemplate; + private $context = []; + + /** + * @return $this + */ + public function textTemplate(?string $template) + { + $this->textTemplate = $template; + + return $this; + } + + /** + * @return $this + */ + public function htmlTemplate(?string $template) + { + $this->htmlTemplate = $template; + + return $this; + } + + public function getTextTemplate(): ?string + { + return $this->textTemplate; + } + + public function getHtmlTemplate(): ?string + { + return $this->htmlTemplate; + } + + /** + * @return $this + */ + public function context(array $context) + { + $this->context = $context; + + return $this; + } + + public function getContext(): array + { + return $this->context; + } + + /** + * @internal + */ + public function __serialize(): array + { + return [$this->htmlTemplate, $this->textTemplate, $this->context, parent::__serialize()]; + } + + /** + * @internal + */ + public function __unserialize(array $data): void + { + [$this->htmlTemplate, $this->textTemplate, $this->context, $parentData] = $data; + + parent::__unserialize($parentData); + } +} diff --git a/src/Symfony/Bridge/Twig/Mime/WrappedTemplatedEmail.php b/src/Symfony/Bridge/Twig/Mime/WrappedTemplatedEmail.php new file mode 100644 index 0000000000000..7c0b585a4eb63 --- /dev/null +++ b/src/Symfony/Bridge/Twig/Mime/WrappedTemplatedEmail.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\Bridge\Twig\Mime; + +use Symfony\Component\Mime\Address; +use Symfony\Component\Mime\NamedAddress; +use Twig\Environment; + +/** + * @internal + * + * @author Fabien Potencier + * + * @experimental in 4.3 + */ +final class WrappedTemplatedEmail +{ + private $twig; + private $message; + + public function __construct(Environment $twig, TemplatedEmail $message) + { + $this->twig = $twig; + $this->message = $message; + } + + public function toName(): string + { + $to = $this->message->getTo()[0]; + + return $to instanceof NamedAddress ? $to->getName() : ''; + } + + public function image(string $image, string $contentType = null): string + { + $file = $this->twig->getLoader()->getSourceContext($image); + if ($path = $file->getPath()) { + $this->message->embedFromPath($path, $image, $contentType); + } else { + $this->message->embed($file->getCode(), $image, $contentType); + } + + return 'cid:'.$image; + } + + public function attach(string $file, string $name = null, string $contentType = null): void + { + $file = $this->twig->getLoader()->getSourceContext($file); + if ($path = $file->getPath()) { + $this->message->attachFromPath($path, $name, $contentType); + } else { + $this->message->attach($file->getCode(), $name, $contentType); + } + } + + /** + * @return $this + */ + public function setSubject(string $subject) + { + $this->message->subject($subject); + + return $this; + } + + public function getSubject(): ?string + { + return $this->message->getSubject(); + } + + /** + * @return $this + */ + public function setReturnPath(string $address) + { + $this->message->returnPath($address); + + return $this; + } + + public function getReturnPath(): string + { + return $this->message->getReturnPath(); + } + + /** + * @return $this + */ + public function addFrom(string $address, string $name = null) + { + $this->message->addFrom($name ? new NamedAddress($address, $name) : new Address($address)); + + return $this; + } + + /** + * @return (Address|NamedAddress)[] + */ + public function getFrom(): array + { + return $this->message->getFrom(); + } + + /** + * @return $this + */ + public function addReplyTo(string $address) + { + $this->message->addReplyTo($address); + + return $this; + } + + /** + * @return Address[] + */ + public function getReplyTo(): array + { + return $this->message->getReplyTo(); + } + + /** + * @return $this + */ + public function addTo(string $address, string $name = null) + { + $this->message->addTo($name ? new NamedAddress($address, $name) : new Address($address)); + + return $this; + } + + /** + * @return (Address|NamedAddress)[] + */ + public function getTo(): array + { + return $this->message->getTo(); + } + + /** + * @return $this + */ + public function addCc(string $address, string $name = null) + { + $this->message->addCc($name ? new NamedAddress($address, $name) : new Address($address)); + + return $this; + } + + /** + * @return (Address|NamedAddress)[] + */ + public function getCc(): array + { + return $this->message->getCc(); + } + + /** + * @return $this + */ + public function addBcc(string $address, string $name = null) + { + $this->message->addBcc($name ? new NamedAddress($address, $name) : new Address($address)); + + return $this; + } + + /** + * @return (Address|NamedAddress)[] + */ + public function getBcc(): array + { + return $this->message->getBcc(); + } + + /** + * @return $this + */ + public function setPriority(int $priority) + { + $this->message->setPriority($priority); + + return $this; + } + + public function getPriority(): int + { + return $this->message->getPriority(); + } +} diff --git a/src/Symfony/Bridge/Twig/Node/DumpNode.php b/src/Symfony/Bridge/Twig/Node/DumpNode.php index 452b64ccb1011..c9cf1e1689ee6 100644 --- a/src/Symfony/Bridge/Twig/Node/DumpNode.php +++ b/src/Symfony/Bridge/Twig/Node/DumpNode.php @@ -23,12 +23,12 @@ class DumpNode extends Node public function __construct($varPrefix, Node $values = null, int $lineno, string $tag = null) { - $nodes = array(); + $nodes = []; if (null !== $values) { $nodes['values'] = $values; } - parent::__construct($nodes, array(), $lineno, $tag); + parent::__construct($nodes, [], $lineno, $tag); $this->varPrefix = $varPrefix; } @@ -44,7 +44,7 @@ public function compile(Compiler $compiler) if (!$this->hasNode('values')) { // remove embedded templates (macros) from the context $compiler - ->write(sprintf('$%svars = array();'."\n", $this->varPrefix)) + ->write(sprintf('$%svars = [];'."\n", $this->varPrefix)) ->write(sprintf('foreach ($context as $%1$skey => $%1$sval) {'."\n", $this->varPrefix)) ->indent() ->write(sprintf('if (!$%sval instanceof \Twig\Template) {'."\n", $this->varPrefix)) @@ -65,7 +65,7 @@ public function compile(Compiler $compiler) } else { $compiler ->addDebugInfo($this) - ->write('\Symfony\Component\VarDumper\VarDumper::dump(array('."\n") + ->write('\Symfony\Component\VarDumper\VarDumper::dump(['."\n") ->indent(); foreach ($values as $node) { $compiler->write(''); @@ -80,7 +80,7 @@ public function compile(Compiler $compiler) } $compiler ->outdent() - ->write("));\n"); + ->write("]);\n"); } $compiler diff --git a/src/Symfony/Bridge/Twig/Node/FormThemeNode.php b/src/Symfony/Bridge/Twig/Node/FormThemeNode.php index b898ec58bf26c..c99675cab3168 100644 --- a/src/Symfony/Bridge/Twig/Node/FormThemeNode.php +++ b/src/Symfony/Bridge/Twig/Node/FormThemeNode.php @@ -22,7 +22,7 @@ class FormThemeNode extends Node { public function __construct(Node $form, Node $resources, int $lineno, string $tag = null, bool $only = false) { - parent::__construct(array('form' => $form, 'resources' => $resources), array('only' => $only), $lineno, $tag); + parent::__construct(['form' => $form, 'resources' => $resources], ['only' => $only], $lineno, $tag); } public function compile(Compiler $compiler) diff --git a/src/Symfony/Bridge/Twig/Node/SearchAndRenderBlockNode.php b/src/Symfony/Bridge/Twig/Node/SearchAndRenderBlockNode.php index 224e1d60e0aad..612bec14e5329 100644 --- a/src/Symfony/Bridge/Twig/Node/SearchAndRenderBlockNode.php +++ b/src/Symfony/Bridge/Twig/Node/SearchAndRenderBlockNode.php @@ -53,7 +53,7 @@ public function compile(Compiler $compiler) // Only insert the label into the array if it is not empty if (!twig_test_empty($label->getAttribute('value'))) { $originalVariables = $variables; - $variables = new ArrayExpression(array(), $lineno); + $variables = new ArrayExpression([], $lineno); $labelKey = new ConstantExpression('label', $lineno); if (null !== $originalVariables) { @@ -100,7 +100,7 @@ public function compile(Compiler $compiler) // If not, add it to the array at runtime. $compiler->raw('(twig_test_empty($_label_ = '); $compiler->subcompile($label); - $compiler->raw(') ? array() : array("label" => $_label_))'); + $compiler->raw(') ? [] : ["label" => $_label_])'); } } } diff --git a/src/Symfony/Bridge/Twig/Node/StopwatchNode.php b/src/Symfony/Bridge/Twig/Node/StopwatchNode.php index d95c63028c2eb..3844b2124c38f 100644 --- a/src/Symfony/Bridge/Twig/Node/StopwatchNode.php +++ b/src/Symfony/Bridge/Twig/Node/StopwatchNode.php @@ -24,7 +24,7 @@ class StopwatchNode extends Node { public function __construct(Node $name, Node $body, AssignNameExpression $var, int $lineno = 0, string $tag = null) { - parent::__construct(array('body' => $body, 'name' => $name, 'var' => $var), array(), $lineno, $tag); + parent::__construct(['body' => $body, 'name' => $name, 'var' => $var], [], $lineno, $tag); } public function compile(Compiler $compiler) diff --git a/src/Symfony/Bridge/Twig/Node/TransDefaultDomainNode.php b/src/Symfony/Bridge/Twig/Node/TransDefaultDomainNode.php index 63ad1f6796152..7f8024aa85640 100644 --- a/src/Symfony/Bridge/Twig/Node/TransDefaultDomainNode.php +++ b/src/Symfony/Bridge/Twig/Node/TransDefaultDomainNode.php @@ -22,7 +22,7 @@ class TransDefaultDomainNode extends Node { public function __construct(AbstractExpression $expr, int $lineno = 0, string $tag = null) { - parent::__construct(array('expr' => $expr), array(), $lineno, $tag); + parent::__construct(['expr' => $expr], [], $lineno, $tag); } public function compile(Compiler $compiler) diff --git a/src/Symfony/Bridge/Twig/Node/TransNode.php b/src/Symfony/Bridge/Twig/Node/TransNode.php index 578e37dd2c3ad..cedc6b740e08d 100644 --- a/src/Symfony/Bridge/Twig/Node/TransNode.php +++ b/src/Symfony/Bridge/Twig/Node/TransNode.php @@ -29,7 +29,7 @@ class TransNode extends Node { public function __construct(Node $body, Node $domain = null, AbstractExpression $count = null, AbstractExpression $vars = null, AbstractExpression $locale = null, int $lineno = 0, string $tag = null) { - $nodes = array('body' => $body); + $nodes = ['body' => $body]; if (null !== $domain) { $nodes['domain'] = $domain; } @@ -43,14 +43,14 @@ public function __construct(Node $body, Node $domain = null, AbstractExpression $nodes['locale'] = $locale; } - parent::__construct($nodes, array(), $lineno, $tag); + parent::__construct($nodes, [], $lineno, $tag); } public function compile(Compiler $compiler) { $compiler->addDebugInfo($this); - $defaults = new ArrayExpression(array(), -1); + $defaults = new ArrayExpression([], -1); if ($this->hasNode('vars') && ($vars = $this->getNode('vars')) instanceof ArrayExpression) { $defaults = $this->getNode('vars'); $vars = null; @@ -112,7 +112,7 @@ protected function compileString(Node $body, ArrayExpression $vars, $ignoreStric } elseif ($body instanceof TextNode) { $msg = $body->getAttribute('data'); } else { - return array($body, $vars); + return [$body, $vars]; } preg_match_all('/(?getTemplateLine()), $vars); + return [new ConstantExpression(str_replace('%%', '%', trim($msg)), $body->getTemplateLine()), $vars]; } } diff --git a/src/Symfony/Bridge/Twig/NodeVisitor/Scope.php b/src/Symfony/Bridge/Twig/NodeVisitor/Scope.php index 59497dc961984..642623f2a693c 100644 --- a/src/Symfony/Bridge/Twig/NodeVisitor/Scope.php +++ b/src/Symfony/Bridge/Twig/NodeVisitor/Scope.php @@ -17,7 +17,7 @@ class Scope { private $parent; - private $data = array(); + private $data = []; private $left = false; public function __construct(self $parent = null) @@ -77,7 +77,7 @@ public function set($key, $value) */ public function has($key) { - if (array_key_exists($key, $this->data)) { + if (\array_key_exists($key, $this->data)) { return true; } @@ -98,7 +98,7 @@ public function has($key) */ public function get($key, $default = null) { - if (array_key_exists($key, $this->data)) { + if (\array_key_exists($key, $this->data)) { return $this->data[$key]; } diff --git a/src/Symfony/Bridge/Twig/NodeVisitor/TranslationDefaultDomainNodeVisitor.php b/src/Symfony/Bridge/Twig/NodeVisitor/TranslationDefaultDomainNodeVisitor.php index 6a34a037e48f2..04b68ef6be199 100644 --- a/src/Symfony/Bridge/Twig/NodeVisitor/TranslationDefaultDomainNodeVisitor.php +++ b/src/Symfony/Bridge/Twig/NodeVisitor/TranslationDefaultDomainNodeVisitor.php @@ -56,7 +56,7 @@ protected function doEnterNode(Node $node, Environment $env) $name = new AssignNameExpression($var, $node->getTemplateLine()); $this->scope->set('domain', new NameExpression($var, $node->getTemplateLine())); - return new SetNode(false, new Node(array($name)), new Node(array($node->getNode('expr'))), $node->getTemplateLine()); + return new SetNode(false, new Node([$name]), new Node([$node->getNode('expr')]), $node->getTemplateLine()); } } @@ -64,7 +64,7 @@ protected function doEnterNode(Node $node, Environment $env) return $node; } - if ($node instanceof FilterExpression && \in_array($node->getNode('filter')->getAttribute('value'), array('trans', 'transchoice'))) { + if ($node instanceof FilterExpression && \in_array($node->getNode('filter')->getAttribute('value'), ['trans', 'transchoice'])) { $arguments = $node->getNode('arguments'); $ind = 'trans' === $node->getNode('filter')->getAttribute('value') ? 1 : 2; if ($this->isNamedArguments($arguments)) { @@ -74,7 +74,7 @@ protected function doEnterNode(Node $node, Environment $env) } else { if (!$arguments->hasNode($ind)) { if (!$arguments->hasNode($ind - 1)) { - $arguments->setNode($ind - 1, new ArrayExpression(array(), $node->getTemplateLine())); + $arguments->setNode($ind - 1, new ArrayExpression([], $node->getTemplateLine())); } $arguments->setNode($ind, $this->scope->get('domain')); @@ -95,7 +95,7 @@ protected function doEnterNode(Node $node, Environment $env) protected function doLeaveNode(Node $node, Environment $env) { if ($node instanceof TransDefaultDomainNode) { - return false; + return null; } if ($node instanceof BlockNode || $node instanceof ModuleNode) { diff --git a/src/Symfony/Bridge/Twig/NodeVisitor/TranslationNodeVisitor.php b/src/Symfony/Bridge/Twig/NodeVisitor/TranslationNodeVisitor.php index fef722be64ce5..3da4141cdd2e0 100644 --- a/src/Symfony/Bridge/Twig/NodeVisitor/TranslationNodeVisitor.php +++ b/src/Symfony/Bridge/Twig/NodeVisitor/TranslationNodeVisitor.php @@ -28,18 +28,18 @@ class TranslationNodeVisitor extends AbstractNodeVisitor const UNDEFINED_DOMAIN = '_undefined'; private $enabled = false; - private $messages = array(); + private $messages = []; public function enable() { $this->enabled = true; - $this->messages = array(); + $this->messages = []; } public function disable() { $this->enabled = false; - $this->messages = array(); + $this->messages = []; } public function getMessages() @@ -62,26 +62,26 @@ protected function doEnterNode(Node $node, Environment $env) $node->getNode('node') instanceof ConstantExpression ) { // extract constant nodes with a trans filter - $this->messages[] = array( + $this->messages[] = [ $node->getNode('node')->getAttribute('value'), $this->getReadDomainFromArguments($node->getNode('arguments'), 1), - ); + ]; } elseif ( $node instanceof FilterExpression && 'transchoice' === $node->getNode('filter')->getAttribute('value') && $node->getNode('node') instanceof ConstantExpression ) { // extract constant nodes with a trans filter - $this->messages[] = array( + $this->messages[] = [ $node->getNode('node')->getAttribute('value'), $this->getReadDomainFromArguments($node->getNode('arguments'), 2), - ); + ]; } elseif ($node instanceof TransNode) { // extract trans nodes - $this->messages[] = array( + $this->messages[] = [ $node->getNode('body')->getAttribute('data'), $node->hasNode('domain') ? $this->getReadDomainFromNode($node->getNode('domain')) : null, - ); + ]; } return $node; diff --git a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_layout.html.twig b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_layout.html.twig index 681999297a44b..03335315e9380 100644 --- a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_layout.html.twig +++ b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_layout.html.twig @@ -98,7 +98,7 @@ {% set label = name|humanize %} {%- endif -%} {%- endif -%} - + {{- widget|raw }} {{ label is not same as(false) ? (translation_domain is same as(false) ? label : label|trans({}, translation_domain)) -}} {%- endif -%} @@ -112,11 +112,10 @@ {%- set widget_attr = {attr: {'aria-describedby': id ~"_help"}} -%} {%- endif -%}
- {{- form_label(form) -}} - {{- form_widget(form, widget_attr) -}} - {{- form_help(form) -}} - {{- form_errors(form) -}} -
+ {{- form_label(form) }} {# -#} + {{ form_widget(form, widget_attr) }} {# -#} + {{ form_errors(form) }} {# -#} + {# -#} {%- endblock form_row %} {% block button_row -%} @@ -178,12 +177,19 @@ {% block form_help -%} {%- if help is not empty -%} {%- set help_attr = help_attr|merge({class: (help_attr.class|default('') ~ ' help-block')|trim}) -%} - - + {%- if translation_domain is same as(false) -%} - {{- help -}} + {%- if help_html is same as(false) -%} + {{- help -}} + {%- else -%} + {{- help|raw -}} + {%- endif -%} {%- else -%} - {{- help|trans({}, translation_domain) -}} + {%- if help_html is same as(false) -%} + {{- help|trans(help_translation_parameters, translation_domain) -}} + {%- else -%} + {{- help|trans(help_translation_parameters, translation_domain)|raw -}} + {%- endif -%} {%- endif -%} {%- endif -%} diff --git a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_4_layout.html.twig b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_4_layout.html.twig index 7cde95a28dac0..1848d0dc9838c 100644 --- a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_4_layout.html.twig +++ b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_4_layout.html.twig @@ -106,19 +106,24 @@ {%- endblock dateinterval_widget %} {% block percent_widget -%} -
- {{- block('form_widget_simple') -}} -
- % + {%- if symbol -%} +
+ {{- block('form_widget_simple') -}} +
+ {{ symbol|default('%') }} +
-
+ {%- else -%} + {{- block('form_widget_simple') -}} + {%- endif -%} {%- endblock percent_widget %} {% block file_widget -%} <{{ element|default('div') }} class="custom-file"> {%- set type = type|default('file') -%} {{- block('form_widget_simple') -}} -