Merge branch '2.8'

* 2.8:
  [Yaml] throw a ParseException on invalid data type
  [TwigBridge] type-dependent path discovery
  Resources as string have the same problem
  Introduce failing test case when a SplFileInfo object is passed to the extract() method in the TwigExtractor.
  #15331 add infos about deprecated classes to UPGRADE-3.0
  [Asset] removed unused private property.
  [Twig+FrameworkBundle] Fix forward compat with Form 2.8
  [2.6] Static Code Analysis for Components
  [Security/Http] Fix test relying on a private property
  [Serializer] Fix bugs reported in b5990be491 (commitcomment-12301266)
  [Form] Fix not-BC test assertion
  [Security] Moved Simple{Form,Pre}AuthenticatorInterfaces to Security\Http
  [Security] removed useless else condition in SwitchUserListener class.
  [travis] Tests deps=low with PHP 5.6
  Implement resettable containers
  [Console] Fix console output with closed stdout
This commit is contained in:
Fabien Potencier 2015-07-26 11:09:51 +02:00
commit 803144d1e0
26 changed files with 282 additions and 45 deletions

View File

@ -12,7 +12,6 @@ matrix:
- php: hhvm
- php: 5.5.9
- php: 5.5
- php: 5.6
- php: 5.6
env: deps=low
- php: 5.6

View File

@ -305,6 +305,17 @@ UPGRADE FROM 2.x to 3.0
```php
echo $form->getErrors(true, false);
```
* The `Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceList` class has been removed in
favor of `Symfony\Component\Form\ChoiceList\ArrayChoiceList`.
* The `Symfony\Component\Form\Extension\Core\ChoiceList\LazyChoiceList` class has been removed in
favor of `Symfony\Component\Form\ChoiceList\LazyChoiceList`.
* The `Symfony\Component\Form\Extension\Core\ChoiceList\ObjectChoiceList` class has been removed in
favor of `Symfony\Component\Form\ChoiceList\ArrayChoiceList`.
* The `Symfony\Component\Form\Extension\Core\ChoiceList\SimpleChoiceList` class has been removed in
favor of `Symfony\Component\Form\ChoiceList\ArrayChoiceList`.
### FrameworkBundle

View File

@ -73,15 +73,28 @@ class TwigExtractorTest extends \PHPUnit_Framework_TestCase
/**
* @expectedException \Twig_Error
* @expectedExceptionMessageRegExp /Unclosed "block" in "extractor(\/|\\)syntax_error\.twig" at line 1/
* @expectedExceptionMessageRegExp /Unclosed "block" in ".*extractor(\/|\\)syntax_error\.twig" at line 1/
* @dataProvider resourcesWithSyntaxErrorsProvider
*/
public function testExtractSyntaxError()
public function testExtractSyntaxError($resources)
{
$twig = new \Twig_Environment(new \Twig_Loader_Array(array()));
$twig->addExtension(new TranslationExtension($this->getMock('Symfony\Component\Translation\TranslatorInterface')));
$extractor = new TwigExtractor($twig);
$extractor->extract(__DIR__.'/../Fixtures', new MessageCatalogue('en'));
$extractor->extract($resources, new MessageCatalogue('en'));
}
/**
* @return array
*/
public function resourcesWithSyntaxErrorsProvider()
{
return array(
array(__DIR__.'/../Fixtures'),
array(__DIR__.'/../Fixtures/extractor/syntax_error.twig'),
array(new \SplFileInfo(__DIR__.'/../Fixtures/extractor/syntax_error.twig')),
);
}
/**

View File

@ -12,6 +12,7 @@
namespace Symfony\Bridge\Twig\Translation;
use Symfony\Component\Finder\Finder;
use Symfony\Component\Finder\SplFileInfo;
use Symfony\Component\Translation\Extractor\AbstractFileExtractor;
use Symfony\Component\Translation\Extractor\ExtractorInterface;
use Symfony\Component\Translation\MessageCatalogue;
@ -60,7 +61,11 @@ class TwigExtractor extends AbstractFileExtractor implements ExtractorInterface
try {
$this->extractTemplate(file_get_contents($file->getPathname()), $catalogue);
} catch (\Twig_Error $e) {
$e->setTemplateFile($file->getRelativePathname());
if ($file instanceof SplFileInfo) {
$e->setTemplateFile($file->getRelativePathname());
} elseif ($file instanceof \SplFileInfo) {
$e->setTemplateFile($file->getRealPath());
}
throw $e;
}

View File

@ -36,7 +36,6 @@ use Symfony\Component\Asset\Exception\LogicException;
class UrlPackage extends Package
{
private $baseUrls = array();
private $sslUrls;
private $sslPackage;
/**
@ -62,7 +61,7 @@ class UrlPackage extends Package
$sslUrls = $this->getSslUrls($baseUrls);
if ($sslUrls && $baseUrls !== $sslUrls) {
$this->sslPackage = new UrlPackage($sslUrls, $versionStrategy);
$this->sslPackage = new self($sslUrls, $versionStrategy);
}
}

View File

@ -46,12 +46,9 @@ class ConsoleOutput extends StreamOutput implements ConsoleOutputInterface
*/
public function __construct($verbosity = self::VERBOSITY_NORMAL, $decorated = null, OutputFormatterInterface $formatter = null)
{
$outputStream = $this->hasStdoutSupport() ? 'php://stdout' : 'php://output';
$errorStream = $this->hasStderrSupport() ? 'php://stderr' : 'php://output';
parent::__construct($this->openOutputStream(), $verbosity, $decorated, $formatter);
parent::__construct(fopen($outputStream, 'w'), $verbosity, $decorated, $formatter);
$this->stderr = new StreamOutput(fopen($errorStream, 'w'), $verbosity, $decorated, $this->getFormatter());
$this->stderr = new StreamOutput($this->openErrorStream(), $verbosity, $decorated, $this->getFormatter());
}
/**
@ -129,4 +126,24 @@ class ConsoleOutput extends StreamOutput implements ConsoleOutputInterface
{
return 'OS400' === php_uname('s');
}
/**
* @return resource
*/
private function openOutputStream()
{
$outputStream = $this->hasStdoutSupport() ? 'php://stdout' : 'php://output';
return @fopen($outputStream, 'w') ?: fopen('php://output', 'w');
}
/**
* @return resource
*/
private function openErrorStream()
{
$errorStream = $this->hasStderrSupport() ? 'php://stderr' : 'php://output';
return fopen($errorStream, 'w');
}
}

View File

@ -7,6 +7,7 @@ CHANGELOG
* allowed specifying a directory to recursively load all configuration files it contains
* deprecated the concept of scopes
* added `Definition::setShared()` and `Definition::isShared()`
* added ResettableContainerInterface to be able to reset the container to release memory on shutdown
2.7.0
-----

View File

@ -13,6 +13,7 @@ namespace Symfony\Component\DependencyInjection;
use Symfony\Component\DependencyInjection\Exception\InactiveScopeException;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Exception\LogicException;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException;
@ -60,7 +61,7 @@ use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;
*
* @api
*/
class Container implements IntrospectableContainerInterface
class Container implements IntrospectableContainerInterface, ResettableContainerInterface
{
/**
* @var ParameterBagInterface
@ -367,6 +368,18 @@ class Container implements IntrospectableContainerInterface
return isset($this->services[$id]) || array_key_exists($id, $this->services);
}
/**
* {@inheritdoc}
*/
public function reset()
{
if (!empty($this->scopedServices)) {
throw new LogicException('Resetting the container is not allowed when a scope is active.');
}
$this->services = array();
}
/**
* Gets all service ids.
*

View File

@ -0,0 +1,31 @@
<?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\DependencyInjection;
/**
* ResettableContainerInterface defines additional resetting functionality
* for containers, allowing to release shared services when the container is
* not needed anymore.
*
* @author Christophe Coevoet <stof@notk.org>
*/
interface ResettableContainerInterface extends ContainerInterface
{
/**
* Resets shared services from the container.
*
* The container is not intended to be used again after being reset in a normal workflow. This method is
* meant as a way to release references for ref-counting.
* A subsequent call to ContainerInterface::get will recreate a new instance of the shared service.
*/
public function reset();
}

View File

@ -308,6 +308,49 @@ class ContainerTest extends \PHPUnit_Framework_TestCase
$this->assertTrue($sc->initialized('alias'), '->initialized() returns true for alias if aliased service is initialized');
}
public function testReset()
{
$c = new Container();
$c->set('bar', new \stdClass());
$c->reset();
$this->assertNull($c->get('bar', ContainerInterface::NULL_ON_INVALID_REFERENCE));
}
/**
* @expectedException \Symfony\Component\DependencyInjection\Exception\LogicException
* @expectedExceptionMessage Resetting the container is not allowed when a scope is active.
* @group legacy
*/
public function testCannotResetInActiveScope()
{
$c = new Container();
$c->addScope(new Scope('foo'));
$c->set('bar', new \stdClass());
$c->enterScope('foo');
$c->reset();
}
/**
* @group legacy
*/
public function testResetAfterLeavingScope()
{
$c = new Container();
$c->addScope(new Scope('foo'));
$c->set('bar', new \stdClass());
$c->enterScope('foo');
$c->leaveScope('foo');
$c->reset();
$this->assertNull($c->get('bar', ContainerInterface::NULL_ON_INVALID_REFERENCE));
}
/**
* @group legacy
*/

View File

@ -101,7 +101,7 @@ class Client extends BaseClient
$r = new \ReflectionClass('\\Symfony\\Component\\ClassLoader\\ClassLoader');
$requirePath = str_replace("'", "\\'", $r->getFileName());
$symfonyPath = str_replace("'", "\\'", realpath(__DIR__.'/../../..'));
$symfonyPath = str_replace("'", "\\'", dirname(dirname(dirname(__DIR__))));
$errorReporting = error_reporting();
$code = <<<EOF

View File

@ -23,6 +23,7 @@ use Symfony\Component\DependencyInjection\Loader\IniFileLoader;
use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
use Symfony\Component\DependencyInjection\Loader\DirectoryLoader;
use Symfony\Component\DependencyInjection\Loader\ClosureLoader;
use Symfony\Component\DependencyInjection\ResettableContainerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Bundle\BundleInterface;
@ -164,6 +165,10 @@ abstract class Kernel implements KernelInterface, TerminableInterface
$bundle->setContainer(null);
}
if ($this->container instanceof ResettableContainerInterface) {
$this->container->reset();
}
$this->container = null;
}

View File

@ -178,8 +178,7 @@ echo "Preparing resource bundle compilation (version $icuVersionInDownload)...\n
$compiler = new GenrbCompiler($genrb, $genrbEnv);
$config = new GeneratorConfig($sourceDir.'/data', $icuVersionInDownload);
// Don't wrap "/data" in realpath(), in case the directory does not exist
$baseDir = realpath(__DIR__.'/..').'/data';
$baseDir = dirname(__DIR__).'/data';
//$txtDir = $baseDir.'/txt';
$jsonDir = $baseDir;

View File

@ -6,6 +6,10 @@ CHANGELOG
* deprecated `getKey()` of the `AnonymousToken`, `RememberMeToken` and `AbstractRememberMeServices` classes
in favor of `getSecret()`.
* deprecated `Symfony\Component\Security\Core\Authentication\SimplePreAuthenticatorInterface`, use
`Symfony\Component\Security\Http\Authentication\SimplePreAuthenticatorInterface` instead
* deprecated `Symfony\Component\Security\Core\Authentication\SimpleFormAuthenticatorInterface`, use
`Symfony\Component\Security\Http\Authentication\SimpleFormAuthenticatorInterface` instead
2.7.0
-----

View File

@ -14,6 +14,8 @@ namespace Symfony\Component\Security\Core\Authentication;
use Symfony\Component\HttpFoundation\Request;
/**
* @deprecated Deprecated since version 2.8, to be removed in 3.0. Use the same interface from Security\Http\Authentication instead.
*
* @author Jordi Boggiano <j.boggiano@seld.be>
*/
interface SimpleFormAuthenticatorInterface extends SimpleAuthenticatorInterface

View File

@ -14,6 +14,8 @@ namespace Symfony\Component\Security\Core\Authentication;
use Symfony\Component\HttpFoundation\Request;
/**
* @deprecated Since version 2.8, to be removed in 3.0. Use the same interface from Security\Http\Authentication instead.
*
* @author Jordi Boggiano <j.boggiano@seld.be>
*/
interface SimplePreAuthenticatorInterface extends SimpleAuthenticatorInterface

View File

@ -0,0 +1,21 @@
<?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\Http\Authentication;
use Symfony\Component\Security\Core\Authentication\SimpleFormAuthenticatorInterface as BaseSimpleFormAuthenticatorInterface;
/**
* @author Jordi Boggiano <j.boggiano@seld.be>
*/
interface SimpleFormAuthenticatorInterface extends BaseSimpleFormAuthenticatorInterface
{
}

View File

@ -0,0 +1,21 @@
<?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\Http\Authentication;
use Symfony\Component\Security\Core\Authentication\SimplePreAuthenticatorInterface as BaseSimplePreAuthenticatorInterface;
/**
* @author Jordi Boggiano <j.boggiano@seld.be>
*/
interface SimplePreAuthenticatorInterface extends BaseSimplePreAuthenticatorInterface
{
}

View File

@ -115,9 +115,9 @@ class SwitchUserListener implements ListenerInterface
if (false !== $originalToken) {
if ($token->getUsername() === $request->get($this->usernameParameter)) {
return $token;
} else {
throw new \LogicException(sprintf('You are already switched to "%s" user.', $token->getUsername()));
}
throw new \LogicException(sprintf('You are already switched to "%s" user.', $token->getUsername()));
}
if (false === $this->accessDecisionManager->decide($token, array($this->role))) {

View File

@ -54,10 +54,9 @@ class AnonymousAuthenticationListenerTest extends \PHPUnit_Framework_TestCase
$authenticationManager
->expects($this->once())
->method('authenticate')
->with(self::logicalAnd(
$this->isInstanceOf('Symfony\Component\Security\Core\Authentication\Token\AnonymousToken'),
$this->attributeEqualTo('secret', 'TheSecret')
))
->with($this->callback(function ($token) {
return 'TheSecret' === $token->getSecret();
}))
->will($this->returnValue($anonymousToken))
;

View File

@ -242,7 +242,7 @@ abstract class AbstractNormalizer extends SerializerAwareNormalizer implements N
*
* @throws RuntimeException
*/
protected function instantiateObject(array $data, $class, array &$context, \ReflectionClass $reflectionClass, $allowedAttributes)
protected function instantiateObject(array &$data, $class, array &$context, \ReflectionClass $reflectionClass, $allowedAttributes)
{
if (
isset($context['object_to_populate']) &&

View File

@ -102,6 +102,7 @@ class GetSetMethodNormalizer extends AbstractNormalizer
$reflectionClass = new \ReflectionClass($class);
$object = $this->instantiateObject($normalizedData, $class, $context, $reflectionClass, $allowedAttributes);
$classMethods = get_class_methods($object);
foreach ($normalizedData as $attribute => $value) {
if ($this->nameConverter) {
$attribute = $this->nameConverter->denormalize($attribute);
@ -113,7 +114,7 @@ class GetSetMethodNormalizer extends AbstractNormalizer
if ($allowed && !$ignored) {
$setter = 'set'.ucfirst($attribute);
if (method_exists($object, $setter)) {
if (in_array($setter, $classMethods)) {
$object->$setter($value);
}
}

View File

@ -156,6 +156,12 @@ class GetSetMethodNormalizerTest extends \PHPUnit_Framework_TestCase
$this->assertEquals('bar', $obj->getBar());
}
public function testConstructorWArgWithPrivateMutator()
{
$obj = $this->normalizer->denormalize(array('foo' => 'bar'), __NAMESPACE__.'\ObjectConstructorArgsWithPrivateMutatorDummy', 'any');
$this->assertEquals('bar', $obj->getFoo());
}
public function testGroupsNormalize()
{
$classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
@ -439,8 +445,8 @@ class GetSetMethodNormalizerTest extends \PHPUnit_Framework_TestCase
public function testDenormalizeNonExistingAttribute()
{
$this->assertEquals(
new PropertyDummy(),
$this->normalizer->denormalize(array('non_existing' => true), __NAMESPACE__.'\PropertyDummy')
new GetSetDummy(),
$this->normalizer->denormalize(array('non_existing' => true), __NAMESPACE__.'\GetSetDummy')
);
}
@ -448,6 +454,12 @@ class GetSetMethodNormalizerTest extends \PHPUnit_Framework_TestCase
{
$this->assertFalse($this->normalizer->supportsNormalization(new \ArrayObject()));
}
public function testPrivateSetter()
{
$obj = $this->normalizer->denormalize(array('foo' => 'foobar'), __NAMESPACE__.'\ObjectWithPrivateSetterDummy');
$this->assertEquals('bar', $obj->getFoo());
}
}
class GetSetDummy
@ -654,3 +666,37 @@ class GetCamelizedDummy
return $this->bar_foo;
}
}
class ObjectConstructorArgsWithPrivateMutatorDummy
{
private $foo;
public function __construct($foo)
{
$this->setFoo($foo);
}
public function getFoo()
{
return $this->foo;
}
private function setFoo($foo)
{
$this->foo = $foo;
}
}
class ObjectWithPrivateSetterDummy
{
private $foo = 'bar';
public function getFoo()
{
return $this->foo;
}
private function setFoo($foo)
{
}
}

View File

@ -216,24 +216,14 @@ abstract class AbstractConstraintValidatorTest extends \PHPUnit_Framework_TestCa
protected function expectValidateAt($i, $propertyPath, $value, $group)
{
switch ($this->getApiVersion()) {
case Validation::API_VERSION_2_4:
$this->context->expects($this->at($i))
->method('validate')
->with($value, $propertyPath, $group);
break;
case Validation::API_VERSION_2_5:
case Validation::API_VERSION_2_5_BC:
$validator = $this->context->getValidator()->inContext($this->context);
$validator->expects($this->at(2 * $i))
->method('atPath')
->with($propertyPath)
->will($this->returnValue($validator));
$validator->expects($this->at(2 * $i + 1))
->method('validate')
->with($value, $this->logicalOr(null, array(), $this->isInstanceOf('\Symfony\Component\Validator\Constraints\Valid')), $group);
break;
}
$validator = $this->context->getValidator()->inContext($this->context);
$validator->expects($this->at(2 * $i))
->method('atPath')
->with($propertyPath)
->will($this->returnValue($validator));
$validator->expects($this->at(2 * $i + 1))
->method('validate')
->with($value, $this->logicalOr(null, array(), $this->isInstanceOf('\Symfony\Component\Validator\Constraints\Valid')), $group);
}
protected function expectValidateValueAt($i, $propertyPath, $value, $constraints, $group = null)

View File

@ -234,7 +234,7 @@ class Parser
}
// 1-liner optionally followed by newline(s)
if ($this->lines[0] === trim($value)) {
if (is_string($value) && $this->lines[0] === trim($value)) {
try {
$value = Inline::parse($this->lines[0], $exceptionOnInvalidType, $objectSupport, $objectForMap, $this->refs);
} catch (ParseException $e) {

View File

@ -551,6 +551,21 @@ EOF
);
}
/**
* @expectedException \Symfony\Component\Yaml\Exception\ParseException
* @expectedExceptionMessage missing colon
*/
public function testScalarInSequence()
{
Yaml::parse(<<<EOF
foo:
- bar
"missing colon"
foo: bar
EOF
);
}
/**
* > It is an error for two equal keys to appear in the same mapping node.
* > In such a case the YAML processor may continue, ignoring the second