diff --git a/.travis.yml b/.travis.yml
index 6ec8f1434b..ca4c19d381 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -6,23 +6,24 @@ php:
- 5.4
- 5.5
- 5.6
- - hhvm
+ - hhvm-nightly
matrix:
allow_failures:
- - php: hhvm
+ - php: hhvm-nightly
services: mongodb
before_script:
- travis_retry sudo apt-get install parallel
- - sh -c 'if [ "$TRAVIS_PHP_VERSION" != "hhvm" ]; then echo "" >> ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/xdebug.ini; fi;'
- - sh -c 'if [ "$TRAVIS_PHP_VERSION" != "hhvm" ]; then echo "extension = mongo.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; fi;'
- - sh -c 'if [ "$TRAVIS_PHP_VERSION" != "hhvm" ] && [ $(php -r "echo PHP_MINOR_VERSION;") -le 4 ]; then echo "extension = apc.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; fi;'
- - sh -c 'if [ "$TRAVIS_PHP_VERSION" != "hhvm" ]; then echo "extension = memcached.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; fi;'
- - sh -c 'if [ "$TRAVIS_PHP_VERSION" != "hhvm" ]; then echo "extension = memcache.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; fi;'
+ - sh -c 'if [ "$TRAVIS_PHP_VERSION" != "hhvm-nightly" ]; then echo "" >> ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/xdebug.ini; fi;'
+ - sh -c 'if [ "$TRAVIS_PHP_VERSION" != "hhvm-nightly" ]; then echo "extension = mongo.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; fi;'
+ - sh -c 'if [ "$TRAVIS_PHP_VERSION" != "hhvm-nightly" ] && [ $(php -r "echo PHP_MINOR_VERSION;") -le 4 ]; then echo "extension = apc.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; fi;'
+ - sh -c 'if [ "$TRAVIS_PHP_VERSION" != "hhvm-nightly" ]; then echo "extension = memcached.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; fi;'
+ - sh -c 'if [ "$TRAVIS_PHP_VERSION" != "hhvm-nightly" ]; then echo "extension = memcache.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; fi;'
- sudo locale-gen fr_FR.UTF-8 && sudo update-locale
- COMPOSER_ROOT_VERSION=dev-master composer --prefer-source --dev install
+ - wget https://phar.phpunit.de/phpunit.phar && rm `which phpunit` && sudo cp phpunit.phar /usr/local/bin/phpunit && sudo chmod +x /usr/local/bin/phpunit
script:
- ls -d src/Symfony/*/* | parallel --gnu --keep-order 'echo "Running {} tests"; phpunit --exclude-group tty,benchmark {};' || exit 1
diff --git a/src/Symfony/Bridge/Twig/Node/TransNode.php b/src/Symfony/Bridge/Twig/Node/TransNode.php
index 2409090699..d459290b6f 100644
--- a/src/Symfony/Bridge/Twig/Node/TransNode.php
+++ b/src/Symfony/Bridge/Twig/Node/TransNode.php
@@ -98,9 +98,13 @@ class TransNode extends \Twig_Node
foreach ($matches[1] as $var) {
$key = new \Twig_Node_Expression_Constant('%'.$var.'%', $body->getLine());
if (!$vars->hasElement($key)) {
- $varExpr = new \Twig_Node_Expression_Name($var, $body->getLine());
- $varExpr->setAttribute('ignore_strict_check', $ignoreStrictCheck);
- $vars->addElement($varExpr, $key);
+ if ('count' === $var) {
+ $vars->addElement($this->getNode('count'), $key);
+ } else {
+ $varExpr = new \Twig_Node_Expression_Name($var, $body->getLine());
+ $varExpr->setAttribute('ignore_strict_check', $ignoreStrictCheck);
+ $vars->addElement($varExpr, $key);
+ }
}
}
diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/TranslationExtensionTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/TranslationExtensionTest.php
index c2e6089dbd..adaa00938a 100644
--- a/src/Symfony/Bridge/Twig/Tests/Extension/TranslationExtensionTest.php
+++ b/src/Symfony/Bridge/Twig/Tests/Extension/TranslationExtensionTest.php
@@ -96,6 +96,8 @@ class TranslationExtensionTest extends \PHPUnit_Framework_TestCase
'There is 5 apples (Symfony2)', array('count' => 5)),
array('{% transchoice count into "fr"%}{0} There is no apples|{1} There is one apple|]1,Inf] There is %count% apples{% endtranschoice %}',
'There is no apples', array('count' => 0)),
+ array('{% transchoice 5 into "fr"%}{0} There is no apples|{1} There is one apple|]1,Inf] There is %count% apples{% endtranschoice %}',
+ 'There is 5 apples'),
// trans filter
array('{{ "Hello"|trans }}', 'Hello'),
diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/Descriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/Descriptor.php
index 493e0eb37a..1b33d832c0 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/Descriptor.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/Descriptor.php
@@ -208,12 +208,8 @@ abstract class Descriptor implements DescriptorInterface
if (is_bool($value) || is_array($value) || (null === $value)) {
$jsonString = json_encode($value);
- if (!function_exists('mb_strlen')) {
- return substr($jsonString, 0, 60).(strlen($jsonString) > 60 ? ' ...' : '');
- }
-
- if (mb_strlen($jsonString) > 60) {
- return mb_substr($jsonString, 0, 60).' ...';
+ if (preg_match('/^(.{60})./us', $jsonString, $matches)) {
+ return $matches[1].'...';
}
return $jsonString;
diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
index b2d14973e9..15e0801fb9 100644
--- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
+++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
@@ -421,6 +421,8 @@ class FrameworkExtension extends Extension
$links = array(
'textmate' => 'txmt://open?url=file://%%f&line=%%l',
'macvim' => 'mvim://open?url=file://%%f&line=%%l',
+ 'emacs' => 'emacs://open?url=file://%file&line=%line',
+ 'sublime' => 'subl://open?url=file://%file&line=%line',
);
$container->setParameter('templating.helper.code.file_link_format', isset($links[$ide]) ? $links[$ide] : $ide);
diff --git a/src/Symfony/Bundle/WebProfilerBundle/Controller/ProfilerController.php b/src/Symfony/Bundle/WebProfilerBundle/Controller/ProfilerController.php
index cd3e17c7f5..a106feae99 100644
--- a/src/Symfony/Bundle/WebProfilerBundle/Controller/ProfilerController.php
+++ b/src/Symfony/Bundle/WebProfilerBundle/Controller/ProfilerController.php
@@ -232,7 +232,7 @@ class ProfilerController
$session->getFlashBag()->setAll($session->getFlashBag()->peekAll());
}
- if (null === $token) {
+ if ('empty' === $token || null === $token) {
return new Response('', 200, array('Content-Type' => 'text/html'));
}
diff --git a/src/Symfony/Bundle/WebProfilerBundle/Tests/Controller/ProfilerControllerTest.php b/src/Symfony/Bundle/WebProfilerBundle/Tests/Controller/ProfilerControllerTest.php
new file mode 100644
index 0000000000..bff1919aa9
--- /dev/null
+++ b/src/Symfony/Bundle/WebProfilerBundle/Tests/Controller/ProfilerControllerTest.php
@@ -0,0 +1,77 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bundle\WebProfilerBundle\Tests\Controller;
+
+use Symfony\Bundle\FrameworkBundle\Tests\TestCase;
+use Symfony\Bundle\WebProfilerBundle\Controller\ProfilerController;
+use Symfony\Component\HttpKernel\Profiler\Profile;
+use Symfony\Component\HttpFoundation\Request;
+
+class ProfilerControllerTest extends TestCase
+{
+ /**
+ * @dataProvider getEmptyTokenCases
+ */
+ public function testEmptyToken($token)
+ {
+ $urlGenerator = $this->getMock('Symfony\Component\Routing\Generator\UrlGeneratorInterface');
+ $twig = $this->getMock('Twig_Environment');
+ $profiler = $this
+ ->getMockBuilder('Symfony\Component\HttpKernel\Profiler\Profiler')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $controller = new ProfilerController($urlGenerator, $profiler, $twig, array());
+
+ $response = $controller->toolbarAction(Request::create('/_wdt/empty'), $token);
+ $this->assertEquals(200, $response->getStatusCode());
+ }
+
+ public function getEmptyTokenCases()
+ {
+ return array(
+ array(null),
+ // "empty" is also a valid empty token case, see https://github.com/symfony/symfony/issues/10806
+ array('empty'),
+ );
+ }
+
+ public function testReturns404onTokenNotFound()
+ {
+ $urlGenerator = $this->getMock('Symfony\Component\Routing\Generator\UrlGeneratorInterface');
+ $twig = $this->getMock('Twig_Environment');
+ $profiler = $this
+ ->getMockBuilder('Symfony\Component\HttpKernel\Profiler\Profiler')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $controller = new ProfilerController($urlGenerator, $profiler, $twig, array());
+
+ $profiler
+ ->expects($this->exactly(2))
+ ->method('loadProfile')
+ ->will($this->returnCallback(function ($token) {
+ if ('found' == $token) {
+ return new Profile($token);
+ }
+
+ return;
+ }))
+ ;
+
+ $response = $controller->toolbarAction(Request::create('/_wdt/found'), 'found');
+ $this->assertEquals(200, $response->getStatusCode());
+
+ $response = $controller->toolbarAction(Request::create('/_wdt/notFound'), 'notFound');
+ $this->assertEquals(404, $response->getStatusCode());
+ }
+}
diff --git a/src/Symfony/Component/BrowserKit/Cookie.php b/src/Symfony/Component/BrowserKit/Cookie.php
index 8b1f18ae3e..90636e9a56 100644
--- a/src/Symfony/Component/BrowserKit/Cookie.php
+++ b/src/Symfony/Component/BrowserKit/Cookie.php
@@ -152,12 +152,12 @@ class Cookie
);
if (null !== $url) {
- if ((false === $urlParts = parse_url($url)) || !isset($urlParts['host']) || !isset($urlParts['path'])) {
+ if ((false === $urlParts = parse_url($url)) || !isset($urlParts['host'])) {
throw new \InvalidArgumentException(sprintf('The URL "%s" is not valid.', $url));
}
$values['domain'] = $urlParts['host'];
- $values['path'] = substr($urlParts['path'], 0, strrpos($urlParts['path'], '/'));
+ $values['path'] = isset($urlParts['path']) ? substr($urlParts['path'], 0, strrpos($urlParts['path'], '/')) : '';
}
foreach ($parts as $part) {
diff --git a/src/Symfony/Component/BrowserKit/Tests/CookieTest.php b/src/Symfony/Component/BrowserKit/Tests/CookieTest.php
index 606b2e2e91..8e3578a28e 100644
--- a/src/Symfony/Component/BrowserKit/Tests/CookieTest.php
+++ b/src/Symfony/Component/BrowserKit/Tests/CookieTest.php
@@ -75,6 +75,8 @@ class CookieTest extends \PHPUnit_Framework_TestCase
public function testFromStringWithUrl()
{
$this->assertEquals('foo=bar; domain=www.example.com; path=/', (string) Cookie::FromString('foo=bar', 'http://www.example.com/'));
+ $this->assertEquals('foo=bar; domain=www.example.com; path=/', (string) Cookie::FromString('foo=bar', 'http://www.example.com'));
+ $this->assertEquals('foo=bar; domain=www.example.com; path=/', (string) Cookie::FromString('foo=bar', 'http://www.example.com?foo'));
$this->assertEquals('foo=bar; domain=www.example.com; path=/foo', (string) Cookie::FromString('foo=bar', 'http://www.example.com/foo/bar'));
$this->assertEquals('foo=bar; domain=www.example.com; path=/', (string) Cookie::FromString('foo=bar; path=/', 'http://www.example.com/foo/bar'));
$this->assertEquals('foo=bar; domain=www.myotherexample.com; path=/', (string) Cookie::FromString('foo=bar; domain=www.myotherexample.com', 'http://www.example.com/'));
diff --git a/src/Symfony/Component/Console/Application.php b/src/Symfony/Component/Console/Application.php
index 60d5c9f5ad..93f2966941 100644
--- a/src/Symfony/Component/Console/Application.php
+++ b/src/Symfony/Component/Console/Application.php
@@ -102,7 +102,7 @@ class Application
* @param InputInterface $input An Input instance
* @param OutputInterface $output An Output instance
*
- * @return int 0 if everything went fine, or an error code
+ * @return int 0 if everything went fine, or an error code
*
* @throws \Exception When doRun returns Exception
*
@@ -161,7 +161,7 @@ class Application
* @param InputInterface $input An Input instance
* @param OutputInterface $output An Output instance
*
- * @return int 0 if everything went fine, or an error code
+ * @return int 0 if everything went fine, or an error code
*/
public function doRun(InputInterface $input, OutputInterface $output)
{
@@ -272,7 +272,7 @@ class Application
/**
* Sets whether to catch exceptions or not during commands execution.
*
- * @param bool $boolean Whether to catch exceptions or not during commands execution
+ * @param bool $boolean Whether to catch exceptions or not during commands execution
*
* @api
*/
@@ -284,7 +284,7 @@ class Application
/**
* Sets whether to automatically exit after a command execution or not.
*
- * @param bool $boolean Whether to automatically exit after a command execution or not
+ * @param bool $boolean Whether to automatically exit after a command execution or not
*
* @api
*/
@@ -455,7 +455,7 @@ class Application
*
* @param string $name The command name or alias
*
- * @return bool true if the command exists, false otherwise
+ * @return bool true if the command exists, false otherwise
*
* @api
*/
@@ -634,8 +634,8 @@ class Application
/**
* Returns a text representation of the Application.
*
- * @param string $namespace An optional namespace name
- * @param bool $raw Whether to return raw command list
+ * @param string $namespace An optional namespace name
+ * @param bool $raw Whether to return raw command list
*
* @return string A string representing the Application
*
@@ -653,8 +653,8 @@ class Application
/**
* Returns an XML representation of the Application.
*
- * @param string $namespace An optional namespace name
- * @param bool $asDom Whether to return a DOM or an XML string
+ * @param string $namespace An optional namespace name
+ * @param bool $asDom Whether to return a DOM or an XML string
*
* @return string|\DOMDocument An XML string representing the Application
*
@@ -677,26 +677,16 @@ class Application
/**
* Renders a caught exception.
*
- * @param \Exception $e An exception instance
+ * @param \Exception $e An exception instance
* @param OutputInterface $output An OutputInterface instance
*/
public function renderException($e, $output)
{
- $strlen = function ($string) {
- if (!function_exists('mb_strlen')) {
- return strlen($string);
- }
-
- if (false === $encoding = mb_detect_encoding($string)) {
- return strlen($string);
- }
-
- return mb_strlen($string, $encoding);
- };
-
do {
$title = sprintf(' [%s] ', get_class($e));
- $len = $strlen($title);
+
+ $len = $this->stringWidth($title);
+
$width = $this->getTerminalWidth() ? $this->getTerminalWidth() - 1 : PHP_INT_MAX;
// HHVM only accepts 32 bits integer in str_split, even when PHP_INT_MAX is a 64 bit integer: https://github.com/facebook/hhvm/issues/1327
if (defined('HHVM_VERSION') && $width > 1 << 31) {
@@ -705,9 +695,9 @@ class Application
$formatter = $output->getFormatter();
$lines = array();
foreach (preg_split('/\r?\n/', $e->getMessage()) as $line) {
- foreach (str_split($line, $width - 4) as $line) {
+ foreach ($this->splitStringByWidth($line, $width - 4) as $line) {
// pre-format lines to get the right string length
- $lineLength = $strlen(preg_replace('/\[[^m]*m/', '', $formatter->format($line))) + 4;
+ $lineLength = $this->stringWidth(preg_replace('/\[[^m]*m/', '', $formatter->format($line))) + 4;
$lines[] = array($line, $lineLength);
$len = max($lineLength, $len);
@@ -716,7 +706,7 @@ class Application
$messages = array('', '');
$messages[] = $emptyLine = $formatter->format(sprintf('%s', str_repeat(' ', $len)));
- $messages[] = $formatter->format(sprintf('%s%s', $title, str_repeat(' ', max(0, $len - $strlen($title)))));
+ $messages[] = $formatter->format(sprintf('%s%s', $title, str_repeat(' ', max(0, $len - $this->stringWidth($title)))));
foreach ($lines as $line) {
$messages[] = $formatter->format(sprintf(' %s %s', $line[0], str_repeat(' ', $len - $line[1])));
}
@@ -883,7 +873,7 @@ class Application
* @param InputInterface $input An Input instance
* @param OutputInterface $output An Output instance
*
- * @return int 0 if everything went fine, or an error code
+ * @return int 0 if everything went fine, or an error code
*/
protected function doRunCommand(Command $command, InputInterface $input, OutputInterface $output)
{
@@ -1114,4 +1104,53 @@ class Application
{
$this->defaultCommand = $commandName;
}
+
+ private function stringWidth($string)
+ {
+ if (!function_exists('mb_strwidth')) {
+ return strlen($string);
+ }
+
+ if (false === $encoding = mb_detect_encoding($string)) {
+ return strlen($string);
+ }
+
+ return mb_strwidth($string, $encoding);
+ }
+
+ private function splitStringByWidth($string, $width)
+ {
+ // str_split is not suitable for multi-byte characters, we should use preg_split to get char array properly.
+ // additionally, array_slice() is not enough as some character has doubled width.
+ // we need a function to split string not by character count but by string width
+
+ if (!function_exists('mb_strwidth')) {
+ return str_split($string, $width);
+ }
+
+ if (false === $encoding = mb_detect_encoding($string)) {
+ return str_split($string, $width);
+ }
+
+ $utf8String = mb_convert_encoding($string, 'utf8', $encoding);
+ $lines = array();
+ $line = '';
+ foreach (preg_split('//u', $utf8String) as $char) {
+ // test if $char could be appended to current line
+ if (mb_strwidth($line.$char) <= $width) {
+ $line .= $char;
+ continue;
+ }
+ // if not, push current line to array and make new line
+ $lines[] = str_pad($line, $width);
+ $line = $char;
+ }
+ if (strlen($line)) {
+ $lines[] = count($lines) ? str_pad($line, $width) : $line;
+ }
+
+ mb_convert_variables($encoding, 'utf8', $lines);
+
+ return $lines;
+ }
}
diff --git a/src/Symfony/Component/Console/Helper/Helper.php b/src/Symfony/Component/Console/Helper/Helper.php
index b739daf402..05e895f765 100644
--- a/src/Symfony/Component/Console/Helper/Helper.php
+++ b/src/Symfony/Component/Console/Helper/Helper.php
@@ -51,7 +51,7 @@ abstract class Helper implements HelperInterface
*/
public static function strlen($string)
{
- if (!function_exists('mb_strlen')) {
+ if (!function_exists('mb_strwidth')) {
return strlen($string);
}
@@ -59,7 +59,7 @@ abstract class Helper implements HelperInterface
return strlen($string);
}
- return mb_strlen($string, $encoding);
+ return mb_strwidth($string, $encoding);
}
public static function formatTime($secs)
diff --git a/src/Symfony/Component/Console/Tests/ApplicationTest.php b/src/Symfony/Component/Console/Tests/ApplicationTest.php
index adbf8630a3..aa53f2ed8b 100644
--- a/src/Symfony/Component/Console/Tests/ApplicationTest.php
+++ b/src/Symfony/Component/Console/Tests/ApplicationTest.php
@@ -523,6 +523,33 @@ class ApplicationTest extends \PHPUnit_Framework_TestCase
$this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception4.txt', $tester->getDisplay(true), '->renderException() wraps messages when they are bigger than the terminal');
}
+ public function testRenderExceptionWithDoubleWidthCharacters()
+ {
+ $application = $this->getMock('Symfony\Component\Console\Application', array('getTerminalWidth'));
+ $application->setAutoExit(false);
+ $application->expects($this->any())
+ ->method('getTerminalWidth')
+ ->will($this->returnValue(120));
+ $application->register('foo')->setCode(function () {throw new \Exception('エラーメッセージ');});
+ $tester = new ApplicationTester($application);
+
+ $tester->run(array('command' => 'foo'), array('decorated' => false));
+ $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception_doublewidth1.txt', $tester->getDisplay(true), '->renderException() renderes a pretty exceptions with previous exceptions');
+
+ $tester->run(array('command' => 'foo'), array('decorated' => true));
+ $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception_doublewidth1decorated.txt', $tester->getDisplay(true), '->renderException() renderes a pretty exceptions with previous exceptions');
+
+ $application = $this->getMock('Symfony\Component\Console\Application', array('getTerminalWidth'));
+ $application->setAutoExit(false);
+ $application->expects($this->any())
+ ->method('getTerminalWidth')
+ ->will($this->returnValue(32));
+ $application->register('foo')->setCode(function () {throw new \Exception('コマンドの実行中にエラーが発生しました。');});
+ $tester = new ApplicationTester($application);
+ $tester->run(array('command' => 'foo'), array('decorated' => false));
+ $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception_doublewidth2.txt', $tester->getDisplay(true), '->renderException() wraps messages when they are bigger than the terminal');
+ }
+
public function testRun()
{
$application = new Application();
diff --git a/src/Symfony/Component/Console/Tests/Fixtures/application_renderexception_doublewidth1.txt b/src/Symfony/Component/Console/Tests/Fixtures/application_renderexception_doublewidth1.txt
new file mode 100644
index 0000000000..6a98660364
--- /dev/null
+++ b/src/Symfony/Component/Console/Tests/Fixtures/application_renderexception_doublewidth1.txt
@@ -0,0 +1,11 @@
+
+
+
+ [Exception]
+ エラーメッセージ
+
+
+
+foo
+
+
diff --git a/src/Symfony/Component/Console/Tests/Fixtures/application_renderexception_doublewidth1decorated.txt b/src/Symfony/Component/Console/Tests/Fixtures/application_renderexception_doublewidth1decorated.txt
new file mode 100644
index 0000000000..c68a60f564
--- /dev/null
+++ b/src/Symfony/Component/Console/Tests/Fixtures/application_renderexception_doublewidth1decorated.txt
@@ -0,0 +1,11 @@
+
+
+[37;41m [0m
+[37;41m [Exception] [0m
+[37;41m エラーメッセージ [0m
+[37;41m [0m
+
+
+[32mfoo[0m
+
+
diff --git a/src/Symfony/Component/Console/Tests/Fixtures/application_renderexception_doublewidth2.txt b/src/Symfony/Component/Console/Tests/Fixtures/application_renderexception_doublewidth2.txt
new file mode 100644
index 0000000000..545cd7b0b4
--- /dev/null
+++ b/src/Symfony/Component/Console/Tests/Fixtures/application_renderexception_doublewidth2.txt
@@ -0,0 +1,12 @@
+
+
+
+ [Exception]
+ コマンドの実行中にエラーが
+ 発生しました。
+
+
+
+foo
+
+
diff --git a/src/Symfony/Component/Console/Tests/Helper/FormatterHelperTest.php b/src/Symfony/Component/Console/Tests/Helper/FormatterHelperTest.php
index 87a45e1d44..db61de4f61 100644
--- a/src/Symfony/Component/Console/Tests/Helper/FormatterHelperTest.php
+++ b/src/Symfony/Component/Console/Tests/Helper/FormatterHelperTest.php
@@ -54,7 +54,7 @@ class FormatterHelperTest extends \PHPUnit_Framework_TestCase
public function testFormatBlockWithDiacriticLetters()
{
- if (!extension_loaded('mbstring')) {
+ if (!function_exists('mb_detect_encoding')) {
$this->markTestSkipped('This test requires mbstring to work.');
}
@@ -69,6 +69,21 @@ class FormatterHelperTest extends \PHPUnit_Framework_TestCase
);
}
+ public function testFormatBlockWithDoubleWidthDiacriticLetters()
+ {
+ if (!extension_loaded('mbstring')) {
+ $this->markTestSkipped('This test requires mbstring to work.');
+ }
+ $formatter = new FormatterHelper();
+ $this->assertEquals(
+ ' '."\n" .
+ ' 表示するテキスト '."\n" .
+ ' ',
+ $formatter->formatBlock('表示するテキスト', 'error', true),
+ '::formatBlock() formats a message in a block'
+ );
+ }
+
public function testFormatBlockLGEscaping()
{
$formatter = new FormatterHelper();
diff --git a/src/Symfony/Component/CssSelector/Parser/Tokenizer/TokenizerEscaping.php b/src/Symfony/Component/CssSelector/Parser/Tokenizer/TokenizerEscaping.php
index c90fc1044d..921fc393a9 100644
--- a/src/Symfony/Component/CssSelector/Parser/Tokenizer/TokenizerEscaping.php
+++ b/src/Symfony/Component/CssSelector/Parser/Tokenizer/TokenizerEscaping.php
@@ -65,14 +65,18 @@ class TokenizerEscaping
*/
private function replaceUnicodeSequences($value)
{
- return preg_replace_callback($this->patterns->getUnicodeEscapePattern(), function (array $match) {
- $code = $match[1];
+ return preg_replace_callback($this->patterns->getUnicodeEscapePattern(), function ($match) {
+ $c = hexdec($match[1]);
- if (bin2hex($code) > 0xFFFD) {
- $code = '\\FFFD';
+ if (0x80 > $c %= 0x200000) {
+ return chr($c);
+ }
+ if (0x800 > $c) {
+ return chr(0xC0 | $c>>6).chr(0x80 | $c & 0x3F);
+ }
+ if (0x10000 > $c) {
+ return chr(0xE0 | $c>>12).chr(0x80 | $c>>6 & 0x3F).chr(0x80 | $c & 0x3F);
}
-
- return mb_convert_encoding(pack('H*', $code), 'UTF-8', 'UCS-2BE');
}, $value);
}
}
diff --git a/src/Symfony/Component/DependencyInjection/Extension/ConfigurationExtensionInterface.php b/src/Symfony/Component/DependencyInjection/Extension/ConfigurationExtensionInterface.php
index 6fcd9018dc..51bff08012 100644
--- a/src/Symfony/Component/DependencyInjection/Extension/ConfigurationExtensionInterface.php
+++ b/src/Symfony/Component/DependencyInjection/Extension/ConfigurationExtensionInterface.php
@@ -24,7 +24,7 @@ interface ConfigurationExtensionInterface
/**
* Returns extension configuration
*
- * @param array $config $config An array of configuration values
+ * @param array $config An array of configuration values
* @param ContainerBuilder $container A ContainerBuilder instance
*
* @return ConfigurationInterface|null The configuration or null
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services8.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services8.xml
index c5fa5fb52d..b17e50043c 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services8.xml
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services8.xml
@@ -1,4 +1,4 @@
-
+
adapters[$adapter->getName()] = array(
'adapter' => $adapter,
diff --git a/src/Symfony/Component/Form/Extension/Core/DataTransformer/NumberToLocalizedStringTransformer.php b/src/Symfony/Component/Form/Extension/Core/DataTransformer/NumberToLocalizedStringTransformer.php
index 0fb2f72c29..3314340259 100644
--- a/src/Symfony/Component/Form/Extension/Core/DataTransformer/NumberToLocalizedStringTransformer.php
+++ b/src/Symfony/Component/Form/Extension/Core/DataTransformer/NumberToLocalizedStringTransformer.php
@@ -195,25 +195,19 @@ class NumberToLocalizedStringTransformer implements DataTransformerInterface
}
if (function_exists('mb_detect_encoding') && false !== $encoding = mb_detect_encoding($value)) {
- $strlen = function ($string) use ($encoding) {
- return mb_strlen($string, $encoding);
- };
- $substr = function ($string, $offset, $length) use ($encoding) {
- return mb_substr($string, $offset, $length, $encoding);
- };
+ $length = mb_strlen($value, $encoding);
+ $remainder = mb_substr($value, $position, $length, $encoding);
} else {
- $strlen = 'strlen';
- $substr = 'substr';
+ $length = strlen($value);
+ $remainder = substr($value, $position, $length);
}
- $length = $strlen($value);
-
// After parsing, position holds the index of the character where the
// parsing stopped
if ($position < $length) {
// Check if there are unrecognized characters at the end of the
// number (excluding whitespace characters)
- $remainder = trim($substr($value, $position, $length), " \t\n\r\0\x0b\xc2\xa0");
+ $remainder = trim($remainder, " \t\n\r\0\x0b\xc2\xa0");
if ('' !== $remainder) {
throw new TransformationFailedException(
diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/NumberToLocalizedStringTransformerTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/NumberToLocalizedStringTransformerTest.php
index 183935e7ab..fa5c2ba50f 100644
--- a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/NumberToLocalizedStringTransformerTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/NumberToLocalizedStringTransformerTest.php
@@ -215,7 +215,7 @@ class NumberToLocalizedStringTransformerTest extends \PHPUnit_Framework_TestCase
// https://github.com/symfony/symfony/issues/7609
public function testReverseTransformWithGroupingAndFixedSpaces()
{
- if (!extension_loaded('mbstring')) {
+ if (!function_exists('mb_detect_encoding')) {
$this->markTestSkipped('The "mbstring" extension is required for this test.');
}
@@ -537,7 +537,7 @@ class NumberToLocalizedStringTransformerTest extends \PHPUnit_Framework_TestCase
*/
public function testReverseTransformDisallowsCenteredExtraCharactersMultibyte()
{
- if (!extension_loaded('mbstring')) {
+ if (!function_exists('mb_detect_encoding')) {
$this->markTestSkipped('The "mbstring" extension is required for this test.');
}
@@ -554,7 +554,7 @@ class NumberToLocalizedStringTransformerTest extends \PHPUnit_Framework_TestCase
*/
public function testReverseTransformIgnoresTrailingSpacesInExceptionMessage()
{
- if (!extension_loaded('mbstring')) {
+ if (!function_exists('mb_detect_encoding')) {
$this->markTestSkipped('The "mbstring" extension is required for this test.');
}
@@ -582,7 +582,7 @@ class NumberToLocalizedStringTransformerTest extends \PHPUnit_Framework_TestCase
*/
public function testReverseTransformDisallowsTrailingExtraCharactersMultibyte()
{
- if (!extension_loaded('mbstring')) {
+ if (!function_exists('mb_detect_encoding')) {
$this->markTestSkipped('The "mbstring" extension is required for this test.');
}
diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/TrimListenerTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/TrimListenerTest.php
index 6e81ed404c..0b9c0c3f63 100644
--- a/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/TrimListenerTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/TrimListenerTest.php
@@ -41,32 +41,65 @@ class TrimListenerTest extends \PHPUnit_Framework_TestCase
}
/**
- * @dataProvider codePointProvider
+ * @dataProvider spaceProvider
*/
- public function testTrimUtf8($chars)
+ public function testTrimUtf8Separators($hex)
{
- if (!function_exists('mb_check_encoding')) {
- $this->markTestSkipped('The "mb_check_encoding" function is not available');
+ if (!function_exists('mb_convert_encoding')) {
+ $this->markTestSkipped('The "mb_convert_encoding" function is not available');
}
- $data = mb_convert_encoding(pack('H*', implode('', $chars)), 'UTF-8', 'UCS-2BE');
- $data = $data."ab\ncd".$data;
+ // Convert hexadecimal representation into binary
+ // H: hex string, high nibble first (UCS-2BE)
+ // *: repeat until end of string
+ $binary = pack('H*', $hex);
+
+ // Convert UCS-2BE to UTF-8
+ $symbol = mb_convert_encoding($binary, 'UTF-8', 'UCS-2BE');
+ $symbol = $symbol."ab\ncd".$symbol;
$form = $this->getMock('Symfony\Component\Form\Test\FormInterface');
- $event = new FormEvent($form, $data);
+ $event = new FormEvent($form, $symbol);
$filter = new TrimListener();
$filter->preSubmit($event);
- $this->assertSame("ab\ncd", $event->getData(), 'TrimListener should trim character(s): '.implode(', ', $chars));
+ $this->assertSame("ab\ncd", $event->getData());
}
- public function codePointProvider()
+ public function spaceProvider()
{
return array(
- 'General category: Separator' => array(array('0020', '00A0', '1680', '180E', '2000', '2001', '2002', '2003', '2004', '2005', '2006', '2007', '2008', '2009', '200A', '2028', '2029', '202F', '205F', '3000')),
- 'General category: Other, control' => array(array('0009', '000A', '000B', '000C', '000D', '0085')),
- //'General category: Other, format. ZERO WIDTH SPACE' => array(array('200B')),
+ // separators
+ array('0020'),
+ array('00A0'),
+ array('1680'),
+// array('180E'),
+ array('2000'),
+ array('2001'),
+ array('2002'),
+ array('2003'),
+ array('2004'),
+ array('2005'),
+ array('2006'),
+ array('2007'),
+ array('2008'),
+ array('2009'),
+ array('200A'),
+ array('2028'),
+ array('2029'),
+ array('202F'),
+ array('205F'),
+ array('3000'),
+ // controls
+ array('0009'),
+ array('000A'),
+ array('000B'),
+ array('000C'),
+ array('000D'),
+ array('0085'),
+ // zero width space
+// array('200B'),
);
}
}
diff --git a/src/Symfony/Component/Security/Acl/Tests/Voter/AclVoterTest.php b/src/Symfony/Component/Security/Acl/Tests/Voter/AclVoterTest.php
index 6bec23124c..f13df1f0d0 100644
--- a/src/Symfony/Component/Security/Acl/Tests/Voter/AclVoterTest.php
+++ b/src/Symfony/Component/Security/Acl/Tests/Voter/AclVoterTest.php
@@ -27,7 +27,7 @@ class AclVoterTest extends \PHPUnit_Framework_TestCase
*/
public function testSupportsAttribute($attribute, $supported)
{
- list($voter,, $permissionMap,,) = $this->getVoter();
+ list($voter,, $permissionMap,,) = $this->getVoter(true, false);
$permissionMap
->expects($this->once())
@@ -39,6 +39,16 @@ class AclVoterTest extends \PHPUnit_Framework_TestCase
$this->assertSame($supported, $voter->supportsAttribute($attribute));
}
+ /**
+ * @dataProvider getSupportsAttributeNonStringTests
+ */
+ public function testSupportsAttributeNonString($attribute)
+ {
+ list($voter,,,,,) = $this->getVoter(true, false);
+
+ $this->assertFalse($voter->supportsAttribute($attribute));
+ }
+
public function getSupportsAttributeTests()
{
return array(
@@ -47,6 +57,16 @@ class AclVoterTest extends \PHPUnit_Framework_TestCase
);
}
+ public function getSupportsAttributeNonStringTests()
+ {
+ return array(
+ array(new \stdClass()),
+ array(1),
+ array(true),
+ array(array()),
+ );
+ }
+
/**
* @dataProvider getSupportsClassTests
*/
@@ -387,13 +407,20 @@ class AclVoterTest extends \PHPUnit_Framework_TestCase
return $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface');
}
- protected function getVoter($allowIfObjectIdentityUnavailable = true)
+ protected function getVoter($allowIfObjectIdentityUnavailable = true, $alwaysContains = true)
{
$provider = $this->getMock('Symfony\Component\Security\Acl\Model\AclProviderInterface');
$permissionMap = $this->getMock('Symfony\Component\Security\Acl\Permission\PermissionMapInterface');
$oidStrategy = $this->getMock('Symfony\Component\Security\Acl\Model\ObjectIdentityRetrievalStrategyInterface');
$sidStrategy = $this->getMock('Symfony\Component\Security\Acl\Model\SecurityIdentityRetrievalStrategyInterface');
+ if ($alwaysContains) {
+ $permissionMap
+ ->expects($this->any())
+ ->method('contains')
+ ->will($this->returnValue(true));
+ }
+
return array(
new AclVoter($provider, $oidStrategy, $sidStrategy, $permissionMap, null, $allowIfObjectIdentityUnavailable),
$provider,
diff --git a/src/Symfony/Component/Security/Acl/Voter/AclVoter.php b/src/Symfony/Component/Security/Acl/Voter/AclVoter.php
index d401ef3bed..b21b1e675b 100644
--- a/src/Symfony/Component/Security/Acl/Voter/AclVoter.php
+++ b/src/Symfony/Component/Security/Acl/Voter/AclVoter.php
@@ -48,12 +48,16 @@ class AclVoter implements VoterInterface
public function supportsAttribute($attribute)
{
- return $this->permissionMap->contains($attribute);
+ return is_string($attribute) && $this->permissionMap->contains($attribute);
}
public function vote(TokenInterface $token, $object, array $attributes)
{
foreach ($attributes as $attribute) {
+ if (!$this->supportsAttribute($attribute)) {
+ continue;
+ }
+
if (null === $masks = $this->permissionMap->getMasks($attribute, $object)) {
continue;
}
diff --git a/src/Symfony/Component/Security/Csrf/TokenGenerator/TokenGeneratorInterface.php b/src/Symfony/Component/Security/Csrf/TokenGenerator/TokenGeneratorInterface.php
index 4d81da9c40..e02ac66ea0 100644
--- a/src/Symfony/Component/Security/Csrf/TokenGenerator/TokenGeneratorInterface.php
+++ b/src/Symfony/Component/Security/Csrf/TokenGenerator/TokenGeneratorInterface.php
@@ -12,19 +12,7 @@
namespace Symfony\Component\Security\Csrf\TokenGenerator;
/**
- * Generates and validates CSRF tokens.
- *
- * You can generate a CSRF token by using the method {@link generateCsrfToken()}.
- * This method expects a unique token ID as argument. The token ID can later be
- * used to validate a token provided by the user.
- *
- * Token IDs do not necessarily have to be secret, but they should NEVER be
- * created from data provided by the client. A good practice is to hard-code the
- * token IDs for the various CSRF tokens used by your application.
- *
- * You should use the method {@link isCsrfTokenValid()} to check a CSRF token
- * submitted by the client. This method will return true if the CSRF token is
- * valid.
+ * Generates CSRF tokens.
*
* @since 2.4
* @author Bernhard Schussek
diff --git a/src/Symfony/Component/Translation/Loader/PoFileLoader.php b/src/Symfony/Component/Translation/Loader/PoFileLoader.php
index 73f7f02364..8ba96680aa 100644
--- a/src/Symfony/Component/Translation/Loader/PoFileLoader.php
+++ b/src/Symfony/Component/Translation/Loader/PoFileLoader.php
@@ -157,7 +157,7 @@ class PoFileLoader extends ArrayLoader implements LoaderInterface
private function addMessage(array &$messages, array $item)
{
if (is_array($item['translated'])) {
- $messages[$item['ids']['singular']] = stripslashes($item['translated'][0]);
+ $messages[$item['ids']['singular']] = stripcslashes($item['translated'][0]);
if (isset($item['ids']['plural'])) {
$plurals = $item['translated'];
// PO are by definition indexed so sort by index.
@@ -172,7 +172,7 @@ class PoFileLoader extends ArrayLoader implements LoaderInterface
$messages[$item['ids']['plural']] = stripcslashes(implode('|', $plurals));
}
} elseif (!empty($item['ids']['singular'])) {
- $messages[$item['ids']['singular']] = stripslashes($item['translated']);
+ $messages[$item['ids']['singular']] = stripcslashes($item['translated']);
}
}
}
diff --git a/src/Symfony/Component/Translation/Tests/Dumper/IcuResFileDumperTest.php b/src/Symfony/Component/Translation/Tests/Dumper/IcuResFileDumperTest.php
index 0320e154e7..4c188aca5d 100644
--- a/src/Symfony/Component/Translation/Tests/Dumper/IcuResFileDumperTest.php
+++ b/src/Symfony/Component/Translation/Tests/Dumper/IcuResFileDumperTest.php
@@ -18,7 +18,7 @@ class IcuResFileDumperTest extends \PHPUnit_Framework_TestCase
{
public function testDump()
{
- if (!extension_loaded('mbstring')) {
+ if (!function_exists('mb_convert_encoding')) {
$this->markTestSkipped('This test requires mbstring to work.');
}
diff --git a/src/Symfony/Component/Validator/Tests/Mapping/Loader/StaticMethodLoaderTest.php b/src/Symfony/Component/Validator/Tests/Mapping/Loader/StaticMethodLoaderTest.php
index f90310cd26..80dac2d866 100644
--- a/src/Symfony/Component/Validator/Tests/Mapping/Loader/StaticMethodLoaderTest.php
+++ b/src/Symfony/Component/Validator/Tests/Mapping/Loader/StaticMethodLoaderTest.php
@@ -79,10 +79,16 @@ class StaticMethodLoaderTest extends \PHPUnit_Framework_TestCase
public function testLoadClassMetadataIgnoresAbstractMethods()
{
$loader = new StaticMethodLoader('loadMetadata');
+ $caught = false;
try {
- include __DIR__ . '/AbstractMethodStaticLoader.php';
- $this->fail('AbstractMethodStaticLoader should produce a strict standard error.');
+ include __DIR__.'/AbstractMethodStaticLoader.php';
} catch (\Exception $e) {
+ // catching the PHP notice that is converted to an exception by PHPUnit
+ $caught = true;
+ }
+
+ if (!$caught) {
+ $this->fail('AbstractMethodStaticLoader should produce a strict standard error.');
}
$metadata = new ClassMetadata(__NAMESPACE__.'\AbstractMethodStaticLoader');
diff --git a/src/Symfony/Component/Yaml/Parser.php b/src/Symfony/Component/Yaml/Parser.php
index d4e0a9fa66..9bde67b57b 100644
--- a/src/Symfony/Component/Yaml/Parser.php
+++ b/src/Symfony/Component/Yaml/Parser.php
@@ -55,7 +55,7 @@ class Parser
$this->currentLine = '';
$this->lines = explode("\n", $this->cleanup($value));
- if (function_exists('mb_detect_encoding') && false === mb_detect_encoding($value, 'UTF-8', true)) {
+ if (!preg_match('//u', $value)) {
throw new ParseException('The YAML value does not appear to be valid UTF-8.');
}
diff --git a/src/Symfony/Component/Yaml/Tests/ParserTest.php b/src/Symfony/Component/Yaml/Tests/ParserTest.php
index 07e6222d7e..2335efc559 100644
--- a/src/Symfony/Component/Yaml/Tests/ParserTest.php
+++ b/src/Symfony/Component/Yaml/Tests/ParserTest.php
@@ -33,12 +33,6 @@ class ParserTest extends \PHPUnit_Framework_TestCase
*/
public function testSpecifications($file, $expected, $yaml, $comment)
{
- if ('escapedCharacters' == $file) {
- if (!function_exists('iconv') && !function_exists('mb_convert_encoding')) {
- $this->markTestSkipped('The iconv and mbstring extensions are not available.');
- }
- }
-
$this->assertEquals($expected, var_export($this->parser->parse($yaml), true), $comment);
}
@@ -446,8 +440,8 @@ EOF;
public function testNonUtf8Exception()
{
- if (!function_exists('mb_detect_encoding') || !function_exists('iconv')) {
- $this->markTestSkipped('Exceptions for non-utf8 charsets require the mb_detect_encoding() and iconv() functions.');
+ if (!function_exists('iconv')) {
+ $this->markTestSkipped('Exceptions for non-utf8 charsets require the iconv() function.');
return;
}
diff --git a/src/Symfony/Component/Yaml/Unescaper.php b/src/Symfony/Component/Yaml/Unescaper.php
index 1b8eeed57a..b47d4a5928 100644
--- a/src/Symfony/Component/Yaml/Unescaper.php
+++ b/src/Symfony/Component/Yaml/Unescaper.php
@@ -21,6 +21,7 @@ class Unescaper
{
// Parser and Inline assume UTF-8 encoding, so escaped Unicode characters
// must be converted to that encoding.
+ // @deprecated since 2.5, to be removed in 3.0
const ENCODING = 'UTF-8';
// Regex fragment that matches an escaped character in a double quoted
@@ -80,13 +81,13 @@ class Unescaper
case 'n':
return "\n";
case 'v':
- return "\xb";
+ return "\xB";
case 'f':
- return "\xc";
+ return "\xC";
case 'r':
- return "\xd";
+ return "\r";
case 'e':
- return "\x1b";
+ return "\x1B";
case ' ':
return ' ';
case '"':
@@ -97,50 +98,44 @@ class Unescaper
return '\\';
case 'N':
// U+0085 NEXT LINE
- return $this->convertEncoding("\x00\x85", self::ENCODING, 'UCS-2BE');
+ return "\xC2\x85";
case '_':
// U+00A0 NO-BREAK SPACE
- return $this->convertEncoding("\x00\xA0", self::ENCODING, 'UCS-2BE');
+ return "\xC2\xA0";
case 'L':
// U+2028 LINE SEPARATOR
- return $this->convertEncoding("\x20\x28", self::ENCODING, 'UCS-2BE');
+ return "\xE2\x80\xA8";
case 'P':
// U+2029 PARAGRAPH SEPARATOR
- return $this->convertEncoding("\x20\x29", self::ENCODING, 'UCS-2BE');
+ return "\xE2\x80\xA9";
case 'x':
- $char = pack('n', hexdec(substr($value, 2, 2)));
-
- return $this->convertEncoding($char, self::ENCODING, 'UCS-2BE');
+ return self::utf8chr(hexdec(substr($value, 2, 2)));
case 'u':
- $char = pack('n', hexdec(substr($value, 2, 4)));
-
- return $this->convertEncoding($char, self::ENCODING, 'UCS-2BE');
+ return self::utf8chr(hexdec(substr($value, 2, 4)));
case 'U':
- $char = pack('N', hexdec(substr($value, 2, 8)));
-
- return $this->convertEncoding($char, self::ENCODING, 'UCS-4BE');
+ return self::utf8chr(hexdec(substr($value, 2, 8)));
}
}
/**
- * Convert a string from one encoding to another.
+ * Get the UTF-8 character for the given code point.
*
- * @param string $value The string to convert
- * @param string $to The input encoding
- * @param string $from The output encoding
+ * @param int $c The unicode code point
*
- * @return string The string with the new encoding
- *
- * @throws \RuntimeException if no suitable encoding function is found (iconv or mbstring)
+ * @return string The corresponding UTF-8 character
*/
- private function convertEncoding($value, $to, $from)
+ private static function utf8chr($c)
{
- if (function_exists('mb_convert_encoding')) {
- return mb_convert_encoding($value, $to, $from);
- } elseif (function_exists('iconv')) {
- return iconv($from, $to, $value);
+ if (0x80 > $c %= 0x200000) {
+ return chr($c);
+ }
+ if (0x800 > $c) {
+ return chr(0xC0 | $c>>6).chr(0x80 | $c & 0x3F);
+ }
+ if (0x10000 > $c) {
+ return chr(0xE0 | $c>>12).chr(0x80 | $c>>6 & 0x3F).chr(0x80 | $c & 0x3F);
}
- throw new \RuntimeException('No suitable convert encoding function (install the iconv or mbstring extension).');
+ return chr(0xF0 | $c>>18).chr(0x80 | $c>>12 & 0x3F).chr(0x80 | $c>>6 & 0x3F).chr(0x80 | $c & 0x3F);
}
}