diff --git a/UPGRADE-3.4.md b/UPGRADE-3.4.md index ffc6458fecd5d..6561c15a0b46f 100644 --- a/UPGRADE-3.4.md +++ b/UPGRADE-3.4.md @@ -204,6 +204,14 @@ FrameworkBundle `TranslationDebugCommand`, `TranslationUpdateCommand`, `XliffLintCommand` and `YamlLintCommand` classes have been marked as final + * The `Symfony\Bundle\FrameworkBundle\Translation\PhpExtractor` + class has been deprecated and will be removed in 4.0. Use the + `Symfony\Component\Translation\Extractor\PhpExtractor` class instead. + + * The `Symfony\Bundle\FrameworkBundle\Translation\PhpStringTokenParser` + class has been deprecated and will be removed in 4.0. Use the + `Symfony\Component\Translation\Extractor\PhpStringTokenParser` class instead. + HttpFoundation -------------- @@ -280,8 +288,8 @@ Profiler Security -------- - * Deprecated the HTTP digest authentication: `NonceExpiredException`, - `DigestAuthenticationListener` and `DigestAuthenticationEntryPoint` will be + * Deprecated the HTTP digest authentication: `NonceExpiredException`, + `DigestAuthenticationListener` and `DigestAuthenticationEntryPoint` will be removed in 4.0. Use another authentication system like `http_basic` instead. SecurityBundle @@ -305,7 +313,7 @@ SecurityBundle * Added `logout_on_user_change` to the firewall options. This config item will trigger a logout when the user has changed. Should be set to true to avoid deprecations in the configuration. - + * Deprecated the HTTP digest authentication: `HttpDigestFactory` will be removed in 4.0. Use another authentication system like `http_basic` instead. diff --git a/UPGRADE-4.0.md b/UPGRADE-4.0.md index 1f122c002dccf..688a309466b5d 100644 --- a/UPGRADE-4.0.md +++ b/UPGRADE-4.0.md @@ -494,6 +494,14 @@ FrameworkBundle `Symfony\Component\Translation\TranslatorInterface` as first argument. + * The `Symfony\Bundle\FrameworkBundle\Translation\PhpExtractor` + class has been deprecated and will be removed in 4.0. Use the + `Symfony\Component\Translation\Extractor\PhpExtractor` class instead. + + * The `Symfony\Bundle\FrameworkBundle\Translation\PhpStringTokenParser` + class has been deprecated and will be removed in 4.0. Use the + `Symfony\Component\Translation\Extractor\PhpStringTokenParser` class instead. + HttpFoundation -------------- @@ -525,9 +533,9 @@ HttpFoundation * The ability to check only for cacheable HTTP methods using `Request::isMethodSafe()` is not supported anymore, use `Request::isMethodCacheable()` instead. - * The `Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeSessionHandler`, + * The `Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeSessionHandler`, `Symfony\Component\HttpFoundation\Session\Storage\Proxy\AbstractProxy`, - `Symfony\Component\HttpFoundation\Session\Storage\Proxy\NativeProxy` and + `Symfony\Component\HttpFoundation\Session\Storage\Proxy\NativeProxy` and `Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy` classes have been removed. * `NativeSessionStorage::setSaveHandler()` now requires an instance of `\SessionHandlerInterface` as argument. @@ -682,7 +690,7 @@ SecurityBundle * The firewall option `logout_on_user_change` is now always true, which will trigger a logout if the user changes between requests. - + * Removed the HTTP digest authentication system. The `HttpDigestFactory` class has been removed. Use another authentication system like `http_basic` instead. diff --git a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md index 2af300950c43b..1e87a15660466 100644 --- a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md +++ b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md @@ -105,6 +105,10 @@ CHANGELOG `Symfony\Component\Workflow\DependencyInjection\ValidateWorkflowsPass` instead * Deprecated `ConstraintValidatorFactory`, use `Symfony\Component\Validator\ContainerConstraintValidatorFactory` instead. + * Deprecated `PhpStringTokenParser`, use + `Symfony\Component\Translation\Extractor\PhpStringTokenParser` instead. + * Deprecated `PhpExtractor`, use + `Symfony\Component\Translation\Extractor\PhpExtractor` instead. 3.2.0 ----- diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Translation/PhpExtractorTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Translation/PhpExtractorTest.php index e8c56ee4d5e52..ca45f43329a2f 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Translation/PhpExtractorTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Translation/PhpExtractorTest.php @@ -15,6 +15,9 @@ use Symfony\Bundle\FrameworkBundle\Translation\PhpExtractor; use Symfony\Component\Translation\MessageCatalogue; +/** + * @group legacy + */ class PhpExtractorTest extends TestCase { /** diff --git a/src/Symfony/Bundle/FrameworkBundle/Translation/PhpExtractor.php b/src/Symfony/Bundle/FrameworkBundle/Translation/PhpExtractor.php index 70658f67232d3..f8530875ae199 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Translation/PhpExtractor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Translation/PhpExtractor.php @@ -11,250 +11,13 @@ namespace Symfony\Bundle\FrameworkBundle\Translation; -use Symfony\Component\Finder\Finder; -use Symfony\Component\Translation\Extractor\AbstractFileExtractor; -use Symfony\Component\Translation\MessageCatalogue; -use Symfony\Component\Translation\Extractor\ExtractorInterface; +use Symfony\Component\Translation\Extractor\PhpExtractor as NewPhpExtractor; + +@trigger_error(sprintf('The class "%s" is deprecated since version 3.4 and will be removed in 4.0. Use "%s" instead. ', PhpExtractor::class, NewPhpExtractor::class), E_USER_DEPRECATED); /** - * PhpExtractor extracts translation messages from a PHP template. - * - * @author Michel Salib + * @deprecated since version 3.4 and will be removed in 4.0. Use Symfony\Component\Translation\Extractor\PhpExtractor instead */ -class PhpExtractor extends AbstractFileExtractor implements ExtractorInterface +class PhpExtractor extends NewPhpExtractor { - const MESSAGE_TOKEN = 300; - const METHOD_ARGUMENTS_TOKEN = 1000; - const DOMAIN_TOKEN = 1001; - - /** - * Prefix for new found message. - * - * @var string - */ - private $prefix = ''; - - /** - * The sequence that captures translation messages. - * - * @var array - */ - protected $sequences = array( - array( - '->', - 'trans', - '(', - self::MESSAGE_TOKEN, - ',', - self::METHOD_ARGUMENTS_TOKEN, - ',', - self::DOMAIN_TOKEN, - ), - array( - '->', - 'transChoice', - '(', - self::MESSAGE_TOKEN, - ',', - self::METHOD_ARGUMENTS_TOKEN, - ',', - self::METHOD_ARGUMENTS_TOKEN, - ',', - self::DOMAIN_TOKEN, - ), - array( - '->', - 'trans', - '(', - self::MESSAGE_TOKEN, - ), - array( - '->', - 'transChoice', - '(', - self::MESSAGE_TOKEN, - ), - ); - - /** - * {@inheritdoc} - */ - public function extract($resource, MessageCatalogue $catalog) - { - $files = $this->extractFiles($resource); - foreach ($files as $file) { - $this->parseTokens(token_get_all(file_get_contents($file)), $catalog); - - if (\PHP_VERSION_ID >= 70000) { - // PHP 7 memory manager will not release after token_get_all(), see https://bugs.php.net/70098 - gc_mem_caches(); - } - } - } - - /** - * {@inheritdoc} - */ - public function setPrefix($prefix) - { - $this->prefix = $prefix; - } - - /** - * Normalizes a token. - * - * @param mixed $token - * - * @return string - */ - protected function normalizeToken($token) - { - if (isset($token[1]) && 'b"' !== $token) { - return $token[1]; - } - - return $token; - } - - /** - * Seeks to a non-whitespace token. - */ - private function seekToNextRelevantToken(\Iterator $tokenIterator) - { - for (; $tokenIterator->valid(); $tokenIterator->next()) { - $t = $tokenIterator->current(); - if (T_WHITESPACE !== $t[0]) { - break; - } - } - } - - private function skipMethodArgument(\Iterator $tokenIterator) - { - $openBraces = 0; - - for (; $tokenIterator->valid(); $tokenIterator->next()) { - $t = $tokenIterator->current(); - - if ('[' === $t[0] || '(' === $t[0]) { - ++$openBraces; - } - - if (']' === $t[0] || ')' === $t[0]) { - --$openBraces; - } - - if ((0 === $openBraces && ',' === $t[0]) || (-1 === $openBraces && ')' === $t[0])) { - break; - } - } - } - - /** - * Extracts the message from the iterator while the tokens - * match allowed message tokens. - */ - private function getValue(\Iterator $tokenIterator) - { - $message = ''; - $docToken = ''; - - for (; $tokenIterator->valid(); $tokenIterator->next()) { - $t = $tokenIterator->current(); - if (!isset($t[1])) { - break; - } - - switch ($t[0]) { - case T_START_HEREDOC: - $docToken = $t[1]; - break; - case T_ENCAPSED_AND_WHITESPACE: - case T_CONSTANT_ENCAPSED_STRING: - $message .= $t[1]; - break; - case T_END_HEREDOC: - return PhpStringTokenParser::parseDocString($docToken, $message); - default: - break 2; - } - } - - if ($message) { - $message = PhpStringTokenParser::parse($message); - } - - return $message; - } - - /** - * Extracts trans message from PHP tokens. - * - * @param array $tokens - * @param MessageCatalogue $catalog - */ - protected function parseTokens($tokens, MessageCatalogue $catalog) - { - $tokenIterator = new \ArrayIterator($tokens); - - for ($key = 0; $key < $tokenIterator->count(); ++$key) { - foreach ($this->sequences as $sequence) { - $message = ''; - $domain = 'messages'; - $tokenIterator->seek($key); - - foreach ($sequence as $sequenceKey => $item) { - $this->seekToNextRelevantToken($tokenIterator); - - if ($this->normalizeToken($tokenIterator->current()) === $item) { - $tokenIterator->next(); - continue; - } elseif (self::MESSAGE_TOKEN === $item) { - $message = $this->getValue($tokenIterator); - - if (count($sequence) === ($sequenceKey + 1)) { - break; - } - } elseif (self::METHOD_ARGUMENTS_TOKEN === $item) { - $this->skipMethodArgument($tokenIterator); - } elseif (self::DOMAIN_TOKEN === $item) { - $domain = $this->getValue($tokenIterator); - - break; - } else { - break; - } - } - - if ($message) { - $catalog->set($message, $this->prefix.$message, $domain); - break; - } - } - } - } - - /** - * @param string $file - * - * @return bool - * - * @throws \InvalidArgumentException - */ - protected function canBeExtracted($file) - { - return $this->isFile($file) && 'php' === pathinfo($file, PATHINFO_EXTENSION); - } - - /** - * @param string|array $directory - * - * @return array - */ - protected function extractFromDirectory($directory) - { - $finder = new Finder(); - - return $finder->files()->name('*.php')->in($directory); - } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Translation/PhpStringTokenParser.php b/src/Symfony/Bundle/FrameworkBundle/Translation/PhpStringTokenParser.php index e950ac478f84c..7b472cb4350ed 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Translation/PhpStringTokenParser.php +++ b/src/Symfony/Bundle/FrameworkBundle/Translation/PhpStringTokenParser.php @@ -11,132 +11,11 @@ namespace Symfony\Bundle\FrameworkBundle\Translation; -/* - * The following is derived from code at http://github.com/nikic/PHP-Parser - * - * Copyright (c) 2011 by Nikita Popov - * - * Some rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * - * * The names of the contributors may not be used to endorse or - * promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ +@trigger_error(sprintf('The class "%s" is deprecated since version 3.4 and will be removed in 4.0. Use "%s" instead. ', PhpStringTokenParser::class, \Symfony\Component\Translation\Extractor\PhpStringTokenParser::class), E_USER_DEPRECATED); -class PhpStringTokenParser +/** + * @deprecated since version 3.4 and will be removed in 4.0. Use Symfony\Component\Translation\Extractor\PhpStringTokenParser instead + */ +class PhpStringTokenParser extends \Symfony\Component\Translation\Extractor\PhpStringTokenParser { - protected static $replacements = array( - '\\' => '\\', - '$' => '$', - 'n' => "\n", - 'r' => "\r", - 't' => "\t", - 'f' => "\f", - 'v' => "\v", - 'e' => "\x1B", - ); - - /** - * Parses a string token. - * - * @param string $str String token content - * - * @return string The parsed string - */ - public static function parse($str) - { - $bLength = 0; - if ('b' === $str[0]) { - $bLength = 1; - } - - if ('\'' === $str[$bLength]) { - return str_replace( - array('\\\\', '\\\''), - array('\\', '\''), - substr($str, $bLength + 1, -1) - ); - } else { - return self::parseEscapeSequences(substr($str, $bLength + 1, -1), '"'); - } - } - - /** - * Parses escape sequences in strings (all string types apart from single quoted). - * - * @param string $str String without quotes - * @param null|string $quote Quote type - * - * @return string String with escape sequences parsed - */ - public static function parseEscapeSequences($str, $quote) - { - if (null !== $quote) { - $str = str_replace('\\'.$quote, $quote, $str); - } - - return preg_replace_callback( - '~\\\\([\\\\$nrtfve]|[xX][0-9a-fA-F]{1,2}|[0-7]{1,3})~', - array(__CLASS__, 'parseCallback'), - $str - ); - } - - private static function parseCallback($matches) - { - $str = $matches[1]; - - if (isset(self::$replacements[$str])) { - return self::$replacements[$str]; - } elseif ('x' === $str[0] || 'X' === $str[0]) { - return chr(hexdec($str)); - } else { - return chr(octdec($str)); - } - } - - /** - * Parses a constant doc string. - * - * @param string $startToken Doc string start token content (<< + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Extractor; + +use Symfony\Component\Finder\Finder; +use Symfony\Component\Translation\MessageCatalogue; + +/** + * PhpExtractor extracts translation messages from a PHP template. + * + * @author Michel Salib + */ +class PhpExtractor extends AbstractFileExtractor implements ExtractorInterface +{ + const MESSAGE_TOKEN = 300; + const METHOD_ARGUMENTS_TOKEN = 1000; + const DOMAIN_TOKEN = 1001; + + /** + * Prefix for new found message. + * + * @var string + */ + private $prefix = ''; + + /** + * The sequence that captures translation messages. + * + * @var array + */ + protected $sequences = array( + array( + '->', + 'trans', + '(', + self::MESSAGE_TOKEN, + ',', + self::METHOD_ARGUMENTS_TOKEN, + ',', + self::DOMAIN_TOKEN, + ), + array( + '->', + 'transChoice', + '(', + self::MESSAGE_TOKEN, + ',', + self::METHOD_ARGUMENTS_TOKEN, + ',', + self::METHOD_ARGUMENTS_TOKEN, + ',', + self::DOMAIN_TOKEN, + ), + array( + '->', + 'trans', + '(', + self::MESSAGE_TOKEN, + ), + array( + '->', + 'transChoice', + '(', + self::MESSAGE_TOKEN, + ), + ); + + /** + * {@inheritdoc} + */ + public function extract($resource, MessageCatalogue $catalog) + { + $files = $this->extractFiles($resource); + foreach ($files as $file) { + $this->parseTokens(token_get_all(file_get_contents($file)), $catalog); + + if (\PHP_VERSION_ID >= 70000) { + // PHP 7 memory manager will not release after token_get_all(), see https://bugs.php.net/70098 + gc_mem_caches(); + } + } + } + + /** + * {@inheritdoc} + */ + public function setPrefix($prefix) + { + $this->prefix = $prefix; + } + + /** + * Normalizes a token. + * + * @param mixed $token + * + * @return string + */ + protected function normalizeToken($token) + { + if (isset($token[1]) && 'b"' !== $token) { + return $token[1]; + } + + return $token; + } + + /** + * Seeks to a non-whitespace token. + */ + private function seekToNextRelevantToken(\Iterator $tokenIterator) + { + for (; $tokenIterator->valid(); $tokenIterator->next()) { + $t = $tokenIterator->current(); + if (T_WHITESPACE !== $t[0]) { + break; + } + } + } + + private function skipMethodArgument(\Iterator $tokenIterator) + { + $openBraces = 0; + + for (; $tokenIterator->valid(); $tokenIterator->next()) { + $t = $tokenIterator->current(); + + if ('[' === $t[0] || '(' === $t[0]) { + ++$openBraces; + } + + if (']' === $t[0] || ')' === $t[0]) { + --$openBraces; + } + + if ((0 === $openBraces && ',' === $t[0]) || (-1 === $openBraces && ')' === $t[0])) { + break; + } + } + } + + /** + * Extracts the message from the iterator while the tokens + * match allowed message tokens. + */ + private function getValue(\Iterator $tokenIterator) + { + $message = ''; + $docToken = ''; + + for (; $tokenIterator->valid(); $tokenIterator->next()) { + $t = $tokenIterator->current(); + if (!isset($t[1])) { + break; + } + + switch ($t[0]) { + case T_START_HEREDOC: + $docToken = $t[1]; + break; + case T_ENCAPSED_AND_WHITESPACE: + case T_CONSTANT_ENCAPSED_STRING: + $message .= $t[1]; + break; + case T_END_HEREDOC: + return PhpStringTokenParser::parseDocString($docToken, $message); + default: + break 2; + } + } + + if ($message) { + $message = PhpStringTokenParser::parse($message); + } + + return $message; + } + + /** + * Extracts trans message from PHP tokens. + * + * @param array $tokens + * @param MessageCatalogue $catalog + */ + protected function parseTokens($tokens, MessageCatalogue $catalog) + { + $tokenIterator = new \ArrayIterator($tokens); + + for ($key = 0; $key < $tokenIterator->count(); ++$key) { + foreach ($this->sequences as $sequence) { + $message = ''; + $domain = 'messages'; + $tokenIterator->seek($key); + + foreach ($sequence as $sequenceKey => $item) { + $this->seekToNextRelevantToken($tokenIterator); + + if ($this->normalizeToken($tokenIterator->current()) === $item) { + $tokenIterator->next(); + continue; + } elseif (self::MESSAGE_TOKEN === $item) { + $message = $this->getValue($tokenIterator); + + if (count($sequence) === ($sequenceKey + 1)) { + break; + } + } elseif (self::METHOD_ARGUMENTS_TOKEN === $item) { + $this->skipMethodArgument($tokenIterator); + } elseif (self::DOMAIN_TOKEN === $item) { + $domain = $this->getValue($tokenIterator); + + break; + } else { + break; + } + } + + if ($message) { + $catalog->set($message, $this->prefix.$message, $domain); + break; + } + } + } + } + + /** + * @param string $file + * + * @return bool + * + * @throws \InvalidArgumentException + */ + protected function canBeExtracted($file) + { + return $this->isFile($file) && 'php' === pathinfo($file, PATHINFO_EXTENSION); + } + + /** + * @param string|array $directory + * + * @return array + */ + protected function extractFromDirectory($directory) + { + $finder = new Finder(); + + return $finder->files()->name('*.php')->in($directory); + } +} diff --git a/src/Symfony/Component/Translation/Extractor/PhpStringTokenParser.php b/src/Symfony/Component/Translation/Extractor/PhpStringTokenParser.php new file mode 100644 index 0000000000000..bab4ce0c97e26 --- /dev/null +++ b/src/Symfony/Component/Translation/Extractor/PhpStringTokenParser.php @@ -0,0 +1,142 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Extractor; + +/* + * The following is derived from code at http://github.com/nikic/PHP-Parser + * + * Copyright (c) 2011 by Nikita Popov + * + * Some rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * * The names of the contributors may not be used to endorse or + * promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +class PhpStringTokenParser +{ + protected static $replacements = array( + '\\' => '\\', + '$' => '$', + 'n' => "\n", + 'r' => "\r", + 't' => "\t", + 'f' => "\f", + 'v' => "\v", + 'e' => "\x1B", + ); + + /** + * Parses a string token. + * + * @param string $str String token content + * + * @return string The parsed string + */ + public static function parse($str) + { + $bLength = 0; + if ('b' === $str[0]) { + $bLength = 1; + } + + if ('\'' === $str[$bLength]) { + return str_replace( + array('\\\\', '\\\''), + array('\\', '\''), + substr($str, $bLength + 1, -1) + ); + } else { + return self::parseEscapeSequences(substr($str, $bLength + 1, -1), '"'); + } + } + + /** + * Parses escape sequences in strings (all string types apart from single quoted). + * + * @param string $str String without quotes + * @param null|string $quote Quote type + * + * @return string String with escape sequences parsed + */ + public static function parseEscapeSequences($str, $quote) + { + if (null !== $quote) { + $str = str_replace('\\'.$quote, $quote, $str); + } + + return preg_replace_callback( + '~\\\\([\\\\$nrtfve]|[xX][0-9a-fA-F]{1,2}|[0-7]{1,3})~', + array(__CLASS__, 'parseCallback'), + $str + ); + } + + private static function parseCallback($matches) + { + $str = $matches[1]; + + if (isset(self::$replacements[$str])) { + return self::$replacements[$str]; + } elseif ('x' === $str[0] || 'X' === $str[0]) { + return chr(hexdec($str)); + } else { + return chr(octdec($str)); + } + } + + /** + * Parses a constant doc string. + * + * @param string $startToken Doc string start token content (<< + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Translation\Tests\Extractor; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Translation\Extractor\PhpExtractor; +use Symfony\Component\Translation\MessageCatalogue; + +class PhpExtractorTest extends TestCase +{ + /** + * @dataProvider resourcesProvider + * + * @param array|string $resource + */ + public function testExtraction($resource) + { + // Arrange + $extractor = new PhpExtractor(); + $extractor->setPrefix('prefix'); + $catalogue = new MessageCatalogue('en'); + + // Act + $extractor->extract($resource, $catalogue); + + $expectedHeredoc = << array( + 'single-quoted key' => 'prefixsingle-quoted key', + 'double-quoted key' => 'prefixdouble-quoted key', + 'heredoc key' => 'prefixheredoc key', + 'nowdoc key' => 'prefixnowdoc key', + "double-quoted key with whitespace and escaped \$\n\" sequences" => "prefixdouble-quoted key with whitespace and escaped \$\n\" sequences", + 'single-quoted key with whitespace and nonescaped \$\n\' sequences' => 'prefixsingle-quoted key with whitespace and nonescaped \$\n\' sequences', + 'single-quoted key with "quote mark at the end"' => 'prefixsingle-quoted key with "quote mark at the end"', + $expectedHeredoc => 'prefix'.$expectedHeredoc, + $expectedNowdoc => 'prefix'.$expectedNowdoc, + '{0} There is no apples|{1} There is one apple|]1,Inf[ There are %count% apples' => 'prefix{0} There is no apples|{1} There is one apple|]1,Inf[ There are %count% apples', + ), + 'not_messages' => array( + 'other-domain-test-no-params-short-array' => 'prefixother-domain-test-no-params-short-array', + 'other-domain-test-no-params-long-array' => 'prefixother-domain-test-no-params-long-array', + 'other-domain-test-params-short-array' => 'prefixother-domain-test-params-short-array', + 'other-domain-test-params-long-array' => 'prefixother-domain-test-params-long-array', + 'other-domain-test-trans-choice-short-array-%count%' => 'prefixother-domain-test-trans-choice-short-array-%count%', + 'other-domain-test-trans-choice-long-array-%count%' => 'prefixother-domain-test-trans-choice-long-array-%count%', + 'typecast' => 'prefixtypecast', + 'msg1' => 'prefixmsg1', + 'msg2' => 'prefixmsg2', + ), + ); + $actualCatalogue = $catalogue->all(); + + $this->assertEquals($expectedCatalogue, $actualCatalogue); + } + + public function resourcesProvider() + { + $directory = __DIR__.'/../fixtures/extractor/'; + $splFiles = array(); + foreach (new \DirectoryIterator($directory) as $fileInfo) { + if ($fileInfo->isDot()) { + continue; + } + if ('translation.html.php' === $fileInfo->getBasename()) { + $phpFile = $fileInfo->getPathname(); + } + $splFiles[] = $fileInfo->getFileInfo(); + } + + return array( + array($directory), + array($phpFile), + array(glob($directory.'*')), + array($splFiles), + array(new \ArrayObject(glob($directory.'*'))), + array(new \ArrayObject($splFiles)), + ); + } +} diff --git a/src/Symfony/Component/Translation/Tests/fixtures/extractor/resource.format.engine b/src/Symfony/Component/Translation/Tests/fixtures/extractor/resource.format.engine new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/Symfony/Component/Translation/Tests/fixtures/extractor/this.is.a.template.format.engine b/src/Symfony/Component/Translation/Tests/fixtures/extractor/this.is.a.template.format.engine new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/Symfony/Component/Translation/Tests/fixtures/extractor/translation.html.php b/src/Symfony/Component/Translation/Tests/fixtures/extractor/translation.html.php new file mode 100644 index 0000000000000..1ce8ea94fd339 --- /dev/null +++ b/src/Symfony/Component/Translation/Tests/fixtures/extractor/translation.html.php @@ -0,0 +1,49 @@ +This template is used for translation message extraction tests +trans('single-quoted key'); ?> +trans('double-quoted key'); ?> +trans(<<<'EOF' +heredoc key +EOF +); ?> +trans(<<<'EOF' +nowdoc key +EOF +); ?> +trans( + "double-quoted key with whitespace and escaped \$\n\" sequences" +); ?> +trans( + 'single-quoted key with whitespace and nonescaped \$\n\' sequences' +); ?> +trans(<< +trans(<<<'EOF' +nowdoc key with whitespace and nonescaped \$\n sequences +EOF +); ?> + +trans('single-quoted key with "quote mark at the end"'); ?> + +transChoice( + '{0} There is no apples|{1} There is one apple|]1,Inf[ There are %count% apples', + 10, + array('%count%' => 10) +); ?> + +trans('other-domain-test-no-params-short-array', array(), 'not_messages'); ?> + +trans('other-domain-test-no-params-long-array', array(), 'not_messages'); ?> + +trans('other-domain-test-params-short-array', array('foo' => 'bar'), 'not_messages'); ?> + +trans('other-domain-test-params-long-array', array('foo' => 'bar'), 'not_messages'); ?> + +transChoice('other-domain-test-trans-choice-short-array-%count%', 10, array('%count%' => 10), 'not_messages'); ?> + +transChoice('other-domain-test-trans-choice-long-array-%count%', 10, array('%count%' => 10), 'not_messages'); ?> + +trans('typecast', array('a' => (int) '123'), 'not_messages'); ?> +transChoice('msg1', 10 + 1, array(), 'not_messages'); ?> +transChoice('msg2', ceil(4.5), array(), 'not_messages'); ?> 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