Merge branch '2.8'

Conflicts:
	composer.json
	src/Symfony/Bundle/FrameworkBundle/Resources/config/annotations.xml
	src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.xml
	src/Symfony/Bundle/FrameworkBundle/Resources/config/security_csrf.xml
	src/Symfony/Bundle/FrameworkBundle/composer.json
	src/Symfony/Component/DependencyInjection/ContainerBuilder.php
	src/Symfony/Component/Security/Core/composer.json
	src/Symfony/Component/Security/Csrf/composer.json
	src/Symfony/Component/Security/Http/composer.json
	src/Symfony/Component/Security/composer.json
	src/Symfony/Component/Translation/PluralizationRules.php
	src/Symfony/Component/VarDumper/Exception/ThrowingCasterException.php
This commit is contained in:
Nicolas Grekas 2015-10-07 09:44:07 +02:00
commit 6005fe53f7
49 changed files with 380 additions and 541 deletions

View File

@ -13,7 +13,7 @@ init:
- SET SYMFONY_DEPRECATIONS_HELPER=strict
- SET PHP=1
- SET ANSICON=121x90 (121x90)
- SET PHP_INI_MATRIX=php.ini-min-ext php.ini-max-ext
- SET PHP_INI_MATRIX=php.ini-min php.ini-max
install:
- IF EXIST c:\php (SET PHP=0) ELSE (mkdir c:\php)
@ -21,19 +21,19 @@ install:
- IF %PHP%==1 appveyor DownloadFile http://windows.php.net/downloads/releases/archives/php-5.5.9-nts-Win32-VC11-x86.zip
- IF %PHP%==1 7z x php-5.5.9-nts-Win32-VC11-x86.zip -y > 7z.log
- IF %PHP%==1 echo @php %%~dp0composer.phar %%* > composer.bat
- IF %PHP%==1 copy /Y php.ini-development php.ini-min-ext
- IF %PHP%==1 echo date.timezone="UTC" >> php.ini-min-ext
- IF %PHP%==1 echo extension_dir=ext >> php.ini-min-ext
- IF %PHP%==1 echo extension=php_openssl.dll >> php.ini-min-ext
- IF %PHP%==1 copy /Y php.ini-min-ext php.ini-max-ext
- IF %PHP%==1 echo extension=php_apc.dll >> php.ini-max-ext
- IF %PHP%==1 echo extension=php_intl.dll >> php.ini-max-ext
- IF %PHP%==1 echo extension=php_mbstring.dll >> php.ini-max-ext
- IF %PHP%==1 echo extension=php_fileinfo.dll >> php.ini-max-ext
- IF %PHP%==1 echo extension=php_pdo_sqlite.dll >> php.ini-max-ext
- IF %PHP%==1 echo extension=php_ldap.dll >> php.ini-max-ext
- IF %PHP%==1 copy /Y php.ini-development php.ini-min
- IF %PHP%==1 echo date.timezone="UTC" >> php.ini-min
- IF %PHP%==1 echo extension_dir=ext >> php.ini-min
- IF %PHP%==1 echo extension=php_openssl.dll >> php.ini-min
- IF %PHP%==1 copy /Y php.ini-min php.ini-max
- IF %PHP%==1 echo extension=php_apc.dll >> php.ini-max
- IF %PHP%==1 echo extension=php_intl.dll >> php.ini-max
- IF %PHP%==1 echo extension=php_mbstring.dll >> php.ini-max
- IF %PHP%==1 echo extension=php_fileinfo.dll >> php.ini-max
- IF %PHP%==1 echo extension=php_pdo_sqlite.dll >> php.ini-max
- IF %PHP%==1 echo extension=php_ldap.dll >> php.ini-max
- appveyor DownloadFile https://getcomposer.org/composer.phar
- copy /Y php.ini-max-ext php.ini
- copy /Y php.ini-max php.ini
- cd c:\projects\symfony
- php phpunit install
- IF %APPVEYOR_REPO_BRANCH%==master (SET COMPOSER_ROOT_VERSION=dev-master) ELSE (SET COMPOSER_ROOT_VERSION=%APPVEYOR_REPO_BRANCH%.x-dev)

View File

@ -19,7 +19,8 @@
"php": ">=5.5.9",
"doctrine/common": "~2.4",
"twig/twig": "~1.20|~2.0",
"psr/log": "~1.0"
"psr/log": "~1.0",
"paragonie/random_compat": "~1.0"
},
"replace": {
"symfony/asset": "self.version",

View File

@ -24,10 +24,6 @@ if (!file_exists($COMPOSER = __DIR__.'/composer.phar')) {
$PHP = ProcessUtils::escapeArgument($PHP);
$COMPOSER = $PHP.' '.ProcessUtils::escapeArgument($COMPOSER);
if (!(isset($argv[1]) && 'install' === $argv[1]) && !file_exists(__DIR__.'/vendor')) {
passthru("$COMPOSER update --no-progress --ansi");
}
if (!file_exists("$PHPUNIT_DIR/phpunit-$PHPUNIT_VERSION/phpunit")) {
// Build a standalone phpunit without symfony/yaml

View File

@ -87,8 +87,6 @@ class FrameworkExtension extends Extension
$this->registerRequestConfiguration($config['request'], $container, $loader);
}
$loader->load('security.xml');
if ($this->isConfigEnabled($container, $config['form'])) {
$this->formConfigEnabled = true;
$this->registerFormConfiguration($config, $container, $loader);
@ -809,22 +807,22 @@ class FrameworkExtension extends Extension
{
$loader->load('annotations.xml');
if ('file' === $config['cache']) {
$cacheDir = $container->getParameterBag()->resolveValue($config['file_cache_dir']);
if (!is_dir($cacheDir) && false === @mkdir($cacheDir, 0777, true) && !is_dir($cacheDir)) {
throw new \RuntimeException(sprintf('Could not create cache directory "%s".', $cacheDir));
if ('none' !== $config['cache']) {
if ('file' === $config['cache']) {
$cacheDir = $container->getParameterBag()->resolveValue($config['file_cache_dir']);
if (!is_dir($cacheDir) && false === @mkdir($cacheDir, 0777, true) && !is_dir($cacheDir)) {
throw new \RuntimeException(sprintf('Could not create cache directory "%s".', $cacheDir));
}
$container
->getDefinition('annotations.php_file_cache')
->replaceArgument(0, $cacheDir)
;
}
$container
->getDefinition('annotations.file_cache_reader')
->replaceArgument(1, $cacheDir)
->replaceArgument(2, $config['debug'])
;
$container->setAlias('annotation_reader', 'annotations.file_cache_reader');
} elseif ('none' !== $config['cache']) {
$container
->getDefinition('annotations.cached_reader')
->replaceArgument(1, new Reference($config['cache']))
->replaceArgument(1, new Reference('file' !== $config['cache'] ? $config['cache'] : 'annotations.php_file_cache'))
->replaceArgument(2, $config['debug'])
;
$container->setAlias('annotation_reader', 'annotations.cached_reader');

View File

@ -13,10 +13,8 @@
<argument /><!-- Debug-Flag -->
</service>
<service id="annotations.file_cache_reader" class="Doctrine\Common\Annotations\FileCacheReader" public="false">
<argument type="service" id="annotations.reader" />
<service id="annotations.php_file_cache" class="Doctrine\Common\Cache\PhpFileCache" public="false">
<argument /><!-- Cache-Directory -->
<argument /><!-- Debug Flag -->
</service>
<service id="annotation_reader" alias="annotations.reader" />

View File

@ -47,9 +47,7 @@
</service>
<service id="routing.loader" class="Symfony\Bundle\FrameworkBundle\Routing\DelegatingLoader">
<tag name="monolog.logger" channel="router" />
<argument type="service" id="controller_name_converter" />
<argument type="service" id="logger" on-invalid="null" />
<argument type="service" id="routing.resolver" />
</service>

View File

@ -1,15 +0,0 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<!-- Pseudo-Random Number Generator -->
<service id="security.secure_random" class="Symfony\Component\Security\Core\Util\SecureRandom">
<tag name="monolog.logger" channel="security" />
<argument>%kernel.cache_dir%/secure_random.seed</argument>
<argument type="service" id="logger" on-invalid="ignore" />
</service>
</services>
</container>

View File

@ -5,9 +5,7 @@
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<service id="security.csrf.token_generator" class="Symfony\Component\Security\Csrf\TokenGenerator\UriSafeTokenGenerator" public="false">
<argument type="service" id="security.secure_random" />
</service>
<service id="security.csrf.token_generator" class="Symfony\Component\Security\Csrf\TokenGenerator\UriSafeTokenGenerator" public="false" />
<service id="security.csrf.token_storage" class="Symfony\Component\Security\Csrf\TokenStorage\SessionTokenStorage" public="false">
<argument type="service" id="session" />

View File

@ -15,7 +15,6 @@ use Symfony\Bundle\FrameworkBundle\Controller\ControllerNameParser;
use Symfony\Component\Config\Exception\FileLoaderLoadException;
use Symfony\Component\Config\Loader\DelegatingLoader as BaseDelegatingLoader;
use Symfony\Component\Config\Loader\LoaderResolverInterface;
use Psr\Log\LoggerInterface;
/**
* DelegatingLoader delegates route loading to other loaders using a loader resolver.
@ -28,20 +27,17 @@ use Psr\Log\LoggerInterface;
class DelegatingLoader extends BaseDelegatingLoader
{
protected $parser;
protected $logger;
private $loading = false;
/**
* Constructor.
*
* @param ControllerNameParser $parser A ControllerNameParser instance
* @param LoggerInterface $logger A LoggerInterface instance
* @param LoaderResolverInterface $resolver A LoaderResolverInterface instance
*/
public function __construct(ControllerNameParser $parser, LoggerInterface $logger = null, LoaderResolverInterface $resolver)
public function __construct(ControllerNameParser $parser, LoaderResolverInterface $resolver)
{
$this->parser = $parser;
$this->logger = $logger;
parent::__construct($resolver);
}

View File

@ -69,13 +69,6 @@ abstract class FrameworkExtensionTest extends TestCase
$this->assertTrue($container->hasDefinition('security.csrf.token_manager'));
}
public function testSecureRandomIsAvailableIfCsrfIsDisabled()
{
$container = $this->createContainerFromFile('csrf_disabled');
$this->assertTrue($container->hasDefinition('security.secure_random'));
}
public function testProxies()
{
$container = $this->createContainerFromFile('full');
@ -319,8 +312,9 @@ abstract class FrameworkExtensionTest extends TestCase
{
$container = $this->createContainerFromFile('full');
$this->assertEquals($container->getParameter('kernel.cache_dir').'/annotations', $container->getDefinition('annotations.file_cache_reader')->getArgument(1));
$this->assertInstanceOf('Doctrine\Common\Annotations\FileCacheReader', $container->get('annotation_reader'));
$this->assertEquals($container->getParameter('kernel.cache_dir').'/annotations', $container->getDefinition('annotations.php_file_cache')->getArgument(0));
$this->assertSame('annotations.cached_reader', (string) $container->getAlias('annotation_reader'));
$this->assertSame('annotations.php_file_cache', (string) $container->getDefinition('annotations.cached_reader')->getArgument(1));
}
public function testFileLinkFormat()

View File

@ -0,0 +1,19 @@
<?php
namespace Symfony\Bundle\FrameworkBundle\Tests\Routing;
use Symfony\Bundle\FrameworkBundle\Controller\ControllerNameParser;
use Symfony\Bundle\FrameworkBundle\Routing\DelegatingLoader;
use Symfony\Component\Config\Loader\LoaderResolver;
class DelegatingLoaderTest extends \PHPUnit_Framework_TestCase
{
public function testConstructorApi()
{
$controllerNameParser = $this->getMockBuilder(ControllerNameParser::class)
->disableOriginalConstructor()
->getMock();
new DelegatingLoader($controllerNameParser, new LoaderResolver());
$this->assertTrue(true, '__construct() takes a ControllerNameParser and LoaderResolverInterface respectively as its first and second argument.');
}
}

View File

@ -30,6 +30,7 @@
"symfony/stopwatch": "~2.8|~3.0",
"symfony/templating": "~2.8|~3.0",
"symfony/translation": "~2.8|~3.0",
"doctrine/cache": "~1.0",
"doctrine/annotations": "~1.0"
},
"require-dev": {

View File

@ -164,6 +164,6 @@ EOF
private function generateSalt()
{
return base64_encode($this->getContainer()->get('security.secure_random')->nextBytes(30));
return base64_encode(random_bytes(30));
}
}

View File

@ -33,9 +33,7 @@
<service id="security.authentication.rememberme.services.persistent"
class="Symfony\Component\Security\Http\RememberMe\PersistentTokenBasedRememberMeServices"
parent="security.authentication.rememberme.services.abstract"
abstract="true">
<argument type="service" id="security.secure_random" />
</service>
abstract="true" />
<service id="security.authentication.rememberme.services.simplehash"
class="Symfony\Component\Security\Http\RememberMe\TokenBasedRememberMeServices"

View File

@ -91,12 +91,16 @@ class ClassLoader
return;
}
if (isset($this->prefixes[$prefix])) {
$this->prefixes[$prefix] = array_merge(
$this->prefixes[$prefix],
(array) $paths
);
if (is_array($paths)) {
$this->prefixes[$prefix] = array_unique(array_merge(
$this->prefixes[$prefix],
$paths
));
} elseif (!in_array($paths, $this->prefixes[$prefix])) {
$this->prefixes[$prefix][] = $paths;
}
} else {
$this->prefixes[$prefix] = (array) $paths;
$this->prefixes[$prefix] = array_unique((array) $paths);
}
}

View File

@ -76,14 +76,36 @@ class ClassLoaderTest extends \PHPUnit_Framework_TestCase
);
}
public function testAddPrefix()
public function testAddPrefixSingle()
{
$loader = new ClassLoader();
$loader->addPrefix('Foo', __DIR__.DIRECTORY_SEPARATOR.'Fixtures');
$loader->addPrefix('Foo', __DIR__.DIRECTORY_SEPARATOR.'Fixtures');
$prefixes = $loader->getPrefixes();
$this->assertArrayHasKey('Foo', $prefixes);
$this->assertCount(1, $prefixes['Foo']);
}
public function testAddPrefixesSingle()
{
$loader = new ClassLoader();
$loader->addPrefixes(array('Foo' => array('foo', 'foo')));
$loader->addPrefixes(array('Foo' => array('foo')));
$prefixes = $loader->getPrefixes();
$this->assertArrayHasKey('Foo', $prefixes);
$this->assertCount(1, $prefixes['Foo'], print_r($prefixes, true));
}
public function testAddPrefixMulti()
{
$loader = new ClassLoader();
$loader->addPrefix('Foo', 'foo');
$loader->addPrefix('Foo', 'bar');
$prefixes = $loader->getPrefixes();
$this->assertArrayHasKey('Foo', $prefixes);
$this->assertCount(2, $prefixes['Foo']);
$this->assertContains('foo', $prefixes['Foo']);
$this->assertContains('bar', $prefixes['Foo']);
}
public function testUseIncludePath()

View File

@ -137,15 +137,17 @@ class ApplicationDescription
private function sortCommands(array $commands)
{
$namespacedCommands = array();
$globalCommands = array();
foreach ($commands as $name => $command) {
$key = $this->application->extractNamespace($name, 1);
if (!$key) {
$key = '_global';
$globalCommands['_global'][$name] = $command;
} else {
$namespacedCommands[$key][$name] = $command;
}
$namespacedCommands[$key][$name] = $command;
}
ksort($namespacedCommands);
$namespacedCommands = array_merge($globalCommands, $namespacedCommands);
foreach ($namespacedCommands as &$commandsSet) {
ksort($commandsSet);

View File

@ -20,6 +20,7 @@ use Symfony\Component\Console\Exception\InvalidArgumentException;
* @author Fabien Potencier <fabien@symfony.com>
* @author Саша Стаменковић <umpirsky@gmail.com>
* @author Abdellatif Ait boudad <a.aitboudad@gmail.com>
* @author Max Grigorian <maxakawizard@gmail.com>
*/
class Table
{
@ -61,6 +62,11 @@ class Table
*/
private $style;
/**
* @var array
*/
private $columnStyles = array();
private static $styles;
public function __construct(OutputInterface $output)
@ -139,6 +145,47 @@ class Table
return $this->style;
}
/**
* Sets table column style.
*
* @param int $columnIndex Column index
* @param TableStyle|string $name The style name or a TableStyle instance
*
* @return Table
*/
public function setColumnStyle($columnIndex, $name)
{
$columnIndex = intval($columnIndex);
if ($name instanceof TableStyle) {
$this->columnStyles[$columnIndex] = $name;
} elseif (isset(self::$styles[$name])) {
$this->columnStyles[$columnIndex] = self::$styles[$name];
} else {
throw new \InvalidArgumentException(sprintf('Style "%s" is not defined.', $name));
}
return $this;
}
/**
* Gets the current style for a column.
*
* If style was not set, it returns the global table style.
*
* @param int $columnIndex Column index
*
* @return TableStyle
*/
public function getColumnStyle($columnIndex)
{
if (isset($this->columnStyles[$columnIndex])) {
return $this->columnStyles[$columnIndex];
}
return $this->getStyle();
}
public function setHeaders(array $headers)
{
$headers = array_values($headers);
@ -308,12 +355,14 @@ class Table
$width += strlen($cell) - mb_strwidth($cell, $encoding);
}
$style = $this->getColumnStyle($column);
if ($cell instanceof TableSeparator) {
$this->output->write(sprintf($this->style->getBorderFormat(), str_repeat($this->style->getHorizontalBorderChar(), $width)));
$this->output->write(sprintf($style->getBorderFormat(), str_repeat($style->getHorizontalBorderChar(), $width)));
} else {
$width += Helper::strlen($cell) - Helper::strlenWithoutDecoration($this->output->getFormatter(), $cell);
$content = sprintf($this->style->getCellRowContentFormat(), $cell);
$this->output->write(sprintf($cellFormat, str_pad($content, $width, $this->style->getPaddingChar(), $this->style->getPadType())));
$content = sprintf($style->getCellRowContentFormat(), $cell);
$this->output->write(sprintf($cellFormat, str_pad($content, $width, $style->getPaddingChar(), $style->getPadType())));
}
}

View File

@ -61,4 +61,52 @@ EOF;
$this->assertEquals($output, $commandTester->getDisplay(true));
}
public function testExecuteListsCommandsOrder()
{
require_once realpath(__DIR__.'/../Fixtures/Foo6Command.php');
$application = new Application();
$application->add(new \Foo6Command());
$commandTester = new CommandTester($command = $application->get('list'));
$commandTester->execute(array('command' => $command->getName()), array('decorated' => false));
$output = <<<EOF
Console Tool
Usage:
command [options] [arguments]
Options:
-h, --help Display this help message
-q, --quiet Do not output any message
-V, --version Display this application version
--ansi Force ANSI output
--no-ansi Disable ANSI output
-n, --no-interaction Do not ask any interactive question
-v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
Available commands:
help Displays help for a command
list Lists commands
0foo
0foo:bar 0foo:bar command
EOF;
$this->assertEquals($output, trim($commandTester->getDisplay(true)));
}
public function testExecuteListsCommandsOrderRaw()
{
require_once realpath(__DIR__.'/../Fixtures/Foo6Command.php');
$application = new Application();
$application->add(new \Foo6Command());
$commandTester = new CommandTester($command = $application->get('list'));
$commandTester->execute(array('command' => $command->getName(), '--raw' => true));
$output = <<<EOF
help Displays help for a command
list Lists commands
0foo:bar 0foo:bar command
EOF;
$this->assertEquals($output, trim($commandTester->getDisplay(true)));
}
}

View File

@ -0,0 +1,12 @@
<?php
use Symfony\Component\Console\Command\Command;
class Foo6Command extends Command
{
protected function configure()
{
$this->setName('0foo:bar')->setDescription('0foo:bar command');
}
}

View File

@ -576,6 +576,36 @@ TABLE;
| foo |
+---+--+
TABLE;
$this->assertEquals($expected, $this->getOutputContent($output));
}
public function testColumnStyle()
{
$table = new Table($output = $this->getOutputStream());
$table
->setHeaders(array('ISBN', 'Title', 'Author', 'Price'))
->setRows(array(
array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri', '9.95'),
array('9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens', '139.25'),
));
$style = new TableStyle();
$style->setPadType(STR_PAD_LEFT);
$table->setColumnStyle(3, $style);
$table->render();
$expected =
<<<TABLE
+---------------+----------------------+-----------------+--------+
| ISBN | Title | Author | Price |
+---------------+----------------------+-----------------+--------+
| 99921-58-10-7 | Divine Comedy | Dante Alighieri | 9.95 |
| 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens | 139.25 |
+---------------+----------------------+-----------------+--------+
TABLE;
$this->assertEquals($expected, $this->getOutputContent($output));

View File

@ -845,10 +845,22 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
}
$service = call_user_func_array($factory, $arguments);
if (!$definition->isDeprecated() && is_array($factory) && is_string($factory[0])) {
$r = new \ReflectionClass($factory[0]);
if (0 < strpos($r->getDocComment(), "\n * @deprecated ")) {
@trigger_error(sprintf('The "%s" service relies on the deprecated "%s" factory class. It should either be deprecated or its factory upgraded.', $id, $r->name), E_USER_DEPRECATED);
}
}
} else {
$r = new \ReflectionClass($parameterBag->resolveValue($definition->getClass()));
$service = null === $r->getConstructor() ? $r->newInstance() : $r->newInstanceArgs($arguments);
if (!$definition->isDeprecated() && 0 < strpos($r->getDocComment(), "\n * @deprecated ")) {
@trigger_error(sprintf('The "%s" service relies on the deprecated "%s" class. It should either be deprecated or its implementation upgraded.', $id, $r->name), E_USER_DEPRECATED);
}
}
if ($tryProxy || !$definition->isLazy()) {

View File

@ -417,7 +417,7 @@ class Filesystem
}
} else {
if (is_link($file)) {
$this->symlink($file->getRealPath(), $target);
$this->symlink($file->getLinkTarget(), $target);
} elseif (is_dir($file)) {
$this->mkdir($target);
} elseif (is_file($file)) {

View File

@ -915,7 +915,7 @@ class FilesystemTest extends FilesystemTestCase
$this->assertTrue(is_dir($targetPath));
$this->assertFileEquals($sourcePath.'/nested/file1.txt', $targetPath.DIRECTORY_SEPARATOR.'link1/file1.txt');
$this->assertTrue(is_link($targetPath.DIRECTORY_SEPARATOR.'link1'));
$this->assertEquals($sourcePath.'nested', readlink($targetPath.DIRECTORY_SEPARATOR.'link1'));
$this->assertEquals('nested', readlink($targetPath.DIRECTORY_SEPARATOR.'link1'));
}
/**

View File

@ -16,11 +16,11 @@
}
],
"require": {
"php": ">=5.3.9",
"php": ">=5.5.9",
"ext-ldap": "*"
},
"require-dev": {
"symfony/phpunit-bridge": "~2.7|~3.0.0"
"symfony/phpunit-bridge": "~2.8|~3.0"
},
"autoload": {
"psr-4": { "Symfony\\Component\\Ldap\\": "" }

View File

@ -23,11 +23,11 @@
}
],
"require": {
"php": ">=5.3.9"
"php": ">=5.5.9"
},
"require-dev": {
"symfony/phpunit-bridge": "~2.7|~3.0.0",
"symfony/serializer": "~2.7|~3.0.0",
"symfony/phpunit-bridge": "~2.8|~3.0",
"symfony/serializer": "~2.8|~3.0",
"phpdocumentor/reflection": "^1.0.7",
"doctrine/annotations": "~1.0"
},

View File

@ -17,6 +17,7 @@ CHANGELOG
`Symfony\Component\Security\Http\Authentication\SimpleFormAuthenticatorInterface` instead
* deprecated `Symfony\Component\Security\Core\Util\ClassUtils`, use
`Symfony\Component\Security\Acl\Util\ClassUtils` instead
* deprecated the `Symfony\Component\Security\Core\Util\SecureRandom` class in favor of the `random_bytes()` function
* deprecated `supportsAttribute()` and `supportsClass()` methods of
`Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface` and
`Symfony\Component\Security\Core\Authorization\Voter\VoterInterface`.

View File

@ -66,7 +66,7 @@ class AccessDecisionManagerTest extends \PHPUnit_Framework_TestCase
protected function getVoterFor2Roles($token, $vote1, $vote2)
{
$voter = $this->getMock('Symfony\Component\Security\Core\Authorization\Voter\VoterInterface');
$voter->expects($this->exactly(2))
$voter->expects($this->any())
->method('vote')
->will($this->returnValueMap(array(
array($token, null, array('ROLE_FOO'), $vote1),

View File

@ -1,201 +0,0 @@
<?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\Component\Security\Core\Tests\Util;
use Symfony\Component\Security\Core\Util\SecureRandom;
class SecureRandomTest extends \PHPUnit_Framework_TestCase
{
/**
* T1: Monobit test.
*
* @dataProvider getSecureRandoms
*/
public function testMonobit($secureRandom)
{
$nbOnBits = substr_count($this->getBitSequence($secureRandom, 20000), '1');
$this->assertTrue($nbOnBits > 9654 && $nbOnBits < 10346, 'Monobit test failed, number of turned on bits: '.$nbOnBits);
}
/**
* T2: Chi-square test with 15 degrees of freedom (chi-Quadrat-Anpassungstest).
*
* @dataProvider getSecureRandoms
*/
public function testPoker($secureRandom)
{
$b = $this->getBitSequence($secureRandom, 20000);
$c = array();
for ($i = 0; $i <= 15; ++$i) {
$c[$i] = 0;
}
for ($j = 1; $j <= 5000; ++$j) {
$k = 4 * $j - 1;
++$c[8 * $b[$k - 3] + 4 * $b[$k - 2] + 2 * $b[$k - 1] + $b[$k]];
}
$f = 0;
for ($i = 0; $i <= 15; ++$i) {
$f += $c[$i] * $c[$i];
}
$Y = 16 / 5000 * $f - 5000;
$this->assertTrue($Y > 1.03 && $Y < 57.4, 'Poker test failed, Y = '.$Y);
}
/**
* Run test.
*
* @dataProvider getSecureRandoms
*/
public function testRun($secureRandom)
{
$b = $this->getBitSequence($secureRandom, 20000);
$runs = array();
for ($i = 1; $i <= 6; ++$i) {
$runs[$i] = 0;
}
$addRun = function ($run) use (&$runs) {
if ($run > 6) {
$run = 6;
}
++$runs[$run];
};
$currentRun = 0;
$lastBit = null;
for ($i = 0; $i < 20000; ++$i) {
if ($lastBit === $b[$i]) {
++$currentRun;
} else {
if ($currentRun > 0) {
$addRun($currentRun);
}
$lastBit = $b[$i];
$currentRun = 0;
}
}
if ($currentRun > 0) {
$addRun($currentRun);
}
$this->assertTrue($runs[1] > 2267 && $runs[1] < 2733, 'Runs of length 1 outside of defined interval: '.$runs[1]);
$this->assertTrue($runs[2] > 1079 && $runs[2] < 1421, 'Runs of length 2 outside of defined interval: '.$runs[2]);
$this->assertTrue($runs[3] > 502 && $runs[3] < 748, 'Runs of length 3 outside of defined interval: '.$runs[3]);
$this->assertTrue($runs[4] > 233 && $runs[4] < 402, 'Runs of length 4 outside of defined interval: '.$runs[4]);
$this->assertTrue($runs[5] > 90 && $runs[5] < 223, 'Runs of length 5 outside of defined interval: '.$runs[5]);
$this->assertTrue($runs[6] > 90 && $runs[6] < 233, 'Runs of length 6 outside of defined interval: '.$runs[6]);
}
/**
* Long-run test.
*
* @dataProvider getSecureRandoms
*/
public function testLongRun($secureRandom)
{
$b = $this->getBitSequence($secureRandom, 20000);
$longestRun = $currentRun = 0;
$lastBit = null;
for ($i = 0; $i < 20000; ++$i) {
if ($lastBit === $b[$i]) {
++$currentRun;
} else {
if ($currentRun > $longestRun) {
$longestRun = $currentRun;
}
$lastBit = $b[$i];
$currentRun = 0;
}
}
if ($currentRun > $longestRun) {
$longestRun = $currentRun;
}
$this->assertTrue($longestRun < 34, 'Failed longest run test: '.$longestRun);
}
/**
* Serial Correlation (Autokorrelationstest).
*
* @dataProvider getSecureRandoms
*/
public function testSerialCorrelation($secureRandom)
{
$shift = mt_rand(1, 5000);
$b = $this->getBitSequence($secureRandom, 20000);
$Z = 0;
for ($i = 0; $i < 5000; ++$i) {
$Z += $b[$i] === $b[$i + $shift] ? 1 : 0;
}
$this->assertTrue($Z > 2326 && $Z < 2674, 'Failed serial correlation test: '.$Z);
}
public function getSecureRandoms()
{
$secureRandoms = array();
// only add if openssl is indeed present
$secureRandom = new SecureRandom();
if ($this->hasOpenSsl($secureRandom)) {
$secureRandoms[] = array($secureRandom);
}
// no-openssl with custom seed provider
$secureRandom = new SecureRandom(sys_get_temp_dir().'/_sf2.seed');
$this->disableOpenSsl($secureRandom);
$secureRandoms[] = array($secureRandom);
return $secureRandoms;
}
protected function disableOpenSsl($secureRandom)
{
$ref = new \ReflectionProperty($secureRandom, 'useOpenSsl');
$ref->setAccessible(true);
$ref->setValue($secureRandom, false);
$ref->setAccessible(false);
}
protected function hasOpenSsl($secureRandom)
{
$ref = new \ReflectionProperty($secureRandom, 'useOpenSsl');
$ref->setAccessible(true);
$ret = $ref->getValue($secureRandom);
$ref->setAccessible(false);
return $ret;
}
private function getBitSequence($secureRandom, $length)
{
$bitSequence = '';
for ($i = 0; $i < $length; $i += 40) {
$value = unpack('H*', $secureRandom->nextBytes(5));
$value = str_pad(base_convert($value[1], 16, 2), 40, '0', STR_PAD_LEFT);
$bitSequence .= $value;
}
return substr($bitSequence, 0, $length);
}
}

View File

@ -1,116 +0,0 @@
<?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\Component\Security\Core\Util;
use Psr\Log\LoggerInterface;
/**
* A secure random number generator implementation.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
final class SecureRandom implements SecureRandomInterface
{
private $logger;
private $useOpenSsl;
private $seed;
private $seedUpdated;
private $seedLastUpdatedAt;
private $seedFile;
/**
* Constructor.
*
* Be aware that a guessable seed will severely compromise the PRNG
* algorithm that is employed.
*
* @param string $seedFile
* @param LoggerInterface $logger
*/
public function __construct($seedFile = null, LoggerInterface $logger = null)
{
$this->seedFile = $seedFile;
$this->logger = $logger;
// determine whether to use OpenSSL
if (!function_exists('random_bytes') && !function_exists('openssl_random_pseudo_bytes')) {
if (null !== $this->logger) {
$this->logger->notice('It is recommended that you install the "paragonie/random_compat" library or enable the "openssl" extension for random number generation.');
}
$this->useOpenSsl = false;
} else {
$this->useOpenSsl = true;
}
}
/**
* {@inheritdoc}
*/
public function nextBytes($nbBytes)
{
if (function_exists('random_bytes')) {
return random_bytes($nbBytes);
}
// try OpenSSL
if ($this->useOpenSsl) {
$bytes = openssl_random_pseudo_bytes($nbBytes, $strong);
if (false !== $bytes && true === $strong) {
return $bytes;
}
if (null !== $this->logger) {
$this->logger->info('OpenSSL did not produce a secure random number.');
}
}
// initialize seed
if (null === $this->seed) {
if (null === $this->seedFile) {
throw new \RuntimeException('You need to specify a file path to store the seed.');
}
if (is_file($this->seedFile)) {
list($this->seed, $this->seedLastUpdatedAt) = $this->readSeed();
} else {
$this->seed = uniqid(mt_rand(), true);
$this->updateSeed();
}
}
$bytes = '';
while (strlen($bytes) < $nbBytes) {
static $incr = 1;
$bytes .= hash('sha512', $incr++.$this->seed.uniqid(mt_rand(), true).$nbBytes, true);
$this->seed = base64_encode(hash('sha512', $this->seed.$bytes.$nbBytes, true));
$this->updateSeed();
}
return substr($bytes, 0, $nbBytes);
}
private function readSeed()
{
return json_decode(file_get_contents($this->seedFile));
}
private function updateSeed()
{
if (!$this->seedUpdated && $this->seedLastUpdatedAt < time() - mt_rand(1, 10)) {
file_put_contents($this->seedFile, json_encode(array($this->seed, microtime(true))));
}
$this->seedUpdated = true;
}
}

View File

@ -1,29 +0,0 @@
<?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\Component\Security\Core\Util;
/**
* Interface that needs to be implemented by all secure random number generators.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
interface SecureRandomInterface
{
/**
* Generates the specified number of secure random bytes.
*
* @param int $nbBytes
*
* @return string
*/
public function nextBytes($nbBytes);
}

View File

@ -16,7 +16,8 @@
}
],
"require": {
"php": ">=5.5.9"
"php": ">=5.5.9",
"paragonie/random_compat" : "~1.0"
},
"require-dev": {
"symfony/phpunit-bridge": "~2.8|~3.0",
@ -25,8 +26,8 @@
"symfony/http-foundation": "~2.8|~3.0",
"symfony/translation": "~2.8|~3.0",
"symfony/validator": "~2.8|~3.0",
"psr/log": "~1.0",
"symfony/ldap": "~2.8|~3.0.0"
"symfony/ldap": "~2.8|~3.0",
"psr/log": "~1.0"
},
"suggest": {
"symfony/event-dispatcher": "",

View File

@ -27,11 +27,6 @@ class UriSafeTokenGeneratorTest extends \PHPUnit_Framework_TestCase
*/
private static $bytes;
/**
* @var \PHPUnit_Framework_MockObject_MockObject
*/
private $random;
/**
* @var UriSafeTokenGenerator
*/
@ -44,23 +39,16 @@ class UriSafeTokenGeneratorTest extends \PHPUnit_Framework_TestCase
protected function setUp()
{
$this->random = $this->getMock('Symfony\Component\Security\Core\Util\SecureRandomInterface');
$this->generator = new UriSafeTokenGenerator($this->random, self::ENTROPY);
$this->generator = new UriSafeTokenGenerator(self::ENTROPY);
}
protected function tearDown()
{
$this->random = null;
$this->generator = null;
}
public function testGenerateToken()
{
$this->random->expects($this->once())
->method('nextBytes')
->with(self::ENTROPY / 8)
->will($this->returnValue(self::$bytes));
$token = $this->generator->generateToken();
$this->assertTrue(ctype_print($token), 'is printable');

View File

@ -11,9 +11,6 @@
namespace Symfony\Component\Security\Csrf\TokenGenerator;
use Symfony\Component\Security\Core\Util\SecureRandomInterface;
use Symfony\Component\Security\Core\Util\SecureRandom;
/**
* Generates CSRF tokens.
*
@ -23,13 +20,6 @@ use Symfony\Component\Security\Core\Util\SecureRandom;
*/
class UriSafeTokenGenerator implements TokenGeneratorInterface
{
/**
* The generator for random values.
*
* @var SecureRandomInterface
*/
private $random;
/**
* The amount of entropy collected for each token (in bits).
*
@ -40,14 +30,10 @@ class UriSafeTokenGenerator implements TokenGeneratorInterface
/**
* Generates URI-safe CSRF tokens.
*
* @param SecureRandomInterface|null $random The random value generator used for
* generating entropy
* @param int $entropy The amount of entropy collected for
* each token (in bits)
* @param int $entropy The amount of entropy collected for each token (in bits)
*/
public function __construct(SecureRandomInterface $random = null, $entropy = 256)
public function __construct($entropy = 256)
{
$this->random = $random ?: new SecureRandom();
$this->entropy = $entropy;
}
@ -59,7 +45,7 @@ class UriSafeTokenGenerator implements TokenGeneratorInterface
// Generate an URI safe base64 encoded string that does not contain "+",
// "/" or "=" which need to be URL encoded and make URLs unnecessarily
// longer.
$bytes = $this->random->nextBytes($this->entropy / 8);
$bytes = random_bytes($this->entropy / 8);
return rtrim(strtr(base64_encode($bytes), '+/', '-_'), '=');
}

View File

@ -17,7 +17,8 @@
],
"require": {
"php": ">=5.5.9",
"symfony/security-core": "~2.8|~3.0"
"symfony/security-core": "~2.8|~3.0",
"paragonie/random_compat" : "~1.0"
},
"require-dev": {
"symfony/phpunit-bridge": "~2.8|~3.0",

View File

@ -35,7 +35,10 @@ abstract class AbstractRememberMeServices implements RememberMeServicesInterface
const COOKIE_DELIMITER = ':';
protected $logger;
protected $options;
protected $options = array(
'secure' => false,
'httponly' => true,
);
private $providerKey;
private $secret;
private $userProviders;
@ -66,7 +69,7 @@ abstract class AbstractRememberMeServices implements RememberMeServicesInterface
$this->userProviders = $userProviders;
$this->secret = $secret;
$this->providerKey = $providerKey;
$this->options = $options;
$this->options = array_merge($this->options, $options);
$this->logger = $logger;
}

View File

@ -19,7 +19,6 @@ use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\CookieTheftException;
use Symfony\Component\Security\Core\Authentication\RememberMe\PersistentToken;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Util\SecureRandomInterface;
use Psr\Log\LoggerInterface;
/**
@ -32,24 +31,6 @@ use Psr\Log\LoggerInterface;
class PersistentTokenBasedRememberMeServices extends AbstractRememberMeServices
{
private $tokenProvider;
private $secureRandom;
/**
* Constructor.
*
* @param array $userProviders
* @param string $secret
* @param string $providerKey
* @param array $options
* @param LoggerInterface $logger
* @param SecureRandomInterface $secureRandom
*/
public function __construct(array $userProviders, $secret, $providerKey, array $options = array(), LoggerInterface $logger = null, SecureRandomInterface $secureRandom)
{
parent::__construct($userProviders, $secret, $providerKey, $options, $logger);
$this->secureRandom = $secureRandom;
}
/**
* Sets the token provider.
@ -98,7 +79,7 @@ class PersistentTokenBasedRememberMeServices extends AbstractRememberMeServices
throw new AuthenticationException('The cookie has expired.');
}
$tokenValue = base64_encode($this->secureRandom->nextBytes(64));
$tokenValue = base64_encode(random_bytes(64));
$this->tokenProvider->updateToken($series, $tokenValue, new \DateTime());
$request->attributes->set(self::COOKIE_ATTR_NAME,
new Cookie(
@ -120,8 +101,8 @@ class PersistentTokenBasedRememberMeServices extends AbstractRememberMeServices
*/
protected function onLoginSuccess(Request $request, Response $response, TokenInterface $token)
{
$series = base64_encode($this->secureRandom->nextBytes(64));
$tokenValue = base64_encode($this->secureRandom->nextBytes(64));
$series = base64_encode(random_bytes(64));
$tokenValue = base64_encode(random_bytes(64));
$this->tokenProvider->createNewToken(
new PersistentToken(

View File

@ -91,11 +91,8 @@ class AbstractRememberMeServicesTest extends \PHPUnit_Framework_TestCase
$request = new Request();
$response = new Response();
$token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface');
$service->logout($request, $response, $token);
$cookie = $request->attributes->get(RememberMeServicesInterface::COOKIE_ATTR_NAME);
$this->assertInstanceOf('Symfony\Component\HttpFoundation\Cookie', $cookie);
$this->assertTrue($cookie->isCleared());
$this->assertSame($options['name'], $cookie->getName());
@ -286,13 +283,6 @@ class AbstractRememberMeServicesTest extends \PHPUnit_Framework_TestCase
$userProvider = $this->getProvider();
}
if (!isset($options['secure'])) {
$options['secure'] = false;
}
if (!isset($options['httponly'])) {
$options['httponly'] = true;
}
return $this->getMockForAbstractClass('Symfony\Component\Security\Http\RememberMe\AbstractRememberMeServices', array(
array($userProvider), 'foosecret', 'fookey', $options, $logger,
));

View File

@ -20,7 +20,6 @@ use Symfony\Component\HttpFoundation\ResponseHeaderBag;
use Symfony\Component\Security\Http\RememberMe\PersistentTokenBasedRememberMeServices;
use Symfony\Component\Security\Core\Exception\TokenNotFoundException;
use Symfony\Component\Security\Core\Exception\CookieTheftException;
use Symfony\Component\Security\Core\Util\SecureRandom;
class PersistentTokenBasedRememberMeServicesTest extends \PHPUnit_Framework_TestCase
{
@ -313,14 +312,7 @@ class PersistentTokenBasedRememberMeServicesTest extends \PHPUnit_Framework_Test
$userProvider = $this->getProvider();
}
if (!isset($options['secure'])) {
$options['secure'] = false;
}
if (!isset($options['httponly'])) {
$options['httponly'] = true;
}
return new PersistentTokenBasedRememberMeServices(array($userProvider), 'foosecret', 'fookey', $options, $logger, new SecureRandom(sys_get_temp_dir().'/_sf2.seed'));
return new PersistentTokenBasedRememberMeServices(array($userProvider), 'foosecret', 'fookey', $options, $logger);
}
protected function getProvider()

View File

@ -266,13 +266,6 @@ class TokenBasedRememberMeServicesTest extends \PHPUnit_Framework_TestCase
$userProvider = $this->getProvider();
}
if (!isset($options['secure'])) {
$options['secure'] = false;
}
if (!isset($options['httponly'])) {
$options['httponly'] = true;
}
$service = new TokenBasedRememberMeServices(array($userProvider), 'foosecret', 'fookey', $options, $logger);
return $service;

View File

@ -21,7 +21,8 @@
"symfony/event-dispatcher": "~2.8|~3.0",
"symfony/http-foundation": "~2.8|~3.0",
"symfony/http-kernel": "~2.8|~3.0",
"symfony/property-access": "~2.8|~3.0.0"
"symfony/property-access": "~2.8|~3.0",
"paragonie/random_compat" : "~1.0"
},
"require-dev": {
"symfony/phpunit-bridge": "~2.8|~3.0",

View File

@ -0,0 +1,63 @@
<?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\Component\Security\Tests;
use Symfony\Component\Finder\Finder;
class TranslationSyncStatusTest extends \PHPUnit_Framework_TestCase
{
/**
* @dataProvider getTranslationDirectoriesData
*/
public function testTranslationFileIsNotMissingInCore($dir1, $dir2)
{
$finder = new Finder();
$files = $finder->in($dir1)->files();
foreach ($files as $file) {
$this->assertFileExists($dir2.'/'.$file->getFilename(), 'Missing file '.$file->getFilename().' in directory '.$dir2);
}
}
public function getTranslationDirectoriesData()
{
$legacyTranslationsDir = $this->getLegacyTranslationsDirectory();
$coreTranslationsDir = $this->getCoreTranslationsDirectory();
return array(
'file-not-missing-in-core' => array($legacyTranslationsDir, $coreTranslationsDir),
'file-not-added-in-core' => array($coreTranslationsDir, $legacyTranslationsDir),
);
}
public function testFileContentsAreEqual()
{
$finder = new Finder();
$files = $finder->in($this->getLegacyTranslationsDirectory())->files();
foreach ($files as $file) {
$coreFile = $this->getCoreTranslationsDirectory().'/'.$file->getFilename();
$this->assertFileEquals($file->getRealPath(), $coreFile, $file.' and '.$coreFile.' have equal content.');
}
}
private function getLegacyTranslationsDirectory()
{
return __DIR__.'/../Resources/translations';
}
private function getCoreTranslationsDirectory()
{
return __DIR__.'/../Core/Resources/translations';
}
}

View File

@ -20,7 +20,8 @@
"symfony/event-dispatcher": "~2.8|~3.0",
"symfony/http-foundation": "~2.8|~3.0",
"symfony/http-kernel": "~2.8|~3.0",
"symfony/property-access": "~2.8|~3.0.0"
"symfony/property-access": "~2.8|~3.0",
"paragonie/random_compat" : "~1.0"
},
"replace": {
"symfony/security-core": "self.version",
@ -29,17 +30,17 @@
"symfony/security-http": "self.version"
},
"require-dev": {
"symfony/phpunit-bridge": "~2.7|~3.0.0",
"symfony/intl": "~2.3|~3.0.0",
"symfony/routing": "~2.2|~3.0.0",
"symfony/translation": "~2.0,>=2.0.5|~3.0.0",
"symfony/validator": "~2.5,>=2.5.5|~3.0.0",
"symfony/finder": "~2.8|~3.0",
"symfony/phpunit-bridge": "~2.8|~3.0",
"symfony/intl": "~2.8|~3.0",
"symfony/routing": "~2.8|~3.0",
"symfony/translation": "~2.8|~3.0",
"symfony/validator": "~2.8|~3.0",
"symfony/expression-language": "~2.8|~3.0",
"symfony/ldap": "~2.8|~3.0",
"doctrine/common": "~2.2",
"doctrine/dbal": "~2.2",
"psr/log": "~1.0",
"ircmaxell/password-compat": "~1.0",
"symfony/expression-language": "~2.6|~3.0.0",
"symfony/ldap": "~2.8|~3.0.0"
"psr/log": "~1.0"
},
"suggest": {
"symfony/class-loader": "For using the ACL generateSql script",
@ -48,8 +49,6 @@
"symfony/validator": "For using the user password constraint",
"symfony/routing": "For using the HttpUtils class to create sub-requests, redirect the user, and match URLs",
"symfony/expression-language": "For using the expression voter",
"ircmaxell/password-compat": "For using the BCrypt password encoder in PHP <5.5",
"paragonie/random_compat": "",
"symfony/ldap": "For using the LDAP user and authentication providers"
},
"autoload": {

View File

@ -12,6 +12,7 @@
<testsuites>
<testsuite name="Symfony Security Component Test Suite">
<directory>./Tests/</directory>
<directory>./Acl/Tests/</directory>
<directory>./Core/Tests/</directory>
<directory>./Http/Tests/</directory>
@ -24,6 +25,7 @@
<directory>./</directory>
<exclude>
<directory>./vendor</directory>
<directory>./Tests</directory>
<directory>./Acl/Tests</directory>
<directory>./Core/Tests</directory>
<directory>./Http/Tests</directory>

View File

@ -46,7 +46,7 @@ class UrlValidator extends ConstraintValidator
throw new UnexpectedTypeException($constraint, __NAMESPACE__.'\Url');
}
if (null === $value || '' === $value) {
if (null === $value) {
return;
}
@ -55,6 +55,10 @@ class UrlValidator extends ConstraintValidator
}
$value = (string) $value;
if ('' === $value) {
return;
}
$pattern = sprintf(static::PATTERN, implode('|', $constraint->protocols));
if (!preg_match($pattern, $value)) {

View File

@ -310,6 +310,10 @@
<source>This value does not match the expected {{ charset }} charset.</source>
<target>この値は予期される文字コード({{ charset }})と異なります。</target>
</trans-unit>
<trans-unit id="81">
<source>This is not a valid Business Identifier Code (BIC).</source>
<target>有効なSWIFTコードではありません。</target>
</trans-unit>
</body>
</file>
</xliff>

View File

@ -36,6 +36,13 @@ class UrlValidatorTest extends AbstractConstraintValidatorTest
$this->assertNoViolation();
}
public function testEmptyStringFromObjectIsValid()
{
$this->validator->validate(new EmailProvider(), new Url());
$this->assertNoViolation();
}
/**
* @expectedException \Symfony\Component\Validator\Exception\UnexpectedTypeException
*/
@ -173,3 +180,11 @@ class UrlValidatorTest extends AbstractConstraintValidatorTest
);
}
}
class EmailProvider
{
public function __toString()
{
return '';
}
}

View File

@ -21,15 +21,15 @@ abstract class VarDumperTestCase extends \PHPUnit_Framework_TestCase
{
public function assertDumpEquals($dump, $data, $message = '')
{
$this->assertSame(rtrim($dump), $this->getVarDumperDump($data), $message);
$this->assertSame(rtrim($dump), $this->getDump($data), $message);
}
public function assertDumpMatchesFormat($dump, $data, $message = '')
{
$this->assertStringMatchesFormat(rtrim($dump), $this->getVarDumperDump($data), $message);
$this->assertStringMatchesFormat(rtrim($dump), $this->getDump($data), $message);
}
private function getVarDumperDump($data)
protected function getDump($data)
{
$h = fopen('php://memory', 'r+b');
$cloner = new VarCloner();

View File

@ -29,7 +29,7 @@ trait VarDumperTestTrait
$this->assertStringMatchesFormat(rtrim($dump), $this->getDump($data), $message);
}
public function getDump($data)
protected function getDump($data)
{
$h = fopen('php://memory', 'r+b');
$cloner = new VarCloner();