8000 Merge branch '3.4' · symfony/symfony@b27c965 · GitHub
[go: up one dir, main page]

Skip to content

Commit b27c965

Browse files
committed
Merge branch '3.4'
* 3.4: [Yaml] Deprecate tags using colon better errors when security deps are missing
2 parents 257b2c9 + 978eca9 commit b27c965

File tree

17 files changed

+349
-18
lines changed

17 files changed

+349
-18
lines changed

UPGRADE-3.4.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,24 @@ Validator
158158
Yaml
159159
----
160160

161+
* using the `!php/object:` tag is deprecated and won't be supported in 4.0. Use
162+
the `!php/object` tag (without the colon) instead.
163+
164+
* using the `!php/const:` tag is deprecated and won't be supported in 4.0. Use
165+
the `!php/const` tag (without the colon) instead.
166+
167+
Before:
168+
169+
```yml
170+
!php/const:PHP_INT_MAX
171+
```
172+
173+
After:
174+
175+
```yml
176+
!php/const PHP_INT_MAX
177+
```
178+
161179
* Support for the `!str` tag is deprecated, use the `!!str` tag instead.
162180

163181
* Using the non-specific tag `!` is deprecated and will have a different

UPGRADE-4.0.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -871,3 +871,21 @@ Yaml
871871

872872
* The behavior of the non-specific tag `!` is changed and now forces
873873
non-evaluating your values.
874+
875+
* The `!php/object:` tag was removed in favor of the `!php/object` tag (without
876+
the colon).
877+
878+
* The `!php/const:` tag was removed in favor of the `!php/const` tag (without
879+
the colon).
880+
881+
Before:
882+
883+
```yml
884+
!php/const:PHP_INT_MAX
885+
```
886+
887+
After:
888+
889+
```yml
890+
!php/const PHP_INT_MAX
891+
```
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler;
13+
14+
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
15+
use Symfony\Component\DependencyInjection\ContainerBuilder;
16+
use Symfony\Component\DependencyInjection\Exception\LogicException;
17+
18+
/**
19+
* @author Christian Flothmann <christian.flothmann@sensiolabs.de>
20+
*/
21+
class WorkflowGuardListenerPass implements CompilerPassInterface
22+
{
23+
/**
24+
* {@inheritdoc}
25+
*/
26+
public function process(ContainerBuilder $container)
27+
{
28+
if (!$container->hasParameter('workflow.has_guard_listeners')) {
29+
return;
30+
}
31+
32+
$container->getParameterBag()->remove('workflow.has_guard_listeners');
33+
34+
if (!$container->has('security.token_storage')) {
35+
throw new LogicException('The "security.token_storage" service is needed to be able to use the workflow guard listener.');
36+
}
37+
38+
if (!$container->has('security.authorization_checker')) {
39+
throw new LogicException('The "security.authorization_checker" service is needed to be able to use the workflow guard listener.');
40+
}
41+
42+
if (!$container->has('security.authentication.trust_resolver')) {
43+
throw new LogicException('The "security.authentication.trust_resolver" service is needed to be able to use the workflow guard listener.');
44+
}
45+
46+
if (!$container->has('security.role_hierarchy')) {
47+
throw new LogicException('The "security.role_hierarchy" service is needed to be able to use the workflow guard listener.');
48+
}
49+
}
50+
}

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface;
5151
use Symfony\Component\Routing\Loader\AnnotationDirectoryLoader;
5252
use Symfony\Component\Routing\Loader\AnnotationFileLoader;
53+
use Symfony\Component\Security\Core\Security;
5354
use Symfony\Component\Serializer\Encoder\CsvEncoder;
5455
use Symfony\Component\Serializer\Encoder\DecoderInterface;
5556
use Symfony\Component\Serializer\Encoder\EncoderInterface;
@@ -559,6 +560,10 @@ private function registerWorkflowConfiguration(array $workflows, ContainerBuilde
559560
throw new LogicException('Cannot guard workflows as the ExpressionLanguage component is not installed.');
560561
}
561562

563+
if (!class_exists(Security::class)) {
564+
throw new LogicException('Cannot guard workflows as the Security component is not installed.');
565+
}
566+
562567
$eventName = sprintf('workflow.%s.guard.%s', $name, $transitionName);
563568
$guard->addTag('kernel.event_listener', array('event' => $eventName, 'method' => 'onTransition'));
564569
$configuration[$eventName] = $config['guard'];
@@ -574,6 +579,7 @@ private function registerWorkflowConfiguration(array $workflows, ContainerBuilde
574579
));
575580

576581
$container->setDefinition(sprintf('%s.listener.guard', $workflowId), $guard);
582+
$container->setParameter('workflow.has_guard_listeners', true);
577583
}
578584
}
579585
}

src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddExpressionLanguageProvidersPass;
2525
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\ContainerBuilderDebugDumpPass;
2626
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\UnusedTagsPass;
27+
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\WorkflowGuardListenerPass;
2728
use Symfony\Component\Config\DependencyInjection\ConfigCachePass;
2829
use Symfony\Component\Console\DependencyInjection\AddConsoleCommandPass;
2930
use Symfony\Component\HttpKernel\DependencyInjection\AddCacheClearerPass;
@@ -103,6 +104,7 @@ public function build(ContainerBuilder $container)
103104
$container->addCompilerPass(new CachePoolClearerPass(), PassConfig::TYPE_AF 1241 TER_REMOVING);
104105
$container->addCompilerPass(new CachePoolPrunerPass(), PassConfig::TYPE_AFTER_REMOVING);
105106
$this->addCompilerPassIfExists($container, FormPass::class);
107+
$container->addCompilerPass(new WorkflowGuardListenerPass());
106108

107109
if ($container->getParameter('kernel.debug')) {
108110
$container->addCompilerPass(new AddDebugLogProcessorPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, -32);
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\WorkflowGuardListenerPass;
16+
use Symfony\Component\DependencyInjection\ContainerBuilder;
17+
use Symfony\Component\DependencyInjection\Exception\LogicException;
18+
use Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolverInterface;
19+
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
20+
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
21+
use Symfony\Component\Security\Core\Role\RoleHierarchy;
22+
use Symfony\Component\Workflow\EventListener\GuardListener;
23+
24+
class WorkflowGuardListenerPassTest extends TestCase
25+
{
26+
private $container;
27+
private $compilerPass;
28+
29+
protected function setUp()
30+
{
31+
$this->container = new ContainerBuilder();
32+
$this->container->register('foo.listener.guard', GuardListener::class);
33+
$this->container->register('bar.listener.guard', GuardListener::class);
34+
$this->compilerPass = new WorkflowGuardListenerPass();
35+
}
36+
37+
public function testListenersAreNotRemovedIfParameterIsNotSet()
38+
{
39+
$this->compilerPass->process($this->container);
40+
41+
$this->assertTrue($this->container->hasDefinition('foo.listener.guard'));
42+
$this->assertTrue($this->container->hasDefinition('bar.listener.guard'));
43+
}
44+
45+
public function testParameterIsRemovedWhenThePassIsProcessed()
46+
{
47+
$this->container->setParameter('workflow.has_guard_listeners', array('foo.listener.guard', 'bar.listener.guard'));
48+
49+
try {
50+
$this->compilerPass->process($this->container);
51+
} catch (LogicException $e) {
52+
// Here, we are not interested in the exception handling. This is tested further down.
53+
}
54+
55+
$this->assertFalse($this->container->hasParameter('workflow.has_guard_listeners'));
56+
}
57+
58+
public function testListenersAreNotRemovedIfAllDependenciesArePresent()
59+
{
60+
$this->container->setParameter('workflow.has_guard_listeners', array('foo.listener.guard', 'bar.listener.guard'));
61+
$this->container->register('security.token_storage', TokenStorageInterface::class);
62+
$this->container->register('security.authorization_checker', AuthorizationCheckerInterface::class);
63+
$this->container->register('security.authentication.trust_resolver', AuthenticationTrustResolverInterface::class);
64+
$this->container->register('security.role_hierarchy', RoleHierarchy::class);
65+
66+
$this->compilerPass->process($this->container);
67+
68+
$this->assertTrue($this->container->hasDefinition('foo.listener.guard'));
69+
$this->assertTrue($this->container->hasDefinition('bar.listener.guard'));
70+
}
71+
72+
/**
73+
* @expectedException \Symfony\Component\DependencyInjection\Exception\LogicException
74+
* @expectedExceptionMessage The "security.token_storage" service is needed to be able to use the workflow guard listener.
75+
*/
76+
public function testListenersAreRemovedIfTheTokenStorageServiceIsNotPresent()
77+
{
78+
$this->container->setParameter('workflow.has_guard_listeners', array('foo.listener.guard', 'bar.listener.guard'));
79+
$this->container->register('security.authorization_checker', AuthorizationCheckerInterface::class);
80+
$this->container->register('security.authentication.trust_resolver', AuthenticationTrustResolverInterface::class);
81+
$this->container->register('security.role_hierarchy', RoleHierarchy::class);
82+
83+
$this->compilerPass->process($this->container);
84+
}
85+
86+
/**
87+
* @expectedException \Symfony\Component\DependencyInjection\Exception\LogicException
88+
* @expectedExceptionMessage The "security.authorization_checker" service is needed to be able to use the workflow guard listener.
89+
*/
90+
public function testListenersAreRemovedIfTheAuthorizationCheckerServiceIsNotPresent()
91+
{
92+
$this->container->setParameter('workflow.has_guard_listeners', array('foo.listener.guard', 'bar.listener.guard'));
93+
$this->container->register('security.token_storage', TokenStorageInterface::class);
94+
$this->container->register('security.authentication.trust_resolver', AuthenticationTrustResolverInterface::class);
95+
$this->container->register('security.role_hierarchy', RoleHierarchy::class);
96+
97+
$this->compilerPass->process($this->container);
98+
}
99+
100+
/**
101+
* @expectedException \Symfony\Component\DependencyInjection\Exception\LogicException
102+
* @expectedExceptionMessage The "security.authentication.trust_resolver" service is needed to be able to use the workflow guard listener.
103+
*/
104+
public function testListenersAreRemovedIfTheAuthenticationTrustResolverServiceIsNotPresent()
105+
{
106+
$this->container->setParameter('workflow.has_guard_listeners', array('foo.listener.guard', 'bar.listener.guard'));
107+
$this->container->register('security.token_storage', TokenStorageInterface::class);
108+
$this->container->register('security.authorization_checker', AuthorizationCheckerInterface::class);
109+
$this->container->register('security.role_hierarchy', RoleHierarchy::class);
110+
111+
$this->compilerPass->process($this->container);
112+
}
113+
114+
/**
115+
* @expectedException \Symfony\Component\DependencyInjection\Exception\LogicException
116+
* @expectedExceptionMessage The "security.role_hierarchy" service is needed to be able to use the workflow guard listener.
117+
*/
118+
public function testListenersAreRemovedIfTheRoleHierarchyServiceIsNotPresent()
119+
{
120+
$this->container->setParameter('workflow.has_guard_listeners', array('foo.listener.guard', 'bar.listener.guard'));
121+
$this->container->register('security.token_storage', TokenStorageInterface::class);
122+
$this->container->register('security.authorization_checker', AuthorizationCheckerInterface::class);
123+
$this->container->register('security.authentication.trust_resolver', AuthenticationTrustResolverInterface::class);
124+
125+
$this->compilerPass->process($this->container);
126+
}
127+
}

src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services2.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ parameters:
55
- false
66
- 0
77
- 1000.3
8-
- !php/const:PHP_INT_MAX
8+
- !php/const PHP_INT_MAX
99
bar: foo
1010
escape: '@@escapeme'
1111
foo_bar: '@foo_bar'

src/Symfony/Component/DependencyInjection/composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
"conflict": {
3535
"symfony/config": "<3.4",
3636
"symfony/finder": "<3.4",
37+
"symfony/proxy-manager-bridge": "<3.4",
3738
"symfony/yaml": "<3.4"
3839
},
3940
"provide": {

src/Symfony/Component/Serializer/Tests/Encoder/YamlEncoderTest.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,9 @@ public function testContext()
6161
$obj = new \stdClass();
6262
$obj->bar = 2;
6363

64-
$this->assertEquals(" foo: !php/object:O:8:\"stdClass\":1:{s:3:\"bar\";i:2;}\n", $encoder->encode(array('foo' => $obj), 'yaml'));
64+
$this->assertEquals(" foo: !php/object 'O:8:\"stdClass\":1:{s:3:\"bar\";i:2;}'\n", $encoder->encode(array('foo' => $obj), 'yaml'));
6565
$this->assertEquals(' { foo: null }', $encoder->encode(array('foo' => $obj), 'yaml', array('yaml_inline' => 0, 'yaml_indent' => 2, 'yaml_flags' => 0)));
66-
$this->assertEquals(array('foo' => $obj), $encoder->decode('foo: !php/object:O:8:"stdClass":1:{s:3:"bar";i:2;}', 'yaml'));
67-
$this->assertEquals(array('foo' => null), $encoder->decode('foo: !php/object:O:8:"stdClass":1:{s:3:"bar";i:2;}', 'yaml', array('yaml_flags' => 0)));
66+
$this->assertEquals(array('foo' => $obj), $encoder->decode("foo: !php/object 'O:8:\"stdClass\":1:{s:3:\"bar\";i:2;}'", 'yaml'));
67+
$this->assertEquals(array('foo' => null), $encoder->decode("foo: !php/object 'O:8:\"stdClass\":1:{s:3:\"bar\";i:2;}'", 'yaml', array('yaml_flags' => 0)));
6868
}
6969
}

src/Symfony/Component/Validator/Tests/Mapping/Loader/mapping-with-constants.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@ Symfony\Component\Validator\Tests\Fixtures\Entity:
55
properties:
66
firstName:
77
- Range:
8-
max: !php/const:PHP_INT_MAX
8+
max: !php/const PHP_INT_MAX

src/Symfony/Component/Yaml/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@ CHANGELOG
3434
3.4.0
3535
-----
3636

37+
* Deprecated the `!php/object:` tag which will be replaced by the
38+
`!php/object` tag (without the colon) in 4.0.
39+
40+
* Deprecated the `!php/const:` tag which will be replaced by the
41+
`!php/const` tag (without the colon) in 4.0.
42+
3743
* Support for the `!str` tag is deprecated, use the `!!str` tag instead.
3844

3945
* Deprecated using the non-specific tag `!` as its behavior will change in 4.0.

src/Symfony/Component/Yaml/Inline.php

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ public static function dump($value, $flags = 0)
120120
}
121121

122122
if (Yaml::DUMP_OBJECT & F438 $flags) {
123-
return '!php/object:'.serialize($value);
123+
return '!php/object '.self::dump(serialize($value));
124124
}
125125

126126
if (Yaml::DUMP_OBJECT_AS_MAP & $flags && ($value instanceof \stdClass || $value instanceof \ArrayObject)) {
@@ -556,16 +556,42 @@ private static function evaluateScalar($scalar, $flags, $references = array())
556556
return substr($scalar, 2);
557557
case 0 === strpos($scalar, '!php/object:'):
558558
if (self::$objectSupport) {
559+
@trigger_error('The !php/object: tag to indicate dumped PHP objects is deprecated since version 3.4 and will be removed in 4.0. Use the !php/object (without the colon) tag instead.', E_USER_DEPRECATED);
560+
559561
return unserialize(substr($scalar, 12));
560562
}
561563

562564
if (self::$exceptionOnInvalidType) {
563565
throw new ParseException('Object support when parsing a YAML file has been disabled.');
564566
}
565567

568+
return;
569+
case 0 === strpos($scalar, '!!php/object:'):
570+
if (self::$objectSupport) {
571+
@trigger_error('The !!php/object: tag to indicate dumped PHP objects is deprecated since version 3.1 and will be removed in 4.0. Use the !php/object (without the colon) tag instead.', E_USER_DEPRECATED);
572+
573+
return unserialize(substr($scalar, 13));
574+
}
575+
576+
if (self::$exceptionOnInvalidType) {
577+
throw new ParseException('Object support when parsing a YAML file has been disabled.');
578+
}
579+
580+
return;
581+
case 0 === strpos($scalar, '!php/object'):
582+
if (self::$objectSupport) {
583+
return unserialize(self::parseScalar(substr($scalar, 12)));
584+
}
585+
586+
if (self::$exceptionOnInvalidType) {
587+
throw new ParseException('Object support when parsing a YAML file has been disabled.');
588+
}
589+
566590
return;
567591
case 0 === strpos($scalar, '!php/const:'):
568592
if (self::$constantSupport) {
593+
@trigger_error('The !php/const: tag to indicate dumped PHP constants is deprecated since version 3.4 and will be removed in 4.0. Use the !php/const (without the colon) tag instead.', E_USER_DEPRECATED);
594+
569595
if (defined($const = substr($scalar, 11))) {
570596
return constant($const);
571597
}
@@ -576,6 +602,19 @@ private static function evaluateScalar($scalar, $flags, $references = array())
576602
throw new ParseException(sprintf('The string "%s" could not be parsed as a constant. Have you forgotten to pass the "Yaml::PARSE_CONSTANT" flag to the parser?', $scalar));
577603
}
578604

605+
return;
606+
case 0 === strpos($scalar, '!php/const'):
607+
if (self::$constantSupport) {
608+
if (defined($const = self::parseScalar(substr($scalar, 11)))) {
609+
return constant($const);
610+
}
611+
612+
throw new ParseException(sprintf('The constant "%s" is not defined.', $const));
613+
}
614+
if (self::$exceptionOnInvalidType) {
615+
throw new ParseException(sprintf('The string "%s" could not be parsed as a constant. Have you forgotten to pass the "Yaml::PARSE_CONSTANT" flag to the parser?', $scalar));
616+
}
617+
579618
return;
580619
case 0 === strpos($scalar, '!!float '):
581620
return (float) substr($scalar, 8);

src/Symfony/Component/Yaml/Parser.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ private function doParse($value, $flags)
169169
$this->refs[$isRef] = end($data);
170170
}
171171
} elseif (
172-
self::preg_match('#^(?P<key>'.Inline::REGEX_QUOTED_STRING.'|(?:!?!php/const:)?(?:![^\s]++\s++)?[^ \'"\[\{!].*?) *\:(\s++(?P<value>.+))?$#u', rtrim($this->currentLine), $values)
172+
self::preg_match('#^(?P<key>(?:![^\s]++\s++)?(?:'.Inline::REGEX_QUOTED_STRING.'|(?:!?!php/const:)?[^ \'"\[\{!].*?)) *\:(\s++(?P<value>.+))?$#u', rtrim($this->currentLine), $values)
173173
&& (false === strpos($values['key'], ' #') || in_array($values['key'][0], array('"', "'")))
174174
) {
175175
if ($context && 'sequence' == $context) {

0 commit comments

Comments
 (0)
0