Merge branch '3.0'
* 3.0: #17676 - making the proxy instantiation compatible with ProxyManager 2.x by detecting proxy features #17676 - making the proxy instantiation compatible with ProxyManager 2.x by detecting proxy features Fix bug when using an private aliased factory service [Form] fix tests added by #17798 by removing `choices_as_values` [Form] fix FQCN in tests added by #17798 [DependencyInjection] Remove unused parameter of private property bug #17798 [Form] allow `choice_label` option to be `false` [Form] fix tests added by #17760 with FQCN ChoiceFormField of type "select" could be "disabled" Update contributing docs [Console] Fix escaping of trailing backslashes Fix constraint validator alias being required [DependencyInjection] Simplified code in AutowirePass [ci] clone with depth=1 to kill push-forced PRs Add check on If-Range header
This commit is contained in:
commit
a99d713b21
|
@ -1,5 +1,6 @@
|
|||
| Q | A
|
||||
| ------------- | ---
|
||||
| Branch | master for features and deprecations / lowest applicable and maintained version otherwise
|
||||
| Bug fix? | yes/no
|
||||
| New feature? | yes/no
|
||||
| BC breaks? | yes/no
|
||||
|
|
|
@ -2,6 +2,9 @@ language: php
|
|||
|
||||
sudo: false
|
||||
|
||||
git:
|
||||
depth: 1
|
||||
|
||||
addons:
|
||||
apt_packages:
|
||||
- parallel
|
||||
|
|
|
@ -5,16 +5,20 @@ Symfony is an open source, community-driven project.
|
|||
|
||||
If you'd like to contribute, please read the following documents:
|
||||
|
||||
* [Contributing Code][1]: The document index related to contributions;
|
||||
* [Reporting a Bug][1]
|
||||
* [Submitting a Patch][2]
|
||||
* [Symfony Core Team][3]
|
||||
* [Security Issues][4]
|
||||
* [Running Symfony Tests][5]
|
||||
* [Our Backwards Compatibility Promise][6]
|
||||
* [Coding Standards][7]
|
||||
* [Conventions][8]
|
||||
|
||||
* [Submitting a Patch][2]: Guidelines for submitting a pull request;
|
||||
|
||||
* [Pull Request Template][3]: Template header to use in your pull request
|
||||
description;
|
||||
|
||||
* [Backwards Compatibility][4]: Backward compatibility rules.
|
||||
|
||||
[1]: https://symfony.com/doc/current/contributing/code/index.html
|
||||
[2]: https://symfony.com/doc/current/contributing/code/patches.html#check-list
|
||||
[3]: https://symfony.com/doc/current/contributing/code/patches.html#make-a-pull-request
|
||||
[4]: https://symfony.com/doc/current/contributing/code/bc.html#working-on-symfony-code
|
||||
[1]: https://symfony.com/doc/current/contributing/code/bugs.html
|
||||
[2]: https://symfony.com/doc/current/contributing/code/patches.html
|
||||
[3]: https://symfony.com/doc/current/contributing/code/core_team.html
|
||||
[4]: https://symfony.com/doc/current/contributing/code/security.html
|
||||
[5]: https://symfony.com/doc/current/contributing/code/tests.html
|
||||
[6]: https://symfony.com/doc/current/contributing/code/bc.html
|
||||
[7]: https://symfony.com/doc/current/contributing/code/standards.html
|
||||
[8]: https://symfony.com/doc/current/contributing/code/conventions.html
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
build: false
|
||||
shallow_clone: true
|
||||
platform: x86
|
||||
clone_depth: 1
|
||||
clone_folder: c:\projects\symfony
|
||||
|
||||
cache:
|
||||
|
|
|
@ -82,7 +82,7 @@
|
|||
"doctrine/orm": "~2.4,>=2.4.5",
|
||||
"doctrine/doctrine-bundle": "~1.4",
|
||||
"monolog/monolog": "~1.11",
|
||||
"ocramius/proxy-manager": "~0.4|~1.0",
|
||||
"ocramius/proxy-manager": "~0.4|~1.0|~2.0",
|
||||
"egulias/email-validator": "~1.2",
|
||||
"symfony/polyfill-apcu": "~1.1",
|
||||
"symfony/security-acl": "~2.8|~3.0",
|
||||
|
|
|
@ -74,10 +74,16 @@ class ProxyDumper implements DumperInterface
|
|||
$methodName = 'get'.Container::camelize($id).'Service';
|
||||
$proxyClass = $this->getProxyClassName($definition);
|
||||
|
||||
$generatedClass = $this->generateProxyClass($definition);
|
||||
|
||||
$constructorCall = $generatedClass->hasMethod('staticProxyConstructor')
|
||||
? $proxyClass.'::staticProxyConstructor'
|
||||
: 'new '.$proxyClass;
|
||||
|
||||
return <<<EOF
|
||||
if (\$lazyLoad) {
|
||||
|
||||
$instantiation new $proxyClass(
|
||||
$instantiation $constructorCall(
|
||||
function (&\$wrappedInstance, \ProxyManager\Proxy\LazyLoadingInterface \$proxy) {
|
||||
\$wrappedInstance = \$this->$methodName(false);
|
||||
|
||||
|
@ -97,11 +103,7 @@ EOF;
|
|||
*/
|
||||
public function getProxyCode(Definition $definition)
|
||||
{
|
||||
$generatedClass = new ClassGenerator($this->getProxyClassName($definition));
|
||||
|
||||
$this->proxyGenerator->generate(new \ReflectionClass($definition->getClass()), $generatedClass);
|
||||
|
||||
return $this->classGenerator->generate($generatedClass);
|
||||
return $this->classGenerator->generate($this->generateProxyClass($definition));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -115,4 +117,16 @@ EOF;
|
|||
{
|
||||
return str_replace('\\', '', $definition->getClass()).'_'.spl_object_hash($definition).$this->salt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ClassGenerator
|
||||
*/
|
||||
private function generateProxyClass(Definition $definition)
|
||||
{
|
||||
$generatedClass = new ClassGenerator($this->getProxyClassName($definition));
|
||||
|
||||
$this->proxyGenerator->generate(new \ReflectionClass($definition->getClass()), $generatedClass);
|
||||
|
||||
return $generatedClass;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
namespace Symfony\Bridge\ProxyManager\Tests\LazyProxy\Dumper;
|
||||
|
||||
use ProxyManager\ProxyGenerator\LazyLoading\MethodGenerator\StaticProxyConstructor;
|
||||
use Symfony\Bridge\ProxyManager\LazyProxy\PhpDumper\ProxyDumper;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Dumper\PhpDumper;
|
||||
|
@ -49,7 +50,11 @@ class PhpDumperTest extends \PHPUnit_Framework_TestCase
|
|||
*/
|
||||
public function testDumpContainerWithProxyServiceWillShareProxies()
|
||||
{
|
||||
require_once __DIR__.'/../Fixtures/php/lazy_service.php';
|
||||
if (class_exists(StaticProxyConstructor::class)) { // detecting ProxyManager v2
|
||||
require_once __DIR__.'/../Fixtures/php/lazy_service_with_hints.php';
|
||||
} else {
|
||||
require_once __DIR__.'/../Fixtures/php/lazy_service.php';
|
||||
}
|
||||
|
||||
$container = new \LazyServiceProjectServiceContainer();
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ class ProjectServiceContainer extends Container
|
|||
{
|
||||
if ($lazyLoad) {
|
||||
|
||||
return $this->services['foo'] = new stdClass_%s(
|
||||
return $this->services['foo'] =%sstdClass_%s(
|
||||
function (&$wrappedInstance, \ProxyManager\Proxy\LazyLoadingInterface $proxy) {
|
||||
$wrappedInstance = $this->getFooService(false);
|
||||
|
||||
|
@ -22,5 +22,5 @@ class ProjectServiceContainer extends Container
|
|||
}
|
||||
}
|
||||
|
||||
class stdClass_%s extends \stdClass implements \ProxyManager\Proxy\VirtualProxyInterface
|
||||
class stdClass_%s extends \stdClass implements \ProxyManager\%s
|
||||
{%a}%A
|
||||
|
|
|
@ -0,0 +1,187 @@
|
|||
<?php
|
||||
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\DependencyInjection\Container;
|
||||
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\DependencyInjection\Exception\LogicException;
|
||||
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
use Symfony\Component\DependencyInjection\Parameter;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;
|
||||
|
||||
/**
|
||||
* ProjectServiceContainer.
|
||||
*
|
||||
* This class has been auto-generated
|
||||
* by the Symfony Dependency Injection Component.
|
||||
*/
|
||||
class LazyServiceProjectServiceContainer extends Container
|
||||
{
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->services = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the 'foo' service.
|
||||
*
|
||||
* This service is shared.
|
||||
* This method always returns the same instance of the service.
|
||||
*
|
||||
* @param bool $lazyLoad whether to try lazy-loading the service with a proxy
|
||||
*
|
||||
* @return stdClass A stdClass instance.
|
||||
*/
|
||||
public function getFooService($lazyLoad = true)
|
||||
{
|
||||
if ($lazyLoad) {
|
||||
return $this->services['foo'] = new stdClass_c1d194250ee2e2b7d2eab8b8212368a8(
|
||||
function (&$wrappedInstance, \ProxyManager\Proxy\LazyLoadingInterface $proxy) {
|
||||
$wrappedInstance = $this->getFooService(false);
|
||||
|
||||
$proxy->setProxyInitializer(null);
|
||||
|
||||
return true;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return new \stdClass();
|
||||
}
|
||||
}
|
||||
|
||||
class stdClass_c1d194250ee2e2b7d2eab8b8212368a8 extends \stdClass implements \ProxyManager\Proxy\LazyLoadingInterface, \ProxyManager\Proxy\ValueHolderInterface
|
||||
{
|
||||
/**
|
||||
* @var \Closure|null initializer responsible for generating the wrapped object
|
||||
*/
|
||||
private $valueHolder5157dd96e88c0 = null;
|
||||
|
||||
/**
|
||||
* @var \Closure|null initializer responsible for generating the wrapped object
|
||||
*/
|
||||
private $initializer5157dd96e8924 = null;
|
||||
|
||||
/**
|
||||
* @override constructor for lazy initialization
|
||||
*
|
||||
* @param \Closure|null $initializer
|
||||
*/
|
||||
public function __construct($initializer)
|
||||
{
|
||||
$this->initializer5157dd96e8924 = $initializer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
*/
|
||||
public function __get($name)
|
||||
{
|
||||
$this->initializer5157dd96e8924 && $this->initializer5157dd96e8924->__invoke($this->valueHolder5157dd96e88c0, $this, '__get', array('name' => $name));
|
||||
|
||||
return $this->valueHolder5157dd96e88c0->$name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function __set($name, $value)
|
||||
{
|
||||
$this->initializer5157dd96e8924 && $this->initializer5157dd96e8924->__invoke($this->valueHolder5157dd96e88c0, $this, '__set', array('name' => $name, 'value' => $value));
|
||||
|
||||
$this->valueHolder5157dd96e88c0->$name = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function __isset($name)
|
||||
{
|
||||
$this->initializer5157dd96e8924 && $this->initializer5157dd96e8924->__invoke($this->valueHolder5157dd96e88c0, $this, '__isset', array('name' => $name));
|
||||
|
||||
return isset($this->valueHolder5157dd96e88c0->$name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
*/
|
||||
public function __unset($name)
|
||||
{
|
||||
$this->initializer5157dd96e8924 && $this->initializer5157dd96e8924->__invoke($this->valueHolder5157dd96e88c0, $this, '__unset', array('name' => $name));
|
||||
|
||||
unset($this->valueHolder5157dd96e88c0->$name);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function __clone()
|
||||
{
|
||||
$this->initializer5157dd96e8924 && $this->initializer5157dd96e8924->__invoke($this->valueHolder5157dd96e88c0, $this, '__clone', array());
|
||||
|
||||
$this->valueHolder5157dd96e88c0 = clone $this->valueHolder5157dd96e88c0;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function __sleep()
|
||||
{
|
||||
$this->initializer5157dd96e8924 && $this->initializer5157dd96e8924->__invoke($this->valueHolder5157dd96e88c0, $this, '__sleep', array());
|
||||
|
||||
return array('valueHolder5157dd96e88c0');
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function __wakeup()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setProxyInitializer(\Closure $initializer = null)
|
||||
{
|
||||
$this->initializer5157dd96e8924 = $initializer;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getProxyInitializer()
|
||||
{
|
||||
return $this->initializer5157dd96e8924;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function initializeProxy() : bool
|
||||
{
|
||||
return $this->initializer5157dd96e8924 && $this->initializer5157dd96e8924->__invoke($this->valueHolder5157dd96e88c0, $this, 'initializeProxy', array());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isProxyInitialized() : bool
|
||||
{
|
||||
return null !== $this->valueHolder5157dd96e88c0;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getWrappedValueHolderValue()
|
||||
{
|
||||
return $this->valueHolder5157dd96e88c0;
|
||||
}
|
||||
}
|
|
@ -69,7 +69,7 @@ class ProxyDumperTest extends \PHPUnit_Framework_TestCase
|
|||
$code = $this->dumper->getProxyFactoryCode($definition, 'foo');
|
||||
|
||||
$this->assertStringMatchesFormat(
|
||||
'%wif ($lazyLoad) {%wreturn $this->services[\'foo\'] = new '
|
||||
'%wif ($lazyLoad) {%wreturn $this->services[\'foo\'] =%s'
|
||||
.'SymfonyBridgeProxyManagerTestsLazyProxyPhpDumperProxyDumperTest_%s(%wfunction '
|
||||
.'(&$wrappedInstance, \ProxyManager\Proxy\LazyLoadingInterface $proxy) {'
|
||||
.'%w$wrappedInstance = $this->getFooService(false);%w$proxy->setProxyInitializer(null);'
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
"require": {
|
||||
"php": ">=5.5.9",
|
||||
"symfony/dependency-injection": "~2.8|~3.0",
|
||||
"ocramius/proxy-manager": "~0.4|~1.0"
|
||||
"ocramius/proxy-manager": "~0.4|~1.0|~2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/config": "~2.8|~3.0"
|
||||
|
|
|
@ -27,6 +27,8 @@ class AddConstraintValidatorsPass implements CompilerPassInterface
|
|||
if (isset($attributes[0]['alias'])) {
|
||||
$validators[$attributes[0]['alias']] = $id;
|
||||
}
|
||||
|
||||
$validators[$container->getDefinition($id)->getClass()] = $id;
|
||||
}
|
||||
|
||||
$container->getDefinition('validator.validator_factory')->replaceArgument(1, $validators);
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
<?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.
|
||||
*/
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddConstraintValidatorsPass;
|
||||
|
||||
class AddConstraintValidatorsPassTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testThatConstraintValidatorServicesAreProcessed()
|
||||
{
|
||||
$services = array(
|
||||
'my_constraint_validator_service1' => array(0 => array('alias' => 'my_constraint_validator_alias1')),
|
||||
'my_constraint_validator_service2' => array(),
|
||||
);
|
||||
|
||||
$validatorFactoryDefinition = $this->getMock('Symfony\Component\DependencyInjection\Definition');
|
||||
$container = $this->getMock(
|
||||
'Symfony\Component\DependencyInjection\ContainerBuilder',
|
||||
array('findTaggedServiceIds', 'getDefinition', 'hasDefinition')
|
||||
);
|
||||
|
||||
$validatorDefinition1 = $this->getMock('Symfony\Component\DependencyInjection\Definition', array('getClass'));
|
||||
$validatorDefinition2 = $this->getMock('Symfony\Component\DependencyInjection\Definition', array('getClass'));
|
||||
|
||||
$validatorDefinition1->expects($this->atLeastOnce())
|
||||
->method('getClass')
|
||||
->willReturn('My\Fully\Qualified\Class\Named\Validator1');
|
||||
$validatorDefinition2->expects($this->atLeastOnce())
|
||||
->method('getClass')
|
||||
->willReturn('My\Fully\Qualified\Class\Named\Validator2');
|
||||
|
||||
$container->expects($this->any())
|
||||
->method('getDefinition')
|
||||
->with($this->anything())
|
||||
->will($this->returnValueMap(array(
|
||||
array('my_constraint_validator_service1', $validatorDefinition1),
|
||||
array('my_constraint_validator_service2', $validatorDefinition2),
|
||||
array('validator.validator_factory', $validatorFactoryDefinition),
|
||||
)));
|
||||
|
||||
$container->expects($this->atLeastOnce())
|
||||
->method('findTaggedServiceIds')
|
||||
->will($this->returnValue($services));
|
||||
$container->expects($this->atLeastOnce())
|
||||
->method('hasDefinition')
|
||||
->with('validator.validator_factory')
|
||||
->will($this->returnValue(true));
|
||||
|
||||
$validatorFactoryDefinition->expects($this->once())
|
||||
->method('replaceArgument')
|
||||
->with(1, array(
|
||||
'My\Fully\Qualified\Class\Named\Validator1' => 'my_constraint_validator_service1',
|
||||
'my_constraint_validator_alias1' => 'my_constraint_validator_service1',
|
||||
'My\Fully\Qualified\Class\Named\Validator2' => 'my_constraint_validator_service2',
|
||||
));
|
||||
|
||||
$addConstraintValidatorsPass = new AddConstraintValidatorsPass();
|
||||
$addConstraintValidatorsPass->process($container);
|
||||
}
|
||||
|
||||
public function testThatCompilerPassIsIgnoredIfThereIsNoConstraintValidatorFactoryDefinition()
|
||||
{
|
||||
$definition = $this->getMock('Symfony\Component\DependencyInjection\Definition');
|
||||
$container = $this->getMock(
|
||||
'Symfony\Component\DependencyInjection\ContainerBuilder',
|
||||
array('hasDefinition', 'findTaggedServiceIds', 'getDefinition')
|
||||
);
|
||||
|
||||
$container->expects($this->never())->method('findTaggedServiceIds');
|
||||
$container->expects($this->never())->method('getDefinition');
|
||||
$container->expects($this->atLeastOnce())
|
||||
->method('hasDefinition')
|
||||
->with('validator.validator_factory')
|
||||
->will($this->returnValue(false));
|
||||
$definition->expects($this->never())->method('replaceArgument');
|
||||
|
||||
$addConstraintValidatorsPass = new AddConstraintValidatorsPass();
|
||||
$addConstraintValidatorsPass->process($container);
|
||||
}
|
||||
}
|
|
@ -33,7 +33,15 @@ class OutputFormatter implements OutputFormatterInterface
|
|||
*/
|
||||
public static function escape($text)
|
||||
{
|
||||
return preg_replace('/([^\\\\]?)</', '$1\\<', $text);
|
||||
$text = preg_replace('/([^\\\\]?)</', '$1\\<', $text);
|
||||
|
||||
if ('\\' === substr($text, -1)) {
|
||||
$len = strlen($text);
|
||||
$text = rtrim($text, '\\');
|
||||
$text .= str_repeat('<<', $len - strlen($text));
|
||||
}
|
||||
|
||||
return $text;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -131,7 +139,7 @@ class OutputFormatter implements OutputFormatterInterface
|
|||
$message = (string) $message;
|
||||
$offset = 0;
|
||||
$output = '';
|
||||
$tagRegex = '[a-z][a-z0-9_=;-]*';
|
||||
$tagRegex = '[a-z][a-z0-9_=;-]*+';
|
||||
preg_match_all("#<(($tagRegex) | /($tagRegex)?)>#ix", $message, $matches, PREG_OFFSET_CAPTURE);
|
||||
foreach ($matches[0] as $i => $match) {
|
||||
$pos = $match[1];
|
||||
|
@ -166,6 +174,10 @@ class OutputFormatter implements OutputFormatterInterface
|
|||
|
||||
$output .= $this->applyCurrentStyle(substr($message, $offset));
|
||||
|
||||
if (false !== strpos($output, '<<')) {
|
||||
return strtr($output, array('\\<' => '<', '<<' => '\\'));
|
||||
}
|
||||
|
||||
return str_replace('\\<', '<', $output);
|
||||
}
|
||||
|
||||
|
|
|
@ -98,8 +98,8 @@ class OutputFormatterTest extends \PHPUnit_Framework_TestCase
|
|||
$formatter = new OutputFormatter(true);
|
||||
|
||||
$this->assertEquals(
|
||||
"(\033[32mz>=2.0,<a2.3\033[39m)",
|
||||
$formatter->format('(<info>'.$formatter->escape('z>=2.0,<a2.3').'</info>)')
|
||||
"(\033[32mz>=2.0,<<<a2.3\\\033[39m)",
|
||||
$formatter->format('(<info>'.$formatter->escape('z>=2.0,<\\<<a2.3\\').'</info>)')
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
|
|
|
@ -148,45 +148,17 @@ class AutowirePass implements CompilerPassInterface
|
|||
$this->types[$type] = $id;
|
||||
}
|
||||
|
||||
// Cannot use reflection if the class isn't set
|
||||
if (!$definition->getClass()) {
|
||||
if (!$reflectionClass = $this->getReflectionClass($id, $definition)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($reflectionClass = $this->getReflectionClass($id, $definition)) {
|
||||
$this->extractInterfaces($id, $reflectionClass);
|
||||
$this->extractAncestors($id, $reflectionClass);
|
||||
foreach ($reflectionClass->getInterfaces() as $reflectionInterface) {
|
||||
$this->set($reflectionInterface->name, $id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the list of all interfaces implemented by a class.
|
||||
*
|
||||
* @param string $id
|
||||
* @param \ReflectionClass $reflectionClass
|
||||
*/
|
||||
private function extractInterfaces($id, \ReflectionClass $reflectionClass)
|
||||
{
|
||||
foreach ($reflectionClass->getInterfaces() as $interfaceName => $reflectionInterface) {
|
||||
$this->set($interfaceName, $id);
|
||||
|
||||
$this->extractInterfaces($id, $reflectionInterface);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts all inherited types of a class.
|
||||
*
|
||||
* @param string $id
|
||||
* @param \ReflectionClass $reflectionClass
|
||||
*/
|
||||
private function extractAncestors($id, \ReflectionClass $reflectionClass)
|
||||
{
|
||||
$this->set($reflectionClass->name, $id);
|
||||
|
||||
if ($reflectionParentClass = $reflectionClass->getParentClass()) {
|
||||
$this->extractAncestors($id, $reflectionParentClass);
|
||||
}
|
||||
do {
|
||||
$this->set($reflectionClass->name, $id);
|
||||
} while ($reflectionClass = $reflectionClass->getParentClass());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -272,6 +244,7 @@ class AutowirePass implements CompilerPassInterface
|
|||
return $this->reflectionClasses[$id];
|
||||
}
|
||||
|
||||
// Cannot use reflection if the class isn't set
|
||||
if (!$class = $definition->getClass()) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -315,7 +315,7 @@ class PhpDumper extends Dumper
|
|||
throw new ServiceCircularReferenceException($id, array($id));
|
||||
}
|
||||
|
||||
$code .= $this->addNewInstance($id, $sDefinition, '$'.$name, ' = ');
|
||||
$code .= $this->addNewInstance($sDefinition, '$'.$name, ' = ');
|
||||
|
||||
if (!$this->hasReference($id, $sDefinition->getMethodCalls(), true) && !$this->hasReference($id, $sDefinition->getProperties(), true)) {
|
||||
$code .= $this->addServiceMethodCalls(null, $sDefinition, $name);
|
||||
|
@ -389,7 +389,7 @@ class PhpDumper extends Dumper
|
|||
$instantiation .= ' = ';
|
||||
}
|
||||
|
||||
$code = $this->addNewInstance($id, $definition, $return, $instantiation);
|
||||
$code = $this->addNewInstance($definition, $return, $instantiation);
|
||||
|
||||
if (!$simple) {
|
||||
$code .= "\n";
|
||||
|
@ -676,7 +676,7 @@ EOF;
|
|||
return $publicServices.$privateServices;
|
||||
}
|
||||
|
||||
private function addNewInstance($id, Definition $definition, $return, $instantiation)
|
||||
private function addNewInstance(Definition $definition, $return, $instantiation)
|
||||
{
|
||||
$class = $this->dumpValue($definition->getClass());
|
||||
|
||||
|
|
|
@ -61,9 +61,10 @@ class AutowirePassTest extends \PHPUnit_Framework_TestCase
|
|||
$pass = new AutowirePass();
|
||||
$pass->process($container);
|
||||
|
||||
$this->assertCount(2, $container->getDefinition('g')->getArguments());
|
||||
$this->assertCount(3, $container->getDefinition('g')->getArguments());
|
||||
$this->assertEquals('f', (string) $container->getDefinition('g')->getArgument(0));
|
||||
$this->assertEquals('f', (string) $container->getDefinition('g')->getArgument(1));
|
||||
$this->assertEquals('f', (string) $container->getDefinition('g')->getArgument(2));
|
||||
}
|
||||
|
||||
public function testCompleteExistingDefinition()
|
||||
|
@ -332,13 +333,21 @@ interface EInterface extends DInterface
|
|||
{
|
||||
}
|
||||
|
||||
class F implements EInterface
|
||||
interface IInterface
|
||||
{
|
||||
}
|
||||
|
||||
class I implements IInterface
|
||||
{
|
||||
}
|
||||
|
||||
class F extends I implements EInterface
|
||||
{
|
||||
}
|
||||
|
||||
class G
|
||||
{
|
||||
public function __construct(DInterface $d, EInterface $e)
|
||||
public function __construct(DInterface $d, EInterface $e, IInterface $i)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,8 @@ use Symfony\Component\DependencyInjection\ContainerBuilder;
|
|||
use Symfony\Component\DependencyInjection\Definition;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
|
||||
require_once __DIR__.'/../Fixtures/includes/foo.php';
|
||||
|
||||
class ReplaceAliasByActualDefinitionPassTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testProcess()
|
||||
|
@ -46,6 +48,26 @@ class ReplaceAliasByActualDefinitionPassTest extends \PHPUnit_Framework_TestCase
|
|||
$this->assertSame('b_alias', (string) $resolvedFactory[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group legacy
|
||||
*/
|
||||
public function testPrivateAliasesInFactory()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
|
||||
$container->register('a', 'FooClass');
|
||||
$container->register('b', 'FooClass')
|
||||
->setFactoryService('a')
|
||||
->setFactoryMethod('getInstance');
|
||||
|
||||
$container->register('c', 'stdClass')->setPublic(false);
|
||||
$container->setAlias('c_alias', 'c');
|
||||
|
||||
$this->process($container);
|
||||
|
||||
$this->assertInstanceOf('FooClass', $container->get('b'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \InvalidArgumentException
|
||||
*/
|
||||
|
|
|
@ -59,6 +59,10 @@ class ChoiceFormField extends FormField
|
|||
*/
|
||||
public function isDisabled()
|
||||
{
|
||||
if (parent::isDisabled() && 'select' === $this->type) {
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach ($this->options as $option) {
|
||||
if ($option['value'] == $this->value && $option['disabled']) {
|
||||
return true;
|
||||
|
|
|
@ -120,6 +120,14 @@ class ChoiceFormFieldTest extends FormFieldTestCase
|
|||
$this->assertEquals('bar', $field->getValue());
|
||||
}
|
||||
|
||||
public function testSelectIsDisabled()
|
||||
{
|
||||
$node = $this->createSelectNode(array('foo' => false, 'bar' => true), array('disabled' => 'disabled'));
|
||||
$field = new ChoiceFormField($node);
|
||||
|
||||
$this->assertTrue($field->isDisabled(), '->isDisabled() returns true for selects with a disabled attribute');
|
||||
}
|
||||
|
||||
public function testMultipleSelects()
|
||||
{
|
||||
$node = $this->createSelectNode(array('foo' => false, 'bar' => false), array('multiple' => 'multiple'));
|
||||
|
|
|
@ -120,11 +120,21 @@ class DefaultChoiceListFactory implements ChoiceListFactoryInterface
|
|||
$key = $keys[$value];
|
||||
$nextIndex = is_int($index) ? $index++ : call_user_func($index, $choice, $key, $value);
|
||||
|
||||
// BC normalize label to accept a false value
|
||||
if (null === $label) {
|
||||
// If the labels are null, use the original choice key by default
|
||||
$label = (string) $key;
|
||||
} elseif (false !== $label) {
|
||||
// If "choice_label" is set to false and "expanded" is true, the value false
|
||||
// should be passed on to the "label" option of the checkboxes/radio buttons
|
||||
$dynamicLabel = call_user_func($label, $choice, $key, $value);
|
||||
$label = false === $dynamicLabel ? false : (string) $dynamicLabel;
|
||||
}
|
||||
|
||||
$view = new ChoiceView(
|
||||
$choice,
|
||||
$value,
|
||||
// If the labels are null, use the original choice key by default
|
||||
null === $label ? (string) $key : (string) call_user_func($label, $choice, $key, $value),
|
||||
$label,
|
||||
// The attributes may be a callable or a mapping from choice indices
|
||||
// to nested arrays
|
||||
is_callable($attr) ? call_user_func($attr, $choice, $key, $value) : (isset($attr[$key]) ? $attr[$key] : array())
|
||||
|
|
|
@ -340,7 +340,7 @@ class ChoiceType extends AbstractType
|
|||
$resolver->setAllowedTypes('choices', array('null', 'array', '\Traversable'));
|
||||
$resolver->setAllowedTypes('choice_translation_domain', array('null', 'bool', 'string'));
|
||||
$resolver->setAllowedTypes('choice_loader', array('null', 'Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface'));
|
||||
$resolver->setAllowedTypes('choice_label', array('null', 'callable', 'string', 'Symfony\Component\PropertyAccess\PropertyPath'));
|
||||
$resolver->setAllowedTypes('choice_label', array('null', 'bool', 'callable', 'string', 'Symfony\Component\PropertyAccess\PropertyPath'));
|
||||
$resolver->setAllowedTypes('choice_name', array('null', 'callable', 'string', 'Symfony\Component\PropertyAccess\PropertyPath'));
|
||||
$resolver->setAllowedTypes('choice_value', array('null', 'callable', 'string', 'Symfony\Component\PropertyAccess\PropertyPath'));
|
||||
$resolver->setAllowedTypes('choice_attr', array('null', 'array', 'callable', 'string', 'Symfony\Component\PropertyAccess\PropertyPath'));
|
||||
|
|
|
@ -693,6 +693,126 @@ abstract class AbstractBootstrap3LayoutTest extends AbstractLayoutTest
|
|||
);
|
||||
}
|
||||
|
||||
public function testSingleChoiceExpandedWithLabelsAsFalse()
|
||||
{
|
||||
$form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array(
|
||||
'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'),
|
||||
'choice_label' => false,
|
||||
'multiple' => false,
|
||||
'expanded' => true,
|
||||
));
|
||||
|
||||
$this->assertWidgetMatchesXpath($form->createView(), array(),
|
||||
'/div
|
||||
[
|
||||
./div
|
||||
[@class="radio"]
|
||||
[
|
||||
./label
|
||||
[
|
||||
./input[@type="radio"][@name="name"][@id="name_0"][@value="&a"][@checked]
|
||||
]
|
||||
]
|
||||
/following-sibling::div
|
||||
[@class="radio"]
|
||||
[
|
||||
./label
|
||||
[
|
||||
./input[@type="radio"][@name="name"][@id="name_1"][@value="&b"][not(@checked)]
|
||||
]
|
||||
]
|
||||
/following-sibling::input[@type="hidden"][@id="name__token"][@class="form-control"]
|
||||
]
|
||||
'
|
||||
);
|
||||
}
|
||||
|
||||
public function testSingleChoiceExpandedWithLabelsSetByCallable()
|
||||
{
|
||||
$form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array(
|
||||
'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b', 'Choice&C' => '&c'),
|
||||
'choice_label' => function ($choice, $label, $value) {
|
||||
if ('&b' === $choice) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return 'label.'.$value;
|
||||
},
|
||||
'multiple' => false,
|
||||
'expanded' => true,
|
||||
));
|
||||
|
||||
$this->assertWidgetMatchesXpath($form->createView(), array(),
|
||||
'/div
|
||||
[
|
||||
./div
|
||||
[@class="radio"]
|
||||
[
|
||||
./label
|
||||
[.=" [trans]label.&a[/trans]"]
|
||||
[
|
||||
./input[@type="radio"][@name="name"][@id="name_0"][@value="&a"][@checked]
|
||||
]
|
||||
]
|
||||
/following-sibling::div
|
||||
[@class="radio"]
|
||||
[
|
||||
./label
|
||||
[
|
||||
./input[@type="radio"][@name="name"][@id="name_1"][@value="&b"][not(@checked)]
|
||||
]
|
||||
]
|
||||
/following-sibling::div
|
||||
[@class="radio"]
|
||||
[
|
||||
./label
|
||||
[.=" [trans]label.&c[/trans]"]
|
||||
[
|
||||
./input[@type="radio"][@name="name"][@id="name_2"][@value="&c"][not(@checked)]
|
||||
]
|
||||
]
|
||||
/following-sibling::input[@type="hidden"][@id="name__token"][@class="form-control"]
|
||||
]
|
||||
'
|
||||
);
|
||||
}
|
||||
|
||||
public function testSingleChoiceExpandedWithLabelsSetFalseByCallable()
|
||||
{
|
||||
$form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array(
|
||||
'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'),
|
||||
'choice_label' => function () {
|
||||
return false;
|
||||
},
|
||||
'multiple' => false,
|
||||
'expanded' => true,
|
||||
));
|
||||
|
||||
$this->assertWidgetMatchesXpath($form->createView(), array(),
|
||||
'/div
|
||||
[
|
||||
./div
|
||||
[@class="radio"]
|
||||
[
|
||||
./label
|
||||
[
|
||||
./input[@type="radio"][@name="name"][@id="name_0"][@value="&a"][@checked]
|
||||
]
|
||||
]
|
||||
/following-sibling::div
|
||||
[@class="radio"]
|
||||
[
|
||||
./label
|
||||
[
|
||||
./input[@type="radio"][@name="name"][@id="name_1"][@value="&b"][not(@checked)]
|
||||
]
|
||||
]
|
||||
/following-sibling::input[@type="hidden"][@id="name__token"][@class="form-control"]
|
||||
]
|
||||
'
|
||||
);
|
||||
}
|
||||
|
||||
public function testSingleChoiceExpandedWithoutTranslation()
|
||||
{
|
||||
$form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array(
|
||||
|
@ -938,6 +1058,126 @@ abstract class AbstractBootstrap3LayoutTest extends AbstractLayoutTest
|
|||
);
|
||||
}
|
||||
|
||||
public function testMultipleChoiceExpandedWithLabelsAsFalse()
|
||||
{
|
||||
$form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', array('&a'), array(
|
||||
'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'),
|
||||
'choice_label' => false,
|
||||
'multiple' => true,
|
||||
'expanded' => true,
|
||||
));
|
||||
|
||||
$this->assertWidgetMatchesXpath($form->createView(), array(),
|
||||
'/div
|
||||
[
|
||||
./div
|
||||
[@class="checkbox"]
|
||||
[
|
||||
./label
|
||||
[
|
||||
./input[@type="checkbox"][@name="name[]"][@id="name_0"][@value="&a"][@checked]
|
||||
]
|
||||
]
|
||||
/following-sibling::div
|
||||
[@class="checkbox"]
|
||||
[
|
||||
./label
|
||||
[
|
||||
./input[@type="checkbox"][@name="name[]"][@id="name_1"][@value="&b"][not(@checked)]
|
||||
]
|
||||
]
|
||||
/following-sibling::input[@type="hidden"][@id="name__token"][@class="form-control"]
|
||||
]
|
||||
'
|
||||
);
|
||||
}
|
||||
|
||||
public function testMultipleChoiceExpandedWithLabelsSetByCallable()
|
||||
{
|
||||
$form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', array('&a'), array(
|
||||
'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b', 'Choice&C' => '&c'),
|
||||
'choice_label' => function ($choice, $label, $value) {
|
||||
if ('&b' === $choice) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return 'label.'.$value;
|
||||
},
|
||||
'multiple' => true,
|
||||
'expanded' => true,
|
||||
));
|
||||
|
||||
$this->assertWidgetMatchesXpath($form->createView(), array(),
|
||||
'/div
|
||||
[
|
||||
./div
|
||||
[@class="checkbox"]
|
||||
[
|
||||
./label
|
||||
[.=" [trans]label.&a[/trans]"]
|
||||
[
|
||||
./input[@type="checkbox"][@name="name[]"][@id="name_0"][@value="&a"][@checked]
|
||||
]
|
||||
]
|
||||
/following-sibling::div
|
||||
[@class="checkbox"]
|
||||
[
|
||||
./label
|
||||
[
|
||||
./input[@type="checkbox"][@name="name[]"][@id="name_1"][@value="&b"][not(@checked)]
|
||||
]
|
||||
]
|
||||
/following-sibling::div
|
||||
[@class="checkbox"]
|
||||
[
|
||||
./label
|
||||
[.=" [trans]label.&c[/trans]"]
|
||||
[
|
||||
./input[@type="checkbox"][@name="name[]"][@id="name_2"][@value="&c"][not(@checked)]
|
||||
]
|
||||
]
|
||||
/following-sibling::input[@type="hidden"][@id="name__token"][@class="form-control"]
|
||||
]
|
||||
'
|
||||
);
|
||||
}
|
||||
|
||||
public function testMultipleChoiceExpandedWithLabelsSetFalseByCallable()
|
||||
{
|
||||
$form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', array('&a'), array(
|
||||
'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'),
|
||||
'choice_label' => function () {
|
||||
return false;
|
||||
},
|
||||
'multiple' => true,
|
||||
'expanded' => true,
|
||||
));
|
||||
|
||||
$this->assertWidgetMatchesXpath($form->createView(), array(),
|
||||
'/div
|
||||
[
|
||||
./div
|
||||
[@class="checkbox"]
|
||||
[
|
||||
./label
|
||||
[
|
||||
./input[@type="checkbox"][@name="name[]"][@id="name_0"][@value="&a"][@checked]
|
||||
]
|
||||
]
|
||||
/following-sibling::div
|
||||
[@class="checkbox"]
|
||||
[
|
||||
./label
|
||||
[
|
||||
./input[@type="checkbox"][@name="name[]"][@id="name_1"][@value="&b"][not(@checked)]
|
||||
]
|
||||
]
|
||||
/following-sibling::input[@type="hidden"][@id="name__token"][@class="form-control"]
|
||||
]
|
||||
'
|
||||
);
|
||||
}
|
||||
|
||||
public function testMultipleChoiceExpandedWithoutTranslation()
|
||||
{
|
||||
$form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', array('&a', '&c'), array(
|
||||
|
|
|
@ -706,6 +706,160 @@ abstract class AbstractDivLayoutTest extends AbstractLayoutTest
|
|||
);
|
||||
}
|
||||
|
||||
public function testSingleChoiceExpandedWithLabelsAsFalse()
|
||||
{
|
||||
$form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array(
|
||||
'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'),
|
||||
'choice_label' => false,
|
||||
'multiple' => false,
|
||||
'expanded' => true,
|
||||
));
|
||||
|
||||
$this->assertWidgetMatchesXpath($form->createView(), array(),
|
||||
'/div
|
||||
[
|
||||
./input[@type="radio"][@name="name"][@id="name_0"][@value="&a"][@checked]
|
||||
/following-sibling::input[@type="radio"][@name="name"][@id="name_1"][@value="&b"][not(@checked)]
|
||||
/following-sibling::input[@type="hidden"][@id="name__token"]
|
||||
]
|
||||
[count(./input)=3]
|
||||
[count(./label)=1]
|
||||
'
|
||||
);
|
||||
}
|
||||
|
||||
public function testSingleChoiceExpandedWithLabelsSetByCallable()
|
||||
{
|
||||
$form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array(
|
||||
'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b', 'Choice&C' => '&c'),
|
||||
'choice_label' => function ($choice, $label, $value) {
|
||||
if ('&b' === $choice) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return 'label.'.$value;
|
||||
},
|
||||
'multiple' => false,
|
||||
'expanded' => true,
|
||||
));
|
||||
|
||||
$this->assertWidgetMatchesXpath($form->createView(), array(),
|
||||
'/div
|
||||
[
|
||||
./input[@type="radio"][@name="name"][@id="name_0"][@value="&a"][@checked]
|
||||
/following-sibling::label[@for="name_0"][.="[trans]label.&a[/trans]"]
|
||||
/following-sibling::input[@type="radio"][@name="name"][@id="name_1"][@value="&b"][not(@checked)]
|
||||
/following-sibling::input[@type="radio"][@name="name"][@id="name_2"][@value="&c"][not(@checked)]
|
||||
/following-sibling::label[@for="name_2"][.="[trans]label.&c[/trans]"]
|
||||
/following-sibling::input[@type="hidden"][@id="name__token"]
|
||||
]
|
||||
[count(./input)=4]
|
||||
[count(./label)=3]
|
||||
'
|
||||
);
|
||||
}
|
||||
|
||||
public function testSingleChoiceExpandedWithLabelsSetFalseByCallable()
|
||||
{
|
||||
$form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array(
|
||||
'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'),
|
||||
'choice_label' => function () {
|
||||
return false;
|
||||
},
|
||||
'multiple' => false,
|
||||
'expanded' => true,
|
||||
));
|
||||
|
||||
$this->assertWidgetMatchesXpath($form->createView(), array(),
|
||||
'/div
|
||||
[
|
||||
./input[@type="radio"][@name="name"][@id="name_0"][@value="&a"][@checked]
|
||||
/following-sibling::input[@type="radio"][@name="name"][@id="name_1"][@value="&b"][not(@checked)]
|
||||
/following-sibling::input[@type="hidden"][@id="name__token"]
|
||||
]
|
||||
[count(./input)=3]
|
||||
[count(./label)=1]
|
||||
'
|
||||
);
|
||||
}
|
||||
|
||||
public function testMultipleChoiceExpandedWithLabelsAsFalse()
|
||||
{
|
||||
$form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', array('&a'), array(
|
||||
'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'),
|
||||
'choice_label' => false,
|
||||
'multiple' => true,
|
||||
'expanded' => true,
|
||||
));
|
||||
|
||||
$this->assertWidgetMatchesXpath($form->createView(), array(),
|
||||
'/div
|
||||
[
|
||||
./input[@type="checkbox"][@name="name[]"][@id="name_0"][@value="&a"][@checked]
|
||||
/following-sibling::input[@type="checkbox"][@name="name[]"][@id="name_1"][@value="&b"][not(@checked)]
|
||||
/following-sibling::input[@type="hidden"][@id="name__token"]
|
||||
]
|
||||
[count(./input)=3]
|
||||
[count(./label)=1]
|
||||
'
|
||||
);
|
||||
}
|
||||
|
||||
public function testMultipleChoiceExpandedWithLabelsSetByCallable()
|
||||
{
|
||||
$form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', array('&a'), array(
|
||||
'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b', 'Choice&C' => '&c'),
|
||||
'choice_label' => function ($choice, $label, $value) {
|
||||
if ('&b' === $choice) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return 'label.'.$value;
|
||||
},
|
||||
'multiple' => true,
|
||||
'expanded' => true,
|
||||
));
|
||||
|
||||
$this->assertWidgetMatchesXpath($form->createView(), array(),
|
||||
'/div
|
||||
[
|
||||
./input[@type="checkbox"][@name="name[]"][@id="name_0"][@value="&a"][@checked]
|
||||
/following-sibling::label[@for="name_0"][.="[trans]label.&a[/trans]"]
|
||||
/following-sibling::input[@type="checkbox"][@name="name[]"][@id="name_1"][@value="&b"][not(@checked)]
|
||||
/following-sibling::input[@type="checkbox"][@name="name[]"][@id="name_2"][@value="&c"][not(@checked)]
|
||||
/following-sibling::label[@for="name_2"][.="[trans]label.&c[/trans]"]
|
||||
/following-sibling::input[@type="hidden"][@id="name__token"]
|
||||
]
|
||||
[count(./input)=4]
|
||||
[count(./label)=3]
|
||||
'
|
||||
);
|
||||
}
|
||||
|
||||
public function testMultipleChoiceExpandedWithLabelsSetFalseByCallable()
|
||||
{
|
||||
$form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', array('&a'), array(
|
||||
'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'),
|
||||
'choice_label' => function () {
|
||||
return false;
|
||||
},
|
||||
'multiple' => true,
|
||||
'expanded' => true,
|
||||
));
|
||||
|
||||
$this->assertWidgetMatchesXpath($form->createView(), array(),
|
||||
'/div
|
||||
[
|
||||
./input[@type="checkbox"][@name="name[]"][@id="name_0"][@value="&a"][@checked]
|
||||
/following-sibling::input[@type="checkbox"][@name="name[]"][@id="name_1"][@value="&b"][not(@checked)]
|
||||
/following-sibling::input[@type="hidden"][@id="name__token"]
|
||||
]
|
||||
[count(./input)=3]
|
||||
[count(./label)=1]
|
||||
'
|
||||
);
|
||||
}
|
||||
|
||||
public function testFormEndWithRest()
|
||||
{
|
||||
$view = $this->factory->createNamedBuilder('name', 'Symfony\Component\Form\Extension\Core\Type\FormType')
|
||||
|
|
|
@ -221,7 +221,7 @@ class BinaryFileResponse extends Response
|
|||
$this->maxlen = 0;
|
||||
} elseif ($request->headers->has('Range')) {
|
||||
// Process the range headers.
|
||||
if (!$request->headers->has('If-Range') || $this->getEtag() === $request->headers->get('If-Range')) {
|
||||
if (!$request->headers->has('If-Range') || $this->hasValidIfRangeHeader($request->headers->get('If-Range'))) {
|
||||
$range = $request->headers->get('Range');
|
||||
$fileSize = $this->file->getSize();
|
||||
|
||||
|
@ -254,6 +254,19 @@ class BinaryFileResponse extends Response
|
|||
return $this;
|
||||
}
|
||||
|
||||
private function hasValidIfRangeHeader($header)
|
||||
{
|
||||
if ($this->getEtag() === $header) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (null === $lastModified = $this->getLastModified()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $lastModified->format('D, d M Y H:i:s').' GMT' === $header;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the file.
|
||||
*
|
||||
|
|
|
@ -80,6 +80,37 @@ class BinaryFileResponseTest extends ResponseTestCase
|
|||
$this->assertEquals($responseRange, $response->headers->get('Content-Range'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideRanges
|
||||
*/
|
||||
public function testRequestsWithoutEtag($requestRange, $offset, $length, $responseRange)
|
||||
{
|
||||
$response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, array('Content-Type' => 'application/octet-stream'));
|
||||
|
||||
// do a request to get the LastModified
|
||||
$request = Request::create('/');
|
||||
$response->prepare($request);
|
||||
$lastModified = $response->headers->get('Last-Modified');
|
||||
|
||||
// prepare a request for a range of the testing file
|
||||
$request = Request::create('/');
|
||||
$request->headers->set('If-Range', $lastModified);
|
||||
$request->headers->set('Range', $requestRange);
|
||||
|
||||
$file = fopen(__DIR__.'/File/Fixtures/test.gif', 'r');
|
||||
fseek($file, $offset);
|
||||
$data = fread($file, $length);
|
||||
fclose($file);
|
||||
|
||||
$this->expectOutputString($data);
|
||||
$response = clone $response;
|
||||
$response->prepare($request);
|
||||
$response->sendContent();
|
||||
|
||||
$this->assertEquals(206, $response->getStatusCode());
|
||||
$this->assertEquals($responseRange, $response->headers->get('Content-Range'));
|
||||
}
|
||||
|
||||
public function provideRanges()
|
||||
{
|
||||
return array(
|
||||
|
@ -91,6 +122,25 @@ class BinaryFileResponseTest extends ResponseTestCase
|
|||
);
|
||||
}
|
||||
|
||||
public function testRangeRequestsWithoutLastModifiedDate()
|
||||
{
|
||||
// prevent auto last modified
|
||||
$response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, array('Content-Type' => 'application/octet-stream'), true, null, false, false);
|
||||
|
||||
// prepare a request for a range of the testing file
|
||||
$request = Request::create('/');
|
||||
$request->headers->set('If-Range', date('D, d M Y H:i:s').' GMT');
|
||||
$request->headers->set('Range', 'bytes=1-4');
|
||||
|
||||
$this->expectOutputString(file_get_contents(__DIR__.'/File/Fixtures/test.gif'));
|
||||
$response = clone $response;
|
||||
$response->prepare($request);
|
||||
$response->sendContent();
|
||||
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
$this->assertNull($response->headers->get('Content-Range'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideFullFileRanges
|
||||
*/
|
||||
|
|
Reference in New Issue