From f94b7aadd3b4f9cbd393feaa5dd24b248fd73d3a Mon Sep 17 00:00:00 2001 From: Sergey Yastrebov Date: Fri, 20 Apr 2018 13:19:35 +0300 Subject: [PATCH 01/26] fix rounding from string --- .../MoneyToLocalizedStringTransformerTest.php | 10 ++++++++++ .../Component/Intl/NumberFormatter/NumberFormatter.php | 1 + .../NumberFormatter/AbstractNumberFormatterTest.php | 7 +++++++ 3 files changed, 18 insertions(+) diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/MoneyToLocalizedStringTransformerTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/MoneyToLocalizedStringTransformerTest.php index 1ad3aa1615c98..d9fafdff13a35 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/MoneyToLocalizedStringTransformerTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/MoneyToLocalizedStringTransformerTest.php @@ -78,6 +78,16 @@ public function testFloatToIntConversionMismatchOnReversTransform() $transformer = new MoneyToLocalizedStringTransformer(null, null, null, 100); IntlTestHelper::requireFullIntl($this, false); \Locale::setDefault('de_AT'); + $this->assertSame(3655, (int) $transformer->reverseTransform('36,55')); } + + public function testFloatToIntConversionMismatchOnTransform() + { + $transformer = new MoneyToLocalizedStringTransformer(null, null, MoneyToLocalizedStringTransformer::ROUND_DOWN, 100); + IntlTestHelper::requireFullIntl($this, false); + \Locale::setDefault('de_AT'); + + $this->assertSame('10,20', $transformer->transform(1020)); + } } diff --git a/src/Symfony/Component/Intl/NumberFormatter/NumberFormatter.php b/src/Symfony/Component/Intl/NumberFormatter/NumberFormatter.php index f68a60143a747..4a9acb5be9ac9 100644 --- a/src/Symfony/Component/Intl/NumberFormatter/NumberFormatter.php +++ b/src/Symfony/Component/Intl/NumberFormatter/NumberFormatter.php @@ -708,6 +708,7 @@ private function round($value, $precision) } elseif (isset(self::$customRoundingList[$roundingModeAttribute])) { $roundingCoef = pow(10, $precision); $value *= $roundingCoef; + $value = (float) (string) $value; switch ($roundingModeAttribute) { case self::ROUND_CEILING: diff --git a/src/Symfony/Component/Intl/Tests/NumberFormatter/AbstractNumberFormatterTest.php b/src/Symfony/Component/Intl/Tests/NumberFormatter/AbstractNumberFormatterTest.php index 6c1cc569a3297..6d681f32248bc 100644 --- a/src/Symfony/Component/Intl/Tests/NumberFormatter/AbstractNumberFormatterTest.php +++ b/src/Symfony/Component/Intl/Tests/NumberFormatter/AbstractNumberFormatterTest.php @@ -428,6 +428,7 @@ public function formatRoundingModeRoundHalfUpProvider() // array(1.125, '1.13'), array(1.127, '1.13'), array(1.129, '1.13'), + array(1020 / 100, '10.20'), ); } @@ -451,6 +452,7 @@ public function formatRoundingModeRoundHalfDownProvider() array(1.125, '1.12'), array(1.127, '1.13'), array(1.129, '1.13'), + array(1020 / 100, '10.20'), ); } @@ -474,6 +476,7 @@ public function formatRoundingModeRoundHalfEvenProvider() array(1.125, '1.12'), array(1.127, '1.13'), array(1.129, '1.13'), + array(1020 / 100, '10.20'), ); } @@ -498,6 +501,7 @@ public function formatRoundingModeRoundCeilingProvider() array(-1.123, '-1.12'), array(-1.125, '-1.12'), array(-1.127, '-1.12'), + array(1020 / 100, '10.20'), ); } @@ -522,6 +526,7 @@ public function formatRoundingModeRoundFloorProvider() array(-1.123, '-1.13'), array(-1.125, '-1.13'), array(-1.127, '-1.13'), + array(1020 / 100, '10.20'), ); } @@ -546,6 +551,7 @@ public function formatRoundingModeRoundDownProvider() array(-1.123, '-1.12'), array(-1.125, '-1.12'), array(-1.127, '-1.12'), + array(1020 / 100, '10.20'), ); } @@ -570,6 +576,7 @@ public function formatRoundingModeRoundUpProvider() array(-1.123, '-1.13'), array(-1.125, '-1.13'), array(-1.127, '-1.13'), + array(1020 / 100, '10.20'), ); } From 64a0f23affd4646983aca7e96daf44ef236df8d7 Mon Sep 17 00:00:00 2001 From: Nikolay Labinskiy Date: Thu, 26 Apr 2018 17:44:59 +0300 Subject: [PATCH 02/26] Fix #27011: Session ini_set bug --- .../Session/Storage/NativeSessionStorage.php | 6 +++--- .../Storage/NativeSessionStorageTest.php | 17 +++++++++++++++++ 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/NativeSessionStorage.php b/src/Symfony/Component/HttpFoundation/Session/Storage/NativeSessionStorage.php index f03cdf343024d..34d94c55bcc9f 100644 --- a/src/Symfony/Component/HttpFoundation/Session/Storage/NativeSessionStorage.php +++ b/src/Symfony/Component/HttpFoundation/Session/Storage/NativeSessionStorage.php @@ -340,7 +340,7 @@ public function setOptions(array $options) } $validOptions = array_flip(array( - 'cache_limiter', 'cookie_domain', 'cookie_httponly', + 'cache_expire', 'cache_limiter', 'cookie_domain', 'cookie_httponly', 'cookie_lifetime', 'cookie_path', 'cookie_secure', 'entropy_file', 'entropy_length', 'gc_divisor', 'gc_maxlifetime', 'gc_probability', 'hash_bits_per_character', @@ -348,13 +348,13 @@ public function setOptions(array $options) 'serialize_handler', 'use_strict_mode', 'use_cookies', 'use_only_cookies', 'use_trans_sid', 'upload_progress.enabled', 'upload_progress.cleanup', 'upload_progress.prefix', 'upload_progress.name', - 'upload_progress.freq', 'upload_progress.min-freq', 'url_rewriter.tags', + 'upload_progress.freq', 'upload_progress.min_freq', 'url_rewriter.tags', 'sid_length', 'sid_bits_per_character', 'trans_sid_hosts', 'trans_sid_tags', )); foreach ($options as $key => $value) { if (isset($validOptions[$key])) { - ini_set('session.'.$key, $value); + ini_set('url_rewriter.tags' !== $key ? 'session.'.$key : $key, $value); } } } diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/NativeSessionStorageTest.php b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/NativeSessionStorageTest.php index 3501f74784a9e..529583c01aade 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/NativeSessionStorageTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/NativeSessionStorageTest.php @@ -183,6 +183,23 @@ public function testCookieOptions() $this->assertEquals($options, $gco); } + public function testSessionOptions() + { + if (defined('HHVM_VERSION')) { + $this->markTestSkipped('HHVM is not handled in this test case.'); + } + + $options = array( + 'url_rewriter.tags' => 'a=href', + 'cache_expire' => '200', + ); + + $this->getStorage($options); + + $this->assertSame('a=href', ini_get('url_rewriter.tags')); + $this->assertSame('200', ini_get('session.cache_expire')); + } + /** * @expectedException \InvalidArgumentException */ From aa05f055262afa8531b1261ce9ae7a3fc5c5a73c Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Fri, 27 Apr 2018 11:17:42 +0200 Subject: [PATCH 03/26] bumped Symfony version to 2.7.47 --- src/Symfony/Component/HttpKernel/Kernel.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 43a441fbe25d5..33bbddabc9eff 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -58,12 +58,12 @@ abstract class Kernel implements KernelInterface, TerminableInterface protected $startTime; protected $loadClassCache; - const VERSION = '2.7.46'; - const VERSION_ID = 20746; + const VERSION = '2.7.47-DEV'; + const VERSION_ID = 20747; const MAJOR_VERSION = 2; const MINOR_VERSION = 7; - const RELEASE_VERSION = 46; - const EXTRA_VERSION = ''; + const RELEASE_VERSION = 47; + const EXTRA_VERSION = 'DEV'; const END_OF_MAINTENANCE = '05/2018'; const END_OF_LIFE = '05/2019'; From f0affb72926bcd20aab28bf4b444c2d69e67a143 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Sun, 29 Apr 2018 18:19:41 -0700 Subject: [PATCH 04/26] Fix suggest.psr/*-implementation in composer.json files --- src/Symfony/Component/Console/composer.json | 2 +- src/Symfony/Component/Templating/composer.json | 2 +- src/Symfony/Component/Translation/composer.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/Console/composer.json b/src/Symfony/Component/Console/composer.json index c4fb8c192a401..9f63559861811 100644 --- a/src/Symfony/Component/Console/composer.json +++ b/src/Symfony/Component/Console/composer.json @@ -27,7 +27,7 @@ "suggest": { "symfony/event-dispatcher": "", "symfony/process": "", - "psr/log": "For using the console logger" + "psr/log-implementation": "For using the console logger" }, "autoload": { "psr-4": { "Symfony\\Component\\Console\\": "" }, diff --git a/src/Symfony/Component/Templating/composer.json b/src/Symfony/Component/Templating/composer.json index 74f4412623328..334909c1a3a67 100644 --- a/src/Symfony/Component/Templating/composer.json +++ b/src/Symfony/Component/Templating/composer.json @@ -22,7 +22,7 @@ "psr/log": "~1.0" }, "suggest": { - "psr/log": "For using debug logging in loaders" + "psr/log-implementation": "For using debug logging in loaders" }, "autoload": { "psr-4": { "Symfony\\Component\\Templating\\": "" }, diff --git a/src/Symfony/Component/Translation/composer.json b/src/Symfony/Component/Translation/composer.json index 3ba1a64367895..128f37f0d7f5d 100644 --- a/src/Symfony/Component/Translation/composer.json +++ b/src/Symfony/Component/Translation/composer.json @@ -30,7 +30,7 @@ "suggest": { "symfony/config": "", "symfony/yaml": "", - "psr/log": "To use logging capability in translator" + "psr/log-implementation": "To use logging capability in translator" }, "autoload": { "psr-4": { "Symfony\\Component\\Translation\\": "" }, From ffe9aaa166d686a38abccfe96b5279219ec81c22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Fri, 27 Apr 2018 17:59:14 +0200 Subject: [PATCH 05/26] Added .github/CODEOWNERS refs https://help.github.com/articles/about-codeowners/ --- .github/CODEOWNERS | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .github/CODEOWNERS diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000000000..db1c2a8a6ff44 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,14 @@ +# LDAP +/src/Symfony/Component/Ldap/* @csarrazi +# Lock +/src/Symfony/Component/Lock/* @jderusse +# Messenger +/src/Symfony/Bridge/Doctrine/Messenger/* @sroze +/src/Symfony/Component/Messenger/* @sroze +# Workflow +/src/Symfony/Bridge/Twig/Extension/WorkflowExtension.php @lyrixx +/src/Symfony/Bridge/Twig/Tests/Extension/WorkflowExtensionTest.php @lyrixx +/src/Symfony/Bundle/FrameworkBundle/Command/WorkflowDumpCommand.php @lyrixx +/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/ValidateWorkflowsPass.php @lyrixx +/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/WorkflowGuardListenerPass.php @lyrixx +/src/Symfony/Component/Workflow/* @lyrixx From bbbafbad5cfaa8ddf3b39f31a3265d54cfdc554c Mon Sep 17 00:00:00 2001 From: Egidijus Gircys Date: Fri, 20 Apr 2018 15:16:14 +0200 Subject: [PATCH 06/26] Add CODE_OF_CONDUCT.md --- .github/CODE_OF_CONDUCT.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .github/CODE_OF_CONDUCT.md diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md new file mode 100644 index 0000000000000..16e2603b76a1d --- /dev/null +++ b/.github/CODE_OF_CONDUCT.md @@ -0,0 +1,8 @@ +# Code of Conduct + +This project follows a [Code of Conduct][code_of_conduct] in order to ensure an open and welcoming environment. +Please read the full text for understanding the accepted and unaccepted behavior. +Please read also the [reporting guidelines][guidelines], in case you encountered or witnessed any misbehavior. + +[code_of_conduct]: https://symfony.com/doc/current/contributing/code_of_conduct/index.html +[guidelines]: https://symfony.com/doc/current/contributing/code_of_conduct/reporting_guidelines.html From 27fddf5927ae59bac89ee4522ba5d795cfc4c627 Mon Sep 17 00:00:00 2001 From: Hugo Hamon Date: Tue, 1 May 2018 14:10:15 +0200 Subject: [PATCH 07/26] [Form] fixes instance variable phpdoc in FormRegistry class --- src/Symfony/Component/Form/FormRegistry.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Form/FormRegistry.php b/src/Symfony/Component/Form/FormRegistry.php index 70b0c78e6cfa5..c4bfb0e303c53 100644 --- a/src/Symfony/Component/Form/FormRegistry.php +++ b/src/Symfony/Component/Form/FormRegistry.php @@ -30,7 +30,7 @@ class FormRegistry implements FormRegistryInterface private $extensions = array(); /** - * @var FormTypeInterface[] + * @var ResolvedFormTypeInterface[] */ private $types = array(); From afc09cc8a797b6ad2f1f729a9523b0e6583b42da Mon Sep 17 00:00:00 2001 From: Gert de Pagter Date: Tue, 1 May 2018 20:24:18 +0200 Subject: [PATCH 08/26] Use symfony/polyfill-ctype Use the polyfill for every package that uses cytpe functions. --- composer.json | 1 + src/Symfony/Bridge/Doctrine/composer.json | 3 ++- src/Symfony/Bundle/TwigBundle/composer.json | 3 ++- src/Symfony/Component/Config/composer.json | 3 ++- src/Symfony/Component/DomCrawler/composer.json | 3 ++- src/Symfony/Component/ExpressionLanguage/composer.json | 3 ++- src/Symfony/Component/Filesystem/composer.json | 3 ++- src/Symfony/Component/Form/composer.json | 1 + src/Symfony/Component/HttpKernel/composer.json | 1 + src/Symfony/Component/Intl/composer.json | 3 ++- src/Symfony/Component/PropertyAccess/composer.json | 3 ++- src/Symfony/Component/Security/Csrf/composer.json | 3 ++- src/Symfony/Component/Serializer/composer.json | 3 ++- src/Symfony/Component/Templating/composer.json | 3 ++- src/Symfony/Component/Validator/composer.json | 1 + src/Symfony/Component/Yaml/composer.json | 3 ++- 16 files changed, 28 insertions(+), 12 deletions(-) diff --git a/composer.json b/composer.json index f263c15d45324..350ea8cce2185 100644 --- a/composer.json +++ b/composer.json @@ -21,6 +21,7 @@ "doctrine/common": "~2.4", "paragonie/random_compat": "~1.0", "symfony/polyfill-apcu": "~1.1", + "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-mbstring": "~1.1", "twig/twig": "~1.34|~2.4", "psr/log": "~1.0" diff --git a/src/Symfony/Bridge/Doctrine/composer.json b/src/Symfony/Bridge/Doctrine/composer.json index e9dda5f59ae43..cd0794f3e4fbc 100644 --- a/src/Symfony/Bridge/Doctrine/composer.json +++ b/src/Symfony/Bridge/Doctrine/composer.json @@ -17,7 +17,8 @@ ], "require": { "php": ">=5.3.9", - "doctrine/common": "~2.4" + "doctrine/common": "~2.4", + "symfony/polyfill-ctype": "~1.8" }, "require-dev": { "symfony/stopwatch": "~2.2", diff --git a/src/Symfony/Bundle/TwigBundle/composer.json b/src/Symfony/Bundle/TwigBundle/composer.json index cd2f229e4ff88..d5b85177bcc56 100644 --- a/src/Symfony/Bundle/TwigBundle/composer.json +++ b/src/Symfony/Bundle/TwigBundle/composer.json @@ -21,7 +21,8 @@ "symfony/twig-bridge": "~2.7", "twig/twig": "~1.34|~2.4", "symfony/http-foundation": "~2.5", - "symfony/http-kernel": "~2.7.23|^2.8.16" + "symfony/http-kernel": "~2.7.23|^2.8.16", + "symfony/polyfill-ctype": "~1.8" }, "require-dev": { "symfony/stopwatch": "~2.2", diff --git a/src/Symfony/Component/Config/composer.json b/src/Symfony/Component/Config/composer.json index 47f8642ae6dbc..2027e190c8d44 100644 --- a/src/Symfony/Component/Config/composer.json +++ b/src/Symfony/Component/Config/composer.json @@ -17,7 +17,8 @@ ], "require": { "php": ">=5.3.9", - "symfony/filesystem": "~2.3" + "symfony/filesystem": "~2.3", + "symfony/polyfill-ctype": "~1.8" }, "require-dev": { "symfony/yaml": "~2.7" diff --git a/src/Symfony/Component/DomCrawler/composer.json b/src/Symfony/Component/DomCrawler/composer.json index 5ea57b2657041..068f8390ed922 100644 --- a/src/Symfony/Component/DomCrawler/composer.json +++ b/src/Symfony/Component/DomCrawler/composer.json @@ -16,7 +16,8 @@ } ], "require": { - "php": ">=5.3.9" + "php": ">=5.3.9", + "symfony/polyfill-ctype": "~1.8" }, "require-dev": { "symfony/css-selector": "~2.3" diff --git a/src/Symfony/Component/ExpressionLanguage/composer.json b/src/Symfony/Component/ExpressionLanguage/composer.json index 5bff703b0ebec..9d4d693812566 100644 --- a/src/Symfony/Component/ExpressionLanguage/composer.json +++ b/src/Symfony/Component/ExpressionLanguage/composer.json @@ -16,7 +16,8 @@ } ], "require": { - "php": ">=5.3.9" + "php": ">=5.3.9", + "symfony/polyfill-ctype": "~1.8" }, "autoload": { "psr-4": { "Symfony\\Component\\ExpressionLanguage\\": "" }, diff --git a/src/Symfony/Component/Filesystem/composer.json b/src/Symfony/Component/Filesystem/composer.json index 20a13bbd92b0d..e1783a7f10247 100644 --- a/src/Symfony/Component/Filesystem/composer.json +++ b/src/Symfony/Component/Filesystem/composer.json @@ -16,7 +16,8 @@ } ], "require": { - "php": ">=5.3.9" + "php": ">=5.3.9", + "symfony/polyfill-ctype": "~1.8" }, "autoload": { "psr-4": { "Symfony\\Component\\Filesystem\\": "" }, diff --git a/src/Symfony/Component/Form/composer.json b/src/Symfony/Component/Form/composer.json index 5b3af06994b82..67441c9ec9804 100644 --- a/src/Symfony/Component/Form/composer.json +++ b/src/Symfony/Component/Form/composer.json @@ -20,6 +20,7 @@ "symfony/event-dispatcher": "~2.1", "symfony/intl": "~2.7.25|^2.8.18", "symfony/options-resolver": "~2.6", + "symfony/polyfill-ctype": "~1.8", "symfony/property-access": "~2.3" }, "require-dev": { diff --git a/src/Symfony/Component/HttpKernel/composer.json b/src/Symfony/Component/HttpKernel/composer.json index 1b6b096a8a61f..6986945497ebf 100644 --- a/src/Symfony/Component/HttpKernel/composer.json +++ b/src/Symfony/Component/HttpKernel/composer.json @@ -20,6 +20,7 @@ "symfony/event-dispatcher": "^2.6.7", "symfony/http-foundation": "~2.7.36|^2.8.29", "symfony/debug": "^2.6.2", + "symfony/polyfill-ctype": "~1.8", "psr/log": "~1.0" }, "require-dev": { diff --git a/src/Symfony/Component/Intl/composer.json b/src/Symfony/Component/Intl/composer.json index 2310fbb396c00..cc9ac80f55a1d 100644 --- a/src/Symfony/Component/Intl/composer.json +++ b/src/Symfony/Component/Intl/composer.json @@ -24,7 +24,8 @@ } ], "require": { - "php": ">=5.3.9" + "php": ">=5.3.9", + "symfony/polyfill-ctype": "~1.8" }, "require-dev": { "symfony/filesystem": "~2.1" diff --git a/src/Symfony/Component/PropertyAccess/composer.json b/src/Symfony/Component/PropertyAccess/composer.json index c02990a198a15..8e881763efcf8 100644 --- a/src/Symfony/Component/PropertyAccess/composer.json +++ b/src/Symfony/Component/PropertyAccess/composer.json @@ -16,7 +16,8 @@ } ], "require": { - "php": ">=5.3.9" + "php": ">=5.3.9", + "symfony/polyfill-ctype": "~1.8" }, "autoload": { "psr-4": { "Symfony\\Component\\PropertyAccess\\": "" }, diff --git a/src/Symfony/Component/Security/Csrf/composer.json b/src/Symfony/Component/Security/Csrf/composer.json index 4f6ee250c0d38..1fb4bcbf6819a 100644 --- a/src/Symfony/Component/Security/Csrf/composer.json +++ b/src/Symfony/Component/Security/Csrf/composer.json @@ -20,7 +20,8 @@ "symfony/security-core": "~2.4" }, "require-dev": { - "symfony/http-foundation": "~2.7" + "symfony/http-foundation": "~2.7", + "symfony/polyfill-ctype": "~1.8" }, "conflict": { "symfony/http-foundation": "<2.7.38|~2.8,<2.8.31" diff --git a/src/Symfony/Component/Serializer/composer.json b/src/Symfony/Component/Serializer/composer.json index bcacfbc7549e9..e1bde54956281 100644 --- a/src/Symfony/Component/Serializer/composer.json +++ b/src/Symfony/Component/Serializer/composer.json @@ -16,7 +16,8 @@ } ], "require": { - "php": ">=5.3.9" + "php": ">=5.3.9", + "symfony/polyfill-ctype": "~1.8" }, "require-dev": { "symfony/yaml": "^2.0.5", diff --git a/src/Symfony/Component/Templating/composer.json b/src/Symfony/Component/Templating/composer.json index 334909c1a3a67..5ccb27c1028b4 100644 --- a/src/Symfony/Component/Templating/composer.json +++ b/src/Symfony/Component/Templating/composer.json @@ -16,7 +16,8 @@ } ], "require": { - "php": ">=5.3.9" + "php": ">=5.3.9", + "symfony/polyfill-ctype": "~1.8" }, "require-dev": { "psr/log": "~1.0" diff --git a/src/Symfony/Component/Validator/composer.json b/src/Symfony/Component/Validator/composer.json index e6db2e6907f0e..7fe92bae77c71 100644 --- a/src/Symfony/Component/Validator/composer.json +++ b/src/Symfony/Component/Validator/composer.json @@ -17,6 +17,7 @@ ], "require": { "php": ">=5.3.9", + "symfony/polyfill-ctype": "~1.8", "symfony/translation": "~2.4" }, "require-dev": { diff --git a/src/Symfony/Component/Yaml/composer.json b/src/Symfony/Component/Yaml/composer.json index 03eb421d294fc..eeab73c355d15 100644 --- a/src/Symfony/Component/Yaml/composer.json +++ b/src/Symfony/Component/Yaml/composer.json @@ -16,7 +16,8 @@ } ], "require": { - "php": ">=5.3.9" + "php": ">=5.3.9", + "symfony/polyfill-ctype": "~1.8" }, "autoload": { "psr-4": { "Symfony\\Component\\Yaml\\": "" }, From 30970c7a9b475c888f65980347df98cbe5c3fc94 Mon Sep 17 00:00:00 2001 From: Hugo Hamon Date: Wed, 2 May 2018 00:50:12 +0200 Subject: [PATCH 09/26] [Validator] make phpdoc of ObjectInitializerInterface interface more accurate --- src/Symfony/Component/Validator/ObjectInitializerInterface.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Validator/ObjectInitializerInterface.php b/src/Symfony/Component/Validator/ObjectInitializerInterface.php index dcbc2cd11dbdd..5f9cdad879632 100644 --- a/src/Symfony/Component/Validator/ObjectInitializerInterface.php +++ b/src/Symfony/Component/Validator/ObjectInitializerInterface.php @@ -15,7 +15,7 @@ * Prepares an object for validation. * * Concrete implementations of this interface are used by {@link ValidationVisitorInterface} - * to initialize objects just before validating them. + * and {@link Validator\ContextualValidatorInterface} to initialize objects just before validating them. * * @author Fabien Potencier * @author Bernhard Schussek From 046f0920c08c6777df97be296bf5e0efbb08aa82 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 1 May 2018 15:50:35 -0700 Subject: [PATCH 10/26] Remove symfony/polyfill-ctype where not needed --- src/Symfony/Component/ExpressionLanguage/composer.json | 3 +-- src/Symfony/Component/Intl/composer.json | 3 +-- src/Symfony/Component/Security/Csrf/composer.json | 3 +-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/Symfony/Component/ExpressionLanguage/composer.json b/src/Symfony/Component/ExpressionLanguage/composer.json index 9d4d693812566..5bff703b0ebec 100644 --- a/src/Symfony/Component/ExpressionLanguage/composer.json +++ b/src/Symfony/Component/ExpressionLanguage/composer.json @@ -16,8 +16,7 @@ } ], "require": { - "php": ">=5.3.9", - "symfony/polyfill-ctype": "~1.8" + "php": ">=5.3.9" }, "autoload": { "psr-4": { "Symfony\\Component\\ExpressionLanguage\\": "" }, diff --git a/src/Symfony/Component/Intl/composer.json b/src/Symfony/Component/Intl/composer.json index cc9ac80f55a1d..2310fbb396c00 100644 --- a/src/Symfony/Component/Intl/composer.json +++ b/src/Symfony/Component/Intl/composer.json @@ -24,8 +24,7 @@ } ], "require": { - "php": ">=5.3.9", - "symfony/polyfill-ctype": "~1.8" + "php": ">=5.3.9" }, "require-dev": { "symfony/filesystem": "~2.1" diff --git a/src/Symfony/Component/Security/Csrf/composer.json b/src/Symfony/Component/Security/Csrf/composer.json index 1fb4bcbf6819a..4f6ee250c0d38 100644 --- a/src/Symfony/Component/Security/Csrf/composer.json +++ b/src/Symfony/Component/Security/Csrf/composer.json @@ -20,8 +20,7 @@ "symfony/security-core": "~2.4" }, "require-dev": { - "symfony/http-foundation": "~2.7", - "symfony/polyfill-ctype": "~1.8" + "symfony/http-foundation": "~2.7" }, "conflict": { "symfony/http-foundation": "<2.7.38|~2.8,<2.8.31" From 34f136e01b9f3246f4d8debca0153d9df143761c Mon Sep 17 00:00:00 2001 From: Ben Johnson Date: Thu, 3 May 2018 12:50:56 -0400 Subject: [PATCH 11/26] Suppress warnings when open_basedir is non-empty If PHP is configured *with a non-empty open_basedir* value that does not permit access to the target location, these calls to is_executable() throw warnings. While Symfony may not raise exceptions for warnings in production environments, other frameworks (such as Laravel) do, in which case any of these checks causes a show-stopping 500 error. We fixed a similar issue in the ExecutableFinder class via symfony/symfony#16182 . This has always been an issue, but 709e15e7a37cb7ed6199548dc70dc33168e6cb2d made it more likely that a warning is triggered. --- src/Symfony/Component/Process/PhpExecutableFinder.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/Process/PhpExecutableFinder.php b/src/Symfony/Component/Process/PhpExecutableFinder.php index f5c97d6bb9f8f..2a0b7a0268d7f 100644 --- a/src/Symfony/Component/Process/PhpExecutableFinder.php +++ b/src/Symfony/Component/Process/PhpExecutableFinder.php @@ -49,7 +49,7 @@ public function find($includeArgs = true) } if ($php = getenv('PHP_PATH')) { - if (!is_executable($php)) { + if (@!is_executable($php)) { return false; } @@ -57,12 +57,12 @@ public function find($includeArgs = true) } if ($php = getenv('PHP_PEAR_PHP_BIN')) { - if (is_executable($php)) { + if (@is_executable($php)) { return $php; } } - if (is_executable($php = PHP_BINDIR.('\\' === DIRECTORY_SEPARATOR ? '\\php.exe' : '/php'))) { + if (@is_executable($php = PHP_BINDIR.('\\' === DIRECTORY_SEPARATOR ? '\\php.exe' : '/php'))) { return $php; } From b11dccebd2781447ba0e9c852aa3e4549d82ec9b Mon Sep 17 00:00:00 2001 From: Valentin Udaltsov Date: Sun, 6 May 2018 17:23:59 +0300 Subject: [PATCH 12/26] Fixed typo RecursiveIterator -> RecursiveIteratorIterator --- src/Symfony/Component/Form/Util/InheritDataAwareIterator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Form/Util/InheritDataAwareIterator.php b/src/Symfony/Component/Form/Util/InheritDataAwareIterator.php index ba157b7d182f4..a400c16f0b294 100644 --- a/src/Symfony/Component/Form/Util/InheritDataAwareIterator.php +++ b/src/Symfony/Component/Form/Util/InheritDataAwareIterator.php @@ -17,7 +17,7 @@ * Contrary to \ArrayIterator, this iterator recognizes changes in the original * array during iteration. * - * You can wrap the iterator into a {@link \RecursiveIterator} in order to + * You can wrap the iterator into a {@link \RecursiveIteratorIterator} in order to * enter any child form that inherits its parent's data and iterate the children * of that form as well. * From ae62d9bc811b011d68ef8a6e88b2df21d8a7a4a5 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Thu, 3 May 2018 14:15:36 +0200 Subject: [PATCH 13/26] use brace-style regex delimiters --- src/Symfony/Component/HttpFoundation/Request.php | 2 +- .../HttpFoundation/Tests/RequestTest.php | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/HttpFoundation/Request.php b/src/Symfony/Component/HttpFoundation/Request.php index ecdcdbc25acc2..e1309d477bd2d 100644 --- a/src/Symfony/Component/HttpFoundation/Request.php +++ b/src/Symfony/Component/HttpFoundation/Request.php @@ -581,7 +581,7 @@ public static function getTrustedProxies() public static function setTrustedHosts(array $hostPatterns) { self::$trustedHostPatterns = array_map(function ($hostPattern) { - return sprintf('#%s#i', $hostPattern); + return sprintf('{%s}i', $hostPattern); }, $hostPatterns); // we need to reset trusted hosts on trusted host patterns change self::$trustedHosts = array(); diff --git a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php index 0c5451dfd6ccc..688a7c714a1d7 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php @@ -18,6 +18,11 @@ class RequestTest extends TestCase { + protected function tearDown() + { + Request::setTrustedHosts(array()); + } + public function testInitialize() { $request = new Request(); @@ -1871,9 +1876,15 @@ public function testTrustedHosts() $request->headers->set('host', 'subdomain.trusted.com'); $this->assertEquals('subdomain.trusted.com', $request->getHost()); + } - // reset request for following tests - Request::setTrustedHosts(array()); + public function testSetTrustedHostsDoesNotBreakOnSpecialCharacters() + { + Request::setTrustedHosts(array('localhost(\.local){0,1}#,example.com', 'localhost')); + + $request = Request::create('/'); + $request->headers->set('host', 'localhost'); + $this->assertSame('localhost', $request->getHost()); } public function testFactory() From 5539f9d6c815d4bc1ec7f8191af37f2c8bf0cfae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarmo=20Lepp=C3=A4nen?= Date: Mon, 7 May 2018 19:50:17 +0300 Subject: [PATCH 14/26] Fixed return type --- src/Symfony/Component/HttpFoundation/Request.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/HttpFoundation/Request.php b/src/Symfony/Component/HttpFoundation/Request.php index e1309d477bd2d..7e8e075a833f3 100644 --- a/src/Symfony/Component/HttpFoundation/Request.php +++ b/src/Symfony/Component/HttpFoundation/Request.php @@ -1322,7 +1322,7 @@ public function getRealMethod() * * @param string $format The format * - * @return string The associated mime type (null if not found) + * @return string|null The associated mime type (null if not found) */ public function getMimeType($format) { From 8072eed4bf6237df15f74647c793cf2e951b6ab8 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Fri, 11 May 2018 17:48:19 +0200 Subject: [PATCH 15/26] fixed CS --- .../Handler/FingersCrossed/NotFoundActivationStrategy.php | 2 +- src/Symfony/Component/Finder/Shell/Command.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Bridge/Monolog/Handler/FingersCrossed/NotFoundActivationStrategy.php b/src/Symfony/Bridge/Monolog/Handler/FingersCrossed/NotFoundActivationStrategy.php index 413b476f2938d..596fcdd84d2c5 100644 --- a/src/Symfony/Bridge/Monolog/Handler/FingersCrossed/NotFoundActivationStrategy.php +++ b/src/Symfony/Bridge/Monolog/Handler/FingersCrossed/NotFoundActivationStrategy.php @@ -42,7 +42,7 @@ public function isHandlerActivated(array $record) $isActivated && isset($record['context']['exception']) && $record['context']['exception'] instanceof HttpException - && $record['context']['exception']->getStatusCode() == 404 + && 404 == $record['context']['exception']->getStatusCode() && ($request = $this->requestStack->getMasterRequest()) ) { return !preg_match($this->blacklist, $request->getPathInfo()); diff --git a/src/Symfony/Component/Finder/Shell/Command.php b/src/Symfony/Component/Finder/Shell/Command.php index 47f4b422216b9..43114dbcc07db 100644 --- a/src/Symfony/Component/Finder/Shell/Command.php +++ b/src/Symfony/Component/Finder/Shell/Command.php @@ -100,7 +100,7 @@ public function top($bit) array_unshift($this->bits, $bit); foreach ($this->labels as $label => $index) { - $this->labels[$label] += 1; + ++$this->labels[$label]; } return $this; From d7e612d2acda29f18d187167c8dbe8e260423b2e Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 11 May 2018 10:00:11 -0700 Subject: [PATCH 16/26] [Debug] Fix populating error_get_last() for handled silent errors --- src/Symfony/Component/Debug/ErrorHandler.php | 8 ++++--- .../Debug/Tests/ErrorHandlerTest.php | 24 +++++++++++++++++++ 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/Debug/ErrorHandler.php b/src/Symfony/Component/Debug/ErrorHandler.php index e056862b8e675..4671b85469d78 100644 --- a/src/Symfony/Component/Debug/ErrorHandler.php +++ b/src/Symfony/Component/Debug/ErrorHandler.php @@ -377,13 +377,15 @@ private function reRegister($prev) */ public function handleError($type, $message, $file, $line) { - $level = error_reporting() | E_RECOVERABLE_ERROR | E_USER_ERROR | E_DEPRECATED | E_USER_DEPRECATED; + $level = error_reporting(); + $silenced = 0 === ($level & $type); + $level |= E_RECOVERABLE_ERROR | E_USER_ERROR | E_DEPRECATED | E_USER_DEPRECATED; $log = $this->loggedErrors & $type; $throw = $this->thrownErrors & $type & $level; $type &= $level | $this->screamedErrors; if (!$type || (!$log && !$throw)) { - return $type && $log; + return !$silenced && $type && $log; } $scope = $this->scopedErrors & $type; @@ -479,7 +481,7 @@ public function handleError($type, $message, $file, $line) } } - return $type && $log; + return !$silenced && $type && $log; } /** diff --git a/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php b/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php index 4eb4aef0c0fa9..d8f4a74d86969 100644 --- a/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php +++ b/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php @@ -64,6 +64,30 @@ public function testRegister() } } + public function testErrorGetLast() + { + $handler = ErrorHandler::register(); + $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock(); + $handler->setDefaultLogger($logger); + $handler->screamAt(E_ALL); + + try { + @trigger_error('Hello', E_USER_WARNING); + $expected = array( + 'type' => E_USER_WARNING, + 'message' => 'Hello', + 'file' => __FILE__, + 'line' => __LINE__ - 5, + ); + $this->assertSame($expected, error_get_last()); + } catch (\Exception $e) { + restore_error_handler(); + restore_exception_handler(); + + throw $e; + } + } + public function testNotice() { ErrorHandler::register(); From 9d015c7c50cac8e954276c21025c030250174013 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 10 May 2018 19:25:00 -0700 Subject: [PATCH 17/26] [Filesystem] Fix usages of error_get_last() --- .../Component/Console/Command/Command.php | 5 +- .../Component/Filesystem/Filesystem.php | 64 ++++++++++++------- src/Symfony/Component/Finder/SplFileInfo.php | 7 +- .../Component/HttpFoundation/File/File.php | 8 ++- .../HttpFoundation/File/UploadedFile.php | 8 ++- .../Component/Process/Pipes/AbstractPipes.php | 14 +++- .../Component/Process/Pipes/UnixPipes.php | 5 +- 7 files changed, 73 insertions(+), 38 deletions(-) diff --git a/src/Symfony/Component/Console/Command/Command.php b/src/Symfony/Component/Console/Command/Command.php index 4b8eaea64e666..3fe12e867a827 100644 --- a/src/Symfony/Component/Console/Command/Command.php +++ b/src/Symfony/Component/Console/Command/Command.php @@ -205,12 +205,11 @@ public function run(InputInterface $input, OutputInterface $output) if (null !== $this->processTitle) { if (function_exists('cli_set_process_title')) { - if (false === @cli_set_process_title($this->processTitle)) { + if (!@cli_set_process_title($this->processTitle)) { if ('Darwin' === PHP_OS) { $output->writeln('Running "cli_get_process_title" as an unprivileged user is not supported on MacOS.'); } else { - $error = error_get_last(); - trigger_error($error['message'], E_USER_WARNING); + cli_set_process_title($this->processTitle); } } } elseif (function_exists('setproctitle')) { diff --git a/src/Symfony/Component/Filesystem/Filesystem.php b/src/Symfony/Component/Filesystem/Filesystem.php index 5c3b2a2d9cb8e..0f6d0a269e6cb 100644 --- a/src/Symfony/Component/Filesystem/Filesystem.php +++ b/src/Symfony/Component/Filesystem/Filesystem.php @@ -21,6 +21,8 @@ */ class Filesystem { + private static $lastError; + /** * Copies a file. * @@ -95,12 +97,11 @@ public function mkdir($dirs, $mode = 0777) continue; } - if (true !== @mkdir($dir, $mode, true)) { - $error = error_get_last(); + if (!self::box('mkdir', $dir, $mode, true)) { 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 ($error) { - throw new IOException(sprintf('Failed to create "%s": %s.', $dir, $error['message']), 0, null, $dir); + if (self::$lastError) { + throw new IOException(sprintf('Failed to create "%s": %s.', $dir, self::$lastError), 0, null, $dir); } throw new IOException(sprintf('Failed to create "%s"', $dir), 0, null, $dir); } @@ -169,20 +170,17 @@ public function remove($files) foreach ($files as $file) { if (is_link($file)) { // See https://bugs.php.net/52176 - if (!@(unlink($file) || '\\' !== DIRECTORY_SEPARATOR || rmdir($file)) && file_exists($file)) { - $error = error_get_last(); - throw new IOException(sprintf('Failed to remove symlink "%s": %s.', $file, $error['message'])); + if (!(self::box('unlink', $file) || '\\' !== DIRECTORY_SEPARATOR || self::box('rmdir', $file)) && file_exists($file)) { + throw new IOException(sprintf('Failed to remove symlink "%s": %s.', $file, self::$lastError)); } } elseif (is_dir($file)) { $this->remove(new \FilesystemIterator($file, \FilesystemIterator::CURRENT_AS_PATHNAME | \FilesystemIterator::SKIP_DOTS)); - if (!@rmdir($file) && file_exists($file)) { - $error = error_get_last(); - throw new IOException(sprintf('Failed to remove directory "%s": %s.', $file, $error['message'])); + if (!self::box('rmdir', $file) && file_exists($file)) { + throw new IOException(sprintf('Failed to remove directory "%s": %s.', $file, self::$lastError)); } - } elseif (!@unlink($file) && file_exists($file)) { - $error = error_get_last(); - throw new IOException(sprintf('Failed to remove file "%s": %s.', $file, $error['message'])); + } elseif (!self::box('unlink', $file) && file_exists($file)) { + throw new IOException(sprintf('Failed to remove file "%s": %s.', $file, self::$lastError)); } } } @@ -336,19 +334,16 @@ public function symlink($originDir, $targetDir, $copyOnWindows = false) $this->mkdir(dirname($targetDir)); - $ok = false; if (is_link($targetDir)) { - if (readlink($targetDir) != $originDir) { - $this->remove($targetDir); - } else { - $ok = true; + if (readlink($targetDir) === $originDir) { + return; } + $this->remove($targetDir); } - if (!$ok && true !== @symlink($originDir, $targetDir)) { - $report = error_get_last(); - if (is_array($report)) { - if ('\\' === DIRECTORY_SEPARATOR && false !== strpos($report['message'], 'error code(1314)')) { + if (!self::box('symlink', $originDir, $targetDir)) { + if (null !== self::$lastError) { + if ('\\' === DIRECTORY_SEPARATOR && false !== strpos(self::$lastError, 'error code(1314)')) { throw new IOException('Unable to create symlink due to error code 1314: \'A required privilege is not held by the client\'. Do you have the required Administrator-rights?', 0, null, $targetDir); } } @@ -580,4 +575,29 @@ private function toIterator($files) return $files; } + + private static function box($func) + { + self::$lastError = null; + \set_error_handler(__CLASS__.'::handleError'); + try { + $result = \call_user_func_array($func, \array_slice(\func_get_args(), 1)); + \restore_error_handler(); + + return $result; + } catch (\Throwable $e) { + } catch (\Exception $e) { + } + \restore_error_handler(); + + throw $e; + } + + /** + * @internal + */ + public static function handleError($type, $msg) + { + self::$lastError = $msg; + } } diff --git a/src/Symfony/Component/Finder/SplFileInfo.php b/src/Symfony/Component/Finder/SplFileInfo.php index 19f95e26be69a..0f4e025b22bd2 100644 --- a/src/Symfony/Component/Finder/SplFileInfo.php +++ b/src/Symfony/Component/Finder/SplFileInfo.php @@ -66,12 +66,11 @@ public function getRelativePathname() */ public function getContents() { - $level = error_reporting(0); + set_error_handler(function ($type, $msg) use (&$error) { $error = $msg; }); $content = file_get_contents($this->getPathname()); - error_reporting($level); + restore_error_handler(); if (false === $content) { - $error = error_get_last(); - throw new \RuntimeException($error['message']); + throw new \RuntimeException($error); } return $content; diff --git a/src/Symfony/Component/HttpFoundation/File/File.php b/src/Symfony/Component/HttpFoundation/File/File.php index e2a67684fcda6..65ece98379019 100644 --- a/src/Symfony/Component/HttpFoundation/File/File.php +++ b/src/Symfony/Component/HttpFoundation/File/File.php @@ -93,9 +93,11 @@ public function move($directory, $name = null) { $target = $this->getTargetFile($directory, $name); - if (!@rename($this->getPathname(), $target)) { - $error = error_get_last(); - throw new FileException(sprintf('Could not move the file "%s" to "%s" (%s)', $this->getPathname(), $target, strip_tags($error['message']))); + set_error_handler(function ($type, $msg) use (&$error) { $error = $msg; }); + $renamed = rename($this->getPathname(), $target); + restore_error_handler(); + if (!$renamed) { + throw new FileException(sprintf('Could not move the file "%s" to "%s" (%s)', $this->getPathname(), $target, strip_tags($error))); } @chmod($target, 0666 & ~umask()); diff --git a/src/Symfony/Component/HttpFoundation/File/UploadedFile.php b/src/Symfony/Component/HttpFoundation/File/UploadedFile.php index 082d8d534e17a..39b29775ccd8e 100644 --- a/src/Symfony/Component/HttpFoundation/File/UploadedFile.php +++ b/src/Symfony/Component/HttpFoundation/File/UploadedFile.php @@ -192,9 +192,11 @@ public function move($directory, $name = null) $target = $this->getTargetFile($directory, $name); - if (!@move_uploaded_file($this->getPathname(), $target)) { - $error = error_get_last(); - throw new FileException(sprintf('Could not move the file "%s" to "%s" (%s)', $this->getPathname(), $target, strip_tags($error['message']))); + set_error_handler(function ($type, $msg) use (&$error) { $error = $msg; }); + $moved = move_uploaded_file($this->getPathname(), $target); + restore_error_handler(); + if (!$moved) { + throw new FileException(sprintf('Could not move the file "%s" to "%s" (%s)', $this->getPathname(), $target, strip_tags($error))); } @chmod($target, 0666 & ~umask()); diff --git a/src/Symfony/Component/Process/Pipes/AbstractPipes.php b/src/Symfony/Component/Process/Pipes/AbstractPipes.php index 9a23d93c98688..97fe728bfd70d 100644 --- a/src/Symfony/Component/Process/Pipes/AbstractPipes.php +++ b/src/Symfony/Component/Process/Pipes/AbstractPipes.php @@ -23,6 +23,7 @@ abstract class AbstractPipes implements PipesInterface private $inputBuffer = ''; private $input; private $blocked = true; + private $lastError; /** * @param resource|null $input @@ -56,10 +57,11 @@ public function close() */ protected function hasSystemCallBeenInterrupted() { - $lastError = error_get_last(); + $lastError = $this->lastError; + $this->lastError = null; // stream_select returns false when the `select` system call is interrupted by an incoming signal - return isset($lastError['message']) && false !== stripos($lastError['message'], 'interrupted system call'); + return null !== $lastError && false !== stripos($lastError, 'interrupted system call'); } /** @@ -137,4 +139,12 @@ protected function write() return array($this->pipes[0]); } } + + /** + * @internal + */ + public function handleError($type, $msg) + { + $this->lastError = $msg; + } } diff --git a/src/Symfony/Component/Process/Pipes/UnixPipes.php b/src/Symfony/Component/Process/Pipes/UnixPipes.php index 65f32ecf2735a..935c43209d9da 100644 --- a/src/Symfony/Component/Process/Pipes/UnixPipes.php +++ b/src/Symfony/Component/Process/Pipes/UnixPipes.php @@ -99,7 +99,9 @@ public function readAndWrite($blocking, $close = false) unset($r[0]); // let's have a look if something changed in streams - if (($r || $w) && false === @stream_select($r, $w, $e, 0, $blocking ? Process::TIMEOUT_PRECISION * 1E6 : 0)) { + set_error_handler(array($this, 'handleError')); + if (($r || $w) && false === stream_select($r, $w, $e, 0, $blocking ? Process::TIMEOUT_PRECISION * 1E6 : 0)) { + restore_error_handler(); // if a system call has been interrupted, forget about it, let's try again // otherwise, an error occurred, let's reset pipes if (!$this->hasSystemCallBeenInterrupted()) { @@ -108,6 +110,7 @@ public function readAndWrite($blocking, $close = false) return $read; } + restore_error_handler(); foreach ($r as $pipe) { // prior PHP 5.4 the array passed to stream_select is modified and From 16ebb43bd4ff29b53cb78b9cd1d1f7d97de3cb32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Ostroluck=C3=BD?= Date: Sat, 12 May 2018 21:17:30 +0200 Subject: [PATCH 18/26] Disallow illegal characters like "." in session.name PHP saves cookie with correct name, but upon deserialization to $_COOKIE, it replaces some characters, e.g. "." becomes "_". This is probably also reason why \SessionHandler is not able to find a session. https://harrybailey.com/2009/04/dots-arent-allowed-in-php-cookie-names/ https://bugs.php.net/bug.php?id=75883 --- .../DependencyInjection/Configuration.php | 11 ++++- .../DependencyInjection/ConfigurationTest.php | 49 +++++++++++++++++++ 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php index b21b3ee8df769..a29d8fada010a 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php @@ -339,7 +339,16 @@ private function addSessionSection(ArrayNodeDefinition $rootNode) ->children() ->scalarNode('storage_id')->defaultValue('session.storage.native')->end() ->scalarNode('handler_id')->defaultValue('session.handler.native_file')->end() - ->scalarNode('name')->end() + ->scalarNode('name') + ->validate() + ->ifTrue(function ($v) { + parse_str($v, $parsed); + + return implode('&', array_keys($parsed)) !== (string) $v; + }) + ->thenInvalid('Session name %s contains illegal character(s)') + ->end() + ->end() ->scalarNode('cookie_lifetime')->end() ->scalarNode('cookie_path')->end() ->scalarNode('cookie_domain')->end() diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php index a20a120d0710b..6505d5a034932 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php @@ -41,6 +41,55 @@ public function testDoNoDuplicateDefaultFormResources() $this->assertEquals(array('FrameworkBundle:Form'), $config['templating']['form']['resources']); } + /** + * @dataProvider getTestValidSessionName + */ + public function testValidSessionName($sessionName) + { + $processor = new Processor(); + $config = $processor->processConfiguration( + new Configuration(true), + array(array('session' => array('name' => $sessionName))) + ); + + $this->assertEquals($sessionName, $config['session']['name']); + } + + public function getTestValidSessionName() + { + return array( + array(null), + array('PHPSESSID'), + array('a&b'), + array(',_-!@#$%^*(){}:<>/?'), + ); + } + + /** + * @dataProvider getTestInvalidSessionName + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException + */ + public function testInvalidSessionName($sessionName) + { + $processor = new Processor(); + $processor->processConfiguration( + new Configuration(true), + array(array('session' => array('name' => $sessionName))) + ); + } + + public function getTestInvalidSessionName() + { + return array( + array('a.b'), + array('a['), + array('a[]'), + array('a[b]'), + array('a=b'), + array('a+b'), + ); + } + /** * @dataProvider getTestValidTrustedProxiesData */ From d52f491bfa94d2092f9beb1e8137fc6b0f32ceab Mon Sep 17 00:00:00 2001 From: Maxime Steinhausser Date: Mon, 14 May 2018 18:07:30 +0200 Subject: [PATCH 19/26] [Profiler] Remove propel & event_listener_loading category identifiers --- .../WebProfilerBundle/Resources/views/Collector/time.html.twig | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/time.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/time.html.twig index 3361be296e26b..469aee8f5fe60 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/time.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/time.html.twig @@ -7,10 +7,8 @@ 'default': '#aacd4e', 'section': '#666', 'event_listener': '#3dd', - 'event_listener_loading': '#add', 'template': '#dd3', 'doctrine': '#d3d', - 'propel': '#f4d', 'child_sections': '#eed', } %} {% endif %} From 9cda96b8b539e85f5be3afed406905f341f4741b Mon Sep 17 00:00:00 2001 From: Oleg Andreyev Date: Mon, 14 May 2018 20:26:58 +0300 Subject: [PATCH 20/26] #27250 limiting GET_LOCK key up to 64 char due to changes in MySQL 5.7.5 and later --- .../Session/Storage/Handler/PdoSessionHandler.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php index dfd66516062c3..0825ee6ea9899 100644 --- a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php +++ b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php @@ -552,14 +552,16 @@ private function doAdvisoryLock($sessionId) { switch ($this->driver) { case 'mysql': + // MySQL 5.7.5 and later enforces a maximum length on lock names of 64 characters. Previously, no limit was enforced. + $lockId = \substr($sessionId, 0, 64); // should we handle the return value? 0 on timeout, null on error // we use a timeout of 50 seconds which is also the default for innodb_lock_wait_timeout $stmt = $this->pdo->prepare('SELECT GET_LOCK(:key, 50)'); - $stmt->bindValue(':key', $sessionId, \PDO::PARAM_STR); + $stmt->bindValue(':key', $lockId, \PDO::PARAM_STR); $stmt->execute(); $releaseStmt = $this->pdo->prepare('DO RELEASE_LOCK(:key)'); - $releaseStmt->bindValue(':key', $sessionId, \PDO::PARAM_STR); + $releaseStmt->bindValue(':key', $lockId, \PDO::PARAM_STR); return $releaseStmt; case 'pgsql': From 9e88eb5aa980e09f0f9ac691ebbe08d1baac0e0a Mon Sep 17 00:00:00 2001 From: MatTheCat Date: Fri, 11 May 2018 20:46:08 +0200 Subject: [PATCH 21/26] [Security] Fix logout --- .../DependencyInjection/SecurityExtension.php | 25 +++++++------- .../Resources/config/security.xml | 1 + .../Security/FirewallContext.php | 7 ++-- .../SecurityBundle/Security/FirewallMap.php | 2 +- .../CompleteConfigurationTest.php | 1 - .../Tests/Functional/LogoutTest.php | 34 +++++++++++++++++++ .../app/RememberMeLogout/bundles.php | 18 ++++++++++ .../app/RememberMeLogout/config.yml | 25 ++++++++++++++ .../app/RememberMeLogout/routing.yml | 5 +++ .../Bundle/SecurityBundle/composer.json | 2 +- .../Component/Security/Http/Firewall.php | 13 +++++-- .../Component/Security/Http/FirewallMap.php | 9 ++--- 12 files changed, 119 insertions(+), 23 deletions(-) create mode 100644 src/Symfony/Bundle/SecurityBundle/Tests/Functional/LogoutTest.php create mode 100644 src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMeLogout/bundles.php create mode 100644 src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMeLogout/config.yml create mode 100644 src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMeLogout/routing.yml diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php index 3ffe2876e88d8..9c526f88daa9d 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php @@ -222,13 +222,14 @@ private function createFirewalls($config, ContainerBuilder $container) $mapDef = $container->getDefinition('security.firewall.map'); $map = $authenticationProviders = array(); foreach ($firewalls as $name => $firewall) { - list($matcher, $listeners, $exceptionListener) = $this->createFirewall($container, $name, $firewall, $authenticationProviders, $providerIds); + list($matcher, $listeners, $exceptionListener, $logoutListener) = $this->createFirewall($container, $name, $firewall, $authenticationProviders, $providerIds); $contextId = 'security.firewall.map.context.'.$name; $context = $container->setDefinition($contextId, new DefinitionDecorator('security.firewall.context')); $context ->replaceArgument(0, $listeners) ->replaceArgument(1, $exceptionListener) + ->replaceArgument(2, $logoutListener) ; $map[$contextId] = $matcher; } @@ -259,7 +260,7 @@ private function createFirewall(ContainerBuilder $container, $id, $firewall, &$a // Security disabled? if (false === $firewall['security']) { - return array($matcher, array(), null); + return array($matcher, array(), null, null); } // Provider id (take the first registered provider if none defined) @@ -286,15 +287,15 @@ private function createFirewall(ContainerBuilder $container, $id, $firewall, &$a } // Logout listener + $logoutListenerId = null; if (isset($firewall['logout'])) { - $listenerId = 'security.logout_listener.'.$id; - $listener = $container->setDefinition($listenerId, new DefinitionDecorator('security.logout_listener')); - $listener->replaceArgument(3, array( + $logoutListenerId = 'security.logout_listener.'.$id; + $logoutListener = $container->setDefinition($logoutListenerId, new DefinitionDecorator('security.logout_listener')); + $logoutListener->replaceArgument(3, array( 'csrf_parameter' => $firewall['logout']['csrf_parameter'], 'intention' => $firewall['logout']['csrf_token_id'], 'logout_path' => $firewall['logout']['path'], )); - $listeners[] = new Reference($listenerId); // add logout success handler if (isset($firewall['logout']['success_handler'])) { @@ -304,16 +305,16 @@ private function createFirewall(ContainerBuilder $container, $id, $firewall, &$a $logoutSuccessHandler = $container->setDefinition($logoutSuccessHandlerId, new DefinitionDecorator('security.logout.success_handler')); $logoutSuccessHandler->replaceArgument(1, $firewall['logout']['target']); } - $listener->replaceArgument(2, new Reference($logoutSuccessHandlerId)); + $logoutListener->replaceArgument(2, new Reference($logoutSuccessHandlerId)); // add CSRF provider if (isset($firewall['logout']['csrf_token_generator'])) { - $listener->addArgument(new Reference($firewall['logout']['csrf_token_generator'])); + $logoutListener->addArgument(new Reference($firewall['logout']['csrf_token_generator'])); } // add session logout handler if (true === $firewall['logout']['invalidate_session'] && false === $firewall['stateless']) { - $listener->addMethodCall('addHandler', array(new Reference('security.logout.handler.session'))); + $logoutListener->addMethodCall('addHandler', array(new Reference('security.logout.handler.session'))); } // add cookie logout handler @@ -322,12 +323,12 @@ private function createFirewall(ContainerBuilder $container, $id, $firewall, &$a $cookieHandler = $container->setDefinition($cookieHandlerId, new DefinitionDecorator('security.logout.handler.cookie_clearing')); $cookieHandler->addArgument($firewall['logout']['delete_cookies']); - $listener->addMethodCall('addHandler', array(new Reference($cookieHandlerId))); + $logoutListener->addMethodCall('addHandler', array(new Reference($cookieHandlerId))); } // add custom handlers foreach ($firewall['logout']['handlers'] as $handlerId) { - $listener->addMethodCall('addHandler', array(new Reference($handlerId))); + $logoutListener->addMethodCall('addHandler', array(new Reference($handlerId))); } // register with LogoutUrlGenerator @@ -362,7 +363,7 @@ private function createFirewall(ContainerBuilder $container, $id, $firewall, &$a // Exception listener $exceptionListener = new Reference($this->createExceptionListener($container, $firewall, $id, $configuredEntryPoint ?: $defaultEntryPoint, $firewall['stateless'])); - return array($matcher, $listeners, $exceptionListener); + return array($matcher, $listeners, $exceptionListener, null !== $logoutListenerId ? new Reference($logoutListenerId) : null); } private function createContextListener($container, $contextKey) diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml b/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml index b7c1407c1cc56..b044ccba98e74 100644 --- a/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml +++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml @@ -150,6 +150,7 @@ + diff --git a/src/Symfony/Bundle/SecurityBundle/Security/FirewallContext.php b/src/Symfony/Bundle/SecurityBundle/Security/FirewallContext.php index 13d096d97e951..e9f8fe66d6395 100644 --- a/src/Symfony/Bundle/SecurityBundle/Security/FirewallContext.php +++ b/src/Symfony/Bundle/SecurityBundle/Security/FirewallContext.php @@ -12,6 +12,7 @@ namespace Symfony\Bundle\SecurityBundle\Security; use Symfony\Component\Security\Http\Firewall\ExceptionListener; +use Symfony\Component\Security\Http\Firewall\LogoutListener; /** * This is a wrapper around the actual firewall configuration which allows us @@ -23,15 +24,17 @@ class FirewallContext { private $listeners; private $exceptionListener; + private $logoutListener; - public function __construct(array $listeners, ExceptionListener $exceptionListener = null) + public function __construct(array $listeners, ExceptionListener $exceptionListener = null, LogoutListener $logoutListener = null) { $this->listeners = $listeners; $this->exceptionListener = $exceptionListener; + $this->logoutListener = $logoutListener; } public function getContext() { - return array($this->listeners, $this->exceptionListener); + return array($this->listeners, $this->exceptionListener, $this->logoutListener); } } diff --git a/src/Symfony/Bundle/SecurityBundle/Security/FirewallMap.php b/src/Symfony/Bundle/SecurityBundle/Security/FirewallMap.php index dc87681c37511..d45d7b87f04a6 100644 --- a/src/Symfony/Bundle/SecurityBundle/Security/FirewallMap.php +++ b/src/Symfony/Bundle/SecurityBundle/Security/FirewallMap.php @@ -41,6 +41,6 @@ public function getListeners(Request $request) } } - return array(array(), null); + return array(array(), null, null); } } diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php index 855bed9b6899d..371baa704a835 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php @@ -76,7 +76,6 @@ public function testFirewalls() array(), array( 'security.channel_listener', - 'security.logout_listener.secure', 'security.authentication.listener.x509.secure', 'security.authentication.listener.remote_user.secure', 'security.authentication.listener.form.secure', diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/LogoutTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/LogoutTest.php new file mode 100644 index 0000000000000..7eeb7c21171ce --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/LogoutTest.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\Tests\Functional; + +class LogoutTest extends WebTestCase +{ + public function testSessionLessRememberMeLogout() + { + $client = $this->createClient(array('test_case' => 'RememberMeLogout', 'root_config' => 'config.yml')); + + $client->request('POST', '/login', array( + '_username' => 'johannes', + '_password' => 'test', + )); + + $cookieJar = $client->getCookieJar(); + $cookieJar->expire(session_name()); + + $this->assertNotNull($cookieJar->get('REMEMBERME')); + + $client->request('GET', '/logout'); + + $this->assertNull($cookieJar->get('REMEMBERME')); + } +} diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMeLogout/bundles.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMeLogout/bundles.php new file mode 100644 index 0000000000000..d90f774abde2b --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMeLogout/bundles.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Bundle\SecurityBundle\SecurityBundle; +use Symfony\Bundle\FrameworkBundle\FrameworkBundle; + +return array( + new FrameworkBundle(), + new SecurityBundle(), +); diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMeLogout/config.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMeLogout/config.yml new file mode 100644 index 0000000000000..48fd4ed6cc3cd --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMeLogout/config.yml @@ -0,0 +1,25 @@ +imports: + - { resource: ./../config/framework.yml } + +security: + encoders: + Symfony\Component\Security\Core\User\User: plaintext + + providers: + in_memory: + memory: + users: + johannes: { password: test, roles: [ROLE_USER] } + + firewalls: + default: + form_login: + check_path: login + remember_me: true + require_previous_session: false + remember_me: + always_remember_me: true + key: key + logout: ~ + anonymous: ~ + stateless: true diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMeLogout/routing.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMeLogout/routing.yml new file mode 100644 index 0000000000000..1dddfca2f8154 --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMeLogout/routing.yml @@ -0,0 +1,5 @@ +login: + path: /login + +logout: + path: /logout diff --git a/src/Symfony/Bundle/SecurityBundle/composer.json b/src/Symfony/Bundle/SecurityBundle/composer.json index e765b16bef867..90edcf6e38cee 100644 --- a/src/Symfony/Bundle/SecurityBundle/composer.json +++ b/src/Symfony/Bundle/SecurityBundle/composer.json @@ -18,7 +18,7 @@ "require": { "php": ">=5.3.9", "ext-xml": "*", - "symfony/security": "~2.7.38|~2.8.31", + "symfony/security": "~2.7.47|~2.8.40", "symfony/security-acl": "~2.7", "symfony/http-kernel": "~2.7" }, diff --git a/src/Symfony/Component/Security/Http/Firewall.php b/src/Symfony/Component/Security/Http/Firewall.php index 62b0071212e54..376194d3a6c8c 100644 --- a/src/Symfony/Component/Security/Http/Firewall.php +++ b/src/Symfony/Component/Security/Http/Firewall.php @@ -47,20 +47,29 @@ public function onKernelRequest(GetResponseEvent $event) } // register listeners for this firewall - list($listeners, $exceptionListener) = $this->map->getListeners($event->getRequest()); + $listeners = $this->map->getListeners($event->getRequest()); + + $authenticationListeners = $listeners[0]; + $exceptionListener = $listeners[1]; + $logoutListener = isset($listeners[2]) ? $listeners[2] : null; + if (null !== $exceptionListener) { $this->exceptionListeners[$event->getRequest()] = $exceptionListener; $exceptionListener->register($this->dispatcher); } // initiate the listener chain - foreach ($listeners as $listener) { + foreach ($authenticationListeners as $listener) { $listener->handle($event); if ($event->hasResponse()) { break; } } + + if (null !== $logoutListener) { + $logoutListener->handle($event); + } } public function onKernelFinishRequest(FinishRequestEvent $event) diff --git a/src/Symfony/Component/Security/Http/FirewallMap.php b/src/Symfony/Component/Security/Http/FirewallMap.php index e767d123cb03e..fc97410d4e698 100644 --- a/src/Symfony/Component/Security/Http/FirewallMap.php +++ b/src/Symfony/Component/Security/Http/FirewallMap.php @@ -14,6 +14,7 @@ use Symfony\Component\HttpFoundation\RequestMatcherInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Security\Http\Firewall\ExceptionListener; +use Symfony\Component\Security\Http\Firewall\LogoutListener; /** * FirewallMap allows configuration of different firewalls for specific parts @@ -25,9 +26,9 @@ class FirewallMap implements FirewallMapInterface { private $map = array(); - public function add(RequestMatcherInterface $requestMatcher = null, array $listeners = array(), ExceptionListener $exceptionListener = null) + public function add(RequestMatcherInterface $requestMatcher = null, array $listeners = array(), ExceptionListener $exceptionListener = null, LogoutListener $logoutListener = null) { - $this->map[] = array($requestMatcher, $listeners, $exceptionListener); + $this->map[] = array($requestMatcher, $listeners, $exceptionListener, $logoutListener); } /** @@ -37,10 +38,10 @@ public function getListeners(Request $request) { foreach ($this->map as $elements) { if (null === $elements[0] || $elements[0]->matches($request)) { - return array($elements[1], $elements[2]); + return array($elements[1], $elements[2], $elements[3]); } } - return array(array(), null); + return array(array(), null, null); } } From 0de3a61cfcf122afa0ce8015c837c8f6d8ebc33e Mon Sep 17 00:00:00 2001 From: Kyle Date: Wed, 16 May 2018 15:58:59 +0200 Subject: [PATCH 22/26] Add Occitan plural rule --- src/Symfony/Component/Translation/PluralizationRules.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Symfony/Component/Translation/PluralizationRules.php b/src/Symfony/Component/Translation/PluralizationRules.php index e5ece89620b7f..2b7b118336938 100644 --- a/src/Symfony/Component/Translation/PluralizationRules.php +++ b/src/Symfony/Component/Translation/PluralizationRules.php @@ -107,6 +107,7 @@ public static function get($number, $locale) case 'nl': case 'nn': case 'no': + case 'oc': case 'om': case 'or': case 'pa': From 919f93d91c73a8d95ccd991d66c9fc4ec5e7f5f2 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Fri, 18 May 2018 20:00:42 +0200 Subject: [PATCH 23/26] do not mock the session in token storage tests --- .../TokenStorage/SessionTokenStorageTest.php | 177 +++--------------- 1 file changed, 25 insertions(+), 152 deletions(-) diff --git a/src/Symfony/Component/Security/Csrf/Tests/TokenStorage/SessionTokenStorageTest.php b/src/Symfony/Component/Security/Csrf/Tests/TokenStorage/SessionTokenStorageTest.php index c629ca15255ff..306e19ad91bb9 100644 --- a/src/Symfony/Component/Security/Csrf/Tests/TokenStorage/SessionTokenStorageTest.php +++ b/src/Symfony/Component/Security/Csrf/Tests/TokenStorage/SessionTokenStorageTest.php @@ -12,6 +12,8 @@ namespace Symfony\Component\Security\Csrf\Tests\TokenStorage; use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpFoundation\Session\Session; +use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage; use Symfony\Component\Security\Csrf\TokenStorage\SessionTokenStorage; /** @@ -22,7 +24,7 @@ class SessionTokenStorageTest extends TestCase const SESSION_NAMESPACE = 'foobar'; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var Session */ private $session; @@ -33,118 +35,53 @@ class SessionTokenStorageTest extends TestCase protected function setUp() { - $this->session = $this->getMockBuilder('Symfony\Component\HttpFoundation\Session\SessionInterface') - ->disableOriginalConstructor() - ->getMock(); + $this->session = new Session(new MockArraySessionStorage()); $this->storage = new SessionTokenStorage($this->session, self::SESSION_NAMESPACE); } - public function testStoreTokenInClosedSession() + public function testStoreTokenInNotStartedSessionStartsTheSession() { - $this->session->expects($this->any()) - ->method('isStarted') - ->will($this->returnValue(false)); - - $this->session->expects($this->once()) - ->method('start'); - - $this->session->expects($this->once()) - ->method('set') - ->with(self::SESSION_NAMESPACE.'/token_id', 'TOKEN'); - $this->storage->setToken('token_id', 'TOKEN'); + + $this->assertTrue($this->session->isStarted()); } public function testStoreTokenInActiveSession() { - $this->session->expects($this->any()) - ->method('isStarted') - ->will($this->returnValue(true)); - - $this->session->expects($this->never()) - ->method('start'); - - $this->session->expects($this->once()) - ->method('set') - ->with(self::SESSION_NAMESPACE.'/token_id', 'TOKEN'); - + $this->session->start(); $this->storage->setToken('token_id', 'TOKEN'); + + $this->assertSame('TOKEN', $this->session->get(self::SESSION_NAMESPACE.'/token_id')); } public function testCheckTokenInClosedSession() { - $this->session->expects($this->any()) - ->method('isStarted') - ->will($this->returnValue(false)); - - $this->session->expects($this->once()) - ->method('start'); + $this->session->set(self::SESSION_NAMESPACE.'/token_id', 'RESULT'); - $this->session->expects($this->once()) - ->method('has') - ->with(self::SESSION_NAMESPACE.'/token_id') - ->will($this->returnValue('RESULT')); - - $this->assertSame('RESULT', $this->storage->hasToken('token_id')); + $this->assertTrue($this->storage->hasToken('token_id')); + $this->assertTrue($this->session->isStarted()); } public function testCheckTokenInActiveSession() { - $this->session->expects($this->any()) - ->method('isStarted') - ->will($this->returnValue(true)); - - $this->session->expects($this->never()) - ->method('start'); + $this->session->start(); + $this->session->set(self::SESSION_NAMESPACE.'/token_id', 'RESULT'); - $this->session->expects($this->once()) - ->method('has') - ->with(self::SESSION_NAMESPACE.'/token_id') - ->will($this->returnValue('RESULT')); - - $this->assertSame('RESULT', $this->storage->hasToken('token_id')); + $this->assertTrue($this->storage->hasToken('token_id')); } public function testGetExistingTokenFromClosedSession() { - $this->session->expects($this->any()) - ->method('isStarted') - ->will($this->returnValue(false)); - - $this->session->expects($this->once()) - ->method('start'); - - $this->session->expects($this->once()) - ->method('has') - ->with(self::SESSION_NAMESPACE.'/token_id') - ->will($this->returnValue(true)); - - $this->session->expects($this->once()) - ->method('get') - ->with(self::SESSION_NAMESPACE.'/token_id') - ->will($this->returnValue('RESULT')); + $this->session->set(self::SESSION_NAMESPACE.'/token_id', 'RESULT'); $this->assertSame('RESULT', $this->storage->getToken('token_id')); + $this->assertTrue($this->session->isStarted()); } public function testGetExistingTokenFromActiveSession() { - $this->session->expects($this->any()) - ->method('isStarted') - ->will($this->returnValue(true)); - - $this->session->expects($this->never()) - ->method('start'); - - $this->session->expects($this->once()) - ->method('has') - ->with(self::SESSION_NAMESPACE.'/token_id') - ->will($this->returnValue(true)); - - $this->session->expects($this->once()) - ->method('get') - ->with(self::SESSION_NAMESPACE.'/token_id') - ->will($this->returnValue('RESULT')); + $this->session->start(); + $this->session->set(self::SESSION_NAMESPACE.'/token_id', 'RESULT'); $this->assertSame('RESULT', $this->storage->getToken('token_id')); } @@ -154,18 +91,6 @@ public function testGetExistingTokenFromActiveSession() */ public function testGetNonExistingTokenFromClosedSession() { - $this->session->expects($this->any()) - ->method('isStarted') - ->will($this->returnValue(false)); - - $this->session->expects($this->once()) - ->method('start'); - - $this->session->expects($this->once()) - ->method('has') - ->with(self::SESSION_NAMESPACE.'/token_id') - ->will($this->returnValue(false)); - $this->storage->getToken('token_id'); } @@ -174,85 +99,33 @@ public function testGetNonExistingTokenFromClosedSession() */ public function testGetNonExistingTokenFromActiveSession() { - $this->session->expects($this->any()) - ->method('isStarted') - ->will($this->returnValue(true)); - - $this->session->expects($this->never()) - ->method('start'); - - $this->session->expects($this->once()) - ->method('has') - ->with(self::SESSION_NAMESPACE.'/token_id') - ->will($this->returnValue(false)); - + $this->session->start(); $this->storage->getToken('token_id'); } public function testRemoveNonExistingTokenFromClosedSession() { - $this->session->expects($this->any()) - ->method('isStarted') - ->will($this->returnValue(false)); - - $this->session->expects($this->once()) - ->method('start'); - - $this->session->expects($this->once()) - ->method('remove') - ->with(self::SESSION_NAMESPACE.'/token_id') - ->will($this->returnValue(null)); - $this->assertNull($this->storage->removeToken('token_id')); } public function testRemoveNonExistingTokenFromActiveSession() { - $this->session->expects($this->any()) - ->method('isStarted') - ->will($this->returnValue(true)); - - $this->session->expects($this->never()) - ->method('start'); - - $this->session->expects($this->once()) - ->method('remove') - ->with(self::SESSION_NAMESPACE.'/token_id') - ->will($this->returnValue(null)); + $this->session->start(); $this->assertNull($this->storage->removeToken('token_id')); } public function testRemoveExistingTokenFromClosedSession() { - $this->session->expects($this->any()) - ->method('isStarted') - ->will($this->returnValue(false)); - - $this->session->expects($this->once()) - ->method('start'); - - $this->session->expects($this->once()) - ->method('remove') - ->with(self::SESSION_NAMESPACE.'/token_id') - ->will($this->returnValue('TOKEN')); + $this->session->set(self::SESSION_NAMESPACE.'/token_id', 'TOKEN'); $this->assertSame('TOKEN', $this->storage->removeToken('token_id')); } public function testRemoveExistingTokenFromActiveSession() { - $this->session->expects($this->any()) - ->method('isStarted') - ->will($this->returnValue(true)); - - $this->session->expects($this->never()) - ->method('start'); - - $this->session->expects($this->once()) - ->method('remove') - ->with(self::SESSION_NAMESPACE.'/token_id') - ->will($this->returnValue('TOKEN')); + $this->session->start(); + $this->session->set(self::SESSION_NAMESPACE.'/token_id', 'TOKEN'); $this->assertSame('TOKEN', $this->storage->removeToken('token_id')); } From e559215fcf8bb8b4ab873c2a052caf7f2009a318 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 21 May 2018 13:47:41 +0200 Subject: [PATCH 24/26] updated CHANGELOG for 2.7.47 --- CHANGELOG-2.7.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/CHANGELOG-2.7.md b/CHANGELOG-2.7.md index 2f6ece9b92809..6903fb4ed455d 100644 --- a/CHANGELOG-2.7.md +++ b/CHANGELOG-2.7.md @@ -7,6 +7,20 @@ in 2.7 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/v2.7.0...v2.7.1 +* 2.7.47 (2018-05-21) + + * bug #26781 [Form] Fix precision of MoneyToLocalizedStringTransformer's divisions on transform() (syastrebov) + * bug #27286 [Translation] Add Occitan plural rule (kylekatarnls) + * bug #27246 Disallow invalid characters in session.name (ostrolucky) + * bug #24805 [Security] Fix logout (MatTheCat) + * bug #27141 [Process] Suppress warnings when open_basedir is non-empty (cbj4074) + * bug #27250 [Session] limiting :key for GET_LOCK to 64 chars (oleg-andreyev) + * bug #27237 [Debug] Fix populating error_get_last() for handled silent errors (nicolas-grekas) + * bug #27236 [Filesystem] Fix usages of error_get_last() (nicolas-grekas) + * bug #27152 [HttpFoundation] use brace-style regex delimiters (xabbuh) + * feature #24896 Add CODE_OF_CONDUCT.md (egircys) + * bug #27067 [HttpFoundation] Fix setting session-related ini settings (e-moe) + * 2.7.46 (2018-04-27) * bug #26831 [Bridge/Doctrine] count(): Parameter must be an array or an object that implements Countable (gpenverne) From fb1aea810e0dda8b7c8aa2247ad3f4e75bdda7a0 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 21 May 2018 13:48:28 +0200 Subject: [PATCH 25/26] update CONTRIBUTORS for 2.7.47 --- CONTRIBUTORS.md | 50 +++++++++++++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index bc4ebf6f78139..672246e7f9da8 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -17,9 +17,9 @@ Symfony is the result of the work of many people who made the code better - Johannes S (johannes) - Jakub Zalas (jakubzalas) - Kris Wallsmith (kriswallsmith) + - Maxime Steinhausser (ogizanagi) - Ryan Weaver (weaverryan) - Javier Eguiluz (javier.eguiluz) - - Maxime Steinhausser (ogizanagi) - Grégoire Pineau (lyrixx) - Hugo Hamon (hhamon) - Abdellatif Ait boudad (aitboudad) @@ -35,17 +35,17 @@ Symfony is the result of the work of many people who made the code better - Jean-François Simon (jfsimon) - Benjamin Eberlei (beberlei) - Igor Wiedler (igorw) + - Samuel ROZE (sroze) - Jules Pietri (heah) - Eriksen Costa (eriksencosta) - Guilhem Niot (energetick) - Sarah Khalil (saro0h) - - Samuel ROZE (sroze) + - Yonel Ceruto (yonelceruto) - Jonathan Wage (jwage) - Hamza Amrouche (simperfit) - Diego Saint Esteben (dosten) - - Yonel Ceruto (yonelceruto) - - Alexandre Salomé (alexandresalome) - Iltar van der Berg (kjarli) + - Alexandre Salomé (alexandresalome) - William Durand (couac) - ornicar - Francis Besset (francisbesset) @@ -59,9 +59,9 @@ Symfony is the result of the work of many people who made the code better - Henrik Bjørnskov (henrikbjorn) - Dany Maillard (maidmaid) - Miha Vrhovnik + - Kevin Bond (kbond) - Tobias Nyholm (tobias) - Diego Saint Esteben (dii3g0) - - Kevin Bond (kbond) - Konstantin Kudryashov (everzet) - Alexander M. Turek (derrabus) - Bilal Amarni (bamarni) @@ -83,12 +83,12 @@ Symfony is the result of the work of many people who made the code better - Dariusz Górecki (canni) - Issei Murasawa (issei_m) - Douglas Greenshields (shieldo) + - David Maicher (dmaicher) - Lee McDermott - Brandon Turner - Luis Cordova (cordoval) - Graham Campbell (graham) - Daniel Holmes (dholmes) - - David Maicher (dmaicher) - Dariusz Ruminski - Toni Uebernickel (havvg) - Bart van den Burg (burgov) @@ -103,9 +103,9 @@ Symfony is the result of the work of many people who made the code better - Maxime STEINHAUSSER - Michal Piotrowski (eventhorizon) - Tim Nagel (merk) + - Grégoire Paris (greg0ire) - Brice BERNARD (brikou) - Baptiste Clavié (talus) - - Grégoire Paris (greg0ire) - marc.weistroff - lenar - Alexander Schwenn (xelaris) @@ -139,21 +139,21 @@ Symfony is the result of the work of many people who made the code better - Sebastian Hörl (blogsh) - Daniel Gomes (danielcsgomes) - Hidenori Goto (hidenorigoto) + - Jérôme Vasseur (jvasseur) + - Valentin Udaltsov (vudaltsov) + - gadelat (gadelat) - Guilherme Blanco (guilhermeblanco) - Pablo Godel (pgodel) - - Jérôme Vasseur (jvasseur) - Jérémie Augustin (jaugustin) - Andréia Bohner (andreia) - Philipp Wahala (hifi) - Julien Falque (julienfalque) - Rafael Dohms (rdohms) - Arnaud Kleinpeter (nanocom) - - gadelat (gadelat) - jwdeitch - Teoh Han Hui (teohhanhui) - Mikael Pajunen - Joel Wurtz (brouznouf) - - Valentin Udaltsov (vudaltsov) - Chris Wilkinson (thewilkybarkid) - Oleg Voronkovich - Vyacheslav Pavlov @@ -226,6 +226,7 @@ Symfony is the result of the work of many people who made the code better - Julien Brochet (mewt) - Leo Feyer - Tristan Darricau (nicofuma) + - Nikolay Labinskiy (e-moe) - Michaël Perrin (michael.perrin) - Marcel Beerta (mazen) - Loïc Faugeron @@ -260,6 +261,7 @@ Symfony is the result of the work of many people who made the code better - Kristen Gilden (kgilden) - Pierre-Yves LEBECQ (pylebecq) - Jordan Samouh (jordansamouh) + - Baptiste Lafontaine (magnetik) - Jakub Kucharovic (jkucharovic) - Uwe Jäger (uwej711) - Eugene Leonovich (rybakit) @@ -270,7 +272,6 @@ Symfony is the result of the work of many people who made the code better - Jan Sorgalla (jsor) - Ray - Tyson Andre - - Nikolay Labinskiy (e-moe) - Chekote - Thomas Adam - Albert Casademont (acasademont) @@ -286,6 +287,7 @@ Symfony is the result of the work of many people who made the code better - Oskar Stark (oskarstark) - Thomas Lallement (raziel057) - Giorgio Premi + - Christian Schmidt - Beau Simensen (simensen) - Michael Hirschler (mvhirsch) - Robert Kiss (kepten) @@ -317,7 +319,6 @@ Symfony is the result of the work of many people who made the code better - Jerzy Zawadzki (jzawadzki) - Wouter J - Ismael Ambrosi (iambrosi) - - Baptiste Lafontaine - François Pluchino (francoispluchino) - Aurelijus Valeiša (aurelijus) - Jan Decavele (jandc) @@ -361,6 +362,7 @@ Symfony is the result of the work of many people who made the code better - Yaroslav Kiliba - Terje Bråten - Mathieu Lechat + - MatTheCat - Robbert Klarenbeek (robbertkl) - JhonnyL - David Badura (davidbadura) @@ -424,7 +426,6 @@ Symfony is the result of the work of many people who made the code better - Jeanmonod David (jeanmonod) - Christopher Davis (chrisguitarguy) - Jan Schumann - - Christian Schmidt - Niklas Fiekas - Markus Bachmann (baachi) - lancergr @@ -437,6 +438,7 @@ Symfony is the result of the work of many people who made the code better - Josip Kruslin - Asmir Mustafic (goetas) - vagrant + - Aurimas Niekis (gcds) - EdgarPE - Florian Pfitzer (marmelatze) - Asier Illarramendi (doup) @@ -514,6 +516,7 @@ Symfony is the result of the work of many people who made the code better - De Cock Xavier (xdecock) - Almog Baku (almogbaku) - Scott Arciszewski + - Xavier HAUSHERR - Norbert Orzechowicz (norzechowicz) - Denis Charrier (brucewouaigne) - Matthijs van den Bos (matthijs) @@ -529,7 +532,6 @@ Symfony is the result of the work of many people who made the code better - Dawid Pakuła (zulusx) - Florian Rey (nervo) - Rodrigo Borrego Bernabé (rodrigobb) - - MatTheCat - Denis Gorbachev (starfall) - Peter van Dommelen - Tim van Densen @@ -563,6 +565,7 @@ Symfony is the result of the work of many people who made the code better - Mantas Var (mvar) - Sebastian Krebs - Jean-Christophe Cuvelier [Artack] + - Simon DELICATA - alcaeus - Fred Cox - vitaliytv @@ -580,6 +583,7 @@ Symfony is the result of the work of many people who made the code better - James Johnston - Sinan Eldem - Alexandre Dupuy (satchette) + - Malte Blättermann - Andre Rømcke (andrerom) - Nahuel Cuesta (ncuesta) - Chris Boden (cboden) @@ -604,7 +608,6 @@ Symfony is the result of the work of many people who made the code better - Michal Trojanowski - David Fuhr - Kamil Kokot (pamil) - - Aurimas Niekis (gcds) - Max Grigorian (maxakawizard) - mcfedr (mcfedr) - Rostyslav Kinash @@ -721,6 +724,7 @@ Symfony is the result of the work of many people who made the code better - Adam Szaraniec (mimol) - Yosmany Garcia (yosmanyga) - Wouter de Wild + - Antoine M (amakdessi) - Degory Valentine - izzyp - Benoit Lévêque (benoit_leveque) @@ -730,6 +734,7 @@ Symfony is the result of the work of many people who made the code better - Xavier Lacot (xavier) - possum - Denis Zunke (donalberto) + - Philipp Cordes - Ahmed TAILOULOUTE (ahmedtai) - Olivier Maisonneuve (olineuve) - Masterklavi @@ -749,7 +754,6 @@ Symfony is the result of the work of many people who made the code better - Adrien Lucas (adrienlucas) - Zhuravlev Alexander (scif) - James Michael DuPont - - Xavier HAUSHERR - Tom Klingenberg - Christopher Hall (mythmakr) - Patrick Dawkins (pjcdawkins) @@ -802,6 +806,7 @@ Symfony is the result of the work of many people who made the code better - corphi - grizlik - Derek ROTH + - Ben Johnson - Dmytro Boiko (eagle) - Shin Ohno (ganchiku) - Geert De Deckere (geertdd) @@ -882,6 +887,7 @@ Symfony is the result of the work of many people who made the code better - Michael Tibben - Billie Thompson - Sander Marechal + - Icode4Food (icode4food) - Radosław Benkel - jean pasqualini (darkilliant) - Ross Motley (rossmotley) @@ -993,6 +999,7 @@ Symfony is the result of the work of many people who made the code better - DerManoMann - Olaf Klischat - orlovv + - Jonathan Hedstrom - Peter Smeets (darkspartan) - Jhonny Lidfors (jhonny) - Julien Bianchi (jubianchi) @@ -1005,7 +1012,6 @@ Symfony is the result of the work of many people who made the code better - Andrew Tch - Alexander Cheprasov - Rodrigo Díez Villamuera (rodrigodiez) - - Malte Blättermann - e-ivanov - Jochen Bayer (jocl) - Alex Bowers @@ -1092,9 +1098,11 @@ Symfony is the result of the work of many people who made the code better - Tobias Stöckler - Mario Young - Ilia (aliance) + - Chris McCafferty (cilefen) - Grégoire Penverne (gpenverne) - Mo Di (modi) - Pablo Schläpfer + - Gert de Pagter - Jelte Steijaert (jelte) - Quique Porta (quiqueporta) - stoccc @@ -1177,9 +1185,9 @@ Symfony is the result of the work of many people who made the code better - Andreas Frömer - Philip Frank - Lance McNearney - - Antoine M (amakdessi) - Gonzalo Vilaseca (gonzalovilaseca) - Giorgio Premi + - ncou - Ian Carroll - caponica - Matt Daum (daum) @@ -1197,7 +1205,6 @@ Symfony is the result of the work of many people who made the code better - Tadcka - Beth Binkovitz - Gonzalo Míguez - - Philipp Cordes - Pierre Rineau - Romain Geissler - Adrien Moiruad @@ -1352,6 +1359,7 @@ Symfony is the result of the work of many people who made the code better - Pablo Maria Martelletti (pmartelletti) - Yassine Guedidi (yguedidi) - Waqas Ahmed + - Bert Hekman - Luis Muñoz - Matthew Donadio - Houziaux mike @@ -1450,6 +1458,7 @@ Symfony is the result of the work of many people who made the code better - Yannick Warnier (ywarnier) - Kevin Decherf - Jason Woods + - Oleg Andreyev - klemens - dened - Dmitry Korotovsky @@ -1508,6 +1517,7 @@ Symfony is the result of the work of many people who made the code better - Pierre Rineau - Maxim Lovchikov - adenkejawen + - Florent SEVESTRE (aniki-taicho) - Ari Pringle (apringle) - Dan Ordille (dordille) - Jan Eichhorn (exeu) @@ -1759,7 +1769,6 @@ Symfony is the result of the work of many people who made the code better - Matt Janssen - Ben Miller - Peter Gribanov - - Ben Johnson - kwiateusz - David Soria Parra - Sergiy Sokolenko @@ -1889,6 +1898,7 @@ Symfony is the result of the work of many people who made the code better - Julien Sanchez (sumbobyboys) - Guillermo Gisinger (t3chn0r) - Markus Tacker (tacker) + - Tarmo Leppänen (tarlepp) - Tyler Stroud (tystr) - Moritz Kraft (userfriendly) - Víctor Mateo (victormateo) From e55c38be006f8a9e47d8d4ab43a3b0698a2b7a87 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 21 May 2018 13:48:33 +0200 Subject: [PATCH 26/26] updated VERSION for 2.7.47 --- src/Symfony/Component/HttpKernel/Kernel.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 33bbddabc9eff..e0d221e2edce8 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -58,12 +58,12 @@ abstract class Kernel implements KernelInterface, TerminableInterface protected $startTime; protected $loadClassCache; - const VERSION = '2.7.47-DEV'; + const VERSION = '2.7.47'; const VERSION_ID = 20747; const MAJOR_VERSION = 2; const MINOR_VERSION = 7; const RELEASE_VERSION = 47; - const EXTRA_VERSION = 'DEV'; + const EXTRA_VERSION = ''; const END_OF_MAINTENANCE = '05/2018'; const END_OF_LIFE = '05/2019'; pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

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.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy