Skip to content

Commit

Permalink
Merge branch '2.6' into 2.7
Browse files Browse the repository at this point in the history
* 2.6:
  Marked the ResolveParameterPlaceHoldersPassTest as legacy
  [ExpressionLanguage] fixed issues when parsing postfix expressions
  remove unused code
  do not inline service factories
  resolve class parameters in service factories
  [Serializer] Fix object normalization exceptions

Conflicts:
	src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_compiled.php
	src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php
	src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php
	src/Symfony/Component/Serializer/Tests/Normalizer/PropertyNormalizerTest.php
  • Loading branch information
fabpot committed Mar 17, 2015
2 parents 17ad6fd + 8962d5d commit 59ca5b3
Show file tree
Hide file tree
Showing 12 changed files with 185 additions and 21 deletions.
Expand Up @@ -65,9 +65,6 @@ public function process(ContainerBuilder $container)

$configurator = $this->inlineArguments($container, array($definition->getConfigurator()));
$definition->setConfigurator($configurator[0]);

$factory = $this->inlineArguments($container, array($definition->getFactory()));
$definition->setFactory($factory[0]);
}
}

Expand Down
Expand Up @@ -41,6 +41,13 @@ public function process(ContainerBuilder $container)
$definition->setFactoryClass($parameterBag->resolveValue($definition->getFactoryClass()));
}

$factory = $definition->getFactory();

if (is_array($factory) && isset($factory[0])) {
$factory[0] = $parameterBag->resolveValue($factory[0]);
$definition->setFactory($factory);
}

$calls = array();
foreach ($definition->getMethodCalls() as $name => $arguments) {
$calls[$parameterBag->resolveValue($name)] = $parameterBag->resolveValue($arguments);
Expand Down
Expand Up @@ -237,6 +237,23 @@ public function testProcessDoesNotInlineWhenServiceReferencesItself()
$this->assertSame($ref, $calls[0][1][0]);
}

public function testProcessDoesNotInlineFactories()
{
$container = new ContainerBuilder();
$container
->register('foo.factory')
->setPublic(false)
;
$container
->register('foo')
->setFactory(array(new Reference('foo.factory'), 'getFoo'))
;
$this->process($container);

$factory = $container->getDefinition('foo')->getFactory();
$this->assertInstanceOf('Symfony\Component\DependencyInjection\Reference', $factory[0]);
}

protected function process(ContainerBuilder $container)
{
$repeatedPass = new RepeatedPass(array(new AnalyzeServiceReferencesPass(), new InlineServiceDefinitionsPass()));
Expand Down
@@ -0,0 +1,37 @@
<?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\Tests\Compiler;

use Symfony\Component\DependencyInjection\Compiler\ResolveParameterPlaceHoldersPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;

/**
* @group legacy
*/
class LegacyResolveParameterPlaceHoldersPassTest extends \PHPUnit_Framework_TestCase
{
public function testFactoryClassParametersShouldBeResolved()
{
$this->iniSet('error_reporting', -1 & ~E_USER_DEPRECATED);

$compilerPass = new ResolveParameterPlaceHoldersPass();

$container = new ContainerBuilder();
$container->setParameter('foo.factory.class', 'FooFactory');
$fooDefinition = $container->register('foo', '%foo.factory.class%');
$fooDefinition->setFactoryClass('%foo.factory.class%');
$compilerPass->process($container);
$fooDefinition = $container->getDefinition('foo');

$this->assertSame('FooFactory', $fooDefinition->getFactoryClass());
}
}
Expand Up @@ -33,9 +33,9 @@ public function testClassParametersShouldBeResolved()
$this->assertSame('Foo', $this->fooDefinition->getClass());
}

public function testFactoryClassParametersShouldBeResolved()
public function testFactoryParametersShouldBeResolved()
{
$this->assertSame('FooFactory', $this->fooDefinition->getFactoryClass());
$this->assertSame(array('FooFactory', 'getFoo'), $this->fooDefinition->getFactory());
}

public function testArgumentParametersShouldBeResolved()
Expand Down Expand Up @@ -78,7 +78,7 @@ private function createContainerBuilder()
$containerBuilder->setParameter('alias.id', 'bar');

$fooDefinition = $containerBuilder->register('foo', '%foo.class%');
$fooDefinition->setFactoryClass('%foo.factory.class%');
$fooDefinition->setFactory(array('%foo.factory.class%', 'getFoo'));
$fooDefinition->setArguments(array('%foo.arg1%', '%foo.arg2%'));
$fooDefinition->addMethodCall('%foo.method%', array('%foo.arg1%', '%foo.arg2%'));
$fooDefinition->setProperty('%foo.property.name%', '%foo.property.value%');
Expand Down
Expand Up @@ -25,12 +25,9 @@ public static function setUpBeforeClass()

public function testDump()
{
$dumper = new XmlDumper($container = new ContainerBuilder());
$dumper = new XmlDumper(new ContainerBuilder());

$this->assertXmlStringEqualsXmlFile(self::$fixturesPath.'/xml/services1.xml', $dumper->dump(), '->dump() dumps an empty container as an empty XML file');

$container = new ContainerBuilder();
$dumper = new XmlDumper($container);
}

public function testExportParameters()
Expand Down Expand Up @@ -155,4 +152,26 @@ public function provideDecoratedServicesData()
", include $fixturesPath.'/containers/container16.php'),
);
}

/**
* @dataProvider provideCompiledContainerData
*/
public function testCompiledContainerCanBeDumped($containerFile)
{
$fixturesPath = __DIR__.'/../Fixtures';
$container = require $fixturesPath.'/containers/'.$containerFile.'.php';
$container->compile();
$dumper = new XmlDumper($container);
$dumper->dump();
}

public function provideCompiledContainerData()
{
return array(
array('container8'),
array('container11'),
array('container12'),
array('container14'),
);
}
}
Expand Up @@ -4,8 +4,14 @@

use Symfony\Component\DependencyInjection\ContainerBuilder;

class ProjectServiceContainer extends ContainerBuilder
{
/**
* This file is included in Tests\Dumper\GraphvizDumperTest::testDumpWithFrozenCustomClassContainer
* and Tests\Dumper\XmlDumperTest::testCompiledContainerCanBeDumped.
*/
if (!class_exists('Container14\ProjectServiceContainer')) {
class ProjectServiceContainer extends ContainerBuilder
{
}
}

return new ProjectServiceContainer();
Expand Up @@ -46,6 +46,7 @@ public function __construct()
'foo_bar' => 'getFooBarService',
'foo_with_inline' => 'getFooWithInlineService',
'method_call1' => 'getMethodCall1Service',
'new_factory' => 'getNewFactoryService',
'new_factory_service' => 'getNewFactoryServiceService',
'request' => 'getRequestService',
'service_from_static_method' => 'getServiceFromStaticMethodService',
Expand Down Expand Up @@ -264,10 +265,7 @@ protected function getMethodCall1Service()
*/
protected function getNewFactoryServiceService()
{
$a = new \FactoryClass();
$a->foo = 'bar';

$this->services['new_factory_service'] = $instance = $a->getInstance();
$this->services['new_factory_service'] = $instance = $this->get('new_factory')->getInstance();

$instance->foo = 'bar';

Expand Down Expand Up @@ -300,6 +298,27 @@ protected function getServiceFromStaticMethodService()
return $this->services['service_from_static_method'] = \Bar\FooClass::getInstance();
}

/**
* Gets the 'new_factory' service.
*
* This service is shared.
* This method always returns the same instance of the service.
*
* This service is private.
* If you want to be able to request this service from the container directly,
* make it public, otherwise you might end up with broken code.
*
* @return \FactoryClass A FactoryClass instance.
*/
protected function getNewFactoryService()
{
$this->services['new_factory'] = $instance = new \FactoryClass();

$instance->foo = 'bar';

return $instance;
}

/**
* {@inheritdoc}
*/
Expand Down
18 changes: 13 additions & 5 deletions src/Symfony/Component/ExpressionLanguage/Parser.php
Expand Up @@ -314,12 +314,20 @@ public function parsePostfixExpression($node)
if (
$token->type !== Token::NAME_TYPE
&&
$token->type !== Token::NUMBER_TYPE
&&
// operators line "not" are valid method or property names
($token->type !== Token::OPERATOR_TYPE && preg_match('/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/A', $token->value))
// Operators like "not" and "matches" are valid method or property names,
//
// In other words, besides NAME_TYPE, OPERATOR_TYPE could also be parsed as a property or method.
// This is because operators are processed by the lexer prior to names. So "not" in "foo.not()" or "matches" in "foo.matches" will be recognized as an operator first.
// But in fact, "not" and "matches" in such expressions shall be parsed as method or property names.
//
// And this ONLY works if the operator consists of valid characters for a property or method name.
//
// Other types, such as STRING_TYPE and NUMBER_TYPE, can't be parsed as property nor method names.
//
// As a result, if $token is NOT an operator OR $token->value is NOT a valid property or method name, an exception shall be thrown.
($token->type !== Token::OPERATOR_TYPE || !preg_match('/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/A', $token->value))
) {
throw new SyntaxError('Expected name or number', $token->cursor);
throw new SyntaxError('Expected name', $token->cursor);
}

$arg = new Node\ConstantNode($token->value);
Expand Down
33 changes: 33 additions & 0 deletions src/Symfony/Component/ExpressionLanguage/Tests/ParserTest.php
Expand Up @@ -161,4 +161,37 @@ private function createGetAttrNode($node, $item, $type)
{
return new Node\GetAttrNode($node, new Node\ConstantNode($item), new Node\ArgumentsNode(), $type);
}

/**
* @dataProvider getInvalidPostfixData
* @expectedException \Symfony\Component\ExpressionLanguage\SyntaxError
*/
public function testParseWithInvalidPostfixData($expr, $names = array())
{
$lexer = new Lexer();
$parser = new Parser(array());
$parser->parse($lexer->tokenize($expr), $names);
}

public function getInvalidPostfixData()
{
return array(
array(
'foo."#"',
array('foo'),
),
array(
'foo."bar"',
array('foo'),
),
array(
'foo.**',
array('foo'),
),
array(
'foo.123',
array('foo'),
),
);
}
}
Expand Up @@ -12,6 +12,7 @@
namespace Symfony\Component\Serializer\Normalizer;

use Symfony\Component\Serializer\Exception\CircularReferenceException;
use Symfony\Component\Serializer\Exception\LogicException;
use Symfony\Component\Serializer\Exception\RuntimeException;

/**
Expand Down Expand Up @@ -68,6 +69,10 @@ public function normalize($object, $format = null, array $context = array())
$attributeValue = call_user_func($this->callbacks[$property->name], $attributeValue);
}
if (null !== $attributeValue && !is_scalar($attributeValue)) {
if (!$this->serializer instanceof NormalizerInterface) {
throw new LogicException(sprintf('Cannot normalize attribute "%s" because injected serializer is not a normalizer', $property->name));
}

$attributeValue = $this->serializer->normalize($attributeValue, $format, $context);
}

Expand Down
Expand Up @@ -350,6 +350,22 @@ public function testDenormalizeNonExistingAttribute()
$this->normalizer->denormalize(array('non_existing' => true), __NAMESPACE__.'\PropertyDummy')
);
}

/**
* @expectedException \Symfony\Component\Serializer\Exception\LogicException
* @expectedExceptionMessage Cannot normalize attribute "bar" because injected serializer is not a normalizer
*/
public function testUnableToNormalizeObjectAttribute()
{
$serializer = $this->getMock('Symfony\Component\Serializer\SerializerInterface');
$this->normalizer->setSerializer($serializer);

$obj = new PropertyDummy();
$object = new \stdClass();
$obj->setBar($object);

$this->normalizer->normalize($obj, 'any');
}
}

class PropertyDummy
Expand Down

0 comments on commit 59ca5b3

Please sign in to comment.