diff --git a/.github/rm-invalid-lowest-lock-files.php b/.github/rm-invalid-lowest-lock-files.php index 3105942a09..c036fd356f 100644 --- a/.github/rm-invalid-lowest-lock-files.php +++ b/.github/rm-invalid-lowest-lock-files.php @@ -104,8 +104,7 @@ foreach ($composerJsons as list($dir, $lockedPackages)) { } } -if (!$referencedCommits || (isset($_SERVER['TRAVIS_PULL_REQUEST']) && 'false' !== $_SERVER['TRAVIS_PULL_REQUEST'])) { - // cached commits cannot be stale for PRs +if (!$referencedCommits) { return; } diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000000..d211dd419d --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,83 @@ +Code of Conduct +=============== + +Our Pledge +---------- + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnic origin, gender identity and expression, level of +experience, education, socio-economic status, nationality, personal appearance, +religion, or sexual identity and orientation. + +Our Standards +------------- + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +Our Responsibilities +-------------------- + +[CoC Active Response Ensurers, or CARE][1], are responsible for clarifying the +standards of acceptable behavior and are expected to take appropriate and fair +corrective action in response to any instances of unacceptable behavior. + +CARE team members have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, or to ban temporarily or permanently any +contributor for other behaviors that they deem inappropriate, threatening, +offensive, or harmful. + +Scope +----- + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by CARE team members. + +Enforcement +----------- + +Instances of abusive, harassing, or otherwise unacceptable behavior +[may be reported][2] by contacting the [CARE team members][1]. +All complaints will be reviewed and investigated and will result in a response +that is deemed necessary and appropriate to the circumstances. The CARE team is +obligated to maintain confidentiality with regard to the reporter of an +incident. Further details of specific enforcement policies may be posted +separately. + +CARE team members who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by the +[core team][3]. + +Attribution +----------- + +This Code of Conduct is adapted from the [Contributor Covenant version 1.4][4]. + +[1]: https://symfony.com/doc/current/contributing/code_of_conduct/care_team.html +[2]: https://symfony.com/doc/current/contributing/code_of_conduct/reporting_guidelines.html +[3]: https://symfony.com/doc/current/contributing/code/core_team.html +[4]: https://www.contributor-covenant.org/version/1/4/code-of-conduct.html diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php index 327166cf65..0106b2ac3b 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php @@ -22,6 +22,7 @@ use Symfony\Component\Form\Form; use Symfony\Component\Lock\Lock; use Symfony\Component\Lock\Store\SemaphoreStore; use Symfony\Component\Messenger\MessageBusInterface; +use Symfony\Component\PropertyInfo\PropertyInfoExtractorInterface; use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface; use Symfony\Component\Serializer\Serializer; use Symfony\Component\Translation\Translator; @@ -833,7 +834,7 @@ class Configuration implements ConfigurationInterface ->children() ->arrayNode('property_info') ->info('Property info configuration') - ->canBeEnabled() + ->{!class_exists(FullStack::class) && interface_exists(PropertyInfoExtractorInterface::class) ? 'canBeDisabled' : 'canBeEnabled'}() ->end() ->end() ; diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd b/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd index 2dbe54b5d9..f4832939bc 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd @@ -260,6 +260,8 @@ + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php index 9d3639b621..9c67da6642 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php @@ -218,7 +218,7 @@ class ConfigurationTest extends TestCase 'throw_exception_on_invalid_index' => false, ), 'property_info' => array( - 'enabled' => false, + 'enabled' => !class_exists(FullStack::class), ), 'router' => array( 'enabled' => false, diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php index b3dc2cc163..2557d19549 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php @@ -1155,12 +1155,6 @@ abstract class FrameworkExtensionTest extends TestCase $this->assertFalse($container->hasDefinition('serializer')); } - public function testPropertyInfoDisabled() - { - $container = $this->createContainerFromFile('default_config'); - $this->assertFalse($container->has('property_info')); - } - public function testPropertyInfoEnabled() { $container = $this->createContainerFromFile('property_info'); diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php index af2c517744..4aea6a70fe 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php @@ -58,9 +58,7 @@ class MainConfiguration implements ConfigurationInterface return false; }) ->then(function ($v) { - $v['access_decision_manager'] = array( - 'strategy' => AccessDecisionManager::STRATEGY_AFFIRMATIVE, - ); + $v['access_decision_manager']['strategy'] = AccessDecisionManager::STRATEGY_AFFIRMATIVE; return $v; }) diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php index 8434a451d5..2f977d397d 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php @@ -438,11 +438,22 @@ abstract class CompleteConfigurationTest extends TestCase /** * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException - * @expectedExceptionMessage "strategy" and "service" cannot be used together. + * @expectedExceptionMessage Invalid configuration for path "security.access_decision_manager": "strategy" and "service" cannot be used together. */ public function testAccessDecisionManagerServiceAndStrategyCannotBeUsedAtTheSameTime() { - $container = $this->getContainer('access_decision_manager_service_and_strategy'); + $this->getContainer('access_decision_manager_service_and_strategy'); + } + + public function testAccessDecisionManagerOptionsAreNotOverriddenByImplicitStrategy() + { + $container = $this->getContainer('access_decision_manager_customized_config'); + + $accessDecisionManagerDefinition = $container->getDefinition('security.access.decision_manager'); + + $this->assertSame(AccessDecisionManager::STRATEGY_AFFIRMATIVE, $accessDecisionManagerDefinition->getArgument(1)); + $this->assertTrue($accessDecisionManagerDefinition->getArgument(2)); + $this->assertFalse($accessDecisionManagerDefinition->getArgument(3)); } /** diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/access_decision_manager_customized_config.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/access_decision_manager_customized_config.php new file mode 100644 index 0000000000..cd3cc12000 --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/access_decision_manager_customized_config.php @@ -0,0 +1,20 @@ +loadFromExtension('security', array( + 'access_decision_manager' => array( + 'allow_if_all_abstain' => true, + 'allow_if_equal_granted_denied' => false, + ), + 'providers' => array( + 'default' => array( + 'memory' => array( + 'users' => array( + 'foo' => array('password' => 'foo', 'roles' => 'ROLE_USER'), + ), + ), + ), + ), + 'firewalls' => array( + 'simple' => array('pattern' => '/login', 'security' => false), + ), +)); diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/access_decision_manager_customized_config.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/access_decision_manager_customized_config.xml new file mode 100644 index 0000000000..22123dfa21 --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/access_decision_manager_customized_config.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/access_decision_manager_customized_config.yml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/access_decision_manager_customized_config.yml new file mode 100644 index 0000000000..a8d044f1de --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/access_decision_manager_customized_config.yml @@ -0,0 +1,11 @@ +security: + access_decision_manager: + allow_if_all_abstain: true + allow_if_equal_granted_denied: false + providers: + default: + memory: + users: + foo: { password: foo, roles: ROLE_USER } + firewalls: + simple: { pattern: /login, security: false } diff --git a/src/Symfony/Bundle/WebServerBundle/Command/ServerStatusCommand.php b/src/Symfony/Bundle/WebServerBundle/Command/ServerStatusCommand.php index 5ee29905fd..938c5ca24a 100644 --- a/src/Symfony/Bundle/WebServerBundle/Command/ServerStatusCommand.php +++ b/src/Symfony/Bundle/WebServerBundle/Command/ServerStatusCommand.php @@ -40,7 +40,7 @@ class ServerStatusCommand extends Command new InputOption('pidfile', null, InputOption::VALUE_REQUIRED, 'PID file'), new InputOption('filter', null, InputOption::VALUE_REQUIRED, 'The value to display (one of port, host, or address)'), )) - ->setDescription('Outputs the status of the local web server for the given address') + ->setDescription('Outputs the status of the local web server') ->setHelp(<<<'EOF' %command.name% shows the details of the given local web server, such as the address and port where it is listening to: diff --git a/src/Symfony/Component/Console/Command/Command.php b/src/Symfony/Component/Console/Command/Command.php index 3bf2f3a533..656ab14c98 100644 --- a/src/Symfony/Component/Console/Command/Command.php +++ b/src/Symfony/Component/Console/Command/Command.php @@ -361,10 +361,12 @@ class Command /** * Adds an argument. * - * @param string $name The argument name - * @param int $mode The argument mode: InputArgument::REQUIRED or InputArgument::OPTIONAL - * @param string $description A description text - * @param mixed $default The default value (for InputArgument::OPTIONAL mode only) + * @param string $name The argument name + * @param int|null $mode The argument mode: self::REQUIRED or self::OPTIONAL + * @param string $description A description text + * @param string|string[]|null $default The default value (for self::OPTIONAL mode only) + * + * @throws InvalidArgumentException When argument mode is not valid * * @return $this */ @@ -378,11 +380,13 @@ class Command /** * Adds an option. * - * @param string $name The option name - * @param string $shortcut The shortcut (can be null) - * @param int $mode The option mode: One of the InputOption::VALUE_* constants - * @param string $description A description text - * @param mixed $default The default value (must be null for InputOption::VALUE_NONE) + * @param string $name The option name + * @param string|array $shortcut The shortcuts, can be null, a string of shortcuts delimited by | or an array of shortcuts + * @param int|null $mode The option mode: One of the VALUE_* constants + * @param string $description A description text + * @param string|string[]|bool|null $default The default value (must be null for self::VALUE_NONE) + * + * @throws InvalidArgumentException If option mode is invalid or incompatible * * @return $this */ diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php index 764b1d6c7f..706a4bbed2 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php @@ -489,10 +489,6 @@ EOF; } } - if (false !== strpos($this->dumpLiteralClass($this->dumpValue($definition->getClass())), '$')) { - return false; - } - return true; } @@ -559,18 +555,16 @@ EOF; $return = array(); if ($class = $definition->getClass()) { - $class = $this->container->resolveEnvPlaceholders($class); + $class = $class instanceof Parameter ? '%'.$class.'%' : $this->container->resolveEnvPlaceholders($class); $return[] = sprintf(0 === strpos($class, '%') ? '@return object A %1$s instance' : '@return \%s', ltrim($class, '\\')); } elseif ($definition->getFactory()) { $factory = $definition->getFactory(); if (\is_string($factory)) { $return[] = sprintf('@return object An instance returned by %s()', $factory); } elseif (\is_array($factory) && (\is_string($factory[0]) || $factory[0] instanceof Definition || $factory[0] instanceof Reference)) { - if (\is_string($factory[0]) || $factory[0] instanceof Reference) { - $return[] = sprintf('@return object An instance returned by %s::%s()', (string) $factory[0], $factory[1]); - } elseif ($factory[0] instanceof Definition) { - $return[] = sprintf('@return object An instance returned by %s::%s()', $factory[0]->getClass(), $factory[1]); - } + $class = $factory[0] instanceof Definition ? $factory[0]->getClass() : (string) $factory[0]; + $class = $class instanceof Parameter ? '%'.$class.'%' : $this->container->resolveEnvPlaceholders($class); + $return[] = sprintf('@return object An instance returned by %s::%s()', $class, $factory[1]); } } @@ -671,7 +665,7 @@ EOF; if (\is_array($argument)) { $hasSelfRef = $this->addInlineVariables($head, $tail, $id, $argument, $forConstructor) || $hasSelfRef; } elseif ($argument instanceof Reference) { - $hasSelfRef = $this->addInlineReference($head, $tail, $id, $argument, $forConstructor) || $hasSelfRef; + $hasSelfRef = $this->addInlineReference($head, $id, $argument, $forConstructor) || $hasSelfRef; } elseif ($argument instanceof Definition) { $hasSelfRef = $this->addInlineService($head, $tail, $id, $argument, $forConstructor) || $hasSelfRef; } @@ -680,37 +674,31 @@ EOF; return $hasSelfRef; } - private function addInlineReference(string &$head, string &$tail, string $id, string $targetId, bool $forConstructor): bool + private function addInlineReference(string &$code, string $id, string $targetId, bool $forConstructor): bool { + $hasSelfRef = isset($this->circularReferences[$id][$targetId]); + if ('service_container' === $targetId || isset($this->referenceVariables[$targetId])) { - return isset($this->circularReferences[$id][$targetId]); + return $hasSelfRef; } list($callCount, $behavior) = $this->serviceCalls[$targetId]; - if (2 > $callCount && (!$forConstructor || !isset($this->circularReferences[$id][$targetId]))) { - return isset($this->circularReferences[$id][$targetId]); + if (2 > $callCount && (!$hasSelfRef || !$forConstructor)) { + return $hasSelfRef; } $name = $this->getNextVariableName(); $this->referenceVariables[$targetId] = new Variable($name); $reference = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE >= $behavior ? new Reference($targetId, $behavior) : null; - $code = sprintf(" \$%s = %s;\n", $name, $this->getServiceCall($targetId, $reference)); + $code .= sprintf(" \$%s = %s;\n", $name, $this->getServiceCall($targetId, $reference)); - if (!isset($this->circularReferences[$id][$targetId])) { - $head .= $code; - - return false; + if (!$hasSelfRef || !$forConstructor) { + return $hasSelfRef; } - if (!$forConstructor) { - $tail .= $code; - - return true; - } - - $head .= $code.sprintf(<<<'EOTXT' + $code .= sprintf(<<<'EOTXT' if (isset($this->%s['%s'])) { return $this->%1$s['%2$s']; @@ -733,7 +721,7 @@ EOTXT $arguments = array($definition->getArguments(), $definition->getFactory()); - if (2 > $this->inlinedDefinitions[$definition] && !$definition->getMethodCalls() && !$definition->getProperties() && !$definition->getConfigurator() && false === strpos($this->dumpValue($definition->getClass()), '$')) { + if (2 > $this->inlinedDefinitions[$definition] && !$definition->getMethodCalls() && !$definition->getProperties() && !$definition->getConfigurator()) { return $this->addInlineVariables($head, $tail, $id, $arguments, $forConstructor); } @@ -741,9 +729,13 @@ EOTXT $this->definitionVariables[$definition] = new Variable($name); $code = ''; - $hasSelfRef = $this->addInlineVariables($code, $tail, $id, $arguments, $forConstructor); + if ($forConstructor) { + $hasSelfRef = $this->addInlineVariables($code, $tail, $id, $arguments, $forConstructor); + } else { + $hasSelfRef = $this->addInlineVariables($code, $code, $id, $arguments, $forConstructor); + } $code .= $this->addNewInstance($definition, '$'.$name, ' = ', $id); - $hasSelfRef ? $tail .= ('' !== $tail ? "\n" : '').$code : $head .= ('' !== $head ? "\n" : '').$code; + $hasSelfRef && !$forConstructor ? $tail .= ('' !== $tail ? "\n" : '').$code : $head .= ('' !== $head ? "\n" : '').$code; $code = ''; $arguments = array($definition->getProperties(), $definition->getMethodCalls(), $definition->getConfigurator()); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php index c992d110ae..8cb11568f2 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php @@ -903,6 +903,28 @@ class PhpDumperTest extends TestCase $this->assertEquals((object) array('p2' => (object) array('p3' => (object) array())), $container->get('foo')->bClone); } + public function testInlineSelfRef() + { + $container = new ContainerBuilder(); + + $bar = (new Definition('App\Bar')) + ->setProperty('foo', new Reference('App\Foo')); + + $baz = (new Definition('App\Baz')) + ->setProperty('bar', $bar) + ->addArgument($bar); + + $container->register('App\Foo') + ->setPublic(true) + ->addArgument($baz); + + $passConfig = $container->getCompiler()->getPassConfig(); + $container->compile(); + + $dumper = new PhpDumper($container); + $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_inline_self_ref.php', $dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_Inline_Self_Ref'))); + } + public function testHotPathOptimizations() { $container = include self::$fixturesPath.'/containers/container_inline_requires.php'; @@ -980,6 +1002,18 @@ class PhpDumperTest extends TestCase $this->assertEquals((object) array('foo' => (object) array(123)), $container->get('bar')); } + public function testAdawsonContainer() + { + $container = new ContainerBuilder(); + $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('services_adawson.yml'); + $container->compile(); + + $dumper = new PhpDumper($container); + $dump = $dumper->dump(); + $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_adawson.php', $dumper->dump()); + } + /** * This test checks the trigger of a deprecation note and should not be removed in major releases. * diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container19.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container19.php index 823a77f534..300a5cc769 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container19.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/containers/container19.php @@ -7,9 +7,12 @@ require_once __DIR__.'/../includes/classes.php'; $container = new ContainerBuilder(); +$container->setParameter('env(FOO)', 'Bar\FaooClass'); +$container->setParameter('foo', '%env(FOO)%'); + $container - ->register('service_from_anonymous_factory', 'Bar\FooClass') - ->setFactory(array(new Definition('Bar\FooClass'), 'getInstance')) + ->register('service_from_anonymous_factory', '%foo%') + ->setFactory(array(new Definition('%foo%'), 'getInstance')) ->setPublic(true) ; diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services19.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services19.php index 673c9d54bb..73e1c4cc1c 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services19.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services19.php @@ -26,6 +26,8 @@ class ProjectServiceContainer extends Container public function __construct() { + $this->parameters = $this->getDefaultParameters(); + $this->services = $this->privates = array(); $this->methodMap = array( 'service_from_anonymous_factory' => 'getServiceFromAnonymousFactoryService', @@ -62,11 +64,11 @@ class ProjectServiceContainer extends Container /** * Gets the public 'service_from_anonymous_factory' shared service. * - * @return \Bar\FooClass + * @return object A %env(FOO)% instance */ protected function getServiceFromAnonymousFactoryService() { - return $this->services['service_from_anonymous_factory'] = (new \Bar\FooClass())->getInstance(); + return $this->services['service_from_anonymous_factory'] = (new ${($_ = $this->getEnv('FOO')) && false ?: "_"}())->getInstance(); } /** @@ -82,4 +84,80 @@ class ProjectServiceContainer extends Container return $instance; } + + public function getParameter($name) + { + $name = (string) $name; + + if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) { + throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name)); + } + if (isset($this->loadedDynamicParameters[$name])) { + return $this->loadedDynamicParameters[$name] ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name); + } + + return $this->parameters[$name]; + } + + public function hasParameter($name) + { + $name = (string) $name; + + return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters); + } + + public function setParameter($name, $value) + { + throw new LogicException('Impossible to call set() on a frozen ParameterBag.'); + } + + public function getParameterBag() + { + if (null === $this->parameterBag) { + $parameters = $this->parameters; + foreach ($this->loadedDynamicParameters as $name => $loaded) { + $parameters[$name] = $loaded ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name); + } + $this->parameterBag = new FrozenParameterBag($parameters); + } + + return $this->parameterBag; + } + + private $loadedDynamicParameters = array( + 'foo' => false, + ); + private $dynamicParameters = array(); + + /** + * Computes a dynamic parameter. + * + * @param string The name of the dynamic parameter to load + * + * @return mixed The value of the dynamic parameter + * + * @throws InvalidArgumentException When the dynamic parameter does not exist + */ + private function getDynamicParameter($name) + { + switch ($name) { + case 'foo': $value = $this->getEnv('FOO'); break; + default: throw new InvalidArgumentException(sprintf('The dynamic parameter "%s" must be defined.', $name)); + } + $this->loadedDynamicParameters[$name] = true; + + return $this->dynamicParameters[$name] = $value; + } + + /** + * Gets the default parameters. + * + * @return array An array of the default parameters + */ + protected function getDefaultParameters() + { + return array( + 'env(FOO)' => 'Bar\\FaooClass', + ); + } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_adawson.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_adawson.php new file mode 100644 index 0000000000..f2d4cebdd5 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_adawson.php @@ -0,0 +1,120 @@ +services = $this->privates = array(); + $this->methodMap = array( + 'App\\Bus' => 'getBusService', + 'App\\Db' => 'getDbService', + ); + + $this->aliases = array(); + } + + public function reset() + { + $this->privates = array(); + parent::reset(); + } + + public function compile() + { + throw new LogicException('You cannot compile a dumped container that was already compiled.'); + } + + public function isCompiled() + { + return true; + } + + public function getRemovedIds() + { + return array( + 'App\\Handler1' => true, + 'App\\Handler2' => true, + 'App\\Processor' => true, + 'App\\Registry' => true, + 'App\\Schema' => true, + 'Psr\\Container\\ContainerInterface' => true, + 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, + ); + } + + /** + * Gets the public 'App\Bus' shared service. + * + * @return \App\Bus + */ + protected function getBusService() + { + $a = ($this->services['App\Db'] ?? $this->getDbService()); + + $this->services['App\Bus'] = $instance = new \App\Bus($a); + + $b = ($this->privates['App\Schema'] ?? $this->getSchemaService()); + + $d = new \App\Registry(); + + $d->processor = array(0 => $a, 1 => $instance); + $c = new \App\Processor($d, $a); + + $instance->handler1 = new \App\Handler1($a, $b, $c); + $instance->handler2 = new \App\Handler2($a, $b, $c); + + return $instance; + } + + /** + * Gets the public 'App\Db' shared service. + * + * @return \App\Db + */ + protected function getDbService() + { + $this->services['App\Db'] = $instance = new \App\Db(); + + $instance->schema = ($this->privates['App\Schema'] ?? $this->getSchemaService()); + + return $instance; + } + + /** + * Gets the private 'App\Schema' shared service. + * + * @return \App\Schema + */ + protected function getSchemaService() + { + $a = ($this->services['App\Db'] ?? $this->getDbService()); + + if (isset($this->privates['App\Schema'])) { + return $this->privates['App\Schema']; + } + + return $this->privates['App\Schema'] = new \App\Schema($a); + } +} diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_inline_self_ref.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_inline_self_ref.php new file mode 100644 index 0000000000..d28ee816c6 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_inline_self_ref.php @@ -0,0 +1,79 @@ +services = $this->privates = array(); + $this->methodMap = array( + 'App\\Foo' => 'getFooService', + ); + + $this->aliases = array(); + } + + public function reset() + { + $this->privates = array(); + parent::reset(); + } + + public function compile() + { + throw new LogicException('You cannot compile a dumped container that was already compiled.'); + } + + public function isCompiled() + { + return true; + } + + public function getRemovedIds() + { + return array( + 'Psr\\Container\\ContainerInterface' => true, + 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, + ); + } + + /** + * Gets the public 'App\Foo' shared service. + * + * @return \App\Foo + */ + protected function getFooService() + { + $b = new \App\Bar(); + $a = new \App\Baz($b); + + $this->services['App\Foo'] = $instance = new \App\Foo($a); + + $b->foo = $instance; + + $a->bar = $b; + + return $instance; + } +} diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_adawson.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_adawson.yml new file mode 100644 index 0000000000..2a26f38a83 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_adawson.yml @@ -0,0 +1,28 @@ +services: + App\Db: + public: true + properties: + schema: '@App\Schema' + + App\Bus: + public: true + arguments: ['@App\Db'] + properties: + handler1: '@App\Handler1' + handler2: '@App\Handler2' + + App\Handler1: + ['@App\Db', '@App\Schema', '@App\Processor'] + + App\Handler2: + ['@App\Db', '@App\Schema', '@App\Processor'] + + App\Processor: + ['@App\Registry', '@App\Db'] + + App\Registry: + properties: + processor: ['@App\Db', '@App\Bus'] + + App\Schema: + arguments: ['@App\Db'] diff --git a/src/Symfony/Component/EventDispatcher/Tests/GenericEventTest.php b/src/Symfony/Component/EventDispatcher/Tests/GenericEventTest.php index 9cf68c987f..b63f69df14 100644 --- a/src/Symfony/Component/EventDispatcher/Tests/GenericEventTest.php +++ b/src/Symfony/Component/EventDispatcher/Tests/GenericEventTest.php @@ -31,8 +31,6 @@ class GenericEventTest extends TestCase */ protected function setUp() { - parent::setUp(); - $this->subject = new \stdClass(); $this->event = new GenericEvent($this->subject, array('name' => 'Event')); } @@ -44,8 +42,6 @@ class GenericEventTest extends TestCase { $this->subject = null; $this->event = null; - - parent::tearDown(); } public function testConstruct() diff --git a/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToRfc3339Transformer.php b/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToRfc3339Transformer.php index 45cc5c641b..1838113f9d 100644 --- a/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToRfc3339Transformer.php +++ b/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToRfc3339Transformer.php @@ -68,7 +68,7 @@ class DateTimeToRfc3339Transformer extends BaseDateTimeTransformer return; } - if (!preg_match('/^(\d{4})-(\d{2})-(\d{2})T\d{2}:\d{2}(?::\d{2})?(?:\.\d)?(?:Z|(?:(?:\+|-)\d{2}:\d{2}))$/', $rfc3339, $matches)) { + if (!preg_match('/^(\d{4})-(\d{2})-(\d{2})T\d{2}:\d{2}(?::\d{2})?(?:\.\d+)?(?:Z|(?:(?:\+|-)\d{2}:\d{2}))$/', $rfc3339, $matches)) { throw new TransformationFailedException(sprintf('The date "%s" is not a valid date.', $rfc3339)); } diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToRfc3339TransformerTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToRfc3339TransformerTest.php index f21c01b7cc..053cb62864 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToRfc3339TransformerTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToRfc3339TransformerTest.php @@ -67,6 +67,7 @@ class DateTimeToRfc3339TransformerTest extends TestCase array('UTC', 'UTC', '2010-02-03 04:05:00 UTC', '2010-02-03T04:05Z'), array('America/New_York', 'Asia/Hong_Kong', '2010-02-03 04:05:00 America/New_York', '2010-02-03T17:05+08:00'), array('Europe/Amsterdam', 'Europe/Amsterdam', '2013-08-21 10:30:00 Europe/Amsterdam', '2013-08-21T08:30:00Z'), + array('UTC', 'UTC', '2018-10-03T10:00:00.000Z', '2018-10-03T10:00:00.000Z'), )); } diff --git a/src/Symfony/Component/HttpFoundation/StreamedResponse.php b/src/Symfony/Component/HttpFoundation/StreamedResponse.php index 40f785c866..06d053eadd 100644 --- a/src/Symfony/Component/HttpFoundation/StreamedResponse.php +++ b/src/Symfony/Component/HttpFoundation/StreamedResponse.php @@ -17,7 +17,7 @@ namespace Symfony\Component\HttpFoundation; * A StreamedResponse uses a callback for its content. * * The callback should use the standard PHP functions like echo - * to stream the response back to the client. The flush() method + * to stream the response back to the client. The flush() function * can also be used if needed. * * @see flush() diff --git a/src/Symfony/Component/Lock/Factory.php b/src/Symfony/Component/Lock/Factory.php index e9fdde97e8..f35ad6d8ef 100644 --- a/src/Symfony/Component/Lock/Factory.php +++ b/src/Symfony/Component/Lock/Factory.php @@ -36,9 +36,9 @@ class Factory implements LoggerAwareInterface /** * Creates a lock for the given resource. * - * @param string $resource The resource to lock - * @param float $ttl Maximum expected lock duration in seconds - * @param bool $autoRelease Whether to automatically release the lock or not when the lock instance is destroyed + * @param string $resource The resource to lock + * @param float|null $ttl Maximum expected lock duration in seconds + * @param bool $autoRelease Whether to automatically release the lock or not when the lock instance is destroyed * * @return Lock */ diff --git a/src/Symfony/Component/Process/Pipes/WindowsPipes.php b/src/Symfony/Component/Process/Pipes/WindowsPipes.php index 9240102a38..7130589cec 100644 --- a/src/Symfony/Component/Process/Pipes/WindowsPipes.php +++ b/src/Symfony/Component/Process/Pipes/WindowsPipes.php @@ -28,6 +28,7 @@ class WindowsPipes extends AbstractPipes { private $files = array(); private $fileHandles = array(); + private $lockHandles = array(); private $readBytes = array( Process::STDOUT => 0, Process::STDERR => 0, @@ -47,31 +48,33 @@ class WindowsPipes extends AbstractPipes Process::STDOUT => Process::OUT, Process::STDERR => Process::ERR, ); - $tmpCheck = false; $tmpDir = sys_get_temp_dir(); $lastError = 'unknown reason'; set_error_handler(function ($type, $msg) use (&$lastError) { $lastError = $msg; }); for ($i = 0;; ++$i) { foreach ($pipes as $pipe => $name) { $file = sprintf('%s\\sf_proc_%02X.%s', $tmpDir, $i, $name); - if (file_exists($file) && !unlink($file)) { - continue 2; - } - $h = fopen($file, 'xb'); - if (!$h) { - $error = $lastError; - if ($tmpCheck || $tmpCheck = unlink(tempnam(false, 'sf_check_'))) { - continue; - } + + if (!$h = fopen($file.'.lock', 'w')) { restore_error_handler(); - throw new RuntimeException(sprintf('A temporary file could not be opened to write the process output: %s', $error)); + throw new RuntimeException(sprintf('A temporary file could not be opened to write the process output: %s', $lastError)); } - if (!$h || !$this->fileHandles[$pipe] = fopen($file, 'rb')) { + if (!flock($h, LOCK_EX | LOCK_NB)) { continue 2; } - if (isset($this->files[$pipe])) { - unlink($this->files[$pipe]); + if (isset($this->lockHandles[$pipe])) { + flock($this->lockHandles[$pipe], LOCK_UN); + fclose($this->lockHandles[$pipe]); } + $this->lockHandles[$pipe] = $h; + + if (!fclose(fopen($file, 'w')) || !$h = fopen($file, 'r')) { + flock($this->lockHandles[$pipe], LOCK_UN); + fclose($this->lockHandles[$pipe]); + unset($this->lockHandles[$pipe]); + continue 2; + } + $this->fileHandles[$pipe] = $h; $this->files[$pipe] = $file; } break; @@ -85,7 +88,6 @@ class WindowsPipes extends AbstractPipes public function __destruct() { $this->close(); - $this->removeFiles(); } /** @@ -145,8 +147,11 @@ class WindowsPipes extends AbstractPipes $read[$type] = $data; } if ($close) { + ftruncate($fileHandle, 0); fclose($fileHandle); - unset($this->fileHandles[$type]); + flock($this->lockHandles[$type], LOCK_UN); + fclose($this->lockHandles[$type]); + unset($this->fileHandles[$type], $this->lockHandles[$type]); } } @@ -175,22 +180,12 @@ class WindowsPipes extends AbstractPipes public function close() { parent::close(); - foreach ($this->fileHandles as $handle) { + foreach ($this->fileHandles as $type => $handle) { + ftruncate($handle, 0); fclose($handle); + flock($this->lockHandles[$type], LOCK_UN); + fclose($this->lockHandles[$type]); } - $this->fileHandles = array(); - } - - /** - * Removes temporary files. - */ - private function removeFiles() - { - foreach ($this->files as $filename) { - if (file_exists($filename)) { - @unlink($filename); - } - } - $this->files = array(); + $this->fileHandles = $this->lockHandles = array(); } } diff --git a/src/Symfony/Component/Security/Http/Firewall/ContextListener.php b/src/Symfony/Component/Security/Http/Firewall/ContextListener.php index 4508a44583..cce915a045 100644 --- a/src/Symfony/Component/Security/Http/Firewall/ContextListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/ContextListener.php @@ -161,6 +161,7 @@ class ContextListener implements ListenerInterface } $userNotFoundByProvider = false; + $userDeauthenticated = false; foreach ($this->userProviders as $provider) { if (!$provider instanceof UserProviderInterface) { @@ -169,17 +170,26 @@ class ContextListener implements ListenerInterface try { $refreshedUser = $provider->refreshUser($user); - $token->setUser($refreshedUser); + $newToken = unserialize(serialize($token)); + $newToken->setUser($refreshedUser); // tokens can be deauthenticated if the user has been changed. - if (!$token->isAuthenticated()) { - if (null !== $this->logger) { - $this->logger->debug('Token was deauthenticated after trying to refresh it.', array('username' => $refreshedUser->getUsername(), 'provider' => \get_class($provider))); + if (!$newToken->isAuthenticated()) { + if ($this->logoutOnUserChange) { + $userDeauthenticated = true; + + if (null !== $this->logger) { + $this->logger->debug('Cannot refresh token because user has changed.', array('username' => $refreshedUser->getUsername(), 'provider' => \get_class($provider))); + } + + continue; } return null; } + $token->setUser($refreshedUser); + if (null !== $this->logger) { $context = array('provider' => \get_class($provider), 'username' => $refreshedUser->getUsername()); @@ -205,6 +215,14 @@ class ContextListener implements ListenerInterface } } + if ($userDeauthenticated) { + if (null !== $this->logger) { + $this->logger->debug('Token was deauthenticated after trying to refresh it.'); + } + + return null; + } + if ($userNotFoundByProvider) { return null; } diff --git a/src/Symfony/Component/Security/Http/Tests/Firewall/ContextListenerTest.php b/src/Symfony/Component/Security/Http/Tests/Firewall/ContextListenerTest.php index f38ddfdf97..02acb2490e 100644 --- a/src/Symfony/Component/Security/Http/Tests/Firewall/ContextListenerTest.php +++ b/src/Symfony/Component/Security/Http/Tests/Firewall/ContextListenerTest.php @@ -260,6 +260,15 @@ class ContextListenerTest extends TestCase $this->assertNull($tokenStorage->getToken()); } + public function testIfTokenIsNotDeauthenticated() + { + $tokenStorage = new TokenStorage(); + $badRefreshedUser = new User('foobar', 'baz'); + $goodRefreshedUser = new User('foobar', 'bar'); + $this->handleEventWithPreviousSession($tokenStorage, array(new SupportingUserProvider($badRefreshedUser), new SupportingUserProvider($goodRefreshedUser)), $goodRefreshedUser, true); + $this->assertSame($goodRefreshedUser, $tokenStorage->getToken()->getUser()); + } + public function testTryAllUserProvidersUntilASupportingUserProviderIsFound() { $tokenStorage = new TokenStorage(); diff --git a/src/Symfony/Component/Validator/Context/ExecutionContextInterface.php b/src/Symfony/Component/Validator/Context/ExecutionContextInterface.php index ac79ab4dc7..544c82f6c0 100644 --- a/src/Symfony/Component/Validator/Context/ExecutionContextInterface.php +++ b/src/Symfony/Component/Validator/Context/ExecutionContextInterface.php @@ -97,7 +97,7 @@ interface ExecutionContextInterface * { * $validator = $this->context->getValidator(); * - * $violations = $validator->validateValue($value, new Length(array('min' => 3))); + * $violations = $validator->validate($value, new Length(array('min' => 3))); * * if (count($violations) > 0) { * // ... diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.lb.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.lb.xlf index 4a06dbd45b..bdbc9da09c 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.lb.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.lb.xlf @@ -318,6 +318,10 @@ Error Feeler + + This is not a valid UUID. + Dëst ass keng gëlteg UUID. + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.pl.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.pl.xlf index f33274e6e6..51a76eefc1 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.pl.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.pl.xlf @@ -318,6 +318,10 @@ Error Błąd + + This is not a valid UUID. + To nie jest poprawne UUID. +