diff --git a/.travis.yml b/.travis.yml
index 4638be740549b..3f34da8c38d19 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -141,6 +141,12 @@ before_install:
(cd php-$MIN_PHP && ./configure --enable-sigchild --enable-pcntl && make -j2)
fi
+ - |
+ # Install vulcain
+ wget https://github.com/symfony/binary-utils/releases/download/v0.1/vulcain_0.1.3_Linux_x86_64.tar.gz -O - | tar xz
+ sudo mv vulcain /usr/local/bin
+ docker pull php:7.3-alpine
+
- |
# php.ini configuration
for PHP in $TRAVIS_PHP_VERSION $php_extra; do
@@ -260,6 +266,11 @@ install:
run_tests () {
set -e
export PHP=$1
+
+ if [[ !$deps && $PHP = 7.2 ]]; then
+ tfold src/Symfony/Component/HttpClient.h2push "$COMPOSER_UP symfony/contracts && docker run -it --rm -v $(pwd):/app -v $(phpenv which composer):/usr/local/bin/composer -v /usr/local/bin/vulcain:/usr/local/bin/vulcain -w /app php:7.3-alpine ./phpunit src/Symfony/Component/HttpClient/Tests/CurlHttpClientTest.php --filter testHttp2Push"
+ fi
+
if [[ $PHP != 7.4* && $PHP != $TRAVIS_PHP_VERSION && $TRAVIS_PULL_REQUEST != false ]]; then
echo -e "\\n\\e[33;1mIntermediate PHP version $PHP is skipped for pull requests.\\e[0m"
return
@@ -307,7 +318,9 @@ install:
fi
echo "$COMPONENTS" | parallel --gnu "tfold {} $PHPUNIT_X {}"
+
tfold src/Symfony/Component/Console.tty $PHPUNIT --group tty
+
if [[ $PHP = ${MIN_PHP%.*} ]]; then
export PHP=$MIN_PHP
tfold src/Symfony/Component/Process.sigchild SYMFONY_DEPRECATIONS_HELPER=weak php-$MIN_PHP/sapi/cli/php ./phpunit --colors=always src/Symfony/Component/Process/
diff --git a/CHANGELOG-4.4.md b/CHANGELOG-4.4.md
index 0f975d9e83dd5..64e77d4a4d718 100644
--- a/CHANGELOG-4.4.md
+++ b/CHANGELOG-4.4.md
@@ -7,6 +7,13 @@ in 4.4 minor versions.
To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash
To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v4.4.0...v4.4.1
+* 4.4.0 (2019-11-21)
+
+ * bug #34464 [Form] group constraints when calling the validator (nicolas-grekas)
+ * bug #34451 [DependencyInjection] Fix dumping multiple deprecated aliases (shyim)
+ * bug #34448 [Form] allow button names to start with uppercase letter (xabbuh)
+ * bug #34428 [Security] Fix best encoder not wired using migrate_from (chalasr)
+
* 4.4.0-RC1 (2019-11-17)
* bug #34419 [Cache] Disable igbinary on PHP >= 7.4 (nicolas-grekas)
diff --git a/CHANGELOG-5.0.md b/CHANGELOG-5.0.md
index 87ac1bdc29b58..920d9960fb944 100644
--- a/CHANGELOG-5.0.md
+++ b/CHANGELOG-5.0.md
@@ -7,6 +7,60 @@ in 5.0 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/v5.0.0...v5.0.1
+* 5.0.1 (2019-12-01)
+
+ * bug #34732 [DependencyInjection][Xml] Fix the attribute 'tag' is not allowed in 'bind' tag (tienvx)
+ * bug #34729 [DI] auto-register singly implemented interfaces by default (nicolas-grekas)
+ * bug #34728 [DI] fix overriding existing services with aliases for singly-implemented interfaces (nicolas-grekas)
+ * bug #34649 more robust initialization from request (dbu)
+ * bug #34715 [TwigBundle] remove service when base class is missing (xabbuh)
+ * bug #34600 [DoctrineBridge] do not depend on the QueryBuilder from the ORM (xabbuh)
+ * bug #34627 [Security/Http] call auth listeners/guards eagerly when they "support" the request (nicolas-grekas)
+ * bug #34671 [Security] Fix clearing remember-me cookie after deauthentication (chalasr)
+ * bug #34711 Fix the translation commands when a template contains a syntax error (fabpot)
+ * bug #34032 [Mime] Fixing multidimensional array structure with FormDataPart (jvahldick)
+ * bug #34697 [MonologBridge] Fix compatibility of ServerLogHandler with Monolog 2 (jderusse)
+ * bug #34560 [Config][ReflectionClassResource] Handle parameters with undefined constant as their default values (fancyweb)
+ * bug #34695 [Config] don't break on virtual stack frames in ClassExistenceResource (nicolas-grekas)
+ * bug #34716 [DependencyInjection] fix dumping number-like string parameters (xabbuh)
+ * bug #34558 [Console] Fix autocomplete multibyte input support (fancyweb)
+ * bug #34130 [Console] Fix commands description with numeric namespaces (fancyweb)
+ * bug #34562 [DI] Skip unknown method calls for factories in check types pass (fancyweb)
+ * bug #34677 [EventDispatcher] Better error reporting when arguments to dispatch() are swapped (rimas-kudelis)
+ * bug #33573 [TwigBridge] Add row_attr to all form themes (fancyweb)
+ * bug #34019 [Serializer] CsvEncoder::NO_HEADERS_KEY ignored when used in constructor (Dario Savella)
+ * bug #34083 [Form] Keep preferred_choices order for choice groups (vilius-g)
+ * bug #34091 [Debug] work around failing chdir() on Darwin (mary2501)
+ * bug #34305 [PhpUnitBridge] Read configuration CLI directive (ro0NL)
+ * bug #34490 [Serializer] Fix MetadataAwareNameConverter usage with string group (antograssiot)
+ * bug #34632 [Console] Fix trying to access array offset on value of type int (Tavafi)
+ * bug #34669 [HttpClient] turn exception into log when the request has no content-type (nicolas-grekas)
+ * bug #34662 [HttpKernel] Support typehint to deprecated FlattenException in controller (andrew-demb)
+ * bug #34619 Restores preview mode support for Html and Serializer error renderers (yceruto)
+ * bug #34636 [VarDumper] notice on potential undefined index (sylvainmetayer)
+ * bug #34668 [Cache] Make sure we get the correct number of values from redis::mget() (thePanz)
+ * bug #34621 [Routing] Continue supporting single colon in object route loaders (fancyweb)
+ * bug #34554 [HttpClient] Fix early cleanup of pushed HTTP/2 responses (lyrixx)
+ * bug #34607 [HttpKernel] Ability to define multiple kernel.reset tags (rmikalkenas)
+ * bug #34599 [Mailer][Mailchimp Bridge] Throwing undefined index _id when setting message id (monteiro)
+ * bug #34569 [Workflow] Apply the same logic of precedence between the apply() and the buildTransitionBlockerList() method (lyrixx)
+ * bug #34580 [HttpKernel] Don't cache "not-fresh" state (nicolas-grekas)
+ * bug #34577 [FrameworkBundle][Cache] Don't deep-merge cache pools configuration (alxndrbauer)
+ * bug #34515 [DependencyInjection] definitions are valid objects (xabbuh)
+ * bug #34536 [SecurityBundle] Don't require a user provider for the anonymous listener (chalasr)
+ * bug #34533 [Monolog Bridge] Fixed accessing static property as non static. (Sander-Toonen)
+ * bug #34502 [FrameworkBundle][ContainerLint] Keep "removing" compiler passes (fancyweb)
+ * bug #34552 [Dotenv] don't fail when referenced env var does not exist (xabbuh)
+ * bug #34546 [Serializer] Add DateTimeZoneNormalizer into Dependency Injection (jewome62)
+ * bug #34547 [Messenger] Error when specified default bus is not among the configured (vudaltsov)
+ * bug #34513 [Validator] remove return type declaration from __sleep() (xabbuh)
+ * bug #34551 [Security] SwitchUser is broken when the User Provider always returns a valid user (tucksaun)
+ * bug #34570 [FrameworkBundle][Notifier] Fixing notifier email definition without mailer (chr-hertel)
+ * bug #34385 Avoid empty "If-Modified-Since" header in validation request (mpdude)
+ * bug #34458 [Validator] ConstraintValidatorTestCase: add missing return value to mocked validate method calls (ogizanagi)
+ * bug #34516 [HttpKernel] drop return type declaration (xabbuh)
+ * bug #34474 [Messenger] Ignore stamps in in-memory transport (tienvx)
+
* 5.0.0 (2019-11-21)
* bug #34464 [Form] group constraints when calling the validator (nicolas-grekas)
diff --git a/README.md b/README.md
index 5796b1acd7ceb..da9e6156c00d7 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
-[Symfony][1] is a **PHP framework** for web applications and a set of reusable
+[Symfony][1] is a **PHP framework** for web and console applications and a set of reusable
**PHP components**. Symfony is used by thousands of web applications (including
BlaBlaCar.com and Spotify.com) and most of the [popular PHP projects][2] (including
Drupal and Magento).
diff --git a/UPGRADE-4.4.md b/UPGRADE-4.4.md
index 1d1863e4f84a2..f3377f43da2eb 100644
--- a/UPGRADE-4.4.md
+++ b/UPGRADE-4.4.md
@@ -104,6 +104,9 @@ HttpFoundation
* `ApacheRequest` is deprecated, use `Request` class instead.
* Passing a third argument to `HeaderBag::get()` is deprecated since Symfony 4.4, use method `all()` instead
+ * [BC BREAK] `PdoSessionHandler` with MySQL changed the type of the lifetime column,
+ make sure to run `ALTER TABLE sessions MODIFY sess_lifetime INTEGER UNSIGNED NOT NULL` to
+ update your database.
* `PdoSessionHandler` now precalculates the expiry timestamp in the lifetime column,
make sure to run `CREATE INDEX EXPIRY ON sessions (sess_lifetime)` to update your database
to speed up garbage collection of expired sessions.
@@ -168,6 +171,8 @@ Mailer
------
* [BC BREAK] Changed the DSN to use for disabling delivery (using the `NullTransport`) from `smtp://null` to `null://null` (host doesn't matter).
+ * [BC BREAK] Renamed class `SmtpEnvelope` to `Envelope` and `DelayedSmtpEnvelope` to `DelayedEnvelope`.
+ * [BC BREAK] Added a required `string $transport` argument to `MessageEvent::__construct`.
Messenger
---------
@@ -217,6 +222,7 @@ Security
* The `LdapUserProvider` class has been deprecated, use `Symfony\Component\Ldap\Security\LdapUserProvider` instead.
* Implementations of `PasswordEncoderInterface` and `UserPasswordEncoderInterface` should add a new `needsRehash()` method
* Deprecated returning a non-boolean value when implementing `Guard\AuthenticatorInterface::checkCredentials()`. Please explicitly return `false` to indicate invalid credentials.
+ * The `ListenerInterface` is deprecated, extend `AbstractListener` instead.
* Deprecated passing more than one attribute to `AccessDecisionManager::decide()` and `AuthorizationChecker::isGranted()` (and indirectly the `is_granted()` Twig and ExpressionLanguage function)
**Before**
@@ -228,7 +234,7 @@ Security
**After**
```php
- if ($this->authorizationChecker->isGranted(new Expression("has_role('ROLE_USER') or has_role('ROLE_ADMIN')"))) {}
+ if ($this->authorizationChecker->isGranted(new Expression("is_granted('ROLE_USER') or is_granted('ROLE_ADMIN')"))) {}
// or:
if ($this->authorizationChecker->isGranted('ROLE_USER')
@@ -269,7 +275,17 @@ TwigBridge
TwigBundle
----------
- * Deprecated `twig.exception_controller` configuration option, set it to "null" and use `framework.error_controller` instead:
+ * Deprecated `twig.exception_controller` configuration option.
+
+ If you were not using this option previously, set it to `null`:
+
+ After:
+ ```yaml
+ twig:
+ exception_controller: null
+ ```
+
+ If you were using this option previously, set it to `null` and use `framework.error_controller` instead:
Before:
```yaml
diff --git a/UPGRADE-5.0.md b/UPGRADE-5.0.md
index 9dbd28e2a3f41..3e67bb5e4d808 100644
--- a/UPGRADE-5.0.md
+++ b/UPGRADE-5.0.md
@@ -271,6 +271,9 @@ HttpFoundation
* `ApacheRequest` has been removed, use the `Request` class instead.
* The third argument of the `HeaderBag::get()` method has been removed, use method `all()` instead.
* Getting the container from a non-booted kernel is not possible anymore.
+ * [BC BREAK] `PdoSessionHandler` with MySQL changed the type of the lifetime column,
+ make sure to run `ALTER TABLE sessions MODIFY sess_lifetime INTEGER UNSIGNED NOT NULL` to
+ update your database.
HttpKernel
----------
@@ -410,7 +413,7 @@ Security
**After**
```php
- if ($this->authorizationChecker->isGranted(new Expression("has_role('ROLE_USER') or has_role('ROLE_ADMIN')"))) {}
+ if ($this->authorizationChecker->isGranted(new Expression("is_granted('ROLE_USER') or is_granted('ROLE_ADMIN')"))) {}
// or:
if ($this->authorizationChecker->isGranted('ROLE_USER')
@@ -434,7 +437,7 @@ Security
* `SimpleAuthenticatorInterface`, `SimpleFormAuthenticatorInterface`, `SimplePreAuthenticatorInterface`,
`SimpleAuthenticationProvider`, `SimpleAuthenticationHandler`, `SimpleFormAuthenticationListener` and
`SimplePreAuthenticationListener` have been removed. Use Guard instead.
- * The `ListenerInterface` has been removed, turn your listeners into callables instead.
+ * The `ListenerInterface` has been removed, extend `AbstractListener` instead.
* The `Firewall::handleRequest()` method has been removed, use `Firewall::callListeners()` instead.
* `\Serializable` interface has been removed from `AbstractToken` and `AuthenticationException`,
thus `serialize()` and `unserialize()` aren't available.
@@ -473,6 +476,7 @@ Security
* Classes implementing the `TokenInterface` must implement the two new methods
`__serialize` and `__unserialize`
* Implementations of `Guard\AuthenticatorInterface::checkCredentials()` must return a boolean value now. Please explicitly return `false` to indicate invalid credentials.
+ * Removed the `has_role()` function from security expressions, use `is_granted()` instead.
SecurityBundle
--------------
diff --git a/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterMappingsPass.php b/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterMappingsPass.php
index c42d8117100c3..f63aca5937e77 100644
--- a/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterMappingsPass.php
+++ b/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterMappingsPass.php
@@ -218,10 +218,7 @@ private function getManagerName(ContainerBuilder $container): string
}
}
- throw new InvalidArgumentException(sprintf(
- 'Could not find the manager name parameter in the container. Tried the following parameter names: "%s"',
- implode('", "', $this->managerParameters)
- ));
+ throw new InvalidArgumentException(sprintf('Could not find the manager name parameter in the container. Tried the following parameter names: "%s"', implode('", "', $this->managerParameters)));
}
/**
diff --git a/src/Symfony/Bridge/Doctrine/Form/ChoiceList/DoctrineChoiceLoader.php b/src/Symfony/Bridge/Doctrine/Form/ChoiceList/DoctrineChoiceLoader.php
index 87c1f8263f877..4e06569b4d6d3 100644
--- a/src/Symfony/Bridge/Doctrine/Form/ChoiceList/DoctrineChoiceLoader.php
+++ b/src/Symfony/Bridge/Doctrine/Form/ChoiceList/DoctrineChoiceLoader.php
@@ -87,7 +87,7 @@ public function loadValuesForChoices(array $choices, callable $value = null)
$optimize = $this->idReader && (null === $value || \is_array($value) && $value[0] === $this->idReader);
// Attention: This optimization does not check choices for existence
- if ($optimize && !$this->choiceList && $this->idReader->isSingleId()) {
+ if ($optimize && !$this->choiceList) {
$values = [];
// Maintain order and indices of the given objects
@@ -123,7 +123,7 @@ public function loadChoicesForValues(array $values, callable $value = null)
// a single-field identifier
$optimize = $this->idReader && (null === $value || \is_array($value) && $this->idReader === $value[0]);
- if ($optimize && !$this->choiceList && $this->objectLoader && $this->idReader->isSingleId()) {
+ if ($optimize && !$this->choiceList && $this->objectLoader) {
$unorderedObjects = $this->objectLoader->getEntitiesByIds($this->idReader->getIdField(), $values);
$objectsById = [];
$objects = [];
diff --git a/src/Symfony/Bridge/Doctrine/Form/Type/DoctrineType.php b/src/Symfony/Bridge/Doctrine/Form/Type/DoctrineType.php
index 8589c3bd042b0..eff1245d41fa2 100644
--- a/src/Symfony/Bridge/Doctrine/Form/Type/DoctrineType.php
+++ b/src/Symfony/Bridge/Doctrine/Form/Type/DoctrineType.php
@@ -50,7 +50,6 @@ abstract class DoctrineType extends AbstractType implements ResetInterface
*
* For backwards compatibility, objects are cast to strings by default.
*
- *
* @internal This method is public to be usable as callback. It should not
* be used in user code.
*/
@@ -83,13 +82,16 @@ public static function createChoiceName(object $choice, $key, string $value): st
* For instance in ORM two query builders with an equal SQL string and
* equal parameters are considered to be equal.
*
+ * @param object $queryBuilder A query builder, type declaration is not present here as there
+ * is no common base class for the different implementations
+ *
* @return array|null Array with important QueryBuilder parts or null if
* they can't be determined
*
* @internal This method is public to be usable as callback. It should not
* be used in user code.
*/
- public function getQueryBuilderPartsForCachingHash(QueryBuilder $queryBuilder): ?array
+ public function getQueryBuilderPartsForCachingHash($queryBuilder): ?array
{
return null;
}
@@ -143,8 +145,7 @@ public function configureOptions(OptionsResolver $resolver)
$options['em'],
$options['class'],
$options['id_reader'],
- $entityLoader,
- false
+ $entityLoader
);
if (null !== $hash) {
diff --git a/src/Symfony/Bridge/Doctrine/Form/Type/EntityType.php b/src/Symfony/Bridge/Doctrine/Form/Type/EntityType.php
index 2cdd8109138cc..6429ede4ca4ab 100644
--- a/src/Symfony/Bridge/Doctrine/Form/Type/EntityType.php
+++ b/src/Symfony/Bridge/Doctrine/Form/Type/EntityType.php
@@ -50,6 +50,10 @@ public function configureOptions(OptionsResolver $resolver)
*/
public function getLoader(ObjectManager $manager, QueryBuilder $queryBuilder, string $class)
{
+ if (!$queryBuilder instanceof QueryBuilder) {
+ throw new \TypeError(sprintf('Expected an instance of %s, but got %s.', QueryBuilder::class, \is_object($queryBuilder) ? \get_class($queryBuilder) : \gettype($queryBuilder)));
+ }
+
return new ORMQueryBuilderLoader($queryBuilder);
}
@@ -65,11 +69,17 @@ public function getBlockPrefix()
* We consider two query builders with an equal SQL string and
* equal parameters to be equal.
*
+ * @param QueryBuilder $queryBuilder
+ *
* @internal This method is public to be usable as callback. It should not
* be used in user code.
*/
- public function getQueryBuilderPartsForCachingHash(QueryBuilder $queryBuilder): ?array
+ public function getQueryBuilderPartsForCachingHash($queryBuilder): ?array
{
+ if (!$queryBuilder instanceof QueryBuilder) {
+ throw new \TypeError(sprintf('Expected an instance of %s, but got %s.', QueryBuilder::class, \is_object($queryBuilder) ? \get_class($queryBuilder) : \gettype($queryBuilder)));
+ }
+
return [
$queryBuilder->getQuery()->getSQL(),
array_map([$this, 'parameterToArray'], $queryBuilder->getParameters()->toArray()),
diff --git a/src/Symfony/Bridge/Monolog/Handler/ChromePhpHandler.php b/src/Symfony/Bridge/Monolog/Handler/ChromePhpHandler.php
index b1d9fcd51e242..e7049c4b614cc 100644
--- a/src/Symfony/Bridge/Monolog/Handler/ChromePhpHandler.php
+++ b/src/Symfony/Bridge/Monolog/Handler/ChromePhpHandler.php
@@ -41,7 +41,7 @@ public function onKernelResponse(ResponseEvent $event)
}
if (!preg_match(static::USER_AGENT_REGEX, $event->getRequest()->headers->get('User-Agent'))) {
- $this->sendHeaders = false;
+ self::$sendHeaders = false;
$this->headers = [];
return;
@@ -59,7 +59,7 @@ public function onKernelResponse(ResponseEvent $event)
*/
protected function sendHeader($header, $content): void
{
- if (!$this->sendHeaders) {
+ if (!self::$sendHeaders) {
return;
}
diff --git a/src/Symfony/Bridge/Monolog/Handler/ServerLogHandler.php b/src/Symfony/Bridge/Monolog/Handler/ServerLogHandler.php
index 389e64d5aca8f..4016d402b9634 100644
--- a/src/Symfony/Bridge/Monolog/Handler/ServerLogHandler.php
+++ b/src/Symfony/Bridge/Monolog/Handler/ServerLogHandler.php
@@ -12,14 +12,43 @@
namespace Symfony\Bridge\Monolog\Handler;
use Monolog\Formatter\FormatterInterface;
-use Monolog\Handler\AbstractHandler;
+use Monolog\Handler\AbstractProcessingHandler;
+use Monolog\Handler\FormattableHandlerTrait;
use Monolog\Logger;
use Symfony\Bridge\Monolog\Formatter\VarDumperFormatter;
+if (trait_exists(FormattableHandlerTrait::class)) {
+ class ServerLogHandler extends AbstractProcessingHandler
+ {
+ use ServerLogHandlerTrait;
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getDefaultFormatter(): FormatterInterface
+ {
+ return new VarDumperFormatter();
+ }
+ }
+} else {
+ class ServerLogHandler extends AbstractProcessingHandler
+ {
+ use ServerLogHandlerTrait;
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getDefaultFormatter()
+ {
+ return new VarDumperFormatter();
+ }
+ }
+}
+
/**
* @author Grégoire Pineau
*/
-class ServerLogHandler extends AbstractHandler
+trait ServerLogHandlerTrait
{
private $host;
private $context;
@@ -56,6 +85,11 @@ public function handle(array $record): bool
restore_error_handler();
}
+ return parent::handle($record);
+ }
+
+ protected function write(array $record): void
+ {
$recordFormatted = $this->formatRecord($record);
set_error_handler(self::class.'::nullErrorHandler');
@@ -72,16 +106,12 @@ public function handle(array $record): bool
} finally {
restore_error_handler();
}
-
- return false === $this->bubble;
}
/**
* {@inheritdoc}
- *
- * @return FormatterInterface
*/
- protected function getDefaultFormatter()
+ protected function getDefaultFormatter(): FormatterInterface
{
return new VarDumperFormatter();
}
@@ -103,13 +133,7 @@ private function createSocket()
private function formatRecord(array $record): string
{
- if ($this->processors) {
- foreach ($this->processors as $processor) {
- $record = $processor($record);
- }
- }
-
- $recordFormatted = $this->getFormatter()->format($record);
+ $recordFormatted = $record['formatted'];
foreach (['log_uuid', 'uuid', 'uid'] as $key) {
if (isset($record['extra'][$key])) {
diff --git a/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit.php b/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit.php
index 6ccfa74ce3e98..8ac4a79beb804 100644
--- a/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit.php
+++ b/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit.php
@@ -14,15 +14,21 @@
error_reporting(-1);
-$getEnvVar = function ($name, $default = false) {
+global $argv, $argc;
+$argv = isset($_SERVER['argv']) ? $_SERVER['argv'] : array();
+$argc = isset($_SERVER['argc']) ? $_SERVER['argc'] : 0;
+$getEnvVar = function ($name, $default = false) use ($argv) {
if (false !== $value = getenv($name)) {
return $value;
}
static $phpunitConfig = null;
if (null === $phpunitConfig) {
+ $opt = min(array_search('-c', $opts = array_reverse($argv), true) ?: INF, array_search('--configuration', $opts, true) ?: INF);
$phpunitConfigFilename = null;
- if (file_exists('phpunit.xml')) {
+ if (INF !== $opt && isset($opts[$opt - 1])) {
+ $phpunitConfigFilename = $opts[$opt - 1];
+ } elseif (file_exists('phpunit.xml')) {
$phpunitConfigFilename = 'phpunit.xml';
} elseif (file_exists('phpunit.xml.dist')) {
$phpunitConfigFilename = 'phpunit.xml.dist';
@@ -185,10 +191,6 @@ class SymfonyBlacklistPhpunit {}
chdir($oldPwd);
}
-global $argv, $argc;
-$argv = isset($_SERVER['argv']) ? $_SERVER['argv'] : array();
-$argc = isset($_SERVER['argc']) ? $_SERVER['argc'] : 0;
-
if ($PHPUNIT_VERSION < 8.0) {
$argv = array_filter($argv, function ($v) use (&$argc) { if ('--do-not-cache-result' !== $v) return true; --$argc; return false; });
} elseif (filter_var(getenv('SYMFONY_PHPUNIT_DISABLE_RESULT_CACHE'), FILTER_VALIDATE_BOOLEAN)) {
diff --git a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_horizontal_layout.html.twig b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_horizontal_layout.html.twig
index b082d9236b927..4dc3e9c896fb7 100644
--- a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_horizontal_layout.html.twig
+++ b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_horizontal_layout.html.twig
@@ -27,7 +27,7 @@ col-sm-2
{%- if help is not empty -%}
{%- set widget_attr = {attr: {'aria-describedby': id ~"_help"}} -%}
{%- endif -%}
-
diff --git a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_4_horizontal_layout.html.twig b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_4_horizontal_layout.html.twig
index e37de07d6b071..a75e364187743 100644
--- a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_4_horizontal_layout.html.twig
+++ b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_4_horizontal_layout.html.twig
@@ -28,7 +28,7 @@ col-sm-2
{%- if help is not empty -%}
{%- set widget_attr = {attr: {'aria-describedby': id ~"_help"}} -%}
{%- endif -%}
-
+
{{- form_label(form) -}}
{{- form_widget(form, widget_attr) -}}
@@ -43,7 +43,7 @@ col-sm-2
{%- if help is not empty -%}
{%- set widget_attr = {attr: {'aria-describedby': id ~"_help"}} -%}
{%- endif -%}
-
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.sl.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.sl.xlf
index 6f5fd98ca192e..cb12a8a9daa4d 100644
--- a/src/Symfony/Component/Validator/Resources/translations/validators.sl.xlf
+++ b/src/Symfony/Component/Validator/Resources/translations/validators.sl.xlf
@@ -318,6 +318,54 @@
ErrorNapaka
+
+ This is not a valid UUID.
+ To ni veljaven UUID.
+
+
+ This value should be a multiple of {{ compared_value }}.
+ Ta vrednost bi morala biti večkratnik od {{ compared_value }}.
+
+
+ This Business Identifier Code (BIC) is not associated with IBAN {{ iban }}.
+ Ta poslovna identifikacijska koda (BIC) ni povezana z IBAN {{ iban }}.
+
+
+ This value should be valid JSON.
+ Ta vrednost bi morala biti veljaven JSON.
+
+
+ This collection should contain only unique elements.
+ Ta zbirka bi morala vsebovati samo edinstvene elemente.
+
+
+ This value should be positive.
+ Ta vrednost bi morala biti pozitivna.
+
+
+ This value should be either positive or zero.
+ Ta vrednost bi morala biti pozitivna ali enaka nič.
+
+
+ This value should be negative.
+ Ta vrednost bi morala biti negativna.
+
+
+ This value should be either negative or zero.
+ Ta vrednost bi morala biti negativna ali enaka nič.
+
+
+ This value is not a valid timezone.
+ Ta vrednost ni veljaven časovni pas.
+
+
+ This password has been leaked in a data breach, it must not be used. Please use another password.
+ To geslo je ušlo pri kršitvi varnosti podatkov in ga ne smete uporabljati. Prosimo, uporabite drugo geslo.
+
+
+ This value should be between {{ min }} and {{ max }}.
+ Ta vrednost bi morala biti med {{ min }} in {{ max }}.
+
diff --git a/src/Symfony/Component/Validator/Test/ConstraintValidatorTestCase.php b/src/Symfony/Component/Validator/Test/ConstraintValidatorTestCase.php
index 3db44b150a58c..8e4fc6ba1bcb2 100644
--- a/src/Symfony/Component/Validator/Test/ConstraintValidatorTestCase.php
+++ b/src/Symfony/Component/Validator/Test/ConstraintValidatorTestCase.php
@@ -177,7 +177,8 @@ protected function expectValidateAt($i, $propertyPath, $value, $group)
->willReturn($validator);
$validator->expects($this->at(2 * $i + 1))
->method('validate')
- ->with($value, $this->logicalOr(null, [], $this->isInstanceOf('\Symfony\Component\Validator\Constraints\Valid')), $group);
+ ->with($value, $this->logicalOr(null, [], $this->isInstanceOf('\Symfony\Component\Validator\Constraints\Valid')), $group)
+ ->willReturn($validator);
}
protected function expectValidateValueAt($i, $propertyPath, $value, $constraints, $group = null)
@@ -189,7 +190,8 @@ protected function expectValidateValueAt($i, $propertyPath, $value, $constraints
->willReturn($contextualValidator);
$contextualValidator->expects($this->at(2 * $i + 1))
->method('validate')
- ->with($value, $constraints, $group);
+ ->with($value, $constraints, $group)
+ ->willReturn($contextualValidator);
}
protected function assertNoViolation()
diff --git a/src/Symfony/Component/VarDumper/Dumper/ContextProvider/SourceContextProvider.php b/src/Symfony/Component/VarDumper/Dumper/ContextProvider/SourceContextProvider.php
index e43e19f4356f6..6f4caba63b989 100644
--- a/src/Symfony/Component/VarDumper/Dumper/ContextProvider/SourceContextProvider.php
+++ b/src/Symfony/Component/VarDumper/Dumper/ContextProvider/SourceContextProvider.php
@@ -52,8 +52,8 @@ public function getContext(): ?array
&& 'dump' === $trace[$i]['function']
&& VarDumper::class === $trace[$i]['class']
) {
- $file = $trace[$i]['file'];
- $line = $trace[$i]['line'];
+ $file = $trace[$i]['file'] ?? $file;
+ $line = $trace[$i]['line'] ?? $line;
while (++$i < $this->limit) {
if (isset($trace[$i]['function'], $trace[$i]['file']) && empty($trace[$i]['class']) && 0 !== strpos($trace[$i]['function'], 'call_user_func')) {
diff --git a/src/Symfony/Component/VarExporter/Internal/Exporter.php b/src/Symfony/Component/VarExporter/Internal/Exporter.php
index e2eb57bc9b865..4d83a4b094dba 100644
--- a/src/Symfony/Component/VarExporter/Internal/Exporter.php
+++ b/src/Symfony/Component/VarExporter/Internal/Exporter.php
@@ -222,14 +222,14 @@ public static function export($value, string $indent = '')
));
if ("'" === $m[2]) {
- return substr($m[1], 0, -2);
+ return substr($m[1], 0, -2);
}
if ('n".\'' === substr($m[1], -4)) {
- return substr_replace($m[1], "\n".$subIndent.".'".$m[2], -2);
+ return substr_replace($m[1], "\n".$subIndent.".'".$m[2], -2);
}
- return $m[1].$m[2];
+ return $m[1].$m[2];
}, $code, -1, $count);
if ($count && 0 === strpos($code, "''.")) {
diff --git a/src/Symfony/Component/Workflow/MarkingStore/MethodMarkingStore.php b/src/Symfony/Component/Workflow/MarkingStore/MethodMarkingStore.php
index 3ef65b315afa8..39d2ccec21844 100644
--- a/src/Symfony/Component/Workflow/MarkingStore/MethodMarkingStore.php
+++ b/src/Symfony/Component/Workflow/MarkingStore/MethodMarkingStore.php
@@ -51,7 +51,7 @@ public function getMarking(object $subject): Marking
$method = 'get'.ucfirst($this->property);
if (!method_exists($subject, $method)) {
- throw new LogicException(sprintf('The method "%s::%s()" does not exists.', \get_class($subject), $method));
+ throw new LogicException(sprintf('The method "%s::%s()" does not exist.', \get_class($subject), $method));
}
$marking = $subject->{$method}();
@@ -81,7 +81,7 @@ public function setMarking(object $subject, Marking $marking, array $context = [
$method = 'set'.ucfirst($this->property);
if (!method_exists($subject, $method)) {
- throw new LogicException(sprintf('The method "%s::%s()" does not exists.', \get_class($subject), $method));
+ throw new LogicException(sprintf('The method "%s::%s()" does not exist.', \get_class($subject), $method));
}
$subject->{$method}($marking, $context);
diff --git a/src/Symfony/Component/Workflow/Tests/StateMachineTest.php b/src/Symfony/Component/Workflow/Tests/StateMachineTest.php
index 9224f7cb129d9..a6c7362f79568 100644
--- a/src/Symfony/Component/Workflow/Tests/StateMachineTest.php
+++ b/src/Symfony/Component/Workflow/Tests/StateMachineTest.php
@@ -5,6 +5,7 @@
use PHPUnit\Framework\TestCase;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\Workflow\Event\GuardEvent;
+use Symfony\Component\Workflow\Exception\NotEnabledTransitionException;
use Symfony\Component\Workflow\StateMachine;
use Symfony\Component\Workflow\TransitionBlocker;
@@ -84,27 +85,52 @@ public function testBuildTransitionBlockerListReturnsExpectedReasonOnBranchMerge
$subject = new Subject();
// There may be multiple transitions with the same name. Make sure that transitions
- // that are not enabled by the marking are evaluated.
+ // that are enabled by the marking are evaluated.
// see https://github.com/symfony/symfony/issues/28432
- // Test if when you are in place "a"trying transition "t1" then returned
+ // Test if when you are in place "a" and trying to apply "t1" then it returns
// blocker list contains guard blocker instead blockedByMarking
$subject->setMarking('a');
$transitionBlockerList = $net->buildTransitionBlockerList($subject, 't1');
$this->assertCount(1, $transitionBlockerList);
$blockers = iterator_to_array($transitionBlockerList);
-
$this->assertSame('Transition blocker of place a', $blockers[0]->getMessage());
$this->assertSame('blocker', $blockers[0]->getCode());
- // Test if when you are in place "d" trying transition "t1" then
- // returned blocker list contains guard blocker instead blockedByMarking
+ // Test if when you are in place "d" and trying to apply "t1" then
+ // it returns blocker list contains guard blocker instead blockedByMarking
$subject->setMarking('d');
$transitionBlockerList = $net->buildTransitionBlockerList($subject, 't1');
$this->assertCount(1, $transitionBlockerList);
$blockers = iterator_to_array($transitionBlockerList);
-
$this->assertSame('Transition blocker of place d', $blockers[0]->getMessage());
$this->assertSame('blocker', $blockers[0]->getCode());
}
+
+ public function testApplyReturnsExpectedReasonOnBranchMerge()
+ {
+ $definition = $this->createComplexStateMachineDefinition();
+
+ $dispatcher = new EventDispatcher();
+ $net = new StateMachine($definition, null, $dispatcher);
+
+ $dispatcher->addListener('workflow.guard', function (GuardEvent $event) {
+ $event->addTransitionBlocker(new TransitionBlocker(sprintf('Transition blocker of place %s', $event->getTransition()->getFroms()[0]), 'blocker'));
+ });
+
+ $subject = new Subject();
+
+ // There may be multiple transitions with the same name. Make sure that all transitions
+ // that are enabled by the marking are evaluated.
+ // see https://github.com/symfony/symfony/issues/34489
+
+ try {
+ $net->apply($subject, 't1');
+ $this->fail();
+ } catch (NotEnabledTransitionException $e) {
+ $blockers = iterator_to_array($e->getTransitionBlockerList());
+ $this->assertSame('Transition blocker of place a', $blockers[0]->getMessage());
+ $this->assertSame('blocker', $blockers[0]->getCode());
+ }
+ }
}
diff --git a/src/Symfony/Component/Workflow/Workflow.php b/src/Symfony/Component/Workflow/Workflow.php
index 6d630d779206d..09418d48fb0e7 100644
--- a/src/Symfony/Component/Workflow/Workflow.php
+++ b/src/Symfony/Component/Workflow/Workflow.php
@@ -154,25 +154,47 @@ public function apply(object $subject, string $transitionName, array $context =
{
$marking = $this->getMarking($subject);
- $transitionBlockerList = null;
- $applied = false;
- $approvedTransitionQueue = [];
+ $transitionExist = false;
+ $approvedTransitions = [];
+ $bestTransitionBlockerList = null;
foreach ($this->definition->getTransitions() as $transition) {
if ($transition->getName() !== $transitionName) {
continue;
}
- $transitionBlockerList = $this->buildTransitionBlockerListForTransition($subject, $marking, $transition);
- if (!$transitionBlockerList->isEmpty()) {
+ $transitionExist = true;
+
+ $tmpTransitionBlockerList = $this->buildTransitionBlockerListForTransition($subject, $marking, $transition);
+
+ if ($tmpTransitionBlockerList->isEmpty()) {
+ $approvedTransitions[] = $transition;
+ continue;
+ }
+
+ if (!$bestTransitionBlockerList) {
+ $bestTransitionBlockerList = $tmpTransitionBlockerList;
continue;
}
- $approvedTransitionQueue[] = $transition;
+
+ // We prefer to return transitions blocker by something else than
+ // marking. Because it means the marking was OK. Transitions are
+ // deterministic: it's not possible to have many transitions enabled
+ // at the same time that match the same marking with the same name
+ if (!$tmpTransitionBlockerList->has(TransitionBlocker::BLOCKED_BY_MARKING)) {
+ $bestTransitionBlockerList = $tmpTransitionBlockerList;
+ }
+ }
+
+ if (!$transitionExist) {
+ throw new UndefinedTransitionException($subject, $transitionName, $this);
}
- foreach ($approvedTransitionQueue as $transition) {
- $applied = true;
+ if (!$approvedTransitions) {
+ throw new NotEnabledTransitionException($subject, $transitionName, $this, $bestTransitionBlockerList);
+ }
+ foreach ($approvedTransitions as $transition) {
$this->leave($subject, $transition, $marking);
$context = $this->transition($subject, $transition, $marking, $context);
@@ -188,14 +210,6 @@ public function apply(object $subject, string $transitionName, array $context =
$this->announce($subject, $transition, $marking);
}
- if (!$transitionBlockerList) {
- throw new UndefinedTransitionException($subject, $transitionName, $this);
- }
-
- if (!$applied) {
- throw new NotEnabledTransitionException($subject, $transitionName, $this, $transitionBlockerList);
- }
-
return $marking;
}
diff --git a/src/Symfony/Contracts/HttpClient/Test/Fixtures/web/index.php b/src/Symfony/Contracts/HttpClient/Test/Fixtures/web/index.php
index d3c4f0f1db57b..96486ca3168c8 100644
--- a/src/Symfony/Contracts/HttpClient/Test/Fixtures/web/index.php
+++ b/src/Symfony/Contracts/HttpClient/Test/Fixtures/web/index.php
@@ -155,6 +155,27 @@
usleep(500);
}
exit;
+
+ case '/json':
+ header("Content-Type: application/json");
+ echo json_encode([
+ 'documents' => [
+ ['id' => '/json/1'],
+ ['id' => '/json/2'],
+ ['id' => '/json/3'],
+ ],
+ ]);
+ exit;
+
+ case '/json/1':
+ case '/json/2':
+ case '/json/3':
+ header("Content-Type: application/json");
+ echo json_encode([
+ 'title' => $vars['REQUEST_URI'],
+ ]);
+
+ exit;
}
header('Content-Type: application/json', true);
diff --git a/src/Symfony/Contracts/HttpClient/Test/HttpClientTestCase.php b/src/Symfony/Contracts/HttpClient/Test/HttpClientTestCase.php
index 4badb4c358909..aad712e3e8dcd 100644
--- a/src/Symfony/Contracts/HttpClient/Test/HttpClientTestCase.php
+++ b/src/Symfony/Contracts/HttpClient/Test/HttpClientTestCase.php
@@ -24,8 +24,6 @@
*/
abstract class HttpClientTestCase extends TestCase
{
- private static $server;
-
public static function setUpBeforeClass(): void
{
TestHttpServer::start();
diff --git a/src/Symfony/Contracts/HttpClient/Test/TestHttpServer.php b/src/Symfony/Contracts/HttpClient/Test/TestHttpServer.php
index 8e7a469c42603..0adb1a52a3036 100644
--- a/src/Symfony/Contracts/HttpClient/Test/TestHttpServer.php
+++ b/src/Symfony/Contracts/HttpClient/Test/TestHttpServer.php
@@ -19,31 +19,22 @@
*/
class TestHttpServer
{
- private static $server;
+ private static $started;
public static function start()
{
- if (null !== self::$server) {
+ if (self::$started) {
return;
}
$finder = new PhpExecutableFinder();
$process = new Process(array_merge([$finder->find(false)], $finder->findArguments(), ['-dopcache.enable=0', '-dvariables_order=EGPCS', '-S', '127.0.0.1:8057']));
$process->setWorkingDirectory(__DIR__.'/Fixtures/web');
- $process->setTimeout(300);
$process->start();
- self::$server = new class() {
- public $process;
-
- public function __destruct()
- {
- $this->process->stop();
- }
- };
-
- self::$server->process = $process;
-
+ register_shutdown_function([$process, 'stop']);
sleep('\\' === \DIRECTORY_SEPARATOR ? 10 : 1);
+
+ self::$started = true;
}
}