diff --git a/composer.json b/composer.json index c9db499a7..c5d2d206e 100644 --- a/composer.json +++ b/composer.json @@ -3,15 +3,18 @@ "description": "All 3rdparty components", "license": "MIT", "repositories": [ - { - "type": "vcs", - "url": "https://github.com/icewind1991/dbal" - } -], + { + "type": "vcs", + "url": "https://github.com/icewind1991/dbal" + } + ], "config": { "vendor-dir": ".", "optimize-autoloader": true, - "classmap-authoritative": true + "classmap-authoritative": true, + "platform": { + "php": "7.0.8" + } }, "require": { "aws/aws-sdk-php": "^3.35", @@ -40,14 +43,10 @@ "sabre/dav": "^3.2.0", "stecman/symfony-console-completion": "^0.7.0", "swiftmailer/swiftmailer": "^5.4", - "symfony/console": "3.4.9", - "symfony/event-dispatcher": "3.4.9", - "symfony/process": "3.4.9", - "symfony/routing": "3.4.9", - "symfony/translation": "3.4.9", - "microsoft/azure-storage-blob": "1.1.0" - }, - "platform": { - "php": "7.0.8" - } + "symfony/console": "3.4.11", + "symfony/event-dispatcher": "3.4.11", + "symfony/process": "3.4.11", + "symfony/routing": "3.4.11", + "symfony/translation": "3.4.11", + "microsoft/azure-storage-blob": "1.1.0" } } diff --git a/composer.lock b/composer.lock index 0a7f91c8e..5b99d2842 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "30f6f4a7bb0b0c0e65c51fb7452509df", + "content-hash": "8ce4c7b5ce8f502095a7249ec42504e8", "packages": [ { "name": "aws/aws-sdk-php", @@ -2981,16 +2981,16 @@ }, { "name": "symfony/console", - "version": "v3.4.9", + "version": "v3.4.11", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "5b1fdfa8eb93464bcc36c34da39cedffef822cdf" + "reference": "36f83f642443c46f3cf751d4d2ee5d047d757a27" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/5b1fdfa8eb93464bcc36c34da39cedffef822cdf", - "reference": "5b1fdfa8eb93464bcc36c34da39cedffef822cdf", + "url": "https://api.github.com/repos/symfony/console/zipball/36f83f642443c46f3cf751d4d2ee5d047d757a27", + "reference": "36f83f642443c46f3cf751d4d2ee5d047d757a27", "shasum": "" }, "require": { @@ -3046,36 +3046,36 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", - "time": "2018-04-30T01:22:56+00:00" + "time": "2018-05-16T08:49:21+00:00" }, { "name": "symfony/debug", - "version": "v3.3.6", + "version": "v3.4.11", "source": { "type": "git", "url": "https://github.com/symfony/debug.git", - "reference": "7c13ae8ce1e2adbbd574fc39de7be498e1284e13" + "reference": "b28fd73fefbac341f673f5efd707d539d6a19f68" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/debug/zipball/7c13ae8ce1e2adbbd574fc39de7be498e1284e13", - "reference": "7c13ae8ce1e2adbbd574fc39de7be498e1284e13", + "url": "https://api.github.com/repos/symfony/debug/zipball/b28fd73fefbac341f673f5efd707d539d6a19f68", + "reference": "b28fd73fefbac341f673f5efd707d539d6a19f68", "shasum": "" }, "require": { - "php": ">=5.5.9", + "php": "^5.5.9|>=7.0.8", "psr/log": "~1.0" }, "conflict": { "symfony/http-kernel": ">=2.3,<2.3.24|~2.4.0|>=2.5,<2.5.9|>=2.6,<2.6.2" }, "require-dev": { - "symfony/http-kernel": "~2.8|~3.0" + "symfony/http-kernel": "~2.8|~3.0|~4.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.3-dev" + "dev-master": "3.4-dev" } }, "autoload": { @@ -3102,11 +3102,11 @@ ], "description": "Symfony Debug Component", "homepage": "https://symfony.com", - "time": "2017-07-28T15:27:31+00:00" + "time": "2018-05-16T14:03:39+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v3.4.9", + "version": "v3.4.11", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", @@ -3169,16 +3169,16 @@ }, { "name": "symfony/polyfill-mbstring", - "version": "v1.2.0", + "version": "v1.8.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "dff51f72b0706335131b00a7f49606168c582594" + "reference": "3296adf6a6454a050679cde90f95350ad604b171" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/dff51f72b0706335131b00a7f49606168c582594", - "reference": "dff51f72b0706335131b00a7f49606168c582594", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/3296adf6a6454a050679cde90f95350ad604b171", + "reference": "3296adf6a6454a050679cde90f95350ad604b171", "shasum": "" }, "require": { @@ -3190,7 +3190,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.2-dev" + "dev-master": "1.8-dev" } }, "autoload": { @@ -3224,20 +3224,20 @@ "portable", "shim" ], - "time": "2016-05-18T14:26:46+00:00" + "time": "2018-04-26T10:06:28+00:00" }, { "name": "symfony/process", - "version": "v3.4.9", + "version": "v3.4.11", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "4b7d64e852886319e93ddfdecff0d744ab87658b" + "reference": "4cbf2db9abcb01486a21b7a059e03a62fae63187" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/4b7d64e852886319e93ddfdecff0d744ab87658b", - "reference": "4b7d64e852886319e93ddfdecff0d744ab87658b", + "url": "https://api.github.com/repos/symfony/process/zipball/4cbf2db9abcb01486a21b7a059e03a62fae63187", + "reference": "4cbf2db9abcb01486a21b7a059e03a62fae63187", "shasum": "" }, "require": { @@ -3273,20 +3273,20 @@ ], "description": "Symfony Process Component", "homepage": "https://symfony.com", - "time": "2018-04-03T05:22:50+00:00" + "time": "2018-05-16T08:49:21+00:00" }, { "name": "symfony/routing", - "version": "v3.4.9", + "version": "v3.4.11", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "9deb375986f5d1f37283d8386716d26985a0f4b6" + "reference": "e382da877f5304aabc12ec3073eec430670c8296" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/9deb375986f5d1f37283d8386716d26985a0f4b6", - "reference": "9deb375986f5d1f37283d8386716d26985a0f4b6", + "url": "https://api.github.com/repos/symfony/routing/zipball/e382da877f5304aabc12ec3073eec430670c8296", + "reference": "e382da877f5304aabc12ec3073eec430670c8296", "shasum": "" }, "require": { @@ -3351,20 +3351,20 @@ "uri", "url" ], - "time": "2018-04-12T09:01:03+00:00" + "time": "2018-05-16T12:49:49+00:00" }, { "name": "symfony/translation", - "version": "v3.4.9", + "version": "v3.4.11", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "d4af50f46cd8171fd5c1cdebdb9a8bbcd8078c6c" + "reference": "7047f725e35eab768137c677f8c38e4a2a8e38fb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/d4af50f46cd8171fd5c1cdebdb9a8bbcd8078c6c", - "reference": "d4af50f46cd8171fd5c1cdebdb9a8bbcd8078c6c", + "url": "https://api.github.com/repos/symfony/translation/zipball/7047f725e35eab768137c677f8c38e4a2a8e38fb", + "reference": "7047f725e35eab768137c677f8c38e4a2a8e38fb", "shasum": "" }, "require": { @@ -3419,7 +3419,7 @@ ], "description": "Symfony Translation Component", "homepage": "https://symfony.com", - "time": "2018-04-30T01:22:56+00:00" + "time": "2018-05-21T10:06:52+00:00" } ], "packages-dev": [], @@ -3432,5 +3432,8 @@ "prefer-stable": false, "prefer-lowest": false, "platform": [], - "platform-dev": [] + "platform-dev": [], + "platform-overrides": { + "php": "7.0.8" + } } diff --git a/composer/installed.json b/composer/installed.json index cb944ce8a..9a6f51ed0 100644 --- a/composer/installed.json +++ b/composer/installed.json @@ -3078,17 +3078,17 @@ }, { "name": "symfony/console", - "version": "v3.4.9", - "version_normalized": "3.4.9.0", + "version": "v3.4.11", + "version_normalized": "3.4.11.0", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "5b1fdfa8eb93464bcc36c34da39cedffef822cdf" + "reference": "36f83f642443c46f3cf751d4d2ee5d047d757a27" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/5b1fdfa8eb93464bcc36c34da39cedffef822cdf", - "reference": "5b1fdfa8eb93464bcc36c34da39cedffef822cdf", + "url": "https://api.github.com/repos/symfony/console/zipball/36f83f642443c46f3cf751d4d2ee5d047d757a27", + "reference": "36f83f642443c46f3cf751d4d2ee5d047d757a27", "shasum": "" }, "require": { @@ -3114,7 +3114,7 @@ "symfony/lock": "", "symfony/process": "" }, - "time": "2018-04-30T01:22:56+00:00", + "time": "2018-05-16T08:49:21+00:00", "type": "library", "extra": { "branch-alias": { @@ -3149,34 +3149,34 @@ }, { "name": "symfony/debug", - "version": "v3.3.6", - "version_normalized": "3.3.6.0", + "version": "v3.4.11", + "version_normalized": "3.4.11.0", "source": { "type": "git", "url": "https://github.com/symfony/debug.git", - "reference": "7c13ae8ce1e2adbbd574fc39de7be498e1284e13" + "reference": "b28fd73fefbac341f673f5efd707d539d6a19f68" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/debug/zipball/7c13ae8ce1e2adbbd574fc39de7be498e1284e13", - "reference": "7c13ae8ce1e2adbbd574fc39de7be498e1284e13", + "url": "https://api.github.com/repos/symfony/debug/zipball/b28fd73fefbac341f673f5efd707d539d6a19f68", + "reference": "b28fd73fefbac341f673f5efd707d539d6a19f68", "shasum": "" }, "require": { - "php": ">=5.5.9", + "php": "^5.5.9|>=7.0.8", "psr/log": "~1.0" }, "conflict": { "symfony/http-kernel": ">=2.3,<2.3.24|~2.4.0|>=2.5,<2.5.9|>=2.6,<2.6.2" }, "require-dev": { - "symfony/http-kernel": "~2.8|~3.0" + "symfony/http-kernel": "~2.8|~3.0|~4.0" }, - "time": "2017-07-28T15:27:31+00:00", + "time": "2018-05-16T14:03:39+00:00", "type": "library", "extra": { "branch-alias": { - "dev-master": "3.3-dev" + "dev-master": "3.4-dev" } }, "installation-source": "dist", @@ -3207,8 +3207,8 @@ }, { "name": "symfony/event-dispatcher", - "version": "v3.4.9", - "version_normalized": "3.4.9.0", + "version": "v3.4.11", + "version_normalized": "3.4.11.0", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", @@ -3272,17 +3272,17 @@ }, { "name": "symfony/polyfill-mbstring", - "version": "v1.2.0", - "version_normalized": "1.2.0.0", + "version": "v1.8.0", + "version_normalized": "1.8.0.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "dff51f72b0706335131b00a7f49606168c582594" + "reference": "3296adf6a6454a050679cde90f95350ad604b171" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/dff51f72b0706335131b00a7f49606168c582594", - "reference": "dff51f72b0706335131b00a7f49606168c582594", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/3296adf6a6454a050679cde90f95350ad604b171", + "reference": "3296adf6a6454a050679cde90f95350ad604b171", "shasum": "" }, "require": { @@ -3291,11 +3291,11 @@ "suggest": { "ext-mbstring": "For best performance" }, - "time": "2016-05-18T14:26:46+00:00", + "time": "2018-04-26T10:06:28+00:00", "type": "library", "extra": { "branch-alias": { - "dev-master": "1.2-dev" + "dev-master": "1.8-dev" } }, "installation-source": "dist", @@ -3333,23 +3333,23 @@ }, { "name": "symfony/process", - "version": "v3.4.9", - "version_normalized": "3.4.9.0", + "version": "v3.4.11", + "version_normalized": "3.4.11.0", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "4b7d64e852886319e93ddfdecff0d744ab87658b" + "reference": "4cbf2db9abcb01486a21b7a059e03a62fae63187" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/4b7d64e852886319e93ddfdecff0d744ab87658b", - "reference": "4b7d64e852886319e93ddfdecff0d744ab87658b", + "url": "https://api.github.com/repos/symfony/process/zipball/4cbf2db9abcb01486a21b7a059e03a62fae63187", + "reference": "4cbf2db9abcb01486a21b7a059e03a62fae63187", "shasum": "" }, "require": { "php": "^5.5.9|>=7.0.8" }, - "time": "2018-04-03T05:22:50+00:00", + "time": "2018-05-16T08:49:21+00:00", "type": "library", "extra": { "branch-alias": { @@ -3384,17 +3384,17 @@ }, { "name": "symfony/routing", - "version": "v3.4.9", - "version_normalized": "3.4.9.0", + "version": "v3.4.11", + "version_normalized": "3.4.11.0", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "9deb375986f5d1f37283d8386716d26985a0f4b6" + "reference": "e382da877f5304aabc12ec3073eec430670c8296" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/9deb375986f5d1f37283d8386716d26985a0f4b6", - "reference": "9deb375986f5d1f37283d8386716d26985a0f4b6", + "url": "https://api.github.com/repos/symfony/routing/zipball/e382da877f5304aabc12ec3073eec430670c8296", + "reference": "e382da877f5304aabc12ec3073eec430670c8296", "shasum": "" }, "require": { @@ -3423,7 +3423,7 @@ "symfony/http-foundation": "For using a Symfony Request object", "symfony/yaml": "For using the YAML loader" }, - "time": "2018-04-12T09:01:03+00:00", + "time": "2018-05-16T12:49:49+00:00", "type": "library", "extra": { "branch-alias": { @@ -3464,17 +3464,17 @@ }, { "name": "symfony/translation", - "version": "v3.4.9", - "version_normalized": "3.4.9.0", + "version": "v3.4.11", + "version_normalized": "3.4.11.0", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "d4af50f46cd8171fd5c1cdebdb9a8bbcd8078c6c" + "reference": "7047f725e35eab768137c677f8c38e4a2a8e38fb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/d4af50f46cd8171fd5c1cdebdb9a8bbcd8078c6c", - "reference": "d4af50f46cd8171fd5c1cdebdb9a8bbcd8078c6c", + "url": "https://api.github.com/repos/symfony/translation/zipball/7047f725e35eab768137c677f8c38e4a2a8e38fb", + "reference": "7047f725e35eab768137c677f8c38e4a2a8e38fb", "shasum": "" }, "require": { @@ -3499,7 +3499,7 @@ "symfony/config": "", "symfony/yaml": "" }, - "time": "2018-04-30T01:22:56+00:00", + "time": "2018-05-21T10:06:52+00:00", "type": "library", "extra": { "branch-alias": { diff --git a/symfony/console/Command/Command.php b/symfony/console/Command/Command.php index 069304251..627da5ec8 100644 --- a/symfony/console/Command/Command.php +++ b/symfony/console/Command/Command.php @@ -218,12 +218,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/symfony/debug/CHANGELOG.md b/symfony/debug/CHANGELOG.md index a853b7a0a..31c67eb60 100644 --- a/symfony/debug/CHANGELOG.md +++ b/symfony/debug/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +3.4.0 +----- + +* deprecated `ErrorHandler::stackErrors()` and `ErrorHandler::unstackErrors()` + 3.3.0 ----- diff --git a/symfony/debug/Debug.php b/symfony/debug/Debug.php index e3665ae5f..aaa54118e 100644 --- a/symfony/debug/Debug.php +++ b/symfony/debug/Debug.php @@ -45,7 +45,7 @@ public static function enable($errorReportingLevel = E_ALL, $displayErrors = tru error_reporting(E_ALL); } - if ('cli' !== PHP_SAPI) { + if (!\in_array(PHP_SAPI, array('cli', 'phpdbg'), true)) { ini_set('display_errors', 0); ExceptionHandler::register(); } elseif ($displayErrors && (!ini_get('log_errors') || ini_get('error_log'))) { diff --git a/symfony/debug/DebugClassLoader.php b/symfony/debug/DebugClassLoader.php index 2e1d71808..33e715a86 100644 --- a/symfony/debug/DebugClassLoader.php +++ b/symfony/debug/DebugClassLoader.php @@ -26,18 +26,17 @@ class DebugClassLoader { private $classLoader; private $isFinder; + private $loaded = array(); private static $caseCheck; + private static $checkedClasses = array(); private static $final = array(); private static $finalMethods = array(); private static $deprecated = array(); - private static $php7Reserved = array('int', 'float', 'bool', 'string', 'true', 'false', 'null'); + private static $internal = array(); + private static $internalMethods = array(); + private static $php7Reserved = array('int' => 1, 'float' => 1, 'bool' => 1, 'string' => 1, 'true' => 1, 'false' => 1, 'null' => 1); private static $darwinCache = array('/' => array('/', array())); - /** - * Constructor. - * - * @param callable $classLoader A class loader - */ public function __construct(callable $classLoader) { $this->classLoader = $classLoader; @@ -136,120 +135,153 @@ public static function disable() */ public function loadClass($class) { - ErrorHandler::stackErrors(); + $e = error_reporting(error_reporting() | E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR); try { - if ($this->isFinder) { - if ($file = $this->classLoader[0]->findFile($class)) { - require_once $file; + if ($this->isFinder && !isset($this->loaded[$class])) { + $this->loaded[$class] = true; + if ($file = $this->classLoader[0]->findFile($class) ?: false) { + $wasCached = \function_exists('opcache_is_script_cached') && @opcache_is_script_cached($file); + + require $file; + + if ($wasCached) { + return; + } } } else { call_user_func($this->classLoader, $class); $file = false; } } finally { - ErrorHandler::unstackErrors(); + error_reporting($e); } - $exists = class_exists($class, false) || interface_exists($class, false) || trait_exists($class, false); + $this->checkClass($class, $file); + } - if ($class && '\\' === $class[0]) { + private function checkClass($class, $file = null) + { + $exists = null === $file || \class_exists($class, false) || \interface_exists($class, false) || \trait_exists($class, false); + + if (null !== $file && $class && '\\' === $class[0]) { $class = substr($class, 1); } if ($exists) { + if (isset(self::$checkedClasses[$class])) { + return; + } + self::$checkedClasses[$class] = true; + $refl = new \ReflectionClass($class); + if (null === $file && $refl->isInternal()) { + return; + } $name = $refl->getName(); - if ($name !== $class && 0 === strcasecmp($name, $class)) { + if ($name !== $class && 0 === \strcasecmp($name, $class)) { throw new \RuntimeException(sprintf('Case mismatch between loaded and declared class names: "%s" vs "%s".', $class, $name)); } - $parent = get_parent_class($class); + // Don't trigger deprecations for classes in the same vendor + if (2 > $len = 1 + (\strpos($name, '\\') ?: \strpos($name, '_'))) { + $len = 0; + $ns = ''; + } else { + $ns = \substr($name, 0, $len); + } + + // Detect annotations on the class + if (false !== $doc = $refl->getDocComment()) { + foreach (array('final', 'deprecated', 'internal') as $annotation) { + if (false !== \strpos($doc, $annotation) && preg_match('#\n \* @'.$annotation.'(?:( .+?)\.?)?\r?\n \*(?: @|/$)#s', $doc, $notice)) { + self::${$annotation}[$name] = isset($notice[1]) ? preg_replace('#\s*\r?\n \* +#', ' ', $notice[1]) : ''; + } + } + } + + $parentAndTraits = \class_uses($name, false); + if ($parent = \get_parent_class($class)) { + $parentAndTraits[] = $parent; - // Not an interface nor a trait - if (class_exists($name, false)) { - if (preg_match('#\n \* @final(?:( .+?)\.?)?\r?\n \*(?: @|/$)#s', $refl->getDocComment(), $notice)) { - self::$final[$name] = isset($notice[1]) ? preg_replace('#\s*\r?\n \* +#', ' ', $notice[1]) : ''; + if (!isset(self::$checkedClasses[$parent])) { + $this->checkClass($parent); } - if ($parent && isset(self::$final[$parent])) { + if (isset(self::$final[$parent])) { @trigger_error(sprintf('The "%s" class is considered final%s. It may change without further notice as of its next major version. You should not extend it from "%s".', $parent, self::$final[$parent], $name), E_USER_DEPRECATED); } + } - // Inherit @final annotations - self::$finalMethods[$name] = $parent && isset(self::$finalMethods[$parent]) ? self::$finalMethods[$parent] : array(); - - foreach ($refl->getMethods(\ReflectionMethod::IS_PUBLIC | \ReflectionMethod::IS_PROTECTED) as $method) { - if ($method->class !== $name) { - continue; - } - - if ($parent && isset(self::$finalMethods[$parent][$method->name])) { - @trigger_error(sprintf('%s It may change without further notice as of its next major version. You should not extend it from "%s".', self::$finalMethods[$parent][$method->name], $name), E_USER_DEPRECATED); - } + // Detect if the parent is annotated + foreach ($parentAndTraits + $this->getOwnInterfaces($name, $parent) as $use) { + if (!isset(self::$checkedClasses[$use])) { + $this->checkClass($use); + } + if (isset(self::$deprecated[$use]) && \strncmp($ns, $use, $len)) { + $type = class_exists($name, false) ? 'class' : (interface_exists($name, false) ? 'interface' : 'trait'); + $verb = class_exists($use, false) || interface_exists($name, false) ? 'extends' : (interface_exists($use, false) ? 'implements' : 'uses'); - $doc = $method->getDocComment(); - if (false === $doc || false === strpos($doc, '@final')) { - continue; - } + @trigger_error(sprintf('The "%s" %s %s "%s" that is deprecated%s.', $name, $type, $verb, $use, self::$deprecated[$use]), E_USER_DEPRECATED); + } + if (isset(self::$internal[$use]) && \strncmp($ns, $use, $len)) { + @trigger_error(sprintf('The "%s" %s is considered internal%s. It may change without further notice. You should not use it from "%s".', $use, class_exists($use, false) ? 'class' : (interface_exists($use, false) ? 'interface' : 'trait'), self::$internal[$use], $name), E_USER_DEPRECATED); + } + } - if (preg_match('#\n\s+\* @final(?:( .+?)\.?)?\r?\n\s+\*(?: @|/$)#s', $doc, $notice)) { - $message = isset($notice[1]) ? preg_replace('#\s*\r?\n \* +#', ' ', $notice[1]) : ''; - self::$finalMethods[$name][$method->name] = sprintf('The "%s::%s()" method is considered final%s.', $name, $method->name, $message); + // Inherit @final and @internal annotations for methods + self::$finalMethods[$name] = array(); + self::$internalMethods[$name] = array(); + foreach ($parentAndTraits as $use) { + foreach (array('finalMethods', 'internalMethods') as $property) { + if (isset(self::${$property}[$use])) { + self::${$property}[$name] = self::${$property}[$name] ? self::${$property}[$use] + self::${$property}[$name] : self::${$property}[$use]; } } } - if (in_array(strtolower($refl->getShortName()), self::$php7Reserved)) { - @trigger_error(sprintf('The "%s" class uses the reserved name "%s", it will break on PHP 7 and higher', $name, $refl->getShortName()), E_USER_DEPRECATED); - } elseif (preg_match('#\n \* @deprecated (.*?)\r?\n \*(?: @|/$)#s', $refl->getDocComment(), $notice)) { - self::$deprecated[$name] = preg_replace('#\s*\r?\n \* +#', ' ', $notice[1]); - } else { - // Don't trigger deprecations for classes in the same vendor - if (2 > $len = 1 + (strpos($name, '\\', 1 + strpos($name, '\\')) ?: strpos($name, '_'))) { - $len = 0; - $ns = ''; - } else { - switch ($ns = substr($name, 0, $len)) { - case 'Symfony\Bridge\\': - case 'Symfony\Bundle\\': - case 'Symfony\Component\\': - $ns = 'Symfony\\'; - $len = strlen($ns); - break; - } + $isClass = \class_exists($name, false); + foreach ($refl->getMethods(\ReflectionMethod::IS_PUBLIC | \ReflectionMethod::IS_PROTECTED) as $method) { + if ($method->class !== $name) { + continue; } - if (!$parent || strncmp($ns, $parent, $len)) { - if ($parent && isset(self::$deprecated[$parent]) && strncmp($ns, $parent, $len)) { - @trigger_error(sprintf('The "%s" class extends "%s" that is deprecated %s', $name, $parent, self::$deprecated[$parent]), E_USER_DEPRECATED); - } + // Method from a trait + if ($method->getFilename() !== $refl->getFileName()) { + continue; + } - $parentInterfaces = array(); - $deprecatedInterfaces = array(); - if ($parent) { - foreach (class_implements($parent) as $interface) { - $parentInterfaces[$interface] = 1; - } - } + if ($isClass && $parent && isset(self::$finalMethods[$parent][$method->name])) { + list($declaringClass, $message) = self::$finalMethods[$parent][$method->name]; + @trigger_error(sprintf('The "%s::%s()" method is considered final%s. It may change without further notice as of its next major version. You should not extend it from "%s".', $declaringClass, $method->name, $message, $name), E_USER_DEPRECATED); + } - foreach ($refl->getInterfaceNames() as $interface) { - if (isset(self::$deprecated[$interface]) && strncmp($ns, $interface, $len)) { - $deprecatedInterfaces[] = $interface; - } - foreach (class_implements($interface) as $interface) { - $parentInterfaces[$interface] = 1; + foreach ($parentAndTraits as $use) { + if (isset(self::$internalMethods[$use][$method->name])) { + list($declaringClass, $message) = self::$internalMethods[$use][$method->name]; + if (\strncmp($ns, $declaringClass, $len)) { + @trigger_error(sprintf('The "%s::%s()" method is considered internal%s. It may change without further notice. You should not extend it from "%s".', $declaringClass, $method->name, $message, $name), E_USER_DEPRECATED); } } + } - foreach ($deprecatedInterfaces as $interface) { - if (!isset($parentInterfaces[$interface])) { - @trigger_error(sprintf('The "%s" %s "%s" that is deprecated %s', $name, $refl->isInterface() ? 'interface extends' : 'class implements', $interface, self::$deprecated[$interface]), E_USER_DEPRECATED); - } + // Detect method annotations + if (false === $doc = $method->getDocComment()) { + continue; + } + + foreach (array('final', 'internal') as $annotation) { + if (false !== \strpos($doc, $annotation) && preg_match('#\n\s+\* @'.$annotation.'(?:( .+?)\.?)?\r?\n\s+\*(?: @|/$)#s', $doc, $notice)) { + $message = isset($notice[1]) ? preg_replace('#\s*\r?\n \* +#', ' ', $notice[1]) : ''; + self::${$annotation.'Methods'}[$name][$method->name] = array($name, $message); } } } + + if (isset(self::$php7Reserved[\strtolower($refl->getShortName())])) { + @trigger_error(sprintf('The "%s" class uses the reserved name "%s", it will break on PHP 7 and higher', $name, $refl->getShortName()), E_USER_DEPRECATED); + } } if ($file) { @@ -345,8 +377,33 @@ public function loadClass($class) throw new \RuntimeException(sprintf('Case mismatch between class and real file names: "%s" vs "%s" in "%s".', substr($tail, -$tailLen + 1), substr($real, -$tailLen + 1), substr($real, 0, -$tailLen + 1))); } } + } + } - return true; + /** + * `class_implements` includes interfaces from the parents so we have to manually exclude them. + * + * @param string $class + * @param string|false $parent + * + * @return string[] + */ + private function getOwnInterfaces($class, $parent) + { + $ownInterfaces = class_implements($class, false); + + if ($parent) { + foreach (class_implements($parent, false) as $interface) { + unset($ownInterfaces[$interface]); + } } + + foreach ($ownInterfaces as $interface) { + foreach (class_implements($interface) as $interface) { + unset($ownInterfaces[$interface]); + } + } + + return $ownInterfaces; } } diff --git a/symfony/debug/ErrorHandler.php b/symfony/debug/ErrorHandler.php index 369ed26f1..bed0e04a4 100644 --- a/symfony/debug/ErrorHandler.php +++ b/symfony/debug/ErrorHandler.php @@ -134,10 +134,24 @@ public static function register(self $handler = null, $replace = true) $handler = $prev[0]; $replace = false; } - if ($replace || !$prev) { - $handler->setExceptionHandler(set_exception_handler(array($handler, 'handleException'))); - } else { + if (!$replace && $prev) { restore_error_handler(); + $handlerIsRegistered = is_array($prev) && $handler === $prev[0]; + } else { + $handlerIsRegistered = true; + } + if (is_array($prev = set_exception_handler(array($handler, 'handleException'))) && $prev[0] instanceof self) { + restore_exception_handler(); + if (!$handlerIsRegistered) { + $handler = $prev[0]; + } elseif ($handler !== $prev[0] && $replace) { + set_exception_handler(array($handler, 'handleException')); + $p = $prev[0]->setExceptionHandler(null); + $handler->setExceptionHandler($p); + $prev[0]->setExceptionHandler($p); + } + } else { + $handler->setExceptionHandler($prev); } $handler->throwAt(E_ALL & $handler->thrownErrors, true); @@ -369,14 +383,16 @@ private function reRegister($prev) public function handleError($type, $message, $file, $line) { // Level is the current error reporting level to manage silent error. + $level = error_reporting(); + $silenced = 0 === ($level & $type); // Strong errors are not authorized to be silenced. - $level = error_reporting() | E_RECOVERABLE_ERROR | E_USER_ERROR | E_DEPRECATED | E_USER_DEPRECATED; + $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; @@ -409,21 +425,25 @@ public function handleError($type, $message, $file, $line) $errorAsException = self::$toStringException; self::$toStringException = null; } elseif (!$throw && !($type & $level)) { - if (isset(self::$silencedErrorCache[$message])) { + if (!isset(self::$silencedErrorCache[$id = $file.':'.$line])) { + $lightTrace = $this->tracedErrors & $type ? $this->cleanTrace(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3), $type, $file, $line, false) : array(); + $errorAsException = new SilencedErrorContext($type, $file, $line, $lightTrace); + } elseif (isset(self::$silencedErrorCache[$id][$message])) { $lightTrace = null; - $errorAsException = self::$silencedErrorCache[$message]; + $errorAsException = self::$silencedErrorCache[$id][$message]; ++$errorAsException->count; } else { - $lightTrace = $this->tracedErrors & $type ? $this->cleanTrace(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3), $type, $file, $line, false) : array(); - $errorAsException = new SilencedErrorContext($type, $file, $line, $lightTrace); + $lightTrace = array(); + $errorAsException = null; } if (100 < ++self::$silencedErrorCount) { self::$silencedErrorCache = $lightTrace = array(); self::$silencedErrorCount = 1; } - self::$silencedErrorCache[$message] = $errorAsException; - + if ($errorAsException) { + self::$silencedErrorCache[$id][$message] = $errorAsException; + } if (null === $lightTrace) { return; } @@ -494,19 +514,19 @@ public function handleError($type, $message, $file, $line) $this->loggers[$type][0], ($type & $level) ? $this->loggers[$type][1] : LogLevel::DEBUG, $logMessage, - array('exception' => $errorAsException), + $errorAsException ? array('exception' => $errorAsException) : array(), ); } else { try { $this->isRecursive = true; $level = ($type & $level) ? $this->loggers[$type][1] : LogLevel::DEBUG; - $this->loggers[$type][0]->log($level, $logMessage, array('exception' => $errorAsException)); + $this->loggers[$type][0]->log($level, $logMessage, $errorAsException ? array('exception' => $errorAsException) : array()); } finally { $this->isRecursive = false; } } - return $type && $log; + return !$silenced && $type && $log; } /** @@ -526,6 +546,7 @@ public function handleException($exception, array $error = null) $exception = new FatalThrowableError($exception); } $type = $exception instanceof FatalErrorException ? $exception->getSeverity() : E_ERROR; + $handlerException = null; if (($this->loggedErrors & $type) || $exception instanceof FatalThrowableError) { if ($exception instanceof FatalErrorException) { @@ -560,18 +581,21 @@ public function handleException($exception, array $error = null) } } } - if (empty($this->exceptionHandler)) { - throw $exception; // Give back $exception to the native handler - } + $exceptionHandler = $this->exceptionHandler; + $this->exceptionHandler = null; try { - call_user_func($this->exceptionHandler, $exception); + if (null !== $exceptionHandler) { + return \call_user_func($exceptionHandler, $exception); + } + $handlerException = $handlerException ?: $exception; } catch (\Exception $handlerException) { } catch (\Throwable $handlerException) { } - if (isset($handlerException)) { - $this->exceptionHandler = null; - $this->handleException($handlerException); + if ($exception === $handlerException) { + self::$reservedMemory = null; // Disable the fatal error handler + throw $exception; // Give back $exception to the native handler } + $this->handleException($handlerException); } /** @@ -587,15 +611,39 @@ public static function handleFatalError(array $error = null) return; } - self::$reservedMemory = null; + $handler = self::$reservedMemory = null; + $handlers = array(); + $previousHandler = null; + $sameHandlerLimit = 10; + + while (!is_array($handler) || !$handler[0] instanceof self) { + $handler = set_exception_handler('var_dump'); + restore_exception_handler(); - $handler = set_error_handler('var_dump'); - $handler = is_array($handler) ? $handler[0] : null; - restore_error_handler(); + if (!$handler) { + break; + } + restore_exception_handler(); - if (!$handler instanceof self) { + if ($handler !== $previousHandler) { + array_unshift($handlers, $handler); + $previousHandler = $handler; + } elseif (0 === --$sameHandlerLimit) { + $handler = null; + break; + } + } + foreach ($handlers as $h) { + set_exception_handler($h); + } + if (!$handler) { return; } + if ($handler !== $h) { + $handler[0]->setExceptionHandler($h); + } + $handler = $handler[0]; + $handlers = array(); if ($exit = null === $error) { $error = error_get_last(); @@ -648,17 +696,25 @@ public static function handleFatalError(array $error = null) * * The most important feature of this is to prevent * autoloading until unstackErrors() is called. + * + * @deprecated since version 3.4, to be removed in 4.0. */ public static function stackErrors() { + @trigger_error('Support for stacking errors is deprecated since Symfony 3.4 and will be removed in 4.0.', E_USER_DEPRECATED); + self::$stackedErrorLevels[] = error_reporting(error_reporting() | E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR); } /** * Unstacks stacked errors and forwards to the logger. + * + * @deprecated since version 3.4, to be removed in 4.0. */ public static function unstackErrors() { + @trigger_error('Support for unstacking errors is deprecated since Symfony 3.4 and will be removed in 4.0.', E_USER_DEPRECATED); + $level = array_pop(self::$stackedErrorLevels); if (null !== $level) { @@ -707,7 +763,7 @@ private function cleanTrace($backtrace, $type, $file, $line, $throw) } if (!($throw || $this->scopedErrors & $type)) { for ($i = 0; isset($lightTrace[$i]); ++$i) { - unset($lightTrace[$i]['args']); + unset($lightTrace[$i]['args'], $lightTrace[$i]['object']); } } diff --git a/symfony/debug/Exception/ContextErrorException.php b/symfony/debug/Exception/ContextErrorException.php index 6561d4df3..554139da3 100644 --- a/symfony/debug/Exception/ContextErrorException.php +++ b/symfony/debug/Exception/ContextErrorException.php @@ -33,7 +33,7 @@ public function __construct($message, $code, $severity, $filename, $lineno, $con */ public function getContext() { - @trigger_error(sprintf('The %s class is deprecated since version 3.3 and will be removed in 4.0.', __CLASS__), E_USER_DEPRECATED); + @trigger_error(sprintf('The %s class is deprecated since Symfony 3.3 and will be removed in 4.0.', __CLASS__), E_USER_DEPRECATED); return $this->context; } diff --git a/symfony/debug/Exception/FatalThrowableError.php b/symfony/debug/Exception/FatalThrowableError.php index 34f43b17b..fafc92263 100644 --- a/symfony/debug/Exception/FatalThrowableError.php +++ b/symfony/debug/Exception/FatalThrowableError.php @@ -36,7 +36,8 @@ public function __construct(\Throwable $e) $e->getCode(), $severity, $e->getFile(), - $e->getLine() + $e->getLine(), + $e->getPrevious() ); $this->setTrace($e->getTrace()); diff --git a/symfony/debug/Exception/FlattenException.php b/symfony/debug/Exception/FlattenException.php index 24679dcaa..f491bf2ac 100644 --- a/symfony/debug/Exception/FlattenException.php +++ b/symfony/debug/Exception/FlattenException.php @@ -157,7 +157,7 @@ public function getPrevious() return $this->previous; } - public function setPrevious(FlattenException $previous) + public function setPrevious(self $previous) { $this->previous = $previous; } diff --git a/symfony/debug/ExceptionHandler.php b/symfony/debug/ExceptionHandler.php index d84cfdd49..f22b70f6e 100644 --- a/symfony/debug/ExceptionHandler.php +++ b/symfony/debug/ExceptionHandler.php @@ -40,7 +40,7 @@ public function __construct($debug = true, $charset = null, $fileLinkFormat = nu { $this->debug = $debug; $this->charset = $charset ?: ini_get('default_charset') ?: 'UTF-8'; - $this->fileLinkFormat = $fileLinkFormat ?: ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format'); + $this->fileLinkFormat = $fileLinkFormat; } /** @@ -196,8 +196,6 @@ public function getHtml($exception) /** * Gets the HTML content associated with the given exception. * - * @param FlattenException $exception A FlattenException instance - * * @return string The content as a string */ public function getContent(FlattenException $exception) @@ -276,8 +274,6 @@ public function getContent(FlattenException $exception) /** * Gets the stylesheet associated with the given exception. * - * @param FlattenException $exception A FlattenException instance - * * @return string The stylesheet as a string */ public function getStylesheet(FlattenException $exception) @@ -310,8 +306,8 @@ public function getStylesheet(FlattenException $exception) .exception-message { flex-grow: 1; padding: 30px 0; } .exception-message, .exception-message a { color: #FFF; font-size: 21px; font-weight: 400; margin: 0; } .exception-message.long { font-size: 18px; } - .exception-message a { text-decoration: none; } - .exception-message a:hover { text-decoration: underline; } + .exception-message a { border-bottom: 1px solid rgba(255, 255, 255, 0.5); font-size: inherit; text-decoration: none; } + .exception-message a:hover { border-bottom-color: #ffffff; } .exception-illustration { flex-basis: 111px; flex-shrink: 0; height: 66px; margin-left: 15px; opacity: .7; } @@ -320,11 +316,11 @@ public function getStylesheet(FlattenException $exception) .trace-message { font-size: 14px; font-weight: normal; margin: .5em 0 0; } - .trace-file-path, .trace-file-path a { margin-top: 3px; color: #999; color: #795da3; color: #B0413E; color: #222; font-size: 13px; } + .trace-file-path, .trace-file-path a { color: #222; margin-top: 3px; font-size: 13px; } .trace-class { color: #B0413E; } .trace-type { padding: 0 2px; } - .trace-method { color: #B0413E; color: #222; font-weight: bold; color: #B0413E; } - .trace-arguments { color: #222; color: #999; font-weight: normal; color: #795da3; color: #777; padding-left: 2px; } + .trace-method { color: #B0413E; font-weight: bold; } + .trace-arguments { color: #777; font-weight: normal; padding-left: 2px; } @media (min-width: 575px) { .hidden-xs-down { display: initial; } @@ -359,13 +355,29 @@ private function formatClass($class) private function formatPath($path, $line) { $file = $this->escapeHtml(preg_match('#[^/\\\\]*+$#', $path, $file) ? $file[0] : $path); - $fmt = $this->fileLinkFormat; + $fmt = $this->fileLinkFormat ?: ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format'); + + if (!$fmt) { + return sprintf('in %s%s', $this->escapeHtml($path), $file, 0 < $line ? ' line '.$line : ''); + } + + if (\is_string($fmt)) { + $i = strpos($f = $fmt, '&', max(strrpos($f, '%f'), strrpos($f, '%l'))) ?: strlen($f); + $fmt = array(substr($f, 0, $i)) + preg_split('/&([^>]++)>/', substr($f, $i), -1, PREG_SPLIT_DELIM_CAPTURE); + + for ($i = 1; isset($fmt[$i]); ++$i) { + if (0 === strpos($path, $k = $fmt[$i++])) { + $path = substr_replace($path, $fmt[$i], 0, strlen($k)); + break; + } + } - if ($fmt && $link = is_string($fmt) ? strtr($fmt, array('%f' => $path, '%l' => $line)) : $fmt->format($path, $line)) { - return sprintf('in %s (line %d)', $this->escapeHtml($link), $file, $line); + $link = strtr($fmt[0], array('%f' => $path, '%l' => $line)); + } else { + $link = $fmt->format($path, $line); } - return sprintf('in %s (line %d)', $this->escapeHtml($path), $file, $line); + return sprintf('in %s%s', $this->escapeHtml($link), $file, 0 < $line ? ' line '.$line : ''); } /** diff --git a/symfony/debug/LICENSE b/symfony/debug/LICENSE index 17d16a133..21d7fb9e2 100644 --- a/symfony/debug/LICENSE +++ b/symfony/debug/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2017 Fabien Potencier +Copyright (c) 2004-2018 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/symfony/debug/Resources/ext/tests/001.phpt b/symfony/debug/Resources/ext/tests/001.phpt index 15e183a70..4a87cd318 100644 --- a/symfony/debug/Resources/ext/tests/001.phpt +++ b/symfony/debug/Resources/ext/tests/001.phpt @@ -1,7 +1,9 @@ --TEST-- Test symfony_zval_info API --SKIPIF-- - + --FILE-- + --FILE-- + --FILE-- + --FILE-- =5.5.9", + "php": "^5.5.9|>=7.0.8", "psr/log": "~1.0" }, "conflict": { "symfony/http-kernel": ">=2.3,<2.3.24|~2.4.0|>=2.5,<2.5.9|>=2.6,<2.6.2" }, "require-dev": { - "symfony/http-kernel": "~2.8|~3.0" + "symfony/http-kernel": "~2.8|~3.0|~4.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Debug\\": "" }, @@ -34,7 +34,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "3.3-dev" + "dev-master": "3.4-dev" } } } diff --git a/symfony/polyfill-mbstring/LICENSE b/symfony/polyfill-mbstring/LICENSE index 39fa189d2..24fa32c2e 100644 --- a/symfony/polyfill-mbstring/LICENSE +++ b/symfony/polyfill-mbstring/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2014-2016 Fabien Potencier +Copyright (c) 2015-2018 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/symfony/polyfill-mbstring/Mbstring.php b/symfony/polyfill-mbstring/Mbstring.php index 85a066817..4bd326e5f 100644 --- a/symfony/polyfill-mbstring/Mbstring.php +++ b/symfony/polyfill-mbstring/Mbstring.php @@ -15,17 +15,23 @@ * Partial mbstring implementation in PHP, iconv based, UTF-8 centric. * * Implemented: + * - mb_chr - Returns a specific character from its Unicode code point * - mb_convert_encoding - Convert character encoding * - mb_convert_variables - Convert character code in variable(s) * - mb_decode_mimeheader - Decode string in MIME header field * - mb_encode_mimeheader - Encode string for MIME header XXX NATIVE IMPLEMENTATION IS REALLY BUGGED + * - mb_decode_numericentity - Decode HTML numeric string reference to character + * - mb_encode_numericentity - Encode character to HTML numeric string reference * - mb_convert_case - Perform case folding on a string + * - mb_detect_encoding - Detect character encoding * - mb_get_info - Get internal settings of mbstring * - mb_http_input - Detect HTTP input character encoding * - mb_http_output - Set/Get HTTP output character encoding * - mb_internal_encoding - Set/Get internal character encoding * - mb_list_encodings - Returns an array of all supported encodings + * - mb_ord - Returns the Unicode code point of a character * - mb_output_handler - Callback function converts character encoding in output buffer + * - mb_scrub - Replaces ill-formed byte sequences with substitute characters * - mb_strlen - Get string length * - mb_strpos - Find position of first occurrence of string in a string * - mb_strrpos - Find position of last occurrence of a string in a string @@ -44,8 +50,6 @@ * * Not implemented: * - mb_convert_kana - Convert "kana" one from another ("zen-kaku", "han-kaku" and more) - * - mb_decode_numericentity - Decode HTML numeric string reference to character - * - mb_encode_numericentity - Encode character to HTML numeric string reference * - mb_ereg_* - Regular expression with multibyte support * - mb_parse_str - Parse GET/POST/COOKIE data and set global variable * - mb_preferred_mime_name - Get MIME charset string @@ -74,7 +78,7 @@ final class Mbstring public static function mb_convert_encoding($s, $toEncoding, $fromEncoding = null) { - if (is_array($fromEncoding) || false !== strpos($fromEncoding, ',')) { + if (\is_array($fromEncoding) || false !== strpos($fromEncoding, ',')) { $fromEncoding = self::mb_detect_encoding($s, $fromEncoding); } else { $fromEncoding = self::getEncoding($fromEncoding); @@ -96,7 +100,7 @@ public static function mb_convert_encoding($s, $toEncoding, $fromEncoding = null $fromEncoding = 'Windows-1252'; } if ('UTF-8' !== $fromEncoding) { - $s = iconv($fromEncoding, 'UTF-8', $s); + $s = iconv($fromEncoding, 'UTF-8//IGNORE', $s); } return preg_replace_callback('/[\x80-\xFF]+/', array(__CLASS__, 'html_encoding_callback'), $s); @@ -107,7 +111,7 @@ public static function mb_convert_encoding($s, $toEncoding, $fromEncoding = null $fromEncoding = 'UTF-8'; } - return iconv($fromEncoding, $toEncoding, $s); + return iconv($fromEncoding, $toEncoding.'//IGNORE', $s); } public static function mb_convert_variables($toEncoding, $fromEncoding, &$a = null, &$b = null, &$c = null, &$d = null, &$e = null, &$f = null) @@ -134,9 +138,134 @@ public static function mb_encode_mimeheader($s, $charset = null, $transferEncodi trigger_error('mb_encode_mimeheader() is bugged. Please use iconv_mime_encode() instead', E_USER_WARNING); } + public static function mb_decode_numericentity($s, $convmap, $encoding = null) + { + if (null !== $s && !\is_scalar($s) && !(\is_object($s) && \method_exists($s, '__toString'))) { + trigger_error('mb_decode_numericentity() expects parameter 1 to be string, '.gettype($s).' given', E_USER_WARNING); + return null; + } + + if (!\is_array($convmap) || !$convmap) { + return false; + } + + if (null !== $encoding && !\is_scalar($encoding)) { + trigger_error('mb_decode_numericentity() expects parameter 3 to be string, '.gettype($s).' given', E_USER_WARNING); + return ''; // Instead of null (cf. mb_encode_numericentity). + } + + $s = (string) $s; + if ('' === $s) { + return ''; + } + + $encoding = self::getEncoding($encoding); + + if ('UTF-8' === $encoding) { + $encoding = null; + if (!preg_match('//u', $s)) { + $s = @iconv('UTF-8', 'UTF-8//IGNORE', $s); + } + } else { + $s = iconv($encoding, 'UTF-8//IGNORE', $s); + } + + $cnt = floor(\count($convmap) / 4) * 4; + + for ($i = 0; $i < $cnt; $i += 4) { + // collector_decode_htmlnumericentity ignores $convmap[$i + 3] + $convmap[$i] += $convmap[$i + 2]; + $convmap[$i + 1] += $convmap[$i + 2]; + } + + $s = preg_replace_callback('/&#(?:0*([0-9]+)|x0*([0-9a-fA-F]+))(?!&);?/', function (array $m) use ($cnt, $convmap) { + $c = isset($m[2]) ? (int) hexdec($m[2]) : $m[1]; + for ($i = 0; $i < $cnt; $i += 4) { + if ($c >= $convmap[$i] && $c <= $convmap[$i + 1]) { + return Mbstring::mb_chr($c - $convmap[$i + 2]); + } + } + return $m[0]; + }, $s); + + if (null === $encoding) { + return $s; + } + + return iconv('UTF-8', $encoding.'//IGNORE', $s); + } + + public static function mb_encode_numericentity($s, $convmap, $encoding = null, $is_hex = false) + { + if (null !== $s && !\is_scalar($s) && !(\is_object($s) && \method_exists($s, '__toString'))) { + trigger_error('mb_encode_numericentity() expects parameter 1 to be string, '.gettype($s).' given', E_USER_WARNING); + return null; + } + + if (!\is_array($convmap) || !$convmap) { + return false; + } + + if (null !== $encoding && !\is_scalar($encoding)) { + trigger_error('mb_encode_numericentity() expects parameter 3 to be string, '.gettype($s).' given', E_USER_WARNING); + return null; // Instead of '' (cf. mb_decode_numericentity). + } + + if (null !== $is_hex && !\is_scalar($is_hex)) { + trigger_error('mb_encode_numericentity() expects parameter 4 to be boolean, '.gettype($s).' given', E_USER_WARNING); + return null; + } + + $s = (string) $s; + if ('' === $s) { + return ''; + } + + $encoding = self::getEncoding($encoding); + + if ('UTF-8' === $encoding) { + $encoding = null; + if (!preg_match('//u', $s)) { + $s = @iconv('UTF-8', 'UTF-8//IGNORE', $s); + } + } else { + $s = iconv($encoding, 'UTF-8//IGNORE', $s); + } + + static $ulenMask = array("\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4); + + $cnt = floor(\count($convmap) / 4) * 4; + $i = 0; + $len = \strlen($s); + $result = ''; + + while ($i < $len) { + $ulen = $s[$i] < "\x80" ? 1 : $ulenMask[$s[$i] & "\xF0"]; + $uchr = substr($s, $i, $ulen); + $i += $ulen; + $c = self::mb_ord($uchr); + + for ($j = 0; $j < $cnt; $j += 4) { + if ($c >= $convmap[$j] && $c <= $convmap[$j + 1]) { + $cOffset = ($c + $convmap[$j + 2]) & $convmap[$j + 3]; + $result .= $is_hex ? sprintf('&#x%X;', $cOffset) : '&#'.$cOffset.';'; + continue 2; + } + } + $result .= $uchr; + } + + if (null === $encoding) { + return $result; + } + + return iconv('UTF-8', $encoding.'//IGNORE', $result); + } + public static function mb_convert_case($s, $mode, $encoding = null) { - if ('' === $s .= '') { + $s = (string) $s; + if ('' === $s) { return ''; } @@ -144,8 +273,11 @@ public static function mb_convert_case($s, $mode, $encoding = null) if ('UTF-8' === $encoding) { $encoding = null; + if (!preg_match('//u', $s)) { + $s = @iconv('UTF-8', 'UTF-8//IGNORE', $s); + } } else { - $s = iconv($encoding, 'UTF-8', $s); + $s = iconv($encoding, 'UTF-8//IGNORE', $s); } if (MB_CASE_TITLE == $mode) { @@ -173,7 +305,7 @@ public static function mb_convert_case($s, $mode, $encoding = null) static $ulenMask = array("\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4); $i = 0; - $len = strlen($s); + $len = \strlen($s); while ($i < $len) { $ulen = $s[$i] < "\x80" ? 1 : $ulenMask[$s[$i] & "\xF0"]; @@ -182,7 +314,7 @@ public static function mb_convert_case($s, $mode, $encoding = null) if (isset($map[$uchr])) { $uchr = $map[$uchr]; - $nlen = strlen($uchr); + $nlen = \strlen($uchr); if ($nlen == $ulen) { $nlen = $i; @@ -202,7 +334,7 @@ public static function mb_convert_case($s, $mode, $encoding = null) return $s; } - return iconv('UTF-8', $encoding, $s); + return iconv('UTF-8', $encoding.'//IGNORE', $s); } public static function mb_internal_encoding($encoding = null) @@ -272,7 +404,7 @@ public static function mb_detect_encoding($str, $encodingList = null, $strict = if (null === $encodingList) { $encodingList = self::$encodingList; } else { - if (!is_array($encodingList)) { + if (!\is_array($encodingList)) { $encodingList = array_map('trim', explode(',', $encodingList)); } $encodingList = array_map('strtoupper', $encodingList); @@ -309,7 +441,7 @@ public static function mb_detect_order($encodingList = null) return self::$encodingList; } - if (!is_array($encodingList)) { + if (!\is_array($encodingList)) { $encodingList = array_map('trim', explode(',', $encodingList)); } $encodingList = array_map('strtoupper', $encodingList); @@ -334,15 +466,22 @@ public static function mb_detect_order($encodingList = null) public static function mb_strlen($s, $encoding = null) { $encoding = self::getEncoding($encoding); + if ('CP850' === $encoding || 'ASCII' === $encoding) { + return \strlen($s); + } - return iconv_strlen($s, $encoding); + return @iconv_strlen($s, $encoding); } public static function mb_strpos($haystack, $needle, $offset = 0, $encoding = null) { $encoding = self::getEncoding($encoding); + if ('CP850' === $encoding || 'ASCII' === $encoding) { + return strpos($haystack, $needle, $offset); + } - if ('' === $needle .= '') { + $needle = (string) $needle; + if ('' === $needle) { trigger_error(__METHOD__.': Empty delimiter', E_USER_WARNING); return false; @@ -354,6 +493,9 @@ public static function mb_strpos($haystack, $needle, $offset = 0, $encoding = nu public static function mb_strrpos($haystack, $needle, $offset = 0, $encoding = null) { $encoding = self::getEncoding($encoding); + if ('CP850' === $encoding || 'ASCII' === $encoding) { + return strrpos($haystack, $needle, $offset); + } if ($offset != (int) $offset) { $offset = 0; @@ -393,6 +535,9 @@ public static function mb_substitute_character($c = null) public static function mb_substr($s, $start, $length = null, $encoding = null) { $encoding = self::getEncoding($encoding); + if ('CP850' === $encoding || 'ASCII' === $encoding) { + return substr($s, $start, null === $length ? 2147483647 : $length); + } if ($start < 0) { $start = iconv_strlen($s, $encoding) + $start; @@ -410,7 +555,7 @@ public static function mb_substr($s, $start, $length = null, $encoding = null) } } - return iconv_substr($s, $start, $length, $encoding).''; + return (string) iconv_substr($s, $start, $length, $encoding); } public static function mb_stripos($haystack, $needle, $offset = 0, $encoding = null) @@ -431,6 +576,9 @@ public static function mb_stristr($haystack, $needle, $part = false, $encoding = public static function mb_strrchr($haystack, $needle, $part = false, $encoding = null) { $encoding = self::getEncoding($encoding); + if ('CP850' === $encoding || 'ASCII' === $encoding) { + return strrchr($haystack, $needle, $part); + } $needle = self::mb_substr($needle, 0, 1, $encoding); $pos = iconv_strrpos($haystack, $needle, $encoding); @@ -510,7 +658,7 @@ public static function mb_strwidth($s, $encoding = null) $encoding = self::getEncoding($encoding); if ('UTF-8' !== $encoding) { - $s = iconv($encoding, 'UTF-8', $s); + $s = iconv($encoding, 'UTF-8//IGNORE', $s); } $s = preg_replace('/[\x{1100}-\x{115F}\x{2329}\x{232A}\x{2E80}-\x{303E}\x{3040}-\x{A4CF}\x{AC00}-\x{D7A3}\x{F900}-\x{FAFF}\x{FE10}-\x{FE19}\x{FE30}-\x{FE6F}\x{FF00}-\x{FF60}\x{FFE0}-\x{FFE6}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}]/u', '', $s, -1, $wide); @@ -528,6 +676,45 @@ public static function mb_output_handler($contents, $status) return $contents; } + public static function mb_chr($code, $encoding = null) + { + if (0x80 > $code %= 0x200000) { + $s = \chr($code); + } elseif (0x800 > $code) { + $s = \chr(0xC0 | $code >> 6).\chr(0x80 | $code & 0x3F); + } elseif (0x10000 > $code) { + $s = \chr(0xE0 | $code >> 12).\chr(0x80 | $code >> 6 & 0x3F).\chr(0x80 | $code & 0x3F); + } else { + $s = \chr(0xF0 | $code >> 18).\chr(0x80 | $code >> 12 & 0x3F).\chr(0x80 | $code >> 6 & 0x3F).\chr(0x80 | $code & 0x3F); + } + + if ('UTF-8' !== $encoding = self::getEncoding($encoding)) { + $s = mb_convert_encoding($s, $encoding, 'UTF-8'); + } + + return $s; + } + + public static function mb_ord($s, $encoding = null) + { + if ('UTF-8' !== $encoding = self::getEncoding($encoding)) { + $s = mb_convert_encoding($s, 'UTF-8', $encoding); + } + + $code = ($s = unpack('C*', substr($s, 0, 4))) ? $s[1] : 0; + if (0xF0 <= $code) { + return (($code - 0xF0) << 18) + (($s[2] - 0x80) << 12) + (($s[3] - 0x80) << 6) + $s[4] - 0x80; + } + if (0xE0 <= $code) { + return (($code - 0xE0) << 12) + (($s[2] - 0x80) << 6) + $s[3] - 0x80; + } + if (0xC0 <= $code) { + return (($code - 0xC0) << 6) + $s[2] - 0x80; + } + + return $code; + } + private static function getSubpart($pos, $part, $haystack, $encoding) { if (false === $pos) { @@ -540,7 +727,7 @@ private static function getSubpart($pos, $part, $haystack, $encoding) return self::mb_substr($haystack, $pos, null, $encoding); } - private static function html_encoding_callback($m) + private static function html_encoding_callback(array $m) { $i = 1; $entities = ''; @@ -548,7 +735,7 @@ private static function html_encoding_callback($m) while (isset($m[$i])) { if (0x80 > $m[$i]) { - $entities .= chr($m[$i++]); + $entities .= \chr($m[$i++]); continue; } if (0xF0 <= $m[$i]) { @@ -565,12 +752,12 @@ private static function html_encoding_callback($m) return $entities; } - private static function title_case_lower($s) + private static function title_case_lower(array $s) { return self::mb_convert_case($s[0], MB_CASE_LOWER, 'UTF-8'); } - private static function title_case_upper($s) + private static function title_case_upper(array $s) { return self::mb_convert_case($s[0], MB_CASE_UPPER, 'UTF-8'); } diff --git a/symfony/polyfill-mbstring/bootstrap.php b/symfony/polyfill-mbstring/bootstrap.php index 4b01b614a..2fdcc5a6f 100644 --- a/symfony/polyfill-mbstring/bootstrap.php +++ b/symfony/polyfill-mbstring/bootstrap.php @@ -19,6 +19,8 @@ function mb_convert_encoding($s, $to, $from = null) { return p\Mbstring::mb_convert_encoding($s, $to, $from); } function mb_decode_mimeheader($s) { return p\Mbstring::mb_decode_mimeheader($s); } function mb_encode_mimeheader($s, $charset = null, $transferEnc = null, $lf = null, $indent = null) { return p\Mbstring::mb_encode_mimeheader($s, $charset, $transferEnc, $lf, $indent); } + function mb_decode_numericentity($s, $convmap, $enc = null) { return p\Mbstring::mb_decode_numericentity($s, $convmap, $enc); } + function mb_encode_numericentity($s, $convmap, $enc = null, $is_hex = false) { return p\Mbstring::mb_encode_numericentity($s, $convmap, $enc, $is_hex); } function mb_convert_case($s, $mode, $enc = null) { return p\Mbstring::mb_convert_case($s, $mode, $enc); } function mb_internal_encoding($enc = null) { return p\Mbstring::mb_internal_encoding($enc); } function mb_language($lang = null) { return p\Mbstring::mb_language($lang); } @@ -47,5 +49,10 @@ function mb_strwidth($s, $enc = null) { return p\Mbstring::mb_strwidth($s, $enc) function mb_substr_count($haystack, $needle, $enc = null) { return p\Mbstring::mb_substr_count($haystack, $needle, $enc); } function mb_output_handler($contents, $status) { return p\Mbstring::mb_output_handler($contents, $status); } function mb_http_input($type = '') { return p\Mbstring::mb_http_input($type); } - function mb_convert_variables($toEncoding, $fromEncoding, &$a = null, &$b = null, &$c = null, &$d = null, &$e = null, &$f = null) { return p\Mbstring::mb_convert_variables($toEncoding, $fromEncoding, $v0, $a, $b, $c, $d, $e, $f); } + function mb_convert_variables($toEncoding, $fromEncoding, &$a = null, &$b = null, &$c = null, &$d = null, &$e = null, &$f = null) { return p\Mbstring::mb_convert_variables($toEncoding, $fromEncoding, $a, $b, $c, $d, $e, $f); } +} +if (!function_exists('mb_chr')) { + function mb_ord($s, $enc = null) { return p\Mbstring::mb_ord($s, $enc); } + function mb_chr($code, $enc = null) { return p\Mbstring::mb_chr($code, $enc); } + function mb_scrub($s, $enc = null) { $enc = null === $enc ? mb_internal_encoding() : $enc; return mb_convert_encoding($s, $enc, $enc); } } diff --git a/symfony/polyfill-mbstring/composer.json b/symfony/polyfill-mbstring/composer.json index 355a0f88f..49b720dd8 100644 --- a/symfony/polyfill-mbstring/composer.json +++ b/symfony/polyfill-mbstring/composer.json @@ -28,7 +28,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "1.2-dev" + "dev-master": "1.8-dev" } } } diff --git a/symfony/process/ExecutableFinder.php b/symfony/process/ExecutableFinder.php index d042a5b13..1ec6526d4 100644 --- a/symfony/process/ExecutableFinder.php +++ b/symfony/process/ExecutableFinder.php @@ -77,7 +77,7 @@ public function find($name, $default = null, array $extraDirs = array()) } foreach ($suffixes as $suffix) { foreach ($dirs as $dir) { - if (@is_file($file = $dir.DIRECTORY_SEPARATOR.$name.$suffix) && ('\\' === DIRECTORY_SEPARATOR || is_executable($file))) { + if (@is_file($file = $dir.DIRECTORY_SEPARATOR.$name.$suffix) && ('\\' === DIRECTORY_SEPARATOR || @is_executable($file))) { return $file; } } diff --git a/symfony/process/PhpExecutableFinder.php b/symfony/process/PhpExecutableFinder.php index c9c113c91..670159022 100644 --- a/symfony/process/PhpExecutableFinder.php +++ b/symfony/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; } diff --git a/symfony/process/Pipes/AbstractPipes.php b/symfony/process/Pipes/AbstractPipes.php index 2bd1fe75b..9a7d88be8 100644 --- a/symfony/process/Pipes/AbstractPipes.php +++ b/symfony/process/Pipes/AbstractPipes.php @@ -25,6 +25,7 @@ abstract class AbstractPipes implements PipesInterface private $inputBuffer = ''; private $input; private $blocked = true; + private $lastError; /** * @param resource|string|int|float|bool|\Iterator|null $input @@ -58,10 +59,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'); } /** @@ -165,4 +167,12 @@ protected function write() return array($this->pipes[0]); } } + + /** + * @internal + */ + public function handleError($type, $msg) + { + $this->lastError = $msg; + } } diff --git a/symfony/process/Pipes/UnixPipes.php b/symfony/process/Pipes/UnixPipes.php index 78ffee7b0..254df5190 100644 --- a/symfony/process/Pipes/UnixPipes.php +++ b/symfony/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 diff --git a/symfony/routing/Matcher/Dumper/DumperCollection.php b/symfony/routing/Matcher/Dumper/DumperCollection.php index 6916297b8..dd057d2ee 100644 --- a/symfony/routing/Matcher/Dumper/DumperCollection.php +++ b/symfony/routing/Matcher/Dumper/DumperCollection.php @@ -106,7 +106,7 @@ protected function getParent() /** * Sets the parent collection. */ - protected function setParent(DumperCollection $parent) + protected function setParent(self $parent) { $this->parent = $parent; } diff --git a/symfony/routing/RouteCollectionBuilder.php b/symfony/routing/RouteCollectionBuilder.php index e8a9a165d..3aec14695 100644 --- a/symfony/routing/RouteCollectionBuilder.php +++ b/symfony/routing/RouteCollectionBuilder.php @@ -118,7 +118,7 @@ public function createBuilder() * @param string $prefix * @param RouteCollectionBuilder $builder */ - public function mount($prefix, RouteCollectionBuilder $builder) + public function mount($prefix, self $builder) { $builder->prefix = trim(trim($prefix), '/'); $this->routes[] = $builder; @@ -251,8 +251,6 @@ public function setMethods($methods) /** * Adds a resource for this collection. * - * @param ResourceInterface $resource - * * @return $this */ private function addResource(ResourceInterface $resource) diff --git a/symfony/translation/Command/XliffLintCommand.php b/symfony/translation/Command/XliffLintCommand.php index fead5edcb..e868a57e8 100644 --- a/symfony/translation/Command/XliffLintCommand.php +++ b/symfony/translation/Command/XliffLintCommand.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Translation\Command; use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Exception\RuntimeException; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; @@ -81,14 +82,14 @@ protected function execute(InputInterface $input, OutputInterface $output) if (!$filename) { if (!$stdin = $this->getStdin()) { - throw new \RuntimeException('Please provide a filename or pipe file content to STDIN.'); + throw new RuntimeException('Please provide a filename or pipe file content to STDIN.'); } return $this->display($io, array($this->validate($stdin))); } if (!$this->isReadable($filename)) { - throw new \RuntimeException(sprintf('File or directory "%s" is not readable.', $filename)); + throw new RuntimeException(sprintf('File or directory "%s" is not readable.', $filename)); } $filesInfo = array(); @@ -136,7 +137,7 @@ private function display(SymfonyStyle $io, array $files) case 'json': return $this->displayJson($io, $files); default: - throw new \InvalidArgumentException(sprintf('The format "%s" is not supported.', $this->format)); + throw new InvalidArgumentException(sprintf('The format "%s" is not supported.', $this->format)); } } diff --git a/symfony/translation/MessageCatalogueInterface.php b/symfony/translation/MessageCatalogueInterface.php index 4dad27fbf..e0dbb2bd9 100644 --- a/symfony/translation/MessageCatalogueInterface.php +++ b/symfony/translation/MessageCatalogueInterface.php @@ -105,7 +105,7 @@ public function add($messages, $domain = 'messages'); * * The two catalogues must have the same locale. */ - public function addCatalogue(MessageCatalogueInterface $catalogue); + public function addCatalogue(self $catalogue); /** * Merges translations from the given Catalogue into the current one @@ -113,7 +113,7 @@ public function addCatalogue(MessageCatalogueInterface $catalogue); * * This is used to provide default translations when they do not exist for the current locale. */ - public function addFallbackCatalogue(MessageCatalogueInterface $catalogue); + public function addFallbackCatalogue(self $catalogue); /** * Gets the fallback catalogue. diff --git a/symfony/translation/PluralizationRules.php b/symfony/translation/PluralizationRules.php index 38dde743a..48a6c608c 100644 --- a/symfony/translation/PluralizationRules.php +++ b/symfony/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':