From a931002ca25b6e3c127dc4df027040a33e24b672 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 29 Nov 2016 18:09:44 +0100 Subject: [PATCH 01/18] [FrameworkBundle] Forbid env parameters in routing configuration --- .../Bundle/FrameworkBundle/Routing/Router.php | 4 ++++ .../FrameworkBundle/Tests/Routing/RouterTest.php | 14 ++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/src/Symfony/Bundle/FrameworkBundle/Routing/Router.php b/src/Symfony/Bundle/FrameworkBundle/Routing/Router.php index afef4093db290..35bd5ef1efed0 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Routing/Router.php +++ b/src/Symfony/Bundle/FrameworkBundle/Routing/Router.php @@ -146,6 +146,10 @@ private function resolve($value) return '%%'; } + if (preg_match('/^env\(\w+\)$/', $match[1])) { + throw new RuntimeException(sprintf('Using "%%%s%%" is not allowed in routing configuration.', $match[1])); + } + $resolved = $container->getParameter($match[1]); if (is_string($resolved) || is_numeric($resolved)) { diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Routing/RouterTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Routing/RouterTest.php index 330e0659ca144..4727fc4b82f53 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Routing/RouterTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Routing/RouterTest.php @@ -131,6 +131,20 @@ public function testPatternPlaceholders() ); } + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException + * @expectedExceptionMessage Using "%env(FOO)%" is not allowed in routing configuration. + */ + public function testEnvPlaceholders() + { + $routes = new RouteCollection(); + + $routes->add('foo', new Route('/%env(FOO)%')); + + $router = new Router($this->getServiceContainer($routes), 'foo'); + $router->getRouteCollection(); + } + public function testHostPlaceholders() { $routes = new RouteCollection(); From 4514c1b0c487c061a0fd2d18afe0f2c6a7e2388b Mon Sep 17 00:00:00 2001 From: Maxime Steinhausser Date: Tue, 29 Nov 2016 21:18:47 +0100 Subject: [PATCH 02/18] [Serializer] Add deprecation missing from UPGRADE files --- UPGRADE-3.2.md | 5 +++++ UPGRADE-4.0.md | 2 ++ 2 files changed, 7 insertions(+) diff --git a/UPGRADE-3.2.md b/UPGRADE-3.2.md index f4b043c754c66..5862071d94093 100644 --- a/UPGRADE-3.2.md +++ b/UPGRADE-3.2.md @@ -116,6 +116,11 @@ HttpFoundation - `isInvalid`/`isSuccessful`/`isRedirection`/`isClientError`/`isServerError` - `isOk`/`isForbidden`/`isNotFound`/`isRedirect`/`isEmpty` +Serializer +---------- + + * Method `AbstractNormalizer::instantiateObject()` will have a 6th `$format = null` argument in Symfony 4.0. Not defining it when overriding the method is deprecated. + TwigBridge ---------- diff --git a/UPGRADE-4.0.md b/UPGRADE-4.0.md index 483b7e6ed1447..227e9b93f36e8 100644 --- a/UPGRADE-4.0.md +++ b/UPGRADE-4.0.md @@ -170,6 +170,8 @@ Serializer * The ability to pass a Doctrine `Cache` instance to the `ClassMetadataFactory` class has been removed. You should use the `CacheClassMetadataFactory` class instead. + + * Not defining the 6th argument `$format = null` of the `AbstractNormalizer::instantiateObject()` method when overriding it is not supported anymore. Translation ----------- From 597bbdc41845a65e04f7deab7cd0d261791d4ce6 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 30 Nov 2016 09:00:20 +0100 Subject: [PATCH 03/18] Lines length should be 80 or less characters --- UPGRADE-3.2.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/UPGRADE-3.2.md b/UPGRADE-3.2.md index 5862071d94093..97a8aeba029c0 100644 --- a/UPGRADE-3.2.md +++ b/UPGRADE-3.2.md @@ -119,7 +119,9 @@ HttpFoundation Serializer ---------- - * Method `AbstractNormalizer::instantiateObject()` will have a 6th `$format = null` argument in Symfony 4.0. Not defining it when overriding the method is deprecated. + * Method `AbstractNormalizer::instantiateObject()` will have a 6th + `$format = null` argument in Symfony 4.0. Not defining it when overriding + the method is deprecated. TwigBridge ---------- From d681c1abc4c2e3e8c616edc7a9aa8ff2582a724f Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 30 Nov 2016 09:01:12 +0100 Subject: [PATCH 04/18] Lines length should be 80 or less characters --- UPGRADE-4.0.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/UPGRADE-4.0.md b/UPGRADE-4.0.md index 227e9b93f36e8..2cd22e1a1236c 100644 --- a/UPGRADE-4.0.md +++ b/UPGRADE-4.0.md @@ -171,7 +171,9 @@ Serializer class has been removed. You should use the `CacheClassMetadataFactory` class instead. - * Not defining the 6th argument `$format = null` of the `AbstractNormalizer::instantiateObject()` method when overriding it is not supported anymore. + * Not defining the 6th argument `$format = null` of the + `AbstractNormalizer::instantiateObject()` method when overriding it is not + supported anymore. Translation ----------- From 7bfe723ba7223e3cec3f95b09d7cb7d5ca19fb9c Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Wed, 30 Nov 2016 09:45:53 +0100 Subject: [PATCH 05/18] updated CHANGELOG for 3.2.0 --- CHANGELOG-3.2.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGELOG-3.2.md b/CHANGELOG-3.2.md index b2de770775c93..c892ebb6755b7 100644 --- a/CHANGELOG-3.2.md +++ b/CHANGELOG-3.2.md @@ -7,6 +7,17 @@ in 3.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/v3.2.0...v3.2.1 +* 3.2.0 (2016-11-30) + + * bug #20687 [FrameworkBundle] Forbid env parameters in routing configuration (nicolas-grekas) + * bug #20607 [Validator] Bring egulias/email-validator ~2.0 to parity with ~1.2 (Lctrs) + * bug #20671 [Config] ConfigCache::isFresh() should return false when unserialize() fails (nicolas-grekas) + * bug #20679 [VarDumper] Use default color for ellipsed namespaces/paths (nicolas-grekas) + * bug #20676 [ClassLoader] Use only forward slashes in generated class map (nicolas-grekas) + * bug #20664 [Validator] ensure the proper context for nested validations (xabbuh) + * bug #20661 bug #20653 [WebProfilerBundle] Profiler includes ghost panels (jzawadzki) + * bug #20652 Fixed getRouteParams() when no parameters are available (wouterj) + * 3.2.0-RC2 (2016-11-27) * bug #20601 [FrameworkBundle] Don't rely on any parent definition for "cache.annotations" (nicolas-grekas) From df8b30b81387f5d4ac91b34689489e913986c826 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Wed, 30 Nov 2016 09:46:02 +0100 Subject: [PATCH 06/18] updated VERSION for 3.2.0 --- src/Symfony/Component/HttpKernel/Kernel.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index f1013f23c61b2..b76db82d81b7d 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -58,12 +58,12 @@ abstract class Kernel implements KernelInterface, TerminableInterface protected $startTime; protected $loadClassCache; - const VERSION = '3.2.0-DEV'; + const VERSION = '3.2.0'; const VERSION_ID = 30200; const MAJOR_VERSION = 3; const MINOR_VERSION = 2; const RELEASE_VERSION = 0; - const EXTRA_VERSION = 'DEV'; + const EXTRA_VERSION = ''; const END_OF_MAINTENANCE = '07/2017'; const END_OF_LIFE = '01/2018'; From b5520e92c9ecc5d11741dbcdb1542e17d6cc996e Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Wed, 30 Nov 2016 10:16:00 +0100 Subject: [PATCH 07/18] bumped Symfony version to 3.2.1 --- src/Symfony/Component/HttpKernel/Kernel.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index b76db82d81b7d..eefd76b16d8f7 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -58,12 +58,12 @@ abstract class Kernel implements KernelInterface, TerminableInterface protected $startTime; protected $loadClassCache; - const VERSION = '3.2.0'; - const VERSION_ID = 30200; + const VERSION = '3.2.1-DEV'; + const VERSION_ID = 30201; const MAJOR_VERSION = 3; const MINOR_VERSION = 2; - const RELEASE_VERSION = 0; - const EXTRA_VERSION = ''; + const RELEASE_VERSION = 1; + const EXTRA_VERSION = 'DEV'; const END_OF_MAINTENANCE = '07/2017'; const END_OF_LIFE = '01/2018'; From 07cdfd533d5ff9c710600d552f0bb5b2f48c8ee3 Mon Sep 17 00:00:00 2001 From: Maxime STEINHAUSSER Date: Wed, 30 Nov 2016 15:40:17 +0100 Subject: [PATCH 08/18] [WebProfiler][Translator] Fix TranslationDataCollector should use cloneVar --- .../Resources/views/Collector/translation.html.twig | 3 +-- .../DataCollector/TranslationDataCollector.php | 4 ++-- .../Tests/DataCollector/TranslationDataCollectorTest.php | 9 ++++++--- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/translation.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/translation.html.twig index 0be04e76117d8..f5ff17680a236 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/translation.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/translation.html.twig @@ -183,8 +183,7 @@ {% endif %} diff --git a/src/Symfony/Component/Translation/DataCollector/TranslationDataCollector.php b/src/Symfony/Component/Translation/DataCollector/TranslationDataCollector.php index cb59d0a7e70e4..6b5e165aad9a7 100644 --- a/src/Symfony/Component/Translation/DataCollector/TranslationDataCollector.php +++ b/src/Symfony/Component/Translation/DataCollector/TranslationDataCollector.php @@ -101,12 +101,12 @@ private function sanitizeCollectedMessages($messages) if (!isset($result[$messageId])) { $message['count'] = 1; - $message['parameters'] = !empty($message['parameters']) ? array($message['parameters']) : array(); + $message['parameters'] = !empty($message['parameters']) ? array($this->cloneVar($message['parameters'])) : array(); $messages[$key]['translation'] = $this->sanitizeString($message['translation']); $result[$messageId] = $message; } else { if (!empty($message['parameters'])) { - $result[$messageId]['parameters'][] = $message['parameters']; + $result[$messageId]['parameters'][] = $this->cloneVar($message['parameters']); } ++$result[$messageId]['count']; diff --git a/src/Symfony/Component/Translation/Tests/DataCollector/TranslationDataCollectorTest.php b/src/Symfony/Component/Translation/Tests/DataCollector/TranslationDataCollectorTest.php index 3d1e86e22cbed..ba9df0751b389 100644 --- a/src/Symfony/Component/Translation/Tests/DataCollector/TranslationDataCollectorTest.php +++ b/src/Symfony/Component/Translation/Tests/DataCollector/TranslationDataCollectorTest.php @@ -13,6 +13,7 @@ use Symfony\Component\Translation\DataCollectorTranslator; use Symfony\Component\Translation\DataCollector\TranslationDataCollector; +use Symfony\Component\VarDumper\Cloner\VarCloner; class TranslationDataCollectorTest extends \PHPUnit_Framework_TestCase { @@ -39,6 +40,8 @@ public function testCollectEmptyMessages() public function testCollect() { + $cloner = new VarCloner(); + $collectedMessages = array( array( 'id' => 'foo', @@ -115,9 +118,9 @@ public function testCollect() 'state' => DataCollectorTranslator::MESSAGE_MISSING, 'count' => 3, 'parameters' => array( - array('%count%' => 3), - array('%count%' => 3), - array('%count%' => 4, '%foo%' => 'bar'), + $cloner->cloneVar(array('%count%' => 3)), + $cloner->cloneVar(array('%count%' => 3)), + $cloner->cloneVar(array('%count%' => 4, '%foo%' => 'bar')), ), 'transChoiceNumber' => 3, ), From 44a0ef169c74df3bc90cae604ae26b434b7fce1b Mon Sep 17 00:00:00 2001 From: Maxime STEINHAUSSER Date: Wed, 30 Nov 2016 16:30:36 +0100 Subject: [PATCH 09/18] [WebProfilerBundle] Fix vertical alignment for profiler open action --- .../Resources/views/Profiler/open.css.twig | 6 ++++-- .../Resources/views/Profiler/open.html.twig | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/open.css.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/open.css.twig index 9e290b22e6432..144e86f062bb1 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/open.css.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/open.css.twig @@ -31,21 +31,23 @@ body { background-color: #222; position: fixed; top: 0; + display: flex; width: 100%; } .header h1 { - float: left; color: #FFF; font-weight: normal; font-size: 21px; margin: 0; padding: 10px 10px 8px; + word-break: break-all; } a.doc { - float: right; color: #FFF; text-decoration: none; + margin: auto; + margin-right: 10px; } a.doc:hover { diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/open.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/open.html.twig index 438146b633ecb..9b482f1f0aa96 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/open.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/open.html.twig @@ -8,8 +8,8 @@ {% block body %}
- Open in your IDE?

{{ file }} line {{ line }}

+ Open in your IDE?
{{ filename|file_excerpt(line, -1) }} From 22e56680564523010a1a8b603f76f6e603f762fb Mon Sep 17 00:00:00 2001 From: Nicolas Dewez Date: Thu, 1 Dec 2016 22:26:29 +0100 Subject: [PATCH 10/18] Fix hide button in toolbar --- .../Resources/views/Profiler/toolbar_js.html.twig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar_js.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar_js.html.twig index 1dfeb1a29c8eb..ea038277c4bec 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar_js.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar_js.html.twig @@ -66,7 +66,7 @@ } }; } - Sfjs.addEventListener(document.getElementById('sfToolbarHideButton-{{ token }}'), 'click', function () { + Sfjs.addEventListener(document.getElementById('sfToolbarHideButton-{{ token }}'), 'click', function (event) { event.preventDefault(); var p = this.parentNode; From 070800359e6901af225055c5c7b960ab38d0ff5d Mon Sep 17 00:00:00 2001 From: Maxime Steinhausser Date: Wed, 30 Nov 2016 20:06:05 +0100 Subject: [PATCH 11/18] [Form] Remove unused var cloner property --- .../Form/Extension/DataCollector/FormDataCollector.php | 2 ++ .../Form/Extension/DataCollector/FormDataExtractor.php | 5 ----- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/Symfony/Component/Form/Extension/DataCollector/FormDataCollector.php b/src/Symfony/Component/Form/Extension/DataCollector/FormDataCollector.php index b72a5739f843d..624632f3b2ce8 100644 --- a/src/Symfony/Component/Form/Extension/DataCollector/FormDataCollector.php +++ b/src/Symfony/Component/Form/Extension/DataCollector/FormDataCollector.php @@ -17,8 +17,10 @@ use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\DataCollector\DataCollector; use Symfony\Component\HttpKernel\DataCollector\Util\ValueExporter; +use Symfony\Component\Validator\ConstraintViolationInterface; use Symfony\Component\VarDumper\Caster\Caster; use Symfony\Component\VarDumper\Caster\ClassStub; +use Symfony\Component\VarDumper\Cloner\ClonerInterface; use Symfony\Component\VarDumper\Cloner\Data; use Symfony\Component\VarDumper\Cloner\Stub; use Symfony\Component\VarDumper\Cloner\VarCloner; diff --git a/src/Symfony/Component/Form/Extension/DataCollector/FormDataExtractor.php b/src/Symfony/Component/Form/Extension/DataCollector/FormDataExtractor.php index a09b87fa1c330..de6fac41231de 100644 --- a/src/Symfony/Component/Form/Extension/DataCollector/FormDataExtractor.php +++ b/src/Symfony/Component/Form/Extension/DataCollector/FormDataExtractor.php @@ -23,11 +23,6 @@ */ class FormDataExtractor implements FormDataExtractorInterface { - /** - * @var VarCloner - */ - private $cloner; - /** * Constructs a new data extractor. */ From b8e77792bc0c838650e3d9a3dec20fbc0fe86684 Mon Sep 17 00:00:00 2001 From: Yonel Ceruto Date: Wed, 30 Nov 2016 17:57:42 -0500 Subject: [PATCH 12/18] Grouping FrameworkBundle upgrade notes --- UPGRADE-3.2.md | 74 ++++++++++++++++++++++++-------------------------- UPGRADE-4.0.md | 72 ++++++++++++++++++++++++------------------------ 2 files changed, 71 insertions(+), 75 deletions(-) diff --git a/UPGRADE-3.2.md b/UPGRADE-3.2.md index f4b043c754c66..a26ed257739ab 100644 --- a/UPGRADE-3.2.md +++ b/UPGRADE-3.2.md @@ -6,28 +6,6 @@ BrowserKit * Client HTTP user agent has been changed to 'Symfony BrowserKit' (was 'Symfony2 BrowserKit' before). -FrameworkBundle ---------------- - - * The `doctrine/annotations` dependency has been removed; require it via `composer - require doctrine/annotations` if you are using annotations in your project - * The `symfony/security-core` and `symfony/security-csrf` dependencies have - been removed; require them via `composer require symfony/security-core - symfony/security-csrf` if you depend on them and don't already depend on - `symfony/symfony` - * The `symfony/templating` dependency has been removed; require it via `composer - require symfony/templating` if you depend on it and don't already depend on - `symfony/symfony` - * The `symfony/translation` dependency has been removed; require it via `composer - require symfony/translation` if you depend on it and don't already depend on - `symfony/symfony` - * The `symfony/asset` dependency has been removed; require it via `composer - require symfony/asset` if you depend on it and don't already depend on - `symfony/symfony` - * The `Resources/public/images/*` files have been removed. - * The `Resources/public/css/*.css` files have been removed (they are now inlined - in TwigBundle). - Console ------- @@ -72,25 +50,26 @@ Form FrameworkBundle --------------- + * The `doctrine/annotations` dependency has been removed; require it via `composer + require doctrine/annotations` if you are using annotations in your project + * The `symfony/security-core` and `symfony/security-csrf` dependencies have + been removed; require them via `composer require symfony/security-core + symfony/security-csrf` if you depend on them and don't already depend on + `symfony/symfony` + * The `symfony/templating` dependency has been removed; require it via `composer + require symfony/templating` if you depend on it and don't already depend on + `symfony/symfony` + * The `symfony/translation` dependency has been removed; require it via `composer + require symfony/translation` if you depend on it and don't already depend on + `symfony/symfony` + * The `symfony/asset` dependency has been removed; require it via `composer + require symfony/asset` if you depend on it and don't already depend on + `symfony/symfony` + * The `Resources/public/images/*` files have been removed. + * The `Resources/public/css/*.css` files have been removed (they are now inlined + in TwigBundle). * The service `serializer.mapping.cache.doctrine.apc` is deprecated. APCu should now be automatically used when available. - -HttpKernel ----------- - - * `DataCollector::varToString()` is deprecated and will be removed in Symfony - 4.0. Use the `cloneVar()` method instead. - - * Surrogate name in a `Surrogate-Capability` HTTP request header has been changed to 'symfony'. - - Before: - ``` - Surrogate-Capability: symfony2="ESI/1.0" - ``` - - After: - ``` - Surrogate-Capability: symfony="ESI/1.0" ``` HttpFoundation @@ -116,6 +95,23 @@ HttpFoundation - `isInvalid`/`isSuccessful`/`isRedirection`/`isClientError`/`isServerError` - `isOk`/`isForbidden`/`isNotFound`/`isRedirect`/`isEmpty` +HttpKernel +---------- + + * `DataCollector::varToString()` is deprecated and will be removed in Symfony + 4.0. Use the `cloneVar()` method instead. + + * Surrogate name in a `Surrogate-Capability` HTTP request header has been changed to 'symfony'. + + Before: + ``` + Surrogate-Capability: symfony2="ESI/1.0" + ``` + + After: + ``` + Surrogate-Capability: symfony="ESI/1.0" + TwigBridge ---------- diff --git a/UPGRADE-4.0.md b/UPGRADE-4.0.md index 483b7e6ed1447..d018136932b56 100644 --- a/UPGRADE-4.0.md +++ b/UPGRADE-4.0.md @@ -182,6 +182,42 @@ TwigBridge * The possibility to inject the Form Twig Renderer into the form extension has been removed. Inject it into the `TwigRendererEngine` instead. +Validator +--------- + + * The `DateTimeValidator::PATTERN` constant was removed. + + * `Tests\Constraints\AbstractConstraintValidatorTest` has been removed in + favor of `Test\ConstraintValidatorTestCase`. + + Before: + + ```php + // ... + use Symfony\Component\Validator\Tests\Constraints\AbstractConstraintValidatorTest; + + class MyCustomValidatorTest extends AbstractConstraintValidatorTest + { + // ... + } + ``` + + After: + + ```php + // ... + use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; + + class MyCustomValidatorTest extends ConstraintValidatorTestCase + { + // ... + } + ``` + + * The default value of the strict option of the `Choice` Constraint has been + changed to `true` as of 4.0. If you need the the previous behaviour ensure to + set the option to `false`. + Yaml ---- @@ -273,39 +309,3 @@ Yaml the `!php/object` tag. * Duplicate mapping keys lead to a `ParseException`. - -Validator ---------- - - * The `DateTimeValidator::PATTERN` constant was removed. - - * `Tests\Constraints\AbstractConstraintValidatorTest` has been removed in - favor of `Test\ConstraintValidatorTestCase`. - - Before: - - ```php - // ... - use Symfony\Component\Validator\Tests\Constraints\AbstractConstraintValidatorTest; - - class MyCustomValidatorTest extends AbstractConstraintValidatorTest - { - // ... - } - ``` - - After: - - ```php - // ... - use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; - - class MyCustomValidatorTest extends ConstraintValidatorTestCase - { - // ... - } - ``` - - * The default value of the strict option of the `Choice` Constraint has been - changed to `true` as of 4.0. If you need the the previous behaviour ensure to - set the option to `false`. From 27de65aa144c089935ba6f318550237ae1508b9c Mon Sep 17 00:00:00 2001 From: Maxime Steinhausser Date: Tue, 29 Nov 2016 20:46:44 +0100 Subject: [PATCH 13/18] [Serializer] Fix argument object denormalization --- .../Normalizer/AbstractNormalizer.php | 6 ++- .../DenormalizerDecoratorSerializer.php | 43 ------------------- .../Tests/Normalizer/ObjectNormalizerTest.php | 5 +-- .../Serializer/Tests/SerializerTest.php | 29 +++++++++++++ 4 files changed, 36 insertions(+), 47 deletions(-) delete mode 100644 src/Symfony/Component/Serializer/Tests/Fixtures/DenormalizerDecoratorSerializer.php diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php index 82f6e1e2a8f39..cd46b5b97d20c 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php @@ -13,6 +13,7 @@ use Symfony\Component\Serializer\Exception\CircularReferenceException; use Symfony\Component\Serializer\Exception\InvalidArgumentException; +use Symfony\Component\Serializer\Exception\LogicException; use Symfony\Component\Serializer\Exception\RuntimeException; use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface; use Symfony\Component\Serializer\Mapping\AttributeMetadataInterface; @@ -336,8 +337,11 @@ protected function instantiateObject(array &$data, $class, array &$context, \Ref $parameterData = $data[$key]; try { if (null !== $constructorParameter->getClass()) { + if (!$this->serializer instanceof DenormalizerInterface) { + throw new LogicException(sprintf('Cannot create an instance of %s from serialized data because the serializer inject in "%s" is not a denormalizer', $constructorParameter->getClass(), static::class)); + } $parameterClass = $constructorParameter->getClass()->getName(); - $parameterData = $this->serializer->deserialize($parameterData, $parameterClass, $format, $context); + $parameterData = $this->serializer->denormalize($parameterData, $parameterClass, $format, $context); } } catch (\ReflectionException $e) { throw new RuntimeException(sprintf('Could not determine the class of the parameter "%s".', $key), 0, $e); diff --git a/src/Symfony/Component/Serializer/Tests/Fixtures/DenormalizerDecoratorSerializer.php b/src/Symfony/Component/Serializer/Tests/Fixtures/DenormalizerDecoratorSerializer.php deleted file mode 100644 index 06d36c4bd85ca..0000000000000 --- a/src/Symfony/Component/Serializer/Tests/Fixtures/DenormalizerDecoratorSerializer.php +++ /dev/null @@ -1,43 +0,0 @@ - - */ -class DenormalizerDecoratorSerializer implements SerializerInterface -{ - private $normalizer; - - /** - * @param NormalizerInterface|DenormalizerInterface $normalizer - */ - public function __construct($normalizer) - { - if (false === $normalizer instanceof NormalizerInterface && false === $normalizer instanceof DenormalizerInterface) { - throw new \InvalidArgumentException(); - } - - $this->normalizer = $normalizer; - } - - /** - * {@inheritdoc} - */ - public function serialize($data, $format, array $context = array()) - { - return $this->normalizer->normalize($data, $format, $context); - } - - /** - * {@inheritdoc} - */ - public function deserialize($data, $type, $format, array $context = array()) - { - return $this->normalizer->denormalize($data, $type, $format, $context); - } -} diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php index 161145bd97497..e1d3a905ecf73 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php @@ -23,7 +23,6 @@ use Symfony\Component\Serializer\SerializerInterface; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; use Symfony\Component\Serializer\Tests\Fixtures\CircularReferenceDummy; -use Symfony\Component\Serializer\Tests\Fixtures\DenormalizerDecoratorSerializer; use Symfony\Component\Serializer\Tests\Fixtures\MaxDepthDummy; use Symfony\Component\Serializer\Tests\Fixtures\SiblingHolder; use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader; @@ -171,7 +170,7 @@ public function testConstructorWithObjectTypeHintDenormalize() ); $normalizer = new ObjectNormalizer(); - $serializer = new DenormalizerDecoratorSerializer($normalizer); + $serializer = new Serializer(array($normalizer)); $normalizer->setSerializer($serializer); $obj = $normalizer->denormalize($data, DummyWithConstructorObject::class); @@ -197,7 +196,7 @@ public function testConstructorWithUnknownObjectTypeHintDenormalize() ); $normalizer = new ObjectNormalizer(); - $serializer = new DenormalizerDecoratorSerializer($normalizer); + $serializer = new Serializer(array($normalizer)); $normalizer->setSerializer($serializer); $normalizer->denormalize($data, DummyWithConstructorInexistingObject::class); diff --git a/src/Symfony/Component/Serializer/Tests/SerializerTest.php b/src/Symfony/Component/Serializer/Tests/SerializerTest.php index 362357864c463..ff7820c0d07e4 100644 --- a/src/Symfony/Component/Serializer/Tests/SerializerTest.php +++ b/src/Symfony/Component/Serializer/Tests/SerializerTest.php @@ -336,6 +336,15 @@ public function testDenormalizerAware() new Serializer(array($denormalizerAware)); } + + public function testDeserializeObjectConstructorWithObjectTypeHint() + { + $jsonData = '{"bar":{"value":"baz"}}'; + + $serializer = new Serializer(array(new ObjectNormalizer()), array('json' => new JsonEncoder())); + + $this->assertEquals(new Foo(new Bar('baz')), $serializer->deserialize($jsonData, Foo::class, 'json')); + } } class Model @@ -381,3 +390,23 @@ public function toArray() return array('title' => $this->title, 'numbers' => $this->numbers); } } + +class Foo +{ + private $bar; + + public function __construct(Bar $bar) + { + $this->bar = $bar; + } +} + +class Bar +{ + private $value; + + public function __construct($value) + { + $this->value = $value; + } +} From 10947739995b315ab17c7db670ff0089a747f7ac Mon Sep 17 00:00:00 2001 From: Arthur de Moulins Date: Fri, 2 Dec 2016 16:57:47 +0100 Subject: [PATCH 14/18] inject project root path into twig filesystem loader --- .../DependencyInjection/Compiler/ExtensionPass.php | 6 +++++- .../Bundle/TwigBundle/Loader/FilesystemLoader.php | 9 +++++---- src/Symfony/Bundle/TwigBundle/Resources/config/twig.xml | 5 +++-- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/ExtensionPass.php b/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/ExtensionPass.php index 603cbe8e22ef1..dbad1b653c1dd 100644 --- a/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/ExtensionPass.php +++ b/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/ExtensionPass.php @@ -64,9 +64,13 @@ public function process(ContainerBuilder $container) $container->getDefinition('twig.extension.debug')->addTag('twig.extension'); } + $composerRootDir = $this->getComposerRootDir($container->getParameter('kernel.root_dir')); + $loader = $container->getDefinition('twig.loader.filesystem'); + $loader->replaceArgument(2, $composerRootDir); + if (!$container->has('templating')) { $loader = $container->getDefinition('twig.loader.native_filesystem'); - $loader->replaceArgument(1, $this->getComposerRootDir($container->getParameter('kernel.root_dir'))); + $loader->replaceArgument(1, $composerRootDir); $loader->addTag('twig.loader'); $loader->setMethodCalls($container->getDefinition('twig.loader.filesystem')->getMethodCalls()); diff --git a/src/Symfony/Bundle/TwigBundle/Loader/FilesystemLoader.php b/src/Symfony/Bundle/TwigBundle/Loader/FilesystemLoader.php index 53fe300e29a62..0a9ac7a3ca19a 100644 --- a/src/Symfony/Bundle/TwigBundle/Loader/FilesystemLoader.php +++ b/src/Symfony/Bundle/TwigBundle/Loader/FilesystemLoader.php @@ -29,12 +29,13 @@ class FilesystemLoader extends \Twig_Loader_Filesystem /** * Constructor. * - * @param FileLocatorInterface $locator A FileLocatorInterface instance - * @param TemplateNameParserInterface $parser A TemplateNameParserInterface instance + * @param FileLocatorInterface $locator A FileLocatorInterface instance + * @param TemplateNameParserInterface $parser A TemplateNameParserInterface instance + * @param string|null $rootPath The root path common to all relative paths (null for getcwd()) */ - public function __construct(FileLocatorInterface $locator, TemplateNameParserInterface $parser) + public function __construct(FileLocatorInterface $locator, TemplateNameParserInterface $parser, $rootPath = null) { - parent::__construct(array()); + parent::__construct(array(), $rootPath); $this->locator = $locator; $this->parser = $parser; diff --git a/src/Symfony/Bundle/TwigBundle/Resources/config/twig.xml b/src/Symfony/Bundle/TwigBundle/Resources/config/twig.xml index c82cbafbeb953..06af08d15ba7a 100644 --- a/src/Symfony/Bundle/TwigBundle/Resources/config/twig.xml +++ b/src/Symfony/Bundle/TwigBundle/Resources/config/twig.xml @@ -46,12 +46,13 @@ - + + @@ -87,7 +88,7 @@ - + %kernel.root_dir% %kernel.charset% From adc4a26971749d31d23f8a5f2ec80243231cb421 Mon Sep 17 00:00:00 2001 From: Gabriel Moreira Date: Sun, 4 Dec 2016 10:01:09 +0100 Subject: [PATCH 15/18] [HttpKernel] Fixed RequestDataCollector handling of null header values. --- .../Component/HttpKernel/DataCollector/RequestDataCollector.php | 2 +- .../HttpKernel/Tests/DataCollector/RequestDataCollectorTest.php | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php index deafe10745732..0f2dc9b6c6084 100644 --- a/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php +++ b/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php @@ -125,7 +125,7 @@ public function collect(Request $request, Response $response, \Exception $except continue; } if ('request_headers' === $key || 'response_headers' === $key) { - $value = array_map(function ($v) { return isset($v[1]) ? $v : $v[0]; }, $value); + $value = array_map(function ($v) { return isset($v[0]) && !isset($v[1]) ? $v[0] : $v; }, $value); } if ('request_server' !== $key && 'request_cookies' !== $key) { $this->data[$key] = array_map(array($this, 'cloneVar'), $value); diff --git a/src/Symfony/Component/HttpKernel/Tests/DataCollector/RequestDataCollectorTest.php b/src/Symfony/Component/HttpKernel/Tests/DataCollector/RequestDataCollectorTest.php index 0e4cfe0d6ba91..a8b4e1ce2e7c9 100644 --- a/src/Symfony/Component/HttpKernel/Tests/DataCollector/RequestDataCollectorTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/DataCollector/RequestDataCollectorTest.php @@ -234,6 +234,7 @@ protected function createResponse() $response = new Response(); $response->setStatusCode(200); $response->headers->set('Content-Type', 'application/json'); + $response->headers->set('X-Foo-Bar', null); $response->headers->setCookie(new Cookie('foo', 'bar', 1, '/foo', 'localhost', true, true)); $response->headers->setCookie(new Cookie('bar', 'foo', new \DateTime('@946684800'))); $response->headers->setCookie(new Cookie('bazz', 'foo', '2000-12-12')); From 164a20c8523bf390118e654ef08081c65ab3619d Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 5 Dec 2016 15:31:21 +0100 Subject: [PATCH 16/18] [Form] Fix FormDataCollector --- .../DataCollector/FormDataCollector.php | 32 ++++++++----------- 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/src/Symfony/Component/Form/Extension/DataCollector/FormDataCollector.php b/src/Symfony/Component/Form/Extension/DataCollector/FormDataCollector.php index 624632f3b2ce8..a0f5a7835ad31 100644 --- a/src/Symfony/Component/Form/Extension/DataCollector/FormDataCollector.php +++ b/src/Symfony/Component/Form/Extension/DataCollector/FormDataCollector.php @@ -209,9 +209,7 @@ public function collectViewVariables(FormView $view) */ public function buildPreliminaryFormTree(FormInterface $form) { - $this->data['forms'][$form->getName()] = array(); - - $this->recursiveBuildPreliminaryFormTree($form, $this->data['forms'][$form->getName()], $this->data['forms_by_hash']); + $this->data['forms'][$form->getName()] = &$this->recursiveBuildPreliminaryFormTree($form, $this->data['forms_by_hash']); } /** @@ -219,9 +217,7 @@ public function buildPreliminaryFormTree(FormInterface $form) */ public function buildFinalFormTree(FormInterface $form, FormView $view) { - $this->data['forms'][$form->getName()] = array(); - - $this->recursiveBuildFinalFormTree($form, $view, $this->data['forms'][$form->getName()], $this->data['forms_by_hash']); + $this->data['forms'][$form->getName()] = &$this->recursiveBuildFinalFormTree($form, $view, $this->data['forms_by_hash']); } /** @@ -354,26 +350,25 @@ protected function cloneVar($var, $isClass = false) return $cache = $this->cloner->cloneVar($var); } - private function recursiveBuildPreliminaryFormTree(FormInterface $form, &$output, array &$outputByHash) + private function &recursiveBuildPreliminaryFormTree(FormInterface $form, array &$outputByHash) { $hash = spl_object_hash($form); + $output = &$outputByHash[$hash]; $output = isset($this->dataByForm[$hash]) ? $this->dataByForm[$hash] : array(); - $outputByHash[$hash] = &$output; - $output['children'] = array(); foreach ($form as $name => $child) { - $output['children'][$name] = array(); - - $this->recursiveBuildPreliminaryFormTree($child, $output['children'][$name], $outputByHash); + $output['children'][$name] = &$this->recursiveBuildPreliminaryFormTree($child, $outputByHash); } + + return $output; } - private function recursiveBuildFinalFormTree(FormInterface $form = null, FormView $view, &$output, array &$outputByHash) + private function &recursiveBuildFinalFormTree(FormInterface $form = null, FormView $view, array &$outputByHash) { $viewHash = spl_object_hash($view); $formHash = null; @@ -386,6 +381,9 @@ private function recursiveBuildFinalFormTree(FormInterface $form = null, FormVie // corresponding FormInterface instance for its view in a different way $formHash = $this->formsByView[$viewHash]; } + if (null !== $formHash) { + $output = &$outputByHash[$formHash]; + } $output = isset($this->dataByView[$viewHash]) ? $this->dataByView[$viewHash] @@ -398,8 +396,6 @@ private function recursiveBuildFinalFormTree(FormInterface $form = null, FormVie ? $this->dataByForm[$formHash] : array() ); - - $outputByHash[$formHash] = &$output; } $output['children'] = array(); @@ -411,9 +407,9 @@ private function recursiveBuildFinalFormTree(FormInterface $form = null, FormVie ? $form->get($name) : null; - $output['children'][$name] = array(); - - $this->recursiveBuildFinalFormTree($childForm, $childView, $output['children'][$name], $outputByHash); + $output['children'][$name] = &$this->recursiveBuildFinalFormTree($childForm, $childView, $outputByHash); } + + return $output; } } From 50400c45eedfd748a953dd39e6ce782fd9f22422 Mon Sep 17 00:00:00 2001 From: Adam Prager Date: Mon, 5 Dec 2016 18:24:28 +0100 Subject: [PATCH 17/18] [Form] Add failing test for data collector bug --- .../DataCollector/FormDataCollector.php | 2 +- .../DataCollector/FormDataCollectorTest.php | 108 ++++++++++++++++++ 2 files changed, 109 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/Form/Extension/DataCollector/FormDataCollector.php b/src/Symfony/Component/Form/Extension/DataCollector/FormDataCollector.php index a0f5a7835ad31..a4b4dcfcc866d 100644 --- a/src/Symfony/Component/Form/Extension/DataCollector/FormDataCollector.php +++ b/src/Symfony/Component/Form/Extension/DataCollector/FormDataCollector.php @@ -254,7 +254,7 @@ public function serialize() case 'resolved_options': case 'default_data': case 'submitted_data': - if ($v) { + if ($v && is_array($v)) { $form[$k] = array_map($cloneVar, $v); } break; diff --git a/src/Symfony/Component/Form/Tests/Extension/DataCollector/FormDataCollectorTest.php b/src/Symfony/Component/Form/Tests/Extension/DataCollector/FormDataCollectorTest.php index 0fddf14dc57bc..ff75cff20a726 100644 --- a/src/Symfony/Component/Form/Tests/Extension/DataCollector/FormDataCollectorTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/DataCollector/FormDataCollectorTest.php @@ -342,6 +342,114 @@ public function testBuildFinalFormTree() ), $this->dataCollector->getData()); } + public function testSerializeWithFormAddedMultipleTimes() + { + $form1 = $this->createForm('form1'); + $form2 = $this->createForm('form2'); + $child1 = $this->createForm('child1'); + + $form1View = new FormView(); + $form2View = new FormView(); + $child1View = new FormView(); + $child1View->vars['is_selected'] = function ($choice, array $values) { + return in_array($choice, $values, true); + }; + + $form1->add($child1); + $form2->add($child1); + + $form1View->children['child1'] = $child1View; + $form2View->children['child1'] = $child1View; + + $this->dataExtractor->expects($this->at(0)) + ->method('extractConfiguration') + ->with($form1) + ->will($this->returnValue(array('config' => 'foo'))); + $this->dataExtractor->expects($this->at(1)) + ->method('extractConfiguration') + ->with($child1) + ->will($this->returnValue(array('config' => 'bar'))); + + $this->dataExtractor->expects($this->at(2)) + ->method('extractDefaultData') + ->with($form1) + ->will($this->returnValue(array('default_data' => 'foo'))); + $this->dataExtractor->expects($this->at(3)) + ->method('extractDefaultData') + ->with($child1) + ->will($this->returnValue(array('default_data' => 'bar'))); + + $this->dataExtractor->expects($this->at(4)) + ->method('extractSubmittedData') + ->with($form1) + ->will($this->returnValue(array('submitted_data' => 'foo'))); + $this->dataExtractor->expects($this->at(5)) + ->method('extractSubmittedData') + ->with($child1) + ->will($this->returnValue(array('submitted_data' => 'bar'))); + + $this->dataExtractor->expects($this->at(6)) + ->method('extractViewVariables') + ->with($form1View) + ->will($this->returnValue(array('view_vars' => 'foo'))); + + $this->dataExtractor->expects($this->at(7)) + ->method('extractViewVariables') + ->with($child1View) + ->will($this->returnValue(array('view_vars' => $child1View->vars))); + + $this->dataExtractor->expects($this->at(8)) + ->method('extractConfiguration') + ->with($form2) + ->will($this->returnValue(array('config' => 'foo'))); + $this->dataExtractor->expects($this->at(9)) + ->method('extractConfiguration') + ->with($child1) + ->will($this->returnValue(array('config' => 'bar'))); + + $this->dataExtractor->expects($this->at(10)) + ->method('extractDefaultData') + ->with($form2) + ->will($this->returnValue(array('default_data' => 'foo'))); + $this->dataExtractor->expects($this->at(11)) + ->method('extractDefaultData') + ->with($child1) + ->will($this->returnValue(array('default_data' => 'bar'))); + + $this->dataExtractor->expects($this->at(12)) + ->method('extractSubmittedData') + ->with($form2) + ->will($this->returnValue(array('submitted_data' => 'foo'))); + $this->dataExtractor->expects($this->at(13)) + ->method('extractSubmittedData') + ->with($child1) + ->will($this->returnValue(array('submitted_data' => 'bar'))); + + $this->dataExtractor->expects($this->at(14)) + ->method('extractViewVariables') + ->with($form2View) + ->will($this->returnValue(array('view_vars' => 'foo'))); + + $this->dataExtractor->expects($this->at(15)) + ->method('extractViewVariables') + ->with($child1View) + ->will($this->returnValue(array('view_vars' => $child1View->vars))); + + $this->dataCollector->collectConfiguration($form1); + $this->dataCollector->collectDefaultData($form1); + $this->dataCollector->collectSubmittedData($form1); + $this->dataCollector->collectViewVariables($form1View); + $this->dataCollector->buildFinalFormTree($form1, $form1View); + + $this->dataCollector->collectConfiguration($form2); + $this->dataCollector->collectDefaultData($form2); + $this->dataCollector->collectSubmittedData($form2); + $this->dataCollector->collectViewVariables($form2View); + $this->dataCollector->buildFinalFormTree($form2, $form2View); + + $this->dataCollector->serialize(); + } + public function testFinalFormReliesOnFormViewStructure() { $this->form->add($child1 = $this->createForm('first')); From 54d66c9c8d0802de1a59a33058c14b3b7d89d857 Mon Sep 17 00:00:00 2001 From: Adam Prager Date: Tue, 6 Dec 2016 16:23:39 +0100 Subject: [PATCH 18/18] [Workflow] Added an entered event --- .../Component/Workflow/Tests/WorkflowTest.php | 4 ++++ src/Symfony/Component/Workflow/Workflow.php | 20 +++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/src/Symfony/Component/Workflow/Tests/WorkflowTest.php b/src/Symfony/Component/Workflow/Tests/WorkflowTest.php index cb5256f412933..81b11afeebd6d 100644 --- a/src/Symfony/Component/Workflow/Tests/WorkflowTest.php +++ b/src/Symfony/Component/Workflow/Tests/WorkflowTest.php @@ -173,6 +173,10 @@ public function testApplyWithEventDispatcher() 'workflow.workflow_name.enter', 'workflow.workflow_name.enter.b', 'workflow.workflow_name.enter.c', + 'workflow.entered', + 'workflow.workflow_name.entered', + 'workflow.workflow_name.entered.b', + 'workflow.workflow_name.entered.c', // Following events are fired because of announce() method 'workflow.guard', 'workflow.workflow_name.guard', diff --git a/src/Symfony/Component/Workflow/Workflow.php b/src/Symfony/Component/Workflow/Workflow.php index b66cc85f51d90..863166cc559c4 100644 --- a/src/Symfony/Component/Workflow/Workflow.php +++ b/src/Symfony/Component/Workflow/Workflow.php @@ -128,6 +128,8 @@ public function apply($subject, $transitionName) $this->markingStore->setMarking($subject, $marking); + $this->entered($subject, $transition, $marking); + $this->announce($subject, $transition, $marking); return $marking; @@ -238,6 +240,24 @@ private function enter($subject, Transition $transition, Marking $marking) } } + private function entered($subject, Transition $transition, Marking $marking) + { + if (null !== $this->dispatcher) { + $event = new Event($subject, $marking, $transition); + + $this->dispatcher->dispatch('workflow.entered', $event); + $this->dispatcher->dispatch(sprintf('workflow.%s.entered', $this->name), $event); + } + + foreach ($transition->getTos() as $place) { + $marking->mark($place); + + if (null !== $this->dispatcher) { + $this->dispatcher->dispatch(sprintf('workflow.%s.entered.%s', $this->name, $place), $event); + } + } + } + private function announce($subject, Transition $initialTransition, Marking $marking) { if (null === $this->dispatcher) {