diff --git a/.travis.yml b/.travis.yml index 73eec7a9eb..24155a5c0c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -151,6 +151,7 @@ before_install: INI=~/.phpenv/versions/$PHP/etc/conf.d/travis.ini echo date.timezone = Europe/Paris >> $INI echo memory_limit = -1 >> $INI + echo default_socket_timeout = 10 >> $INI echo session.gc_probability = 0 >> $INI echo opcache.enable_cli = 1 >> $INI echo apc.enable_cli = 1 >> $INI diff --git a/src/Symfony/Bridge/Monolog/Command/ServerLogCommand.php b/src/Symfony/Bridge/Monolog/Command/ServerLogCommand.php index eb1a4dc257..f04db50c8c 100644 --- a/src/Symfony/Bridge/Monolog/Command/ServerLogCommand.php +++ b/src/Symfony/Bridge/Monolog/Command/ServerLogCommand.php @@ -102,7 +102,7 @@ EOF } if (!$socket = stream_socket_server($host, $errno, $errstr)) { - throw new RuntimeException(sprintf('Server start failed on "%s": '.$errstr.' '.$errno, $host)); + throw new RuntimeException(sprintf('Server start failed on "%s": ', $host).$errstr.' '.$errno); } foreach ($this->getLogs($socket) as $clientId => $message) { diff --git a/src/Symfony/Bridge/Twig/Mime/BodyRenderer.php b/src/Symfony/Bridge/Twig/Mime/BodyRenderer.php index 14535f232a..510cfcdd5b 100644 --- a/src/Symfony/Bridge/Twig/Mime/BodyRenderer.php +++ b/src/Symfony/Bridge/Twig/Mime/BodyRenderer.php @@ -74,6 +74,6 @@ final class BodyRenderer implements BodyRendererInterface return $this->converter->convert($html); } - return strip_tags($html); + return strip_tags(preg_replace('{<(head|style)\b.*?}i', '', $html)); } } diff --git a/src/Symfony/Bridge/Twig/Tests/Mime/BodyRendererTest.php b/src/Symfony/Bridge/Twig/Tests/Mime/BodyRendererTest.php index 6eeade3a73..175a8e1978 100644 --- a/src/Symfony/Bridge/Twig/Tests/Mime/BodyRendererTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Mime/BodyRendererTest.php @@ -29,11 +29,12 @@ class BodyRendererTest extends TestCase public function testRenderHtmlOnly(): void { - $email = $this->prepareEmail(null, 'HTML'); + $html = 'headHTML'; + $email = $this->prepareEmail(null, $html); $body = $email->getBody(); $this->assertInstanceOf(AlternativePart::class, $body); $this->assertEquals('HTML', $body->getParts()[0]->bodyToString()); - $this->assertEquals('HTML', $body->getParts()[1]->bodyToString()); + $this->assertEquals(str_replace('=', '=3D', $html), $body->getParts()[1]->bodyToString()); } public function testRenderHtmlOnlyWithTextSet(): void diff --git a/src/Symfony/Component/Asset/VersionStrategy/JsonManifestVersionStrategy.php b/src/Symfony/Component/Asset/VersionStrategy/JsonManifestVersionStrategy.php index e48f6f2241..c4de580d0b 100644 --- a/src/Symfony/Component/Asset/VersionStrategy/JsonManifestVersionStrategy.php +++ b/src/Symfony/Component/Asset/VersionStrategy/JsonManifestVersionStrategy.php @@ -59,7 +59,7 @@ class JsonManifestVersionStrategy implements VersionStrategyInterface $this->manifestData = json_decode(file_get_contents($this->manifestPath), true); if (0 < json_last_error()) { - throw new \RuntimeException(sprintf('Error parsing JSON from asset manifest file "%s": '.json_last_error_msg(), $this->manifestPath)); + throw new \RuntimeException(sprintf('Error parsing JSON from asset manifest file "%s": ', $this->manifestPath).json_last_error_msg()); } } diff --git a/src/Symfony/Component/Console/Helper/ProcessHelper.php b/src/Symfony/Component/Console/Helper/ProcessHelper.php index 0198968157..f82c16bae8 100644 --- a/src/Symfony/Component/Console/Helper/ProcessHelper.php +++ b/src/Symfony/Component/Console/Helper/ProcessHelper.php @@ -36,6 +36,10 @@ class ProcessHelper extends Helper */ public function run(OutputInterface $output, $cmd, string $error = null, callable $callback = null, int $verbosity = OutputInterface::VERBOSITY_VERY_VERBOSE): Process { + if (!class_exists(Process::class)) { + throw new \LogicException('The ProcessHelper cannot be run as the Process component is not installed. Try running "compose require symfony/process".'); + } + if ($output instanceof ConsoleOutputInterface) { $output = $output->getErrorOutput(); } diff --git a/src/Symfony/Component/Console/Helper/QuestionHelper.php b/src/Symfony/Component/Console/Helper/QuestionHelper.php index a4bd54a97e..7dad156206 100644 --- a/src/Symfony/Component/Console/Helper/QuestionHelper.php +++ b/src/Symfony/Component/Console/Helper/QuestionHelper.php @@ -35,7 +35,7 @@ class QuestionHelper extends Helper { private $inputStream; private static $shell; - private static $stty; + private static $stty = true; /** * Asks a question to the user. @@ -109,7 +109,7 @@ class QuestionHelper extends Helper $inputStream = $this->inputStream ?: STDIN; $autocomplete = $question->getAutocompleterCallback(); - if (null === $autocomplete || !Terminal::hasSttyAvailable()) { + if (null === $autocomplete || !self::$stty || !Terminal::hasSttyAvailable()) { $ret = false; if ($question->isHidden()) { try { @@ -416,7 +416,7 @@ class QuestionHelper extends Helper return $value; } - if (Terminal::hasSttyAvailable()) { + if (self::$stty && Terminal::hasSttyAvailable()) { $sttyMode = shell_exec('stty -g'); shell_exec('stty -echo'); diff --git a/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php b/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php index 8164f743bc..9afad24357 100644 --- a/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php +++ b/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Console\Tests\Helper; +use Symfony\Component\Console\Exception\InvalidArgumentException; use Symfony\Component\Console\Formatter\OutputFormatter; use Symfony\Component\Console\Helper\FormatterHelper; use Symfony\Component\Console\Helper\HelperSet; @@ -783,6 +784,35 @@ class QuestionHelperTest extends AbstractQuestionHelperTest $this->assertEquals('FooBundle', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); } + public function testDisableSttby() + { + if (!Terminal::hasSttyAvailable()) { + $this->markTestSkipped('`stty` is required to test autocomplete functionality'); + } + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('invalid'); + + QuestionHelper::disableStty(); + $dialog = new QuestionHelper(); + $dialog->setHelperSet(new HelperSet([new FormatterHelper()])); + + $question = new ChoiceQuestion('Please select a bundle', [1 => 'AcmeDemoBundle', 4 => 'AsseticBundle']); + $question->setMaxAttempts(1); + + // + // Gives `AcmeDemoBundle` with stty + $inputStream = $this->getInputStream("\033[A\033[A\n\033[B\033[B\n"); + + try { + $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question); + } finally { + $reflection = new \ReflectionProperty(QuestionHelper::class, 'stty'); + $reflection->setAccessible(true); + $reflection->setValue(null, true); + } + } + public function testTraversableMultiselectAutocomplete() { // diff --git a/src/Symfony/Component/DependencyInjection/EnvVarProcessor.php b/src/Symfony/Component/DependencyInjection/EnvVarProcessor.php index eae626f0e6..b16bad18df 100644 --- a/src/Symfony/Component/DependencyInjection/EnvVarProcessor.php +++ b/src/Symfony/Component/DependencyInjection/EnvVarProcessor.php @@ -227,7 +227,7 @@ class EnvVarProcessor implements EnvVarProcessorInterface $env = json_decode($env, true); if (JSON_ERROR_NONE !== json_last_error()) { - throw new RuntimeException(sprintf('Invalid JSON in env var "%s": '.json_last_error_msg(), $name)); + throw new RuntimeException(sprintf('Invalid JSON in env var "%s": ', $name).json_last_error_msg()); } if (null !== $env && !\is_array($env)) { diff --git a/src/Symfony/Component/ErrorHandler/Error/FatalError.php b/src/Symfony/Component/ErrorHandler/Error/FatalError.php index 68172d876c..98490b5acc 100644 --- a/src/Symfony/Component/ErrorHandler/Error/FatalError.php +++ b/src/Symfony/Component/ErrorHandler/Error/FatalError.php @@ -72,9 +72,11 @@ class FatalError extends \Error 'line' => $error['line'], 'trace' => $trace, ] as $property => $value) { - $refl = new \ReflectionProperty(\Error::class, $property); - $refl->setAccessible(true); - $refl->setValue($this, $value); + if (null !== $value) { + $refl = new \ReflectionProperty(\Error::class, $property); + $refl->setAccessible(true); + $refl->setValue($this, $value); + } } } diff --git a/src/Symfony/Component/Filesystem/Filesystem.php b/src/Symfony/Component/Filesystem/Filesystem.php index a5539fa5eb..b6df1bd967 100644 --- a/src/Symfony/Component/Filesystem/Filesystem.php +++ b/src/Symfony/Component/Filesystem/Filesystem.php @@ -97,7 +97,7 @@ class Filesystem if (!is_dir($dir)) { // The directory was not created by a concurrent process. Let's throw an exception with a developer friendly error message if we have one if (self::$lastError) { - throw new IOException(sprintf('Failed to create "%s": '.self::$lastError, $dir), 0, null, $dir); + throw new IOException(sprintf('Failed to create "%s": ', $dir).self::$lastError, 0, null, $dir); } throw new IOException(sprintf('Failed to create "%s".', $dir), 0, null, $dir); } @@ -167,16 +167,16 @@ class Filesystem if (is_link($file)) { // See https://bugs.php.net/52176 if (!(self::box('unlink', $file) || '\\' !== \DIRECTORY_SEPARATOR || self::box('rmdir', $file)) && file_exists($file)) { - throw new IOException(sprintf('Failed to remove symlink "%s": '.self::$lastError, $file)); + throw new IOException(sprintf('Failed to remove symlink "%s": ', $file).self::$lastError); } } elseif (is_dir($file)) { $this->remove(new \FilesystemIterator($file, \FilesystemIterator::CURRENT_AS_PATHNAME | \FilesystemIterator::SKIP_DOTS)); if (!self::box('rmdir', $file) && file_exists($file)) { - throw new IOException(sprintf('Failed to remove directory "%s": '.self::$lastError, $file)); + throw new IOException(sprintf('Failed to remove directory "%s": ', $file).self::$lastError); } } elseif (!self::box('unlink', $file) && file_exists($file)) { - throw new IOException(sprintf('Failed to remove file "%s": '.self::$lastError, $file)); + throw new IOException(sprintf('Failed to remove file "%s": ', $file).self::$lastError); } } } diff --git a/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php b/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php index 9454adb068..bd7f553cde 100644 --- a/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php +++ b/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php @@ -63,12 +63,16 @@ class FormValidator extends ConstraintValidator /** @var Constraint[] $constraints */ $constraints = $config->getOption('constraints', []); + $hasChildren = $form->count() > 0; + + if ($hasChildren && $form->isRoot()) { + $this->resolvedGroups = new \SplObjectStorage(); + } + if ($groups instanceof GroupSequence) { // Validate the data, the form AND nested fields in sequence $violationsCount = $this->context->getViolations()->count(); $fieldPropertyPath = \is_object($data) ? 'children[%s]' : 'children%s'; - $hasChildren = $form->count() > 0; - $this->resolvedGroups = $hasChildren ? new \SplObjectStorage() : null; foreach ($groups->groups as $group) { if ($validateDataGraph) { @@ -86,7 +90,8 @@ class FormValidator extends ConstraintValidator // sequence recursively, thus some fields could fail // in different steps without breaking early enough $this->resolvedGroups[$field] = (array) $group; - $validator->atPath(sprintf($fieldPropertyPath, $field->getPropertyPath()))->validate($field, $formConstraint); + $fieldFormConstraint = new Form(); + $validator->atPath(sprintf($fieldPropertyPath, $field->getPropertyPath()))->validate($field, $fieldFormConstraint); } } @@ -94,12 +99,9 @@ class FormValidator extends ConstraintValidator break; } } - - if ($hasChildren) { - // destroy storage at the end of the sequence to avoid memory leaks - $this->resolvedGroups = null; - } } else { + $fieldPropertyPath = \is_object($data) ? 'children[%s]' : 'children%s'; + if ($validateDataGraph) { $validator->atPath('data')->validate($data, null, $groups); } @@ -131,6 +133,19 @@ class FormValidator extends ConstraintValidator foreach ($groupedConstraints as $group => $constraint) { $validator->atPath('data')->validate($data, $constraint, $group); } + + foreach ($form->all() as $field) { + if ($field->isSubmitted()) { + $this->resolvedGroups[$field] = $groups; + $fieldFormConstraint = new Form(); + $validator->atPath(sprintf($fieldPropertyPath, $field->getPropertyPath()))->validate($field, $fieldFormConstraint); + } + } + } + + if ($hasChildren && $form->isRoot()) { + // destroy storage to avoid memory leaks + $this->resolvedGroups = new \SplObjectStorage(); } } elseif (!$form->isSynchronized()) { $childrenSynchronized = true; diff --git a/src/Symfony/Component/Form/Extension/Validator/ValidatorExtension.php b/src/Symfony/Component/Form/Extension/Validator/ValidatorExtension.php index a5e38859c0..ac2d61238f 100644 --- a/src/Symfony/Component/Form/Extension/Validator/ValidatorExtension.php +++ b/src/Symfony/Component/Form/Extension/Validator/ValidatorExtension.php @@ -13,7 +13,7 @@ namespace Symfony\Component\Form\Extension\Validator; use Symfony\Component\Form\AbstractExtension; use Symfony\Component\Form\Extension\Validator\Constraints\Form; -use Symfony\Component\Validator\Constraints\Valid; +use Symfony\Component\Validator\Constraints\Traverse; use Symfony\Component\Validator\Mapping\ClassMetadata; use Symfony\Component\Validator\Validator\ValidatorInterface; @@ -37,7 +37,7 @@ class ValidatorExtension extends AbstractExtension /* @var $metadata ClassMetadata */ $metadata->addConstraint(new Form()); - $metadata->addPropertyConstraint('children', new Valid()); + $metadata->addConstraint(new Traverse(false)); $this->validator = $validator; } diff --git a/src/Symfony/Component/Form/Form.php b/src/Symfony/Component/Form/Form.php index a14b0ce05c..e22415c95e 100644 --- a/src/Symfony/Component/Form/Form.php +++ b/src/Symfony/Component/Form/Form.php @@ -1063,7 +1063,7 @@ class Form implements \IteratorAggregate, FormInterface, ClearableErrorsInterfac $value = $transformer->transform($value); } } catch (TransformationFailedException $exception) { - throw new TransformationFailedException(sprintf('Unable to transform data for property path "%s": '.$exception->getMessage(), $this->getPropertyPath()), $exception->getCode(), $exception, $exception->getInvalidMessage(), $exception->getInvalidMessageParameters()); + throw new TransformationFailedException(sprintf('Unable to transform data for property path "%s": ', $this->getPropertyPath()).$exception->getMessage(), $exception->getCode(), $exception, $exception->getInvalidMessage(), $exception->getInvalidMessageParameters()); } return $value; @@ -1085,7 +1085,7 @@ class Form implements \IteratorAggregate, FormInterface, ClearableErrorsInterfac $value = $transformers[$i]->reverseTransform($value); } } catch (TransformationFailedException $exception) { - throw new TransformationFailedException(sprintf('Unable to reverse value for property path "%s": '.$exception->getMessage(), $this->getPropertyPath()), $exception->getCode(), $exception, $exception->getInvalidMessage(), $exception->getInvalidMessageParameters()); + throw new TransformationFailedException(sprintf('Unable to reverse value for property path "%s": ', $this->getPropertyPath()).$exception->getMessage(), $exception->getCode(), $exception, $exception->getInvalidMessage(), $exception->getInvalidMessageParameters()); } return $value; @@ -1114,7 +1114,7 @@ class Form implements \IteratorAggregate, FormInterface, ClearableErrorsInterfac $value = $transformer->transform($value); } } catch (TransformationFailedException $exception) { - throw new TransformationFailedException(sprintf('Unable to transform value for property path "%s": '.$exception->getMessage(), $this->getPropertyPath()), $exception->getCode(), $exception, $exception->getInvalidMessage(), $exception->getInvalidMessageParameters()); + throw new TransformationFailedException(sprintf('Unable to transform value for property path "%s": ', $this->getPropertyPath()).$exception->getMessage(), $exception->getCode(), $exception, $exception->getInvalidMessage(), $exception->getInvalidMessageParameters()); } return $value; @@ -1138,7 +1138,7 @@ class Form implements \IteratorAggregate, FormInterface, ClearableErrorsInterfac $value = $transformers[$i]->reverseTransform($value); } } catch (TransformationFailedException $exception) { - throw new TransformationFailedException(sprintf('Unable to reverse value for property path "%s": '.$exception->getMessage(), $this->getPropertyPath()), $exception->getCode(), $exception, $exception->getInvalidMessage(), $exception->getInvalidMessageParameters()); + throw new TransformationFailedException(sprintf('Unable to reverse value for property path "%s": ', $this->getPropertyPath()).$exception->getMessage(), $exception->getCode(), $exception, $exception->getInvalidMessage(), $exception->getInvalidMessageParameters()); } return $value; diff --git a/src/Symfony/Component/Form/Resources/config/validation.xml b/src/Symfony/Component/Form/Resources/config/validation.xml index b2b935442d..918f101f42 100644 --- a/src/Symfony/Component/Form/Resources/config/validation.xml +++ b/src/Symfony/Component/Form/Resources/config/validation.xml @@ -6,8 +6,8 @@ - - - + + + diff --git a/src/Symfony/Component/Form/Resources/translations/validators.cs.xlf b/src/Symfony/Component/Form/Resources/translations/validators.cs.xlf index 776da49481..44d597db98 100644 --- a/src/Symfony/Component/Form/Resources/translations/validators.cs.xlf +++ b/src/Symfony/Component/Form/Resources/translations/validators.cs.xlf @@ -14,6 +14,10 @@ The CSRF token is invalid. Please try to resubmit the form. CSRF token je neplatný. Zkuste prosím znovu odeslat formulář. + + This value is not a valid HTML5 color. + Tato hodnota není platná HTML5 barva. + diff --git a/src/Symfony/Component/Form/Resources/translations/validators.hu.xlf b/src/Symfony/Component/Form/Resources/translations/validators.hu.xlf index 374cfaaea3..bc7f6055e4 100644 --- a/src/Symfony/Component/Form/Resources/translations/validators.hu.xlf +++ b/src/Symfony/Component/Form/Resources/translations/validators.hu.xlf @@ -14,6 +14,10 @@ The CSRF token is invalid. Please try to resubmit the form. Érvénytelen CSRF token. Kérem, próbálja újra elküldeni az űrlapot. + + This value is not a valid HTML5 color. + Ez az érték nem egy érvényes HTML5 szín. + diff --git a/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorTest.php b/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorTest.php index ea216673f0..b6e0cfa164 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorTest.php @@ -658,7 +658,8 @@ class FormValidatorTest extends ConstraintValidatorTestCase $this->assertTrue($form->isSubmitted()); $this->assertTrue($form->isSynchronized()); - $this->expectNoValidate(); + + $this->expectValidateValueAt(0, 'children[child]', $form->get('child'), new Form()); $this->validator->validate($form, new Form()); @@ -682,7 +683,8 @@ class FormValidatorTest extends ConstraintValidatorTestCase $this->assertTrue($form->isSubmitted()); $this->assertTrue($form->isSynchronized()); - $this->expectNoValidate(); + + $this->expectValidateValueAt(0, 'children[child]', $form->get('child'), new Form()); $this->validator->validate($form, new Form()); diff --git a/src/Symfony/Component/Form/Tests/Extension/Validator/ValidatorExtensionTest.php b/src/Symfony/Component/Form/Tests/Extension/Validator/ValidatorExtensionTest.php index 383b7556d5..4324541cb7 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Validator/ValidatorExtensionTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Validator/ValidatorExtensionTest.php @@ -54,9 +54,8 @@ class ValidatorExtensionTest extends TestCase $this->assertInstanceOf(FormConstraint::class, $metadata->getConstraints()[0]); $this->assertSame(CascadingStrategy::NONE, $metadata->cascadingStrategy); - $this->assertSame(TraversalStrategy::IMPLICIT, $metadata->traversalStrategy); - $this->assertSame(CascadingStrategy::CASCADE, $metadata->getPropertyMetadata('children')[0]->cascadingStrategy); - $this->assertSame(TraversalStrategy::IMPLICIT, $metadata->getPropertyMetadata('children')[0]->traversalStrategy); + $this->assertSame(TraversalStrategy::NONE, $metadata->traversalStrategy); + $this->assertCount(0, $metadata->getPropertyMetadata('children')); } public function testDataConstraintsInvalidateFormEvenIfFieldIsNotSubmitted() @@ -142,6 +141,33 @@ class ValidatorExtensionTest extends TestCase $this->assertInstanceOf(Length::class, $errors[1]->getCause()->getConstraint()); } + public function testConstraintsInDifferentGroupsOnSingleField() + { + $form = $this->createForm(FormType::class, null, [ + 'validation_groups' => new GroupSequence(['group1', 'group2']), + ]) + ->add('foo', TextType::class, [ + 'constraints' => [ + new NotBlank([ + 'groups' => ['group1'], + ]), + new Length([ + 'groups' => ['group2'], + 'max' => 3, + ]), + ], + ]); + $form->submit([ + 'foo' => 'test@example.com', + ]); + + $errors = $form->getErrors(true); + + $this->assertFalse($form->isValid()); + $this->assertCount(1, $errors); + $this->assertInstanceOf(Length::class, $errors[0]->getCause()->getConstraint()); + } + private function createForm($type, $data = null, array $options = []) { $validator = Validation::createValidatorBuilder() diff --git a/src/Symfony/Component/HttpClient/Tests/DataCollector/HttpClientDataCollectorTest.php b/src/Symfony/Component/HttpClient/Tests/DataCollector/HttpClientDataCollectorTest.php index d173c17250..b578710546 100755 --- a/src/Symfony/Component/HttpClient/Tests/DataCollector/HttpClientDataCollectorTest.php +++ b/src/Symfony/Component/HttpClient/Tests/DataCollector/HttpClientDataCollectorTest.php @@ -9,6 +9,8 @@ * file that was distributed with this source code. */ +namespace Symfony\Component\HttpClient\Tests\DataCollector; + use PHPUnit\Framework\TestCase; use Symfony\Component\HttpClient\DataCollector\HttpClientDataCollector; use Symfony\Component\HttpClient\NativeHttpClient; @@ -19,9 +21,13 @@ use Symfony\Contracts\HttpClient\Test\TestHttpServer; class HttpClientDataCollectorTest extends TestCase { - public function testItCollectsRequestCount() + public static function setUpBeforeClass(): void { TestHttpServer::start(); + } + + public function testItCollectsRequestCount() + { $httpClient1 = $this->httpClientThatHasTracedRequests([ [ 'method' => 'GET', @@ -50,7 +56,6 @@ class HttpClientDataCollectorTest extends TestCase public function testItCollectsErrorCount() { - TestHttpServer::start(); $httpClient1 = $this->httpClientThatHasTracedRequests([ [ 'method' => 'GET', @@ -80,7 +85,6 @@ class HttpClientDataCollectorTest extends TestCase public function testItCollectsErrorCountByClient() { - TestHttpServer::start(); $httpClient1 = $this->httpClientThatHasTracedRequests([ [ 'method' => 'GET', @@ -113,7 +117,6 @@ class HttpClientDataCollectorTest extends TestCase public function testItCollectsTracesByClient() { - TestHttpServer::start(); $httpClient1 = $this->httpClientThatHasTracedRequests([ [ 'method' => 'GET', @@ -146,7 +149,6 @@ class HttpClientDataCollectorTest extends TestCase public function testItIsEmptyAfterReset() { - TestHttpServer::start(); $httpClient1 = $this->httpClientThatHasTracedRequests([ [ 'method' => 'GET', diff --git a/src/Symfony/Component/HttpClient/Tests/HttplugClientTest.php b/src/Symfony/Component/HttpClient/Tests/HttplugClientTest.php index 66a8cef6e7..6a8cadbbc8 100644 --- a/src/Symfony/Component/HttpClient/Tests/HttplugClientTest.php +++ b/src/Symfony/Component/HttpClient/Tests/HttplugClientTest.php @@ -22,8 +22,6 @@ use Symfony\Contracts\HttpClient\Test\TestHttpServer; class HttplugClientTest extends TestCase { - private static $server; - public static function setUpBeforeClass(): void { TestHttpServer::start(); diff --git a/src/Symfony/Component/HttpClient/Tests/Psr18ClientTest.php b/src/Symfony/Component/HttpClient/Tests/Psr18ClientTest.php index 42e627b590..1ef36fc5bd 100644 --- a/src/Symfony/Component/HttpClient/Tests/Psr18ClientTest.php +++ b/src/Symfony/Component/HttpClient/Tests/Psr18ClientTest.php @@ -21,8 +21,6 @@ use Symfony\Contracts\HttpClient\Test\TestHttpServer; class Psr18ClientTest extends TestCase { - private static $server; - public static function setUpBeforeClass(): void { TestHttpServer::start(); diff --git a/src/Symfony/Component/HttpClient/Tests/TraceableHttpClientTest.php b/src/Symfony/Component/HttpClient/Tests/TraceableHttpClientTest.php index 8c0ec0d48c..9b6752f7b6 100755 --- a/src/Symfony/Component/HttpClient/Tests/TraceableHttpClientTest.php +++ b/src/Symfony/Component/HttpClient/Tests/TraceableHttpClientTest.php @@ -21,6 +21,11 @@ use Symfony\Contracts\HttpClient\Test\TestHttpServer; class TraceableHttpClientTest extends TestCase { + public static function setUpBeforeClass(): void + { + TestHttpServer::start(); + } + public function testItTracesRequest() { $httpClient = $this->getMockBuilder(HttpClientInterface::class)->getMock(); @@ -85,8 +90,6 @@ class TraceableHttpClientTest extends TestCase public function testStream() { - TestHttpServer::start(); - $sut = new TraceableHttpClient(new NativeHttpClient()); $response = $sut->request('GET', 'http://localhost:8057/chunked'); $chunks = []; diff --git a/src/Symfony/Component/HttpKernel/Controller/ControllerResolver.php b/src/Symfony/Component/HttpKernel/Controller/ControllerResolver.php index 0f2f6d980a..cffe45904d 100644 --- a/src/Symfony/Component/HttpKernel/Controller/ControllerResolver.php +++ b/src/Symfony/Component/HttpKernel/Controller/ControllerResolver.php @@ -64,7 +64,7 @@ class ControllerResolver implements ControllerResolverInterface } if (!\is_callable($controller)) { - throw new \InvalidArgumentException(sprintf('The controller for URI "%s" is not callable: '.$this->getControllerError($controller), $request->getPathInfo())); + throw new \InvalidArgumentException(sprintf('The controller for URI "%s" is not callable: ', $request->getPathInfo()).$this->getControllerError($controller)); } return $controller; @@ -72,7 +72,7 @@ class ControllerResolver implements ControllerResolverInterface if (\is_object($controller)) { if (!\is_callable($controller)) { - throw new \InvalidArgumentException(sprintf('The controller for URI "%s" is not callable: '.$this->getControllerError($controller), $request->getPathInfo())); + throw new \InvalidArgumentException(sprintf('The controller for URI "%s" is not callable: ', $request->getPathInfo()).$this->getControllerError($controller)); } return $controller; @@ -89,7 +89,7 @@ class ControllerResolver implements ControllerResolverInterface } if (!\is_callable($callable)) { - throw new \InvalidArgumentException(sprintf('The controller for URI "%s" is not callable: '.$this->getControllerError($callable), $request->getPathInfo())); + throw new \InvalidArgumentException(sprintf('The controller for URI "%s" is not callable: ', $request->getPathInfo()).$this->getControllerError($callable)); } return $callable; diff --git a/src/Symfony/Component/Intl/Data/Bundle/Reader/JsonBundleReader.php b/src/Symfony/Component/Intl/Data/Bundle/Reader/JsonBundleReader.php index 566fced840..36b578d84c 100644 --- a/src/Symfony/Component/Intl/Data/Bundle/Reader/JsonBundleReader.php +++ b/src/Symfony/Component/Intl/Data/Bundle/Reader/JsonBundleReader.php @@ -46,7 +46,7 @@ class JsonBundleReader implements BundleReaderInterface $data = json_decode(file_get_contents($fileName), true); if (null === $data) { - throw new RuntimeException(sprintf('The resource bundle "%s" contains invalid JSON: '.json_last_error_msg(), $fileName)); + throw new RuntimeException(sprintf('The resource bundle "%s" contains invalid JSON: ', $fileName).json_last_error_msg()); } return $data; diff --git a/src/Symfony/Component/Ldap/Adapter/ExtLdap/EntryManager.php b/src/Symfony/Component/Ldap/Adapter/ExtLdap/EntryManager.php index 674ae0957a..33d3bb957f 100644 --- a/src/Symfony/Component/Ldap/Adapter/ExtLdap/EntryManager.php +++ b/src/Symfony/Component/Ldap/Adapter/ExtLdap/EntryManager.php @@ -38,7 +38,7 @@ class EntryManager implements EntryManagerInterface $con = $this->getConnectionResource(); if (!@ldap_add($con, $entry->getDn(), $entry->getAttributes())) { - throw new LdapException(sprintf('Could not add entry "%s": '.ldap_error($con), $entry->getDn()), ldap_errno($con)); + throw new LdapException(sprintf('Could not add entry "%s": ', $entry->getDn()).ldap_error($con), ldap_errno($con)); } return $this; @@ -52,7 +52,7 @@ class EntryManager implements EntryManagerInterface $con = $this->getConnectionResource(); if (!@ldap_modify($con, $entry->getDn(), $entry->getAttributes())) { - throw new LdapException(sprintf('Could not update entry "%s": '.ldap_error($con), $entry->getDn()), ldap_errno($con)); + throw new LdapException(sprintf('Could not update entry "%s": ', $entry->getDn()).ldap_error($con), ldap_errno($con)); } } @@ -64,7 +64,7 @@ class EntryManager implements EntryManagerInterface $con = $this->getConnectionResource(); if (!@ldap_delete($con, $entry->getDn())) { - throw new LdapException(sprintf('Could not remove entry "%s": '.ldap_error($con), $entry->getDn()), ldap_errno($con)); + throw new LdapException(sprintf('Could not remove entry "%s": ', $entry->getDn()).ldap_error($con), ldap_errno($con)); } } @@ -79,7 +79,7 @@ class EntryManager implements EntryManagerInterface $con = $this->getConnectionResource(); if (!@ldap_mod_add($con, $entry->getDn(), [$attribute => $values])) { - throw new LdapException(sprintf('Could not add values to entry "%s", attribute %s: '.ldap_error($con), $entry->getDn(), $attribute), ldap_errno($con)); + throw new LdapException(sprintf('Could not add values to entry "%s", attribute %s: ', $entry->getDn(), $attribute).ldap_error($con), ldap_errno($con)); } } @@ -94,7 +94,7 @@ class EntryManager implements EntryManagerInterface $con = $this->getConnectionResource(); if (!@ldap_mod_del($con, $entry->getDn(), [$attribute => $values])) { - throw new LdapException(sprintf('Could not remove values from entry "%s", attribute %s: '.ldap_error($con), $entry->getDn(), $attribute), ldap_errno($con)); + throw new LdapException(sprintf('Could not remove values from entry "%s", attribute %s: ', $entry->getDn(), $attribute).ldap_error($con), ldap_errno($con)); } } @@ -106,7 +106,7 @@ class EntryManager implements EntryManagerInterface $con = $this->getConnectionResource(); if (!@ldap_rename($con, $entry->getDn(), $newRdn, null, $removeOldRdn)) { - throw new LdapException(sprintf('Could not rename entry "%s" to "%s": '.ldap_error($con), $entry->getDn(), $newRdn), ldap_errno($con)); + throw new LdapException(sprintf('Could not rename entry "%s" to "%s": ', $entry->getDn(), $newRdn).ldap_error($con), ldap_errno($con)); } } @@ -122,7 +122,7 @@ class EntryManager implements EntryManagerInterface $rdn = $this->parseRdnFromEntry($entry); // deleteOldRdn does not matter here, since the Rdn will not be changing in the move. if (!@ldap_rename($con, $entry->getDn(), $rdn, $newParent, true)) { - throw new LdapException(sprintf('Could not move entry "%s" to "%s": '.ldap_error($con), $entry->getDn(), $newParent), ldap_errno($con)); + throw new LdapException(sprintf('Could not move entry "%s" to "%s": ', $entry->getDn(), $newParent).ldap_error($con), ldap_errno($con)); } } @@ -152,7 +152,7 @@ class EntryManager implements EntryManagerInterface } if (!@ldap_modify_batch($this->getConnectionResource(), $dn, $operationsMapped)) { - throw new UpdateOperationException(sprintf('Error executing UpdateOperation on "%s": '.ldap_error($this->getConnectionResource()), $dn), ldap_errno($con)); + throw new UpdateOperationException(sprintf('Error executing UpdateOperation on "%s": ', $dn).ldap_error($this->getConnectionResource()), ldap_errno($con)); } } diff --git a/src/Symfony/Component/Mailer/Bridge/Amazon/Transport/SesApiTransport.php b/src/Symfony/Component/Mailer/Bridge/Amazon/Transport/SesApiTransport.php index dc174e3cd1..0a7fde06ce 100644 --- a/src/Symfony/Component/Mailer/Bridge/Amazon/Transport/SesApiTransport.php +++ b/src/Symfony/Component/Mailer/Bridge/Amazon/Transport/SesApiTransport.php @@ -67,7 +67,7 @@ class SesApiTransport extends AbstractApiTransport $result = new \SimpleXMLElement($response->getContent(false)); if (200 !== $response->getStatusCode()) { - throw new HttpTransportException(sprintf('Unable to send an email: '.$result->Error->Message.' (code %d).', $result->Error->Code), $response); + throw new HttpTransportException('Unable to send an email: '.$result->Error->Message.sprintf(' (code %d).', $result->Error->Code), $response); } $property = $payload['Action'].'Result'; diff --git a/src/Symfony/Component/Mailer/Bridge/Amazon/Transport/SesHttpTransport.php b/src/Symfony/Component/Mailer/Bridge/Amazon/Transport/SesHttpTransport.php index 2b4dd651e2..e3fefd4583 100644 --- a/src/Symfony/Component/Mailer/Bridge/Amazon/Transport/SesHttpTransport.php +++ b/src/Symfony/Component/Mailer/Bridge/Amazon/Transport/SesHttpTransport.php @@ -67,7 +67,7 @@ class SesHttpTransport extends AbstractHttpTransport $result = new \SimpleXMLElement($response->getContent(false)); if (200 !== $response->getStatusCode()) { - throw new HttpTransportException(sprintf('Unable to send an email: '.$result->Error->Message.' (code %d).', $result->Error->Code), $response); + throw new HttpTransportException('Unable to send an email: '.$result->Error->Message.sprintf(' (code %d).', $result->Error->Code), $response); } $message->setMessageId($result->SendRawEmailResult->MessageId); diff --git a/src/Symfony/Component/Mailer/Bridge/Mailchimp/Transport/MandrillApiTransport.php b/src/Symfony/Component/Mailer/Bridge/Mailchimp/Transport/MandrillApiTransport.php index 82508e6481..50be4e02cb 100644 --- a/src/Symfony/Component/Mailer/Bridge/Mailchimp/Transport/MandrillApiTransport.php +++ b/src/Symfony/Component/Mailer/Bridge/Mailchimp/Transport/MandrillApiTransport.php @@ -53,7 +53,7 @@ class MandrillApiTransport extends AbstractApiTransport $result = $response->toArray(false); if (200 !== $response->getStatusCode()) { if ('error' === ($result['status'] ?? false)) { - throw new HttpTransportException(sprintf('Unable to send an email: '.$result['message'].' (code %d).', $result['code']), $response); + throw new HttpTransportException('Unable to send an email: '.$result['message'].sprintf(' (code %d).', $result['code']), $response); } throw new HttpTransportException(sprintf('Unable to send an email (code %d).', $result['code']), $response); diff --git a/src/Symfony/Component/Mailer/Bridge/Mailchimp/Transport/MandrillHttpTransport.php b/src/Symfony/Component/Mailer/Bridge/Mailchimp/Transport/MandrillHttpTransport.php index e67b17ecb9..2bb23c5af2 100644 --- a/src/Symfony/Component/Mailer/Bridge/Mailchimp/Transport/MandrillHttpTransport.php +++ b/src/Symfony/Component/Mailer/Bridge/Mailchimp/Transport/MandrillHttpTransport.php @@ -59,7 +59,7 @@ class MandrillHttpTransport extends AbstractHttpTransport $result = $response->toArray(false); if (200 !== $response->getStatusCode()) { if ('error' === ($result['status'] ?? false)) { - throw new HttpTransportException(sprintf('Unable to send an email: '.$result['message'].' (code %d).', $result['code']), $response); + throw new HttpTransportException('Unable to send an email: '.$result['message'].sprintf(' (code %d).', $result['code']), $response); } throw new HttpTransportException(sprintf('Unable to send an email (code %d).', $result['code']), $response); diff --git a/src/Symfony/Component/Mailer/Bridge/Mailgun/Transport/MailgunApiTransport.php b/src/Symfony/Component/Mailer/Bridge/Mailgun/Transport/MailgunApiTransport.php index 1f6dabffbd..636a218de1 100644 --- a/src/Symfony/Component/Mailer/Bridge/Mailgun/Transport/MailgunApiTransport.php +++ b/src/Symfony/Component/Mailer/Bridge/Mailgun/Transport/MailgunApiTransport.php @@ -67,10 +67,10 @@ class MailgunApiTransport extends AbstractApiTransport $result = $response->toArray(false); if (200 !== $response->getStatusCode()) { if ('application/json' === $response->getHeaders(false)['content-type'][0]) { - throw new HttpTransportException(sprintf('Unable to send an email: '.$result['message'].' (code %d).', $response->getStatusCode()), $response); + throw new HttpTransportException('Unable to send an email: '.$result['message'].sprintf(' (code %d).', $response->getStatusCode()), $response); } - throw new HttpTransportException(sprintf('Unable to send an email: '.$response->getContent(false).' (code %d).', $response->getStatusCode()), $response); + throw new HttpTransportException('Unable to send an email: '.$response->getContent(false).sprintf(' (code %d).', $response->getStatusCode()), $response); } $sentMessage->setMessageId($result['id']); diff --git a/src/Symfony/Component/Mailer/Bridge/Mailgun/Transport/MailgunHttpTransport.php b/src/Symfony/Component/Mailer/Bridge/Mailgun/Transport/MailgunHttpTransport.php index 9e143b34b3..150c5c8245 100644 --- a/src/Symfony/Component/Mailer/Bridge/Mailgun/Transport/MailgunHttpTransport.php +++ b/src/Symfony/Component/Mailer/Bridge/Mailgun/Transport/MailgunHttpTransport.php @@ -69,10 +69,10 @@ class MailgunHttpTransport extends AbstractHttpTransport $result = $response->toArray(false); if (200 !== $response->getStatusCode()) { if ('application/json' === $response->getHeaders(false)['content-type'][0]) { - throw new HttpTransportException(sprintf('Unable to send an email: '.$result['message'].' (code %d).', $response->getStatusCode()), $response); + throw new HttpTransportException('Unable to send an email: '.$result['message'].sprintf(' (code %d).', $response->getStatusCode()), $response); } - throw new HttpTransportException(sprintf('Unable to send an email: '.$response->getContent(false).' (code %d).', $response->getStatusCode()), $response); + throw new HttpTransportException('Unable to send an email: '.$response->getContent(false).sprintf(' (code %d).', $response->getStatusCode()), $response); } $message->setMessageId($result['id']); diff --git a/src/Symfony/Component/Mailer/Bridge/Postmark/Transport/PostmarkApiTransport.php b/src/Symfony/Component/Mailer/Bridge/Postmark/Transport/PostmarkApiTransport.php index 61b8fad3d8..2b9930a308 100644 --- a/src/Symfony/Component/Mailer/Bridge/Postmark/Transport/PostmarkApiTransport.php +++ b/src/Symfony/Component/Mailer/Bridge/Postmark/Transport/PostmarkApiTransport.php @@ -56,7 +56,7 @@ class PostmarkApiTransport extends AbstractApiTransport $result = $response->toArray(false); if (200 !== $response->getStatusCode()) { - throw new HttpTransportException(sprintf('Unable to send an email: '.$result['Message'].' (code %d).', $result['ErrorCode']), $response); + throw new HttpTransportException('Unable to send an email: '.$result['Message'].sprintf(' (code %d).', $result['ErrorCode']), $response); } $sentMessage->setMessageId($result['MessageID']); diff --git a/src/Symfony/Component/Mailer/Bridge/Sendgrid/Transport/SendgridApiTransport.php b/src/Symfony/Component/Mailer/Bridge/Sendgrid/Transport/SendgridApiTransport.php index aee333ddb1..28eb76d25b 100644 --- a/src/Symfony/Component/Mailer/Bridge/Sendgrid/Transport/SendgridApiTransport.php +++ b/src/Symfony/Component/Mailer/Bridge/Sendgrid/Transport/SendgridApiTransport.php @@ -53,7 +53,7 @@ class SendgridApiTransport extends AbstractApiTransport if (202 !== $response->getStatusCode()) { $errors = $response->toArray(false); - throw new HttpTransportException(sprintf('Unable to send an email: '.implode('; ', array_column($errors['errors'], 'message')).' (code %d).', $response->getStatusCode()), $response); + throw new HttpTransportException('Unable to send an email: '.implode('; ', array_column($errors['errors'], 'message')).sprintf(' (code %d).', $response->getStatusCode()), $response); } $sentMessage->setMessageId($response->getHeaders(false)['x-message-id'][0]); diff --git a/src/Symfony/Component/Mailer/Transport/Smtp/Stream/SocketStream.php b/src/Symfony/Component/Mailer/Transport/Smtp/Stream/SocketStream.php index 3c131051f8..41e8c368e1 100644 --- a/src/Symfony/Component/Mailer/Transport/Smtp/Stream/SocketStream.php +++ b/src/Symfony/Component/Mailer/Transport/Smtp/Stream/SocketStream.php @@ -136,7 +136,7 @@ final class SocketStream extends AbstractStream $timeout = $this->getTimeout(); set_error_handler(function ($type, $msg) { - throw new TransportException(sprintf('Connection could not be established with host "%s": '.$msg, $this->url)); + throw new TransportException(sprintf('Connection could not be established with host "%s": ', $this->url).$msg); }); try { $this->stream = stream_socket_client($this->url, $errno, $errstr, $timeout, STREAM_CLIENT_CONNECT, $streamContext); diff --git a/src/Symfony/Component/Notifier/Bridge/Nexmo/NexmoTransport.php b/src/Symfony/Component/Notifier/Bridge/Nexmo/NexmoTransport.php index e50c6f1837..56b72ee012 100644 --- a/src/Symfony/Component/Notifier/Bridge/Nexmo/NexmoTransport.php +++ b/src/Symfony/Component/Notifier/Bridge/Nexmo/NexmoTransport.php @@ -70,7 +70,7 @@ final class NexmoTransport extends AbstractTransport $result = $response->toArray(false); foreach ($result['messages'] as $msg) { if ($msg['status'] ?? false) { - throw new TransportException(sprintf('Unable to send the SMS: '.$msg['error-text'].' (%s).', $msg['status']), $response); + throw new TransportException('Unable to send the SMS: '.$msg['error-text'].sprintf(' (code %s).', $msg['status']), $response); } } } diff --git a/src/Symfony/Component/Notifier/Bridge/Telegram/TelegramTransport.php b/src/Symfony/Component/Notifier/Bridge/Telegram/TelegramTransport.php index 7cd637f579..5e2710ac7a 100644 --- a/src/Symfony/Component/Notifier/Bridge/Telegram/TelegramTransport.php +++ b/src/Symfony/Component/Notifier/Bridge/Telegram/TelegramTransport.php @@ -80,7 +80,7 @@ final class TelegramTransport extends AbstractTransport if (200 !== $response->getStatusCode()) { $result = $response->toArray(false); - throw new TransportException(sprintf('Unable to post the Telegram message: '.$result['description'].' (%s).', $result['error_code']), $response); + throw new TransportException('Unable to post the Telegram message: '.$result['description'].sprintf(' (code %s).', $result['error_code']), $response); } } } diff --git a/src/Symfony/Component/Notifier/Bridge/Twilio/TwilioTransport.php b/src/Symfony/Component/Notifier/Bridge/Twilio/TwilioTransport.php index b09a7277a5..92a31fbc0a 100644 --- a/src/Symfony/Component/Notifier/Bridge/Twilio/TwilioTransport.php +++ b/src/Symfony/Component/Notifier/Bridge/Twilio/TwilioTransport.php @@ -70,7 +70,7 @@ final class TwilioTransport extends AbstractTransport if (201 !== $response->getStatusCode()) { $error = $response->toArray(false); - throw new TransportException(sprintf('Unable to send the SMS: '.$error['message'].' (see %s).', $error['more_info']), $response); + throw new TransportException('Unable to send the SMS: '.$error['message'].sprintf(' (see %s).', $error['more_info']), $response); } } } diff --git a/src/Symfony/Component/Process/Process.php b/src/Symfony/Component/Process/Process.php index 33fb5f9eef..e1efb9ccc8 100644 --- a/src/Symfony/Component/Process/Process.php +++ b/src/Symfony/Component/Process/Process.php @@ -1608,7 +1608,7 @@ class Process implements \IteratorAggregate { return preg_replace_callback('/"\$\{:([_a-zA-Z]++[_a-zA-Z0-9]*+)\}"/', function ($matches) use ($commandline, $env) { if (!isset($env[$matches[1]]) || false === $env[$matches[1]]) { - throw new InvalidArgumentException(sprintf('Command line is missing a value for parameter "%s": '.$commandline, $matches[1])); + throw new InvalidArgumentException(sprintf('Command line is missing a value for parameter "%s": ', $matches[1]).$commandline); } return $this->escapeArgument($env[$matches[1]]); diff --git a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php index 1fb342f0fa..7c1f36f7aa 100644 --- a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php +++ b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php @@ -404,8 +404,15 @@ class PropertyAccessor implements PropertyAccessorInterface try { $result[self::VALUE] = $object->$name(); } catch (\TypeError $e) { - if (preg_match((sprintf('/^Return value of %s::%s\(\) must be of (?:the )?type (\w+), null returned$/', preg_quote(\get_class($object)), $name)), $e->getMessage(), $matches)) { - throw new UninitializedPropertyException(sprintf('The method "%s::%s()" returned "null", but expected type "%3$s". Did you forget to initialize a property or to make the return type nullable using "?%3$s"?', \get_class($object), $name, $matches[1]), 0, $e); + list($trace) = $e->getTrace(); + + // handle uninitialized properties in PHP >= 7 + if (__FILE__ === $trace['file'] + && $access[self::ACCESS_NAME] === $trace['function'] + && $object instanceof $trace['class'] + && preg_match((sprintf('/Return value (?:of .*::\w+\(\) )?must be of (?:the )?type (\w+), null returned$/')), $e->getMessage(), $matches) + ) { + throw new UninitializedPropertyException(sprintf('The method "%s::%s()" returned "null", but expected type "%3$s". Did you forget to initialize a property or to make the return type nullable using "?%3$s"?', false === strpos(\get_class($object), "@anonymous\0") ? \get_class($object) : (get_parent_class($object) ?: 'class').'@anonymous', $access[self::ACCESS_NAME], $matches[1]), 0, $e); } throw $e; diff --git a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php index 34f0fc4c44..89e02f334d 100644 --- a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php +++ b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php @@ -153,6 +153,59 @@ class PropertyAccessorTest extends TestCase $this->propertyAccessor->getValue(new UninitializedPrivateProperty(), 'uninitialized'); } + /** + * @requires PHP 7 + */ + public function testGetValueThrowsExceptionIfUninitializedPropertyWithGetterOfAnonymousClass() + { + $this->expectException('Symfony\Component\PropertyAccess\Exception\AccessException'); + $this->expectExceptionMessage('The method "class@anonymous::getUninitialized()" returned "null", but expected type "array". Did you forget to initialize a property or to make the return type nullable using "?array"?'); + + $object = eval('return new class() { + private $uninitialized; + + public function getUninitialized(): array + { + return $this->uninitialized; + } + };'); + + $this->propertyAccessor->getValue($object, 'uninitialized'); + } + + /** + * @requires PHP 7 + */ + public function testGetValueThrowsExceptionIfUninitializedPropertyWithGetterOfAnonymousStdClass() + { + $this->expectException('Symfony\Component\PropertyAccess\Exception\AccessException'); + $this->expectExceptionMessage('The method "stdClass@anonymous::getUninitialized()" returned "null", but expected type "array". Did you forget to initialize a property or to make the return type nullable using "?array"?'); + + $object = eval('return new class() extends \stdClass { + private $uninitialized; + + public function getUninitialized(): array + { + return $this->uninitialized; + } + };'); + + $this->propertyAccessor->getValue($object, 'uninitialized'); + } + + /** + * @requires PHP 7 + */ + public function testGetValueThrowsExceptionIfUninitializedPropertyWithGetterOfAnonymousChildClass() + { + $this->expectException('Symfony\Component\PropertyAccess\Exception\AccessException'); + $this->expectExceptionMessage('The method "Symfony\Component\PropertyAccess\Tests\Fixtures\UninitializedPrivateProperty@anonymous::getUninitialized()" returned "null", but expected type "array". Did you forget to initialize a property or to make the return type nullable using "?array"?'); + + $object = eval('return new class() extends \Symfony\Component\PropertyAccess\Tests\Fixtures\UninitializedPrivateProperty {};'); + + $this->propertyAccessor->getValue($object, 'uninitialized'); + } + public function testGetValueThrowsExceptionIfNotArrayAccess() { $this->expectException('Symfony\Component\PropertyAccess\Exception\NoSuchIndexException'); diff --git a/src/Symfony/Component/Routing/Loader/AnnotationClassLoader.php b/src/Symfony/Component/Routing/Loader/AnnotationClassLoader.php index 8800e89854..89d0bfa317 100644 --- a/src/Symfony/Component/Routing/Loader/AnnotationClassLoader.php +++ b/src/Symfony/Component/Routing/Loader/AnnotationClassLoader.php @@ -22,7 +22,7 @@ use Symfony\Component\Routing\RouteCollection; /** * AnnotationClassLoader loads routing information from a PHP class and its methods. * - * You need to define an implementation for the getRouteDefaults() method. Most of the + * You need to define an implementation for the configureRoute() method. Most of the * time, this method should define some PHP callable to be called for the route * (a controller in MVC speak). * diff --git a/src/Symfony/Component/Security/Http/Firewall/ExceptionListener.php b/src/Symfony/Component/Security/Http/Firewall/ExceptionListener.php index f07843ec8b..dde4c01c49 100644 --- a/src/Symfony/Component/Security/Http/Firewall/ExceptionListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/ExceptionListener.php @@ -185,7 +185,7 @@ class ExceptionListener private function handleLogoutException(ExceptionEvent $event, LogoutException $exception): void { - $event->setException(new AccessDeniedHttpException($exception->getMessage(), $exception)); + $event->setThrowable(new AccessDeniedHttpException($exception->getMessage(), $exception)); if (null !== $this->logger) { $this->logger->info('A LogoutException was thrown; wrapping with AccessDeniedHttpException', ['exception' => $exception]); diff --git a/src/Symfony/Component/Security/Http/Tests/Firewall/ExceptionListenerTest.php b/src/Symfony/Component/Security/Http/Tests/Firewall/ExceptionListenerTest.php index 53204129fe..7328394b48 100644 --- a/src/Symfony/Component/Security/Http/Tests/Firewall/ExceptionListenerTest.php +++ b/src/Symfony/Component/Security/Http/Tests/Firewall/ExceptionListenerTest.php @@ -165,8 +165,8 @@ class ExceptionListenerTest extends TestCase $listener = $this->createExceptionListener(); $listener->onKernelException($event); - $this->assertEquals('Invalid CSRF.', $event->getException()->getMessage()); - $this->assertEquals(403, $event->getException()->getStatusCode()); + $this->assertEquals('Invalid CSRF.', $event->getThrowable()->getMessage()); + $this->assertEquals(403, $event->getThrowable()->getStatusCode()); } public function getAccessDeniedExceptionProvider() diff --git a/src/Symfony/Component/Serializer/Normalizer/DateTimeNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/DateTimeNormalizer.php index d37358ce23..c12e23470a 100644 --- a/src/Symfony/Component/Serializer/Normalizer/DateTimeNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/DateTimeNormalizer.php @@ -94,7 +94,7 @@ class DateTimeNormalizer implements NormalizerInterface, DenormalizerInterface, $dateTimeErrors = \DateTime::class === $type ? \DateTime::getLastErrors() : \DateTimeImmutable::getLastErrors(); - throw new NotNormalizableValueException(sprintf('Parsing datetime string "%s" using format "%s" resulted in %d errors:.'."\n".'%s', $data, $dateTimeFormat, $dateTimeErrors['error_count'], implode("\n", $this->formatDateTimeErrors($dateTimeErrors['errors'])))); + throw new NotNormalizableValueException(sprintf('Parsing datetime string "%s" using format "%s" resulted in %d errors: ', $data, $dateTimeFormat, $dateTimeErrors['error_count'])."\n".implode("\n", $this->formatDateTimeErrors($dateTimeErrors['errors']))); } try { diff --git a/src/Symfony/Component/Translation/Formatter/IntlFormatter.php b/src/Symfony/Component/Translation/Formatter/IntlFormatter.php index 9101a63aa2..f7f1c36a2a 100644 --- a/src/Symfony/Component/Translation/Formatter/IntlFormatter.php +++ b/src/Symfony/Component/Translation/Formatter/IntlFormatter.php @@ -40,7 +40,7 @@ class IntlFormatter implements IntlFormatterInterface try { $this->cache[$locale][$message] = $formatter = new \MessageFormatter($locale, $message); } catch (\IntlException $e) { - throw new InvalidArgumentException(sprintf('Invalid message format (error #%d): '.intl_get_error_message(), intl_get_error_code()), 0, $e); + throw new InvalidArgumentException(sprintf('Invalid message format (error #%d): ', intl_get_error_code()).intl_get_error_message(), 0, $e); } } @@ -52,7 +52,7 @@ class IntlFormatter implements IntlFormatterInterface } if (false === $message = $formatter->format($parameters)) { - throw new InvalidArgumentException(sprintf('Unable to format message (error #%s): '.$formatter->getErrorMessage(), $formatter->getErrorCode())); + throw new InvalidArgumentException(sprintf('Unable to format message (error #%s): ', $formatter->getErrorCode()).$formatter->getErrorMessage()); } return $message; diff --git a/src/Symfony/Component/Translation/Loader/XliffFileLoader.php b/src/Symfony/Component/Translation/Loader/XliffFileLoader.php index 291e8c3ebb..dc7e936860 100644 --- a/src/Symfony/Component/Translation/Loader/XliffFileLoader.php +++ b/src/Symfony/Component/Translation/Loader/XliffFileLoader.php @@ -63,7 +63,7 @@ class XliffFileLoader implements LoaderInterface $xliffVersion = XliffUtils::getVersionNumber($dom); if ($errors = XliffUtils::validateSchema($dom)) { - throw new InvalidResourceException(sprintf('Invalid resource provided: "%s"; Errors: '.XliffUtils::getErrorsAsString($errors), $resource)); + throw new InvalidResourceException(sprintf('Invalid resource provided: "%s"; Errors: ', $resource).XliffUtils::getErrorsAsString($errors)); } if ('1.2' === $xliffVersion) { diff --git a/src/Symfony/Component/Validator/Constraints/FileValidator.php b/src/Symfony/Component/Validator/Constraints/FileValidator.php index 52e9379441..31218c2020 100644 --- a/src/Symfony/Component/Validator/Constraints/FileValidator.php +++ b/src/Symfony/Component/Validator/Constraints/FileValidator.php @@ -13,8 +13,10 @@ namespace Symfony\Component\Validator\Constraints; use Symfony\Component\HttpFoundation\File\File as FileObject; use Symfony\Component\HttpFoundation\File\UploadedFile; +use Symfony\Component\Mime\MimeTypes; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\ConstraintValidator; +use Symfony\Component\Validator\Exception\LogicException; use Symfony\Component\Validator\Exception\UnexpectedTypeException; use Symfony\Component\Validator\Exception\UnexpectedValueException; @@ -170,12 +172,17 @@ class FileValidator extends ConstraintValidator } if ($constraint->mimeTypes) { - if (!$value instanceof FileObject) { - $value = new FileObject($value); + if ($value instanceof FileObject) { + $mime = $value->getMimeType(); + } elseif (class_exists(MimeTypes::class)) { + $mime = MimeTypes::getDefault()->guessMimeType($path); + } elseif (!class_exists(FileObject::class)) { + throw new LogicException('You cannot validate the mime-type of files as the Mime component is not installed. Try running "composer require symfony/mime".'); + } else { + $mime = (new FileObject($value))->getMimeType(); } $mimeTypes = (array) $constraint->mimeTypes; - $mime = $value->getMimeType(); foreach ($mimeTypes as $mimeType) { if ($mimeType === $mime) { diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.cs.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.cs.xlf index e637d09aa9..ce32f1368d 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.cs.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.cs.xlf @@ -334,6 +334,54 @@ This value should be valid JSON. Tato hodnota musí být validní JSON. + + This collection should contain only unique elements. + Tato kolekce musí obsahovat pouze unikátní prvky. + + + This value should be positive. + Tato hodnota musí být kladná. + + + This value should be either positive or zero. + Tato hodnota musí být buď kladná nebo nula. + + + This value should be negative. + Tato hodnota musí být záporná. + + + This value should be either negative or zero. + Tato hodnota musí být buď záporná nebo nula. + + + This value is not a valid timezone. + Tato časová zóna neexistuje. + + + This password has been leaked in a data breach, it must not be used. Please use another password. + Zadané heslo bylo součástí úniku dat, takže ho není možné použít. Použijte prosím jiné heslo. + + + This value should be between {{ min }} and {{ max }}. + Hodnota musí být mezi {{ min }} a {{ max }}. + + + This value is not a valid hostname. + Tato hodnota není platný hostname. + + + The number of elements in this collection should be a multiple of {{ compared_value }}. + Počet prvků v této kolekci musí být násobek {{ compared_value }}. + + + This value should satisfy at least one of the following constraints: + Tato hodnota musí splňovat alespoň jedno z následujících omezení: + + + Each element of this collection should satisfy its own set of constraints. + Každý prvek v této kolekci musí splňovat svá vlastní omezení. + diff --git a/src/Symfony/Component/Validator/Tests/Validator/RecursiveValidatorTest.php b/src/Symfony/Component/Validator/Tests/Validator/RecursiveValidatorTest.php index 3d41539de2..ef69a5bc00 100644 --- a/src/Symfony/Component/Validator/Tests/Validator/RecursiveValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Validator/RecursiveValidatorTest.php @@ -19,6 +19,8 @@ use Symfony\Component\Validator\Constraints\IsTrue; use Symfony\Component\Validator\Constraints\Length; use Symfony\Component\Validator\Constraints\NotBlank; use Symfony\Component\Validator\Constraints\NotNull; +use Symfony\Component\Validator\Constraints\Optional; +use Symfony\Component\Validator\Constraints\Required; use Symfony\Component\Validator\ConstraintValidatorFactory; use Symfony\Component\Validator\Context\ExecutionContextFactory; use Symfony\Component\Validator\Mapping\ClassMetadata; @@ -159,4 +161,18 @@ class RecursiveValidatorTest extends AbstractTest $this->assertInstanceOf(Length::class, $violations->get(1)->getConstraint()); $this->assertInstanceOf(Length::class, $violations->get(2)->getConstraint()); } + + public function testRequiredConstraintIsIgnored() + { + $violations = $this->validator->validate([], new Required()); + + $this->assertCount(0, $violations); + } + + public function testOptionalConstraintIsIgnored() + { + $violations = $this->validator->validate([], new Optional()); + + $this->assertCount(0, $violations); + } } diff --git a/src/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php b/src/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php index fd2e658afa..75d8e50175 100644 --- a/src/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php +++ b/src/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php @@ -13,6 +13,7 @@ namespace Symfony\Component\Validator\Validator; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Constraints\Composite; +use Symfony\Component\Validator\Constraints\Existence; use Symfony\Component\Validator\Constraints\GroupSequence; use Symfony\Component\Validator\Constraints\Valid; use Symfony\Component\Validator\ConstraintValidatorFactoryInterface; @@ -718,6 +719,10 @@ class RecursiveContextualValidator implements ContextualValidatorInterface $context->setGroup($group); foreach ($metadata->findConstraints($group) as $constraint) { + if ($constraint instanceof Existence) { + continue; + } + // Prevent duplicate validation of constraints, in the case // that constraints belong to multiple validated groups if (null !== $cacheKey) { diff --git a/src/Symfony/Component/VarDumper/Server/DumpServer.php b/src/Symfony/Component/VarDumper/Server/DumpServer.php index e9e93f137a..3e6343e350 100644 --- a/src/Symfony/Component/VarDumper/Server/DumpServer.php +++ b/src/Symfony/Component/VarDumper/Server/DumpServer.php @@ -41,7 +41,7 @@ class DumpServer public function start(): void { if (!$this->socket = stream_socket_server($this->host, $errno, $errstr)) { - throw new \RuntimeException(sprintf('Server start failed on "%s": '.$errstr.' '.$errno, $this->host)); + throw new \RuntimeException(sprintf('Server start failed on "%s": ', $this->host).$errstr.' '.$errno); } } diff --git a/src/Symfony/Contracts/HttpClient/Test/TestHttpServer.php b/src/Symfony/Contracts/HttpClient/Test/TestHttpServer.php index 16e9dad299..5dae27c912 100644 --- a/src/Symfony/Contracts/HttpClient/Test/TestHttpServer.php +++ b/src/Symfony/Contracts/HttpClient/Test/TestHttpServer.php @@ -16,12 +16,12 @@ use Symfony\Component\Process\Process; class TestHttpServer { - private static $started; + private static $process; public static function start() { - if (self::$started) { - return; + if (self::$process) { + self::$process->stop(); } $finder = new PhpExecutableFinder(); @@ -29,9 +29,10 @@ class TestHttpServer $process->setWorkingDirectory(__DIR__.'/Fixtures/web'); $process->start(); - register_shutdown_function([$process, 'stop']); - sleep('\\' === \DIRECTORY_SEPARATOR ? 10 : 1); + do { + usleep(50000); + } while (!@fopen('http://127.0.0.1:8057/', 'r')); - self::$started = true; + self::$process = $process; } }