Merge branch '2.3' into 2.4

* 2.3:
  [Console]Improve formatter for double-width character
  Lower mbstring dep, remove it for Yaml and CssSelector components
  [Security] Add check for supported attributes in AclVoter
  [Form] Fixed TrimListenerTest as of PHP 5.5
  Added more IDE links
  [DependencyInjection] Fix parameter description in ConfigurationExtensionInterface
  [Finder] fixed typehint of the Finder::addAdapter() method
  [TwigBridge][Transchoice] set %count% from the current context.
  [DependencyInjection] Fix travis unit tests
  Update PHPUnit before run
  [Validator] fixed wrong test
  [WebProfilerBundle] added test case for #10773
  [WebProfilerBundle] fixed profiler homepage, fixed #10806
  [WebProfilerBundle] Added test case for #10806
  changed travis to run on the nightly builds of HHVM until everything gets stable
  Fixed issue #5427
  Allow URLs that don't contain a path

Conflicts:
	.travis.yml
This commit is contained in:
Fabien Potencier 2014-05-12 11:27:48 +02:00
commit 934cd28ba6
30 changed files with 386 additions and 126 deletions

View File

@ -6,22 +6,23 @@ 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;'
- 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

View File

@ -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);
}
}
}

View File

@ -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'),

View File

@ -398,6 +398,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);

View File

@ -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'));
}

View File

@ -0,0 +1,77 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* 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());
}
}

View File

@ -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) {

View File

@ -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/'));

View File

@ -99,7 +99,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
*
@ -159,7 +159,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)
{
@ -270,7 +270,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
*/
@ -282,7 +282,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
*/
@ -453,7 +453,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
*/
@ -632,8 +632,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
*
@ -651,8 +651,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
*
@ -675,26 +675,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) {
@ -703,9 +693,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);
@ -714,7 +704,7 @@ class Application
$messages = array('', '');
$messages[] = $emptyLine = $formatter->format(sprintf('<error>%s</error>', str_repeat(' ', $len)));
$messages[] = $formatter->format(sprintf('<error>%s%s</error>', $title, str_repeat(' ', max(0, $len - $strlen($title)))));
$messages[] = $formatter->format(sprintf('<error>%s%s</error>', $title, str_repeat(' ', max(0, $len - $this->stringWidth($title)))));
foreach ($lines as $line) {
$messages[] = $formatter->format(sprintf('<error> %s %s</error>', $line[0], str_repeat(' ', $len - $line[1])));
}
@ -881,7 +871,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)
{
@ -1101,4 +1091,53 @@ class Application
return array_keys($alternatives);
}
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;
}
}

View File

@ -49,7 +49,7 @@ abstract class Helper implements HelperInterface
*/
protected function strlen($string)
{
if (!function_exists('mb_strlen')) {
if (!function_exists('mb_strwidth')) {
return strlen($string);
}
@ -57,6 +57,6 @@ abstract class Helper implements HelperInterface
return strlen($string);
}
return mb_strlen($string, $encoding);
return mb_strwidth($string, $encoding);
}
}

View File

@ -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();

View File

@ -0,0 +1,11 @@
[Exception]
エラーメッセージ
foo

View File

@ -0,0 +1,11 @@
 
 [Exception] 
 エラーメッセージ 
 
foo

View File

@ -0,0 +1,12 @@
[Exception]
コマンドの実行中にエラーが
発生しました。
foo

View File

@ -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(
'<error> </error>'."\n" .
'<error> 表示するテキスト </error>'."\n" .
'<error> </error>',
$formatter->formatBlock('表示するテキスト', 'error', true),
'::formatBlock() formats a message in a block'
);
}
public function testFormatBlockLGEscaping()
{
$formatter = new FormatterHelper();

View File

@ -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);
}
}

View File

@ -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

View File

@ -1,4 +1,4 @@
<?xml version="1.0" ?>
<?xml version="1.0" encoding="utf-8"?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

View File

@ -94,7 +94,7 @@ class Finder implements \IteratorAggregate, \Countable
*
* @return Finder The current Finder instance
*/
public function addAdapter(Adapter\AdapterInterface $adapter, $priority = 0)
public function addAdapter(AdapterInterface $adapter, $priority = 0)
{
$this->adapters[$adapter->getName()] = array(
'adapter' => $adapter,

View File

@ -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(

View File

@ -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.');
}

View File

@ -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'),
);
}
}

View File

@ -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,

View File

@ -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;
}

View File

@ -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']);
}
}
}

View File

@ -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.');
}

View File

@ -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');

View File

@ -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.');
}

View File

@ -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;
}

View File

@ -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);
}
}