Actualización

This commit is contained in:
Xes
2025-04-10 12:24:57 +02:00
parent 8969cc929d
commit 45420b6f0d
39760 changed files with 4303286 additions and 0 deletions

View File

@@ -0,0 +1,27 @@
<?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\Config\Tests;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\ConfigCacheFactory;
class ConfigCacheFactoryTest extends TestCase
{
public function testCacheWithInvalidCallback()
{
$this->expectException('InvalidArgumentException');
$this->expectExceptionMessage('Invalid type for callback argument. Expected callable, but got "object".');
$cacheFactory = new ConfigCacheFactory(true);
$cacheFactory->cache('file', new \stdClass());
}
}

View File

@@ -0,0 +1,99 @@
<?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\Config\Tests;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\ConfigCache;
use Symfony\Component\Config\Tests\Resource\ResourceStub;
class ConfigCacheTest extends TestCase
{
private $cacheFile = null;
protected function setUp()
{
$this->cacheFile = tempnam(sys_get_temp_dir(), 'config_');
}
protected function tearDown()
{
$files = [$this->cacheFile, $this->cacheFile.'.meta'];
foreach ($files as $file) {
if (file_exists($file)) {
@unlink($file);
}
}
}
/**
* @dataProvider debugModes
*/
public function testCacheIsNotValidIfNothingHasBeenCached($debug)
{
unlink($this->cacheFile); // remove tempnam() side effect
$cache = new ConfigCache($this->cacheFile, $debug);
$this->assertFalse($cache->isFresh());
}
public function testIsAlwaysFreshInProduction()
{
$staleResource = new ResourceStub();
$staleResource->setFresh(false);
$cache = new ConfigCache($this->cacheFile, false);
$cache->write('', [$staleResource]);
$this->assertTrue($cache->isFresh());
}
/**
* @dataProvider debugModes
*/
public function testIsFreshWhenNoResourceProvided($debug)
{
$cache = new ConfigCache($this->cacheFile, $debug);
$cache->write('', []);
$this->assertTrue($cache->isFresh());
}
public function testFreshResourceInDebug()
{
$freshResource = new ResourceStub();
$freshResource->setFresh(true);
$cache = new ConfigCache($this->cacheFile, true);
$cache->write('', [$freshResource]);
$this->assertTrue($cache->isFresh());
}
public function testStaleResourceInDebug()
{
$staleResource = new ResourceStub();
$staleResource->setFresh(false);
$cache = new ConfigCache($this->cacheFile, true);
$cache->write('', [$staleResource]);
$this->assertFalse($cache->isFresh());
}
public function debugModes()
{
return [
[true],
[false],
];
}
}

View File

@@ -0,0 +1,237 @@
<?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\Config\Tests\Definition;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\Definition\ArrayNode;
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
use Symfony\Component\Config\Definition\ScalarNode;
class ArrayNodeTest extends TestCase
{
public function testNormalizeThrowsExceptionWhenFalseIsNotAllowed()
{
$this->expectException('Symfony\Component\Config\Definition\Exception\InvalidTypeException');
$node = new ArrayNode('root');
$node->normalize(false);
}
public function testExceptionThrownOnUnrecognizedChild()
{
$this->expectException('Symfony\Component\Config\Definition\Exception\InvalidConfigurationException');
$this->expectExceptionMessage('Unrecognized option "foo" under "root"');
$node = new ArrayNode('root');
$node->normalize(['foo' => 'bar']);
}
public function ignoreAndRemoveMatrixProvider()
{
$unrecognizedOptionException = new InvalidConfigurationException('Unrecognized option "foo" under "root"');
return [
[true, true, [], 'no exception is thrown for an unrecognized child if the ignoreExtraKeys option is set to true'],
[true, false, ['foo' => 'bar'], 'extra keys are not removed when ignoreExtraKeys second option is set to false'],
[false, true, $unrecognizedOptionException],
[false, false, $unrecognizedOptionException],
];
}
/**
* @dataProvider ignoreAndRemoveMatrixProvider
*/
public function testIgnoreAndRemoveBehaviors($ignore, $remove, $expected, $message = '')
{
if ($expected instanceof \Exception) {
$this->expectException(\get_class($expected));
$this->expectExceptionMessage($expected->getMessage());
}
$node = new ArrayNode('root');
$node->setIgnoreExtraKeys($ignore, $remove);
$result = $node->normalize(['foo' => 'bar']);
$this->assertSame($expected, $result, $message);
}
/**
* @dataProvider getPreNormalizationTests
*/
public function testPreNormalize($denormalized, $normalized)
{
$node = new ArrayNode('foo');
$r = new \ReflectionMethod($node, 'preNormalize');
$r->setAccessible(true);
$this->assertSame($normalized, $r->invoke($node, $denormalized));
}
public function getPreNormalizationTests()
{
return [
[
['foo-bar' => 'foo'],
['foo_bar' => 'foo'],
],
[
['foo-bar_moo' => 'foo'],
['foo-bar_moo' => 'foo'],
],
[
['anything-with-dash-and-no-underscore' => 'first', 'no_dash' => 'second'],
['anything_with_dash_and_no_underscore' => 'first', 'no_dash' => 'second'],
],
[
['foo-bar' => null, 'foo_bar' => 'foo'],
['foo-bar' => null, 'foo_bar' => 'foo'],
],
];
}
/**
* @dataProvider getZeroNamedNodeExamplesData
*/
public function testNodeNameCanBeZero($denormalized, $normalized)
{
$zeroNode = new ArrayNode(0);
$zeroNode->addChild(new ScalarNode('name'));
$fiveNode = new ArrayNode(5);
$fiveNode->addChild(new ScalarNode(0));
$fiveNode->addChild(new ScalarNode('new_key'));
$rootNode = new ArrayNode('root');
$rootNode->addChild($zeroNode);
$rootNode->addChild($fiveNode);
$rootNode->addChild(new ScalarNode('string_key'));
$r = new \ReflectionMethod($rootNode, 'normalizeValue');
$r->setAccessible(true);
$this->assertSame($normalized, $r->invoke($rootNode, $denormalized));
}
public function getZeroNamedNodeExamplesData()
{
return [
[
[
0 => [
'name' => 'something',
],
5 => [
0 => 'this won\'t work too',
'new_key' => 'some other value',
],
'string_key' => 'just value',
],
[
0 => [
'name' => 'something',
],
5 => [
0 => 'this won\'t work too',
'new_key' => 'some other value',
],
'string_key' => 'just value',
],
],
];
}
/**
* @dataProvider getPreNormalizedNormalizedOrderedData
*/
public function testChildrenOrderIsMaintainedOnNormalizeValue($prenormalized, $normalized)
{
$scalar1 = new ScalarNode('1');
$scalar2 = new ScalarNode('2');
$scalar3 = new ScalarNode('3');
$node = new ArrayNode('foo');
$node->addChild($scalar1);
$node->addChild($scalar3);
$node->addChild($scalar2);
$r = new \ReflectionMethod($node, 'normalizeValue');
$r->setAccessible(true);
$this->assertSame($normalized, $r->invoke($node, $prenormalized));
}
public function getPreNormalizedNormalizedOrderedData()
{
return [
[
['2' => 'two', '1' => 'one', '3' => 'three'],
['2' => 'two', '1' => 'one', '3' => 'three'],
],
];
}
public function testAddChildEmptyName()
{
$this->expectException('InvalidArgumentException');
$this->expectExceptionMessage('Child nodes must be named.');
$node = new ArrayNode('root');
$childNode = new ArrayNode('');
$node->addChild($childNode);
}
public function testAddChildNameAlreadyExists()
{
$this->expectException('InvalidArgumentException');
$this->expectExceptionMessage('A child node named "foo" already exists.');
$node = new ArrayNode('root');
$childNode = new ArrayNode('foo');
$node->addChild($childNode);
$childNodeWithSameName = new ArrayNode('foo');
$node->addChild($childNodeWithSameName);
}
public function testGetDefaultValueWithoutDefaultValue()
{
$this->expectException('RuntimeException');
$this->expectExceptionMessage('The node at path "foo" has no default value.');
$node = new ArrayNode('foo');
$node->getDefaultValue();
}
public function testSetDeprecated()
{
$childNode = new ArrayNode('foo');
$childNode->setDeprecated('"%node%" is deprecated');
$this->assertTrue($childNode->isDeprecated());
$this->assertSame('"foo" is deprecated', $childNode->getDeprecationMessage($childNode->getName(), $childNode->getPath()));
$node = new ArrayNode('root');
$node->addChild($childNode);
$deprecationTriggered = false;
$deprecationHandler = function ($level, $message, $file, $line) use (&$prevErrorHandler, &$deprecationTriggered) {
if (\E_USER_DEPRECATED === $level) {
return $deprecationTriggered = true;
}
return $prevErrorHandler ? $prevErrorHandler($level, $message, $file, $line) : false;
};
$prevErrorHandler = set_error_handler($deprecationHandler);
$node->finalize([]);
restore_error_handler();
$this->assertFalse($deprecationTriggered, '->finalize() should not trigger if the deprecated node is not set');
$prevErrorHandler = set_error_handler($deprecationHandler);
$node->finalize(['foo' => []]);
restore_error_handler();
$this->assertTrue($deprecationTriggered, '->finalize() should trigger if the deprecated node is set');
}
}

View File

@@ -0,0 +1,74 @@
<?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\Config\Tests\Definition;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\Definition\BooleanNode;
class BooleanNodeTest extends TestCase
{
/**
* @dataProvider getValidValues
*/
public function testNormalize($value)
{
$node = new BooleanNode('test');
$this->assertSame($value, $node->normalize($value));
}
/**
* @dataProvider getValidValues
*
* @param bool $value
*/
public function testValidNonEmptyValues($value)
{
$node = new BooleanNode('test');
$node->setAllowEmptyValue(false);
$this->assertSame($value, $node->finalize($value));
}
public function getValidValues()
{
return [
[false],
[true],
];
}
/**
* @dataProvider getInvalidValues
*/
public function testNormalizeThrowsExceptionOnInvalidValues($value)
{
$this->expectException('Symfony\Component\Config\Definition\Exception\InvalidTypeException');
$node = new BooleanNode('test');
$node->normalize($value);
}
public function getInvalidValues()
{
return [
[null],
[''],
['foo'],
[0],
[1],
[0.0],
[0.1],
[[]],
[['foo' => 'bar']],
[new \stdClass()],
];
}
}

View File

@@ -0,0 +1,362 @@
<?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\Config\Tests\Definition\Builder;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
use Symfony\Component\Config\Definition\Builder\ScalarNodeDefinition;
use Symfony\Component\Config\Definition\Exception\InvalidDefinitionException;
use Symfony\Component\Config\Definition\Processor;
class ArrayNodeDefinitionTest extends TestCase
{
public function testAppendingSomeNode()
{
$parent = new ArrayNodeDefinition('root');
$child = new ScalarNodeDefinition('child');
$parent
->children()
->scalarNode('foo')->end()
->scalarNode('bar')->end()
->end()
->append($child);
$this->assertCount(3, $this->getField($parent, 'children'));
$this->assertContains($child, $this->getField($parent, 'children'));
}
/**
* @dataProvider providePrototypeNodeSpecificCalls
*/
public function testPrototypeNodeSpecificOption($method, $args)
{
$this->expectException('Symfony\Component\Config\Definition\Exception\InvalidDefinitionException');
$node = new ArrayNodeDefinition('root');
\call_user_func_array([$node, $method], $args);
$node->getNode();
}
public function providePrototypeNodeSpecificCalls()
{
return [
['defaultValue', [[]]],
['addDefaultChildrenIfNoneSet', []],
['requiresAtLeastOneElement', []],
['useAttributeAsKey', ['foo']],
];
}
public function testConcreteNodeSpecificOption()
{
$this->expectException('Symfony\Component\Config\Definition\Exception\InvalidDefinitionException');
$node = new ArrayNodeDefinition('root');
$node
->addDefaultsIfNotSet()
->prototype('array')
;
$node->getNode();
}
public function testPrototypeNodesCantHaveADefaultValueWhenUsingDefaultChildren()
{
$this->expectException('Symfony\Component\Config\Definition\Exception\InvalidDefinitionException');
$node = new ArrayNodeDefinition('root');
$node
->defaultValue([])
->addDefaultChildrenIfNoneSet('foo')
->prototype('array')
;
$node->getNode();
}
public function testPrototypedArrayNodeDefaultWhenUsingDefaultChildren()
{
$node = new ArrayNodeDefinition('root');
$node
->addDefaultChildrenIfNoneSet()
->prototype('array')
;
$tree = $node->getNode();
$this->assertEquals([[]], $tree->getDefaultValue());
}
/**
* @dataProvider providePrototypedArrayNodeDefaults
*/
public function testPrototypedArrayNodeDefault($args, $shouldThrowWhenUsingAttrAsKey, $shouldThrowWhenNotUsingAttrAsKey, $defaults)
{
$node = new ArrayNodeDefinition('root');
$node
->addDefaultChildrenIfNoneSet($args)
->prototype('array')
;
try {
$tree = $node->getNode();
$this->assertFalse($shouldThrowWhenNotUsingAttrAsKey);
$this->assertEquals($defaults, $tree->getDefaultValue());
} catch (InvalidDefinitionException $e) {
$this->assertTrue($shouldThrowWhenNotUsingAttrAsKey);
}
$node = new ArrayNodeDefinition('root');
$node
->useAttributeAsKey('attr')
->addDefaultChildrenIfNoneSet($args)
->prototype('array')
;
try {
$tree = $node->getNode();
$this->assertFalse($shouldThrowWhenUsingAttrAsKey);
$this->assertEquals($defaults, $tree->getDefaultValue());
} catch (InvalidDefinitionException $e) {
$this->assertTrue($shouldThrowWhenUsingAttrAsKey);
}
}
public function providePrototypedArrayNodeDefaults()
{
return [
[null, true, false, [[]]],
[2, true, false, [[], []]],
['2', false, true, ['2' => []]],
['foo', false, true, ['foo' => []]],
[['foo'], false, true, ['foo' => []]],
[['foo', 'bar'], false, true, ['foo' => [], 'bar' => []]],
];
}
public function testNestedPrototypedArrayNodes()
{
$nodeDefinition = new ArrayNodeDefinition('root');
$nodeDefinition
->addDefaultChildrenIfNoneSet()
->prototype('array')
->prototype('array')
;
$node = $nodeDefinition->getNode();
$this->assertInstanceOf('Symfony\Component\Config\Definition\PrototypedArrayNode', $node);
$this->assertInstanceOf('Symfony\Component\Config\Definition\PrototypedArrayNode', $node->getPrototype());
}
public function testEnabledNodeDefaults()
{
$node = new ArrayNodeDefinition('root');
$node
->canBeEnabled()
->children()
->scalarNode('foo')->defaultValue('bar')->end()
;
$this->assertEquals(['enabled' => false, 'foo' => 'bar'], $node->getNode()->getDefaultValue());
}
/**
* @dataProvider getEnableableNodeFixtures
*/
public function testTrueEnableEnabledNode($expected, $config, $message)
{
$processor = new Processor();
$node = new ArrayNodeDefinition('root');
$node
->canBeEnabled()
->children()
->scalarNode('foo')->defaultValue('bar')->end()
;
$this->assertEquals(
$expected,
$processor->process($node->getNode(), $config),
$message
);
}
public function testCanBeDisabled()
{
$node = new ArrayNodeDefinition('root');
$node->canBeDisabled();
$this->assertTrue($this->getField($node, 'addDefaults'));
$this->assertEquals(['enabled' => false], $this->getField($node, 'falseEquivalent'));
$this->assertEquals(['enabled' => true], $this->getField($node, 'trueEquivalent'));
$this->assertEquals(['enabled' => true], $this->getField($node, 'nullEquivalent'));
$nodeChildren = $this->getField($node, 'children');
$this->assertArrayHasKey('enabled', $nodeChildren);
$enabledNode = $nodeChildren['enabled'];
$this->assertTrue($this->getField($enabledNode, 'default'));
$this->assertTrue($this->getField($enabledNode, 'defaultValue'));
}
public function testIgnoreExtraKeys()
{
$node = new ArrayNodeDefinition('root');
$this->assertFalse($this->getField($node, 'ignoreExtraKeys'));
$result = $node->ignoreExtraKeys();
$this->assertEquals($node, $result);
$this->assertTrue($this->getField($node, 'ignoreExtraKeys'));
}
public function testNormalizeKeys()
{
$node = new ArrayNodeDefinition('root');
$this->assertTrue($this->getField($node, 'normalizeKeys'));
$result = $node->normalizeKeys(false);
$this->assertEquals($node, $result);
$this->assertFalse($this->getField($node, 'normalizeKeys'));
}
public function testUnsetChild()
{
$node = new ArrayNodeDefinition('root');
$node
->children()
->scalarNode('value')
->beforeNormalization()
->ifTrue(function ($value) {
return empty($value);
})
->thenUnset()
->end()
->end()
->end()
;
$this->assertSame([], $node->getNode()->normalize(['value' => null]));
}
public function testPrototypeVariable()
{
$node = new ArrayNodeDefinition('root');
$this->assertEquals($node->prototype('variable'), $node->variablePrototype());
}
public function testPrototypeScalar()
{
$node = new ArrayNodeDefinition('root');
$this->assertEquals($node->prototype('scalar'), $node->scalarPrototype());
}
public function testPrototypeBoolean()
{
$node = new ArrayNodeDefinition('root');
$this->assertEquals($node->prototype('boolean'), $node->booleanPrototype());
}
public function testPrototypeInteger()
{
$node = new ArrayNodeDefinition('root');
$this->assertEquals($node->prototype('integer'), $node->integerPrototype());
}
public function testPrototypeFloat()
{
$node = new ArrayNodeDefinition('root');
$this->assertEquals($node->prototype('float'), $node->floatPrototype());
}
public function testPrototypeArray()
{
$node = new ArrayNodeDefinition('root');
$this->assertEquals($node->prototype('array'), $node->arrayPrototype());
}
public function testPrototypeEnum()
{
$node = new ArrayNodeDefinition('root');
$this->assertEquals($node->prototype('enum'), $node->enumPrototype());
}
public function getEnableableNodeFixtures()
{
return [
[['enabled' => true, 'foo' => 'bar'], [true], 'true enables an enableable node'],
[['enabled' => true, 'foo' => 'bar'], [null], 'null enables an enableable node'],
[['enabled' => true, 'foo' => 'bar'], [['enabled' => true]], 'An enableable node can be enabled'],
[['enabled' => true, 'foo' => 'baz'], [['foo' => 'baz']], 'any configuration enables an enableable node'],
[['enabled' => false, 'foo' => 'baz'], [['foo' => 'baz', 'enabled' => false]], 'An enableable node can be disabled'],
[['enabled' => false, 'foo' => 'bar'], [false], 'false disables an enableable node'],
];
}
public function testRequiresAtLeastOneElement()
{
$node = new ArrayNodeDefinition('root');
$node
->requiresAtLeastOneElement()
->integerPrototype();
$node->getNode()->finalize([1]);
$this->addToAssertionCount(1);
}
/**
* @group legacy
* @expectedDeprecation Using Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition::cannotBeEmpty() at path "root" has no effect, consider requiresAtLeastOneElement() instead. In 4.0 both methods will behave the same.
*/
public function testCannotBeEmpty()
{
$node = new ArrayNodeDefinition('root');
$node
->cannotBeEmpty()
->integerPrototype();
$node->getNode()->finalize([]);
}
public function testSetDeprecated()
{
$node = new ArrayNodeDefinition('root');
$node
->children()
->arrayNode('foo')->setDeprecated('The "%path%" node is deprecated.')->end()
->end()
;
$deprecatedNode = $node->getNode()->getChildren()['foo'];
$this->assertTrue($deprecatedNode->isDeprecated());
$this->assertSame('The "root.foo" node is deprecated.', $deprecatedNode->getDeprecationMessage($deprecatedNode->getName(), $deprecatedNode->getPath()));
}
/**
* @group legacy
* @expectedDeprecation ->cannotBeEmpty() is not applicable to concrete nodes at path "root". In 4.0 it will throw an exception.
*/
public function testCannotBeEmptyOnConcreteNode()
{
$node = new ArrayNodeDefinition('root');
$node->cannotBeEmpty();
$node->getNode()->finalize([]);
}
protected function getField($object, $field)
{
$reflection = new \ReflectionProperty($object, $field);
$reflection->setAccessible(true);
return $reflection->getValue($object);
}
}

View File

@@ -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\Config\Tests\Definition\Builder;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\Definition\Builder\BooleanNodeDefinition;
class BooleanNodeDefinitionTest extends TestCase
{
public function testCannotBeEmptyThrowsAnException()
{
$this->expectException('Symfony\Component\Config\Definition\Exception\InvalidDefinitionException');
$this->expectExceptionMessage('->cannotBeEmpty() is not applicable to BooleanNodeDefinition.');
$def = new BooleanNodeDefinition('foo');
$def->cannotBeEmpty();
}
public function testSetDeprecated()
{
$def = new BooleanNodeDefinition('foo');
$def->setDeprecated('The "%path%" node is deprecated.');
$node = $def->getNode();
$this->assertTrue($node->isDeprecated());
$this->assertSame('The "foo" node is deprecated.', $node->getDeprecationMessage($node->getName(), $node->getPath()));
}
}

View File

@@ -0,0 +1,73 @@
<?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\Config\Tests\Definition\Builder;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\Definition\Builder\EnumNodeDefinition;
class EnumNodeDefinitionTest extends TestCase
{
public function testWithOneValue()
{
$def = new EnumNodeDefinition('foo');
$def->values(['foo']);
$node = $def->getNode();
$this->assertEquals(['foo'], $node->getValues());
}
public function testWithOneDistinctValue()
{
$def = new EnumNodeDefinition('foo');
$def->values(['foo', 'foo']);
$node = $def->getNode();
$this->assertEquals(['foo'], $node->getValues());
}
public function testNoValuesPassed()
{
$this->expectException('RuntimeException');
$this->expectExceptionMessage('You must call ->values() on enum nodes.');
$def = new EnumNodeDefinition('foo');
$def->getNode();
}
public function testWithNoValues()
{
$this->expectException('InvalidArgumentException');
$this->expectExceptionMessage('->values() must be called with at least one value.');
$def = new EnumNodeDefinition('foo');
$def->values([]);
}
public function testGetNode()
{
$def = new EnumNodeDefinition('foo');
$def->values(['foo', 'bar']);
$node = $def->getNode();
$this->assertEquals(['foo', 'bar'], $node->getValues());
}
public function testSetDeprecated()
{
$def = new EnumNodeDefinition('foo');
$def->values(['foo', 'bar']);
$def->setDeprecated('The "%path%" node is deprecated.');
$node = $def->getNode();
$this->assertTrue($node->isDeprecated());
$this->assertSame('The "foo" node is deprecated.', $def->getNode()->getDeprecationMessage($node->getName(), $node->getPath()));
}
}

View File

@@ -0,0 +1,264 @@
<?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\Config\Tests\Definition\Builder;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
class ExprBuilderTest extends TestCase
{
public function testAlwaysExpression()
{
$test = $this->getTestBuilder()
->always($this->returnClosure('new_value'))
->end();
$this->assertFinalizedValueIs('new_value', $test);
}
public function testIfTrueExpression()
{
$test = $this->getTestBuilder()
->ifTrue()
->then($this->returnClosure('new_value'))
->end();
$this->assertFinalizedValueIs('new_value', $test, ['key' => true]);
$test = $this->getTestBuilder()
->ifTrue(function ($v) { return true; })
->then($this->returnClosure('new_value'))
->end();
$this->assertFinalizedValueIs('new_value', $test);
$test = $this->getTestBuilder()
->ifTrue(function ($v) { return false; })
->then($this->returnClosure('new_value'))
->end();
$this->assertFinalizedValueIs('value', $test);
}
public function testIfStringExpression()
{
$test = $this->getTestBuilder()
->ifString()
->then($this->returnClosure('new_value'))
->end();
$this->assertFinalizedValueIs('new_value', $test);
$test = $this->getTestBuilder()
->ifString()
->then($this->returnClosure('new_value'))
->end();
$this->assertFinalizedValueIs(45, $test, ['key' => 45]);
}
public function testIfNullExpression()
{
$test = $this->getTestBuilder()
->ifNull()
->then($this->returnClosure('new_value'))
->end();
$this->assertFinalizedValueIs('new_value', $test, ['key' => null]);
$test = $this->getTestBuilder()
->ifNull()
->then($this->returnClosure('new_value'))
->end();
$this->assertFinalizedValueIs('value', $test);
}
public function testIfEmptyExpression()
{
$test = $this->getTestBuilder()
->ifEmpty()
->then($this->returnClosure('new_value'))
->end();
$this->assertFinalizedValueIs('new_value', $test, ['key' => []]);
$test = $this->getTestBuilder()
->ifEmpty()
->then($this->returnClosure('new_value'))
->end();
$this->assertFinalizedValueIs('value', $test);
}
public function testIfArrayExpression()
{
$test = $this->getTestBuilder()
->ifArray()
->then($this->returnClosure('new_value'))
->end();
$this->assertFinalizedValueIs('new_value', $test, ['key' => []]);
$test = $this->getTestBuilder()
->ifArray()
->then($this->returnClosure('new_value'))
->end();
$this->assertFinalizedValueIs('value', $test);
}
public function testIfInArrayExpression()
{
$test = $this->getTestBuilder()
->ifInArray(['foo', 'bar', 'value'])
->then($this->returnClosure('new_value'))
->end();
$this->assertFinalizedValueIs('new_value', $test);
$test = $this->getTestBuilder()
->ifInArray(['foo', 'bar'])
->then($this->returnClosure('new_value'))
->end();
$this->assertFinalizedValueIs('value', $test);
}
public function testIfNotInArrayExpression()
{
$test = $this->getTestBuilder()
->ifNotInArray(['foo', 'bar'])
->then($this->returnClosure('new_value'))
->end();
$this->assertFinalizedValueIs('new_value', $test);
$test = $this->getTestBuilder()
->ifNotInArray(['foo', 'bar', 'value_from_config'])
->then($this->returnClosure('new_value'))
->end();
$this->assertFinalizedValueIs('new_value', $test);
}
public function testThenEmptyArrayExpression()
{
$test = $this->getTestBuilder()
->ifString()
->thenEmptyArray()
->end();
$this->assertFinalizedValueIs([], $test);
}
/**
* @dataProvider castToArrayValues
*/
public function testCastToArrayExpression($configValue, $expectedValue)
{
$test = $this->getTestBuilder()
->castToArray()
->end();
$this->assertFinalizedValueIs($expectedValue, $test, ['key' => $configValue]);
}
public function castToArrayValues()
{
yield ['value', ['value']];
yield [-3.14, [-3.14]];
yield [null, [null]];
yield [['value'], ['value']];
}
public function testThenInvalid()
{
$this->expectException('Symfony\Component\Config\Definition\Exception\InvalidConfigurationException');
$test = $this->getTestBuilder()
->ifString()
->thenInvalid('Invalid value')
->end();
$this->finalizeTestBuilder($test);
}
public function testThenUnsetExpression()
{
$test = $this->getTestBuilder()
->ifString()
->thenUnset()
->end();
$this->assertEquals([], $this->finalizeTestBuilder($test));
}
public function testEndIfPartNotSpecified()
{
$this->expectException('RuntimeException');
$this->expectExceptionMessage('You must specify an if part.');
$this->getTestBuilder()->end();
}
public function testEndThenPartNotSpecified()
{
$this->expectException('RuntimeException');
$this->expectExceptionMessage('You must specify a then part.');
$builder = $this->getTestBuilder();
$builder->ifPart = 'test';
$builder->end();
}
/**
* Create a test treebuilder with a variable node, and init the validation.
*
* @return TreeBuilder
*/
protected function getTestBuilder()
{
$builder = new TreeBuilder();
return $builder
->root('test')
->children()
->variableNode('key')
->validate()
;
}
/**
* Close the validation process and finalize with the given config.
*
* @param TreeBuilder $testBuilder The tree builder to finalize
* @param array $config The config you want to use for the finalization, if nothing provided
* a simple ['key'=>'value'] will be used
*
* @return array The finalized config values
*/
protected function finalizeTestBuilder($testBuilder, $config = null)
{
return $testBuilder
->end()
->end()
->end()
->buildTree()
->finalize(null === $config ? ['key' => 'value'] : $config)
;
}
/**
* Return a closure that will return the given value.
*
* @param mixed $val The value that the closure must return
*
* @return \Closure
*/
protected function returnClosure($val)
{
return function ($v) use ($val) {
return $val;
};
}
/**
* Assert that the given test builder, will return the given value.
*
* @param mixed $value The value to test
* @param TreeBuilder $treeBuilder The tree builder to finalize
* @param mixed $config The config values that new to be finalized
*/
protected function assertFinalizedValueIs($value, $treeBuilder, $config = null)
{
$this->assertEquals(['key' => $value], $this->finalizeTestBuilder($treeBuilder, $config));
}
}

View File

@@ -0,0 +1,91 @@
<?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\Config\Tests\Definition\Builder;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\Definition\Builder\NodeBuilder as BaseNodeBuilder;
use Symfony\Component\Config\Definition\Builder\VariableNodeDefinition as BaseVariableNodeDefinition;
class NodeBuilderTest extends TestCase
{
public function testThrowsAnExceptionWhenTryingToCreateANonRegisteredNodeType()
{
$this->expectException('RuntimeException');
$builder = new BaseNodeBuilder();
$builder->node('', 'foobar');
}
public function testThrowsAnExceptionWhenTheNodeClassIsNotFound()
{
$this->expectException('RuntimeException');
$builder = new BaseNodeBuilder();
$builder
->setNodeClass('noclasstype', '\\foo\\bar\\noclass')
->node('', 'noclasstype');
}
public function testAddingANewNodeType()
{
$class = SomeNodeDefinition::class;
$builder = new BaseNodeBuilder();
$node = $builder
->setNodeClass('newtype', $class)
->node('', 'newtype');
$this->assertInstanceOf($class, $node);
}
public function testOverridingAnExistingNodeType()
{
$class = SomeNodeDefinition::class;
$builder = new BaseNodeBuilder();
$node = $builder
->setNodeClass('variable', $class)
->node('', 'variable');
$this->assertInstanceOf($class, $node);
}
public function testNodeTypesAreNotCaseSensitive()
{
$builder = new BaseNodeBuilder();
$node1 = $builder->node('', 'VaRiAbLe');
$node2 = $builder->node('', 'variable');
$this->assertInstanceOf(\get_class($node1), $node2);
$builder->setNodeClass('CuStOm', SomeNodeDefinition::class);
$node1 = $builder->node('', 'CUSTOM');
$node2 = $builder->node('', 'custom');
$this->assertInstanceOf(\get_class($node1), $node2);
}
public function testNumericNodeCreation()
{
$builder = new BaseNodeBuilder();
$node = $builder->integerNode('foo')->min(3)->max(5);
$this->assertInstanceOf('Symfony\Component\Config\Definition\Builder\IntegerNodeDefinition', $node);
$node = $builder->floatNode('bar')->min(3.0)->max(5.0);
$this->assertInstanceOf('Symfony\Component\Config\Definition\Builder\FloatNodeDefinition', $node);
}
}
class SomeNodeDefinition extends BaseVariableNodeDefinition
{
}

View File

@@ -0,0 +1,89 @@
<?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\Config\Tests\Definition\Builder;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\Definition\Builder\FloatNodeDefinition;
use Symfony\Component\Config\Definition\Builder\IntegerNodeDefinition;
class NumericNodeDefinitionTest extends TestCase
{
public function testIncoherentMinAssertion()
{
$this->expectException('InvalidArgumentException');
$this->expectExceptionMessage('You cannot define a min(4) as you already have a max(3)');
$def = new IntegerNodeDefinition('foo');
$def->max(3)->min(4);
}
public function testIncoherentMaxAssertion()
{
$this->expectException('InvalidArgumentException');
$this->expectExceptionMessage('You cannot define a max(2) as you already have a min(3)');
$node = new IntegerNodeDefinition('foo');
$node->min(3)->max(2);
}
public function testIntegerMinAssertion()
{
$this->expectException('Symfony\Component\Config\Definition\Exception\InvalidConfigurationException');
$this->expectExceptionMessage('The value 4 is too small for path "foo". Should be greater than or equal to 5');
$def = new IntegerNodeDefinition('foo');
$def->min(5)->getNode()->finalize(4);
}
public function testIntegerMaxAssertion()
{
$this->expectException('Symfony\Component\Config\Definition\Exception\InvalidConfigurationException');
$this->expectExceptionMessage('The value 4 is too big for path "foo". Should be less than or equal to 3');
$def = new IntegerNodeDefinition('foo');
$def->max(3)->getNode()->finalize(4);
}
public function testIntegerValidMinMaxAssertion()
{
$def = new IntegerNodeDefinition('foo');
$node = $def->min(3)->max(7)->getNode();
$this->assertEquals(4, $node->finalize(4));
}
public function testFloatMinAssertion()
{
$this->expectException('Symfony\Component\Config\Definition\Exception\InvalidConfigurationException');
$this->expectExceptionMessage('The value 400 is too small for path "foo". Should be greater than or equal to 500');
$def = new FloatNodeDefinition('foo');
$def->min(5E2)->getNode()->finalize(4e2);
}
public function testFloatMaxAssertion()
{
$this->expectException('Symfony\Component\Config\Definition\Exception\InvalidConfigurationException');
$this->expectExceptionMessage('The value 4.3 is too big for path "foo". Should be less than or equal to 0.3');
$def = new FloatNodeDefinition('foo');
$def->max(0.3)->getNode()->finalize(4.3);
}
public function testFloatValidMinMaxAssertion()
{
$def = new FloatNodeDefinition('foo');
$node = $def->min(3.0)->max(7e2)->getNode();
$this->assertEquals(4.5, $node->finalize(4.5));
}
public function testCannotBeEmptyThrowsAnException()
{
$this->expectException('Symfony\Component\Config\Definition\Exception\InvalidDefinitionException');
$this->expectExceptionMessage('->cannotBeEmpty() is not applicable to NumericNodeDefinition.');
$def = new IntegerNodeDefinition('foo');
$def->cannotBeEmpty();
}
}

View File

@@ -0,0 +1,134 @@
<?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\Config\Tests\Definition\Builder;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Tests\Fixtures\Builder\NodeBuilder as CustomNodeBuilder;
class TreeBuilderTest extends TestCase
{
public function testUsingACustomNodeBuilder()
{
$builder = new TreeBuilder();
$root = $builder->root('custom', 'array', new CustomNodeBuilder());
$nodeBuilder = $root->children();
$this->assertInstanceOf('Symfony\Component\Config\Tests\Fixtures\Builder\NodeBuilder', $nodeBuilder);
$nodeBuilder = $nodeBuilder->arrayNode('deeper')->children();
$this->assertInstanceOf('Symfony\Component\Config\Tests\Fixtures\Builder\NodeBuilder', $nodeBuilder);
}
public function testOverrideABuiltInNodeType()
{
$builder = new TreeBuilder();
$root = $builder->root('override', 'array', new CustomNodeBuilder());
$definition = $root->children()->variableNode('variable');
$this->assertInstanceOf('Symfony\Component\Config\Tests\Fixtures\Builder\VariableNodeDefinition', $definition);
}
public function testAddANodeType()
{
$builder = new TreeBuilder();
$root = $builder->root('override', 'array', new CustomNodeBuilder());
$definition = $root->children()->barNode('variable');
$this->assertInstanceOf('Symfony\Component\Config\Tests\Fixtures\Builder\BarNodeDefinition', $definition);
}
public function testCreateABuiltInNodeTypeWithACustomNodeBuilder()
{
$builder = new TreeBuilder();
$root = $builder->root('builtin', 'array', new CustomNodeBuilder());
$definition = $root->children()->booleanNode('boolean');
$this->assertInstanceOf('Symfony\Component\Config\Definition\Builder\BooleanNodeDefinition', $definition);
}
public function testPrototypedArrayNodeUseTheCustomNodeBuilder()
{
$builder = new TreeBuilder();
$root = $builder->root('override', 'array', new CustomNodeBuilder());
$root->prototype('bar')->end();
$this->assertInstanceOf('Symfony\Component\Config\Tests\Fixtures\BarNode', $root->getNode(true)->getPrototype());
}
public function testAnExtendedNodeBuilderGetsPropagatedToTheChildren()
{
$builder = new TreeBuilder();
$builder->root('propagation')
->children()
->setNodeClass('extended', 'Symfony\Component\Config\Definition\Builder\BooleanNodeDefinition')
->node('foo', 'extended')->end()
->arrayNode('child')
->children()
->node('foo', 'extended')
->end()
->end()
->end()
->end();
$node = $builder->buildTree();
$children = $node->getChildren();
$this->assertInstanceOf('Symfony\Component\Config\Definition\BooleanNode', $children['foo']);
$childChildren = $children['child']->getChildren();
$this->assertInstanceOf('Symfony\Component\Config\Definition\BooleanNode', $childChildren['foo']);
}
public function testDefinitionInfoGetsTransferredToNode()
{
$builder = new TreeBuilder();
$builder->root('test')->info('root info')
->children()
->node('child', 'variable')->info('child info')->defaultValue('default')
->end()
->end();
$tree = $builder->buildTree();
$children = $tree->getChildren();
$this->assertEquals('root info', $tree->getInfo());
$this->assertEquals('child info', $children['child']->getInfo());
}
public function testDefinitionExampleGetsTransferredToNode()
{
$builder = new TreeBuilder();
$builder->root('test')
->example(['key' => 'value'])
->children()
->node('child', 'variable')->info('child info')->defaultValue('default')->example('example')
->end()
->end();
$tree = $builder->buildTree();
$children = $tree->getChildren();
$this->assertIsArray($tree->getExample());
$this->assertEquals('example', $children['child']->getExample());
}
}

View File

@@ -0,0 +1,114 @@
<?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\Config\Tests\Definition\Dumper;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\Definition\Dumper\XmlReferenceDumper;
use Symfony\Component\Config\Tests\Fixtures\Configuration\ExampleConfiguration;
class XmlReferenceDumperTest extends TestCase
{
public function testDumper()
{
$configuration = new ExampleConfiguration();
$dumper = new XmlReferenceDumper();
$this->assertEquals($this->getConfigurationAsString(), $dumper->dump($configuration));
}
public function testNamespaceDumper()
{
$configuration = new ExampleConfiguration();
$dumper = new XmlReferenceDumper();
$this->assertEquals(str_replace('http://example.org/schema/dic/acme_root', 'http://symfony.com/schema/dic/symfony', $this->getConfigurationAsString()), $dumper->dump($configuration, 'http://symfony.com/schema/dic/symfony'));
}
private function getConfigurationAsString()
{
return str_replace("\n", \PHP_EOL, <<<'EOL'
<!-- Namespace: http://example.org/schema/dic/acme_root -->
<!-- scalar-required: Required -->
<!-- scalar-deprecated: Deprecated (The child node "scalar_deprecated" at path "acme_root" is deprecated.) -->
<!-- scalar-deprecated-with-message: Deprecated (Deprecation custom message for "scalar_deprecated_with_message" at "acme_root") -->
<!-- enum-with-default: One of "this"; "that" -->
<!-- enum: One of "this"; "that" -->
<config
boolean="true"
scalar-empty=""
scalar-null="null"
scalar-true="true"
scalar-false="false"
scalar-default="default"
scalar-array-empty=""
scalar-array-defaults="elem1,elem2"
scalar-required=""
scalar-deprecated=""
scalar-deprecated-with-message=""
node-with-a-looong-name=""
enum-with-default="this"
enum=""
>
<!-- some info -->
<!--
child3: this is a long
multi-line info text
which should be indented;
Example: example setting
-->
<array
child1=""
child2=""
child3=""
/>
<!-- prototype -->
<scalar-prototyped>scalar value</scalar-prototyped>
<!-- prototype: Parameter name -->
<parameter name="parameter name">scalar value</parameter>
<!-- prototype -->
<connection
user=""
pass=""
/>
<!-- prototype -->
<cms-page page="cms page page">
<!-- prototype -->
<!-- title: Required -->
<!-- path: Required -->
<page
locale="page locale"
title=""
path=""
/>
</cms-page>
<!-- prototype -->
<pipou name="pipou name">
<!-- prototype -->
<name didou="" />
</pipou>
</config>
EOL
);
}
}

View File

@@ -0,0 +1,143 @@
<?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\Config\Tests\Definition\Dumper;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\Definition\Dumper\YamlReferenceDumper;
use Symfony\Component\Config\Tests\Fixtures\Configuration\ExampleConfiguration;
class YamlReferenceDumperTest extends TestCase
{
public function testDumper()
{
$configuration = new ExampleConfiguration();
$dumper = new YamlReferenceDumper();
$this->assertEquals($this->getConfigurationAsString(), $dumper->dump($configuration));
}
public function provideDumpAtPath()
{
return [
'Regular node' => ['scalar_true', <<<EOL
scalar_true: true
EOL
],
'Array node' => ['array', <<<EOL
# some info
array:
child1: ~
child2: ~
# this is a long
# multi-line info text
# which should be indented
child3: ~ # Example: example setting
EOL
],
'Regular nested' => ['array.child2', <<<EOL
child2: ~
EOL
],
'Prototype' => ['cms_pages.page', <<<EOL
# Prototype
page:
# Prototype
locale:
title: ~ # Required
path: ~ # Required
EOL
],
'Nested prototype' => ['cms_pages.page.locale', <<<EOL
# Prototype
locale:
title: ~ # Required
path: ~ # Required
EOL
],
];
}
/**
* @dataProvider provideDumpAtPath
*/
public function testDumpAtPath($path, $expected)
{
$configuration = new ExampleConfiguration();
$dumper = new YamlReferenceDumper();
$this->assertSame(trim($expected), trim($dumper->dumpAtPath($configuration, $path)));
}
private function getConfigurationAsString()
{
return <<<'EOL'
acme_root:
boolean: true
scalar_empty: ~
scalar_null: null
scalar_true: true
scalar_false: false
scalar_default: default
scalar_array_empty: []
scalar_array_defaults:
# Defaults:
- elem1
- elem2
scalar_required: ~ # Required
scalar_deprecated: ~ # Deprecated (The child node "scalar_deprecated" at path "acme_root" is deprecated.)
scalar_deprecated_with_message: ~ # Deprecated (Deprecation custom message for "scalar_deprecated_with_message" at "acme_root")
node_with_a_looong_name: ~
enum_with_default: this # One of "this"; "that"
enum: ~ # One of "this"; "that"
# some info
array:
child1: ~
child2: ~
# this is a long
# multi-line info text
# which should be indented
child3: ~ # Example: example setting
scalar_prototyped: []
parameters:
# Prototype: Parameter name
name: ~
connections:
# Prototype
-
user: ~
pass: ~
cms_pages:
# Prototype
page:
# Prototype
locale:
title: ~ # Required
path: ~ # Required
pipou:
# Prototype
name: []
EOL;
}
}

View File

@@ -0,0 +1,51 @@
<?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\Config\Tests\Definition;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\Definition\EnumNode;
class EnumNodeTest extends TestCase
{
public function testFinalizeValue()
{
$node = new EnumNode('foo', null, ['foo', 'bar']);
$this->assertSame('foo', $node->finalize('foo'));
}
public function testConstructionWithNoValues()
{
$this->expectException('InvalidArgumentException');
$this->expectExceptionMessage('$values must contain at least one element.');
new EnumNode('foo', null, []);
}
public function testConstructionWithOneValue()
{
$node = new EnumNode('foo', null, ['foo']);
$this->assertSame('foo', $node->finalize('foo'));
}
public function testConstructionWithOneDistinctValue()
{
$node = new EnumNode('foo', null, ['foo', 'foo']);
$this->assertSame('foo', $node->finalize('foo'));
}
public function testFinalizeWithInvalidValue()
{
$this->expectException('Symfony\Component\Config\Definition\Exception\InvalidConfigurationException');
$this->expectExceptionMessage('The value "foobar" is not allowed for path "foo". Permissible values: "foo", "bar"');
$node = new EnumNode('foo', null, ['foo', 'bar']);
$node->finalize('foobar');
}
}

View File

@@ -0,0 +1,74 @@
<?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\Config\Tests\Definition;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\NodeInterface;
use Symfony\Component\Config\Definition\Processor;
class FinalizationTest extends TestCase
{
public function testUnsetKeyWithDeepHierarchy()
{
$tb = new TreeBuilder();
$tree = $tb
->root('config', 'array')
->children()
->node('level1', 'array')
->canBeUnset()
->children()
->node('level2', 'array')
->canBeUnset()
->children()
->node('somevalue', 'scalar')->end()
->node('anothervalue', 'scalar')->end()
->end()
->end()
->node('level1_scalar', 'scalar')->end()
->end()
->end()
->end()
->end()
->buildTree()
;
$a = [
'level1' => [
'level2' => [
'somevalue' => 'foo',
'anothervalue' => 'bar',
],
'level1_scalar' => 'foo',
],
];
$b = [
'level1' => [
'level2' => false,
],
];
$this->assertEquals([
'level1' => [
'level1_scalar' => 'foo',
],
], $this->process($tree, [$a, $b]));
}
protected function process(NodeInterface $tree, array $configs)
{
$processor = new Processor();
return $processor->process($tree, $configs);
}
}

View File

@@ -0,0 +1,78 @@
<?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\Config\Tests\Definition;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\Definition\FloatNode;
class FloatNodeTest extends TestCase
{
/**
* @dataProvider getValidValues
*/
public function testNormalize($value)
{
$node = new FloatNode('test');
$this->assertSame($value, $node->normalize($value));
}
/**
* @dataProvider getValidValues
*
* @param int $value
*/
public function testValidNonEmptyValues($value)
{
$node = new FloatNode('test');
$node->setAllowEmptyValue(false);
$this->assertSame($value, $node->finalize($value));
}
public function getValidValues()
{
return [
[1798.0],
[-678.987],
[12.56E45],
[0.0],
// Integer are accepted too, they will be cast
[17],
[-10],
[0],
];
}
/**
* @dataProvider getInvalidValues
*/
public function testNormalizeThrowsExceptionOnInvalidValues($value)
{
$this->expectException('Symfony\Component\Config\Definition\Exception\InvalidTypeException');
$node = new FloatNode('test');
$node->normalize($value);
}
public function getInvalidValues()
{
return [
[null],
[''],
['foo'],
[true],
[false],
[[]],
[['foo' => 'bar']],
[new \stdClass()],
];
}
}

View File

@@ -0,0 +1,75 @@
<?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\Config\Tests\Definition;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\Definition\IntegerNode;
class IntegerNodeTest extends TestCase
{
/**
* @dataProvider getValidValues
*/
public function testNormalize($value)
{
$node = new IntegerNode('test');
$this->assertSame($value, $node->normalize($value));
}
/**
* @dataProvider getValidValues
*
* @param int $value
*/
public function testValidNonEmptyValues($value)
{
$node = new IntegerNode('test');
$node->setAllowEmptyValue(false);
$this->assertSame($value, $node->finalize($value));
}
public function getValidValues()
{
return [
[1798],
[-678],
[0],
];
}
/**
* @dataProvider getInvalidValues
*/
public function testNormalizeThrowsExceptionOnInvalidValues($value)
{
$this->expectException('Symfony\Component\Config\Definition\Exception\InvalidTypeException');
$node = new IntegerNode('test');
$node->normalize($value);
}
public function getInvalidValues()
{
return [
[null],
[''],
['foo'],
[true],
[false],
[0.0],
[0.1],
[[]],
[['foo' => 'bar']],
[new \stdClass()],
];
}
}

View File

@@ -0,0 +1,192 @@
<?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\Config\Tests\Definition;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
class MergeTest extends TestCase
{
public function testForbiddenOverwrite()
{
$this->expectException('Symfony\Component\Config\Definition\Exception\ForbiddenOverwriteException');
$tb = new TreeBuilder();
$tree = $tb
->root('root', 'array')
->children()
->node('foo', 'scalar')
->cannotBeOverwritten()
->end()
->end()
->end()
->buildTree()
;
$a = [
'foo' => 'bar',
];
$b = [
'foo' => 'moo',
];
$tree->merge($a, $b);
}
public function testUnsetKey()
{
$tb = new TreeBuilder();
$tree = $tb
->root('root', 'array')
->children()
->node('foo', 'scalar')->end()
->node('bar', 'scalar')->end()
->node('unsettable', 'array')
->canBeUnset()
->children()
->node('foo', 'scalar')->end()
->node('bar', 'scalar')->end()
->end()
->end()
->node('unsetted', 'array')
->canBeUnset()
->prototype('scalar')->end()
->end()
->end()
->end()
->buildTree()
;
$a = [
'foo' => 'bar',
'unsettable' => [
'foo' => 'a',
'bar' => 'b',
],
'unsetted' => false,
];
$b = [
'foo' => 'moo',
'bar' => 'b',
'unsettable' => false,
'unsetted' => ['a', 'b'],
];
$this->assertEquals([
'foo' => 'moo',
'bar' => 'b',
'unsettable' => false,
'unsetted' => ['a', 'b'],
], $tree->merge($a, $b));
}
public function testDoesNotAllowNewKeysInSubsequentConfigs()
{
$this->expectException('Symfony\Component\Config\Definition\Exception\InvalidConfigurationException');
$tb = new TreeBuilder();
$tree = $tb
->root('config', 'array')
->children()
->node('test', 'array')
->disallowNewKeysInSubsequentConfigs()
->useAttributeAsKey('key')
->prototype('array')
->children()
->node('value', 'scalar')->end()
->end()
->end()
->end()
->end()
->end()
->buildTree();
$a = [
'test' => [
'a' => ['value' => 'foo'],
],
];
$b = [
'test' => [
'b' => ['value' => 'foo'],
],
];
$tree->merge($a, $b);
}
public function testPerformsNoDeepMerging()
{
$tb = new TreeBuilder();
$tree = $tb
->root('config', 'array')
->children()
->node('no_deep_merging', 'array')
->performNoDeepMerging()
->children()
->node('foo', 'scalar')->end()
->node('bar', 'scalar')->end()
->end()
->end()
->end()
->end()
->buildTree()
;
$a = [
'no_deep_merging' => [
'foo' => 'a',
'bar' => 'b',
],
];
$b = [
'no_deep_merging' => [
'c' => 'd',
],
];
$this->assertEquals([
'no_deep_merging' => [
'c' => 'd',
],
], $tree->merge($a, $b));
}
public function testPrototypeWithoutAKeyAttribute()
{
$tb = new TreeBuilder();
$tree = $tb
->root('config', 'array')
->children()
->arrayNode('append_elements')
->prototype('scalar')->end()
->end()
->end()
->end()
->buildTree()
;
$a = [
'append_elements' => ['a', 'b'],
];
$b = [
'append_elements' => ['c', 'd'],
];
$this->assertEquals(['append_elements' => ['a', 'b', 'c', 'd']], $tree->merge($a, $b));
}
}

View File

@@ -0,0 +1,228 @@
<?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\Config\Tests\Definition;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\NodeInterface;
class NormalizationTest extends TestCase
{
/**
* @dataProvider getEncoderTests
*/
public function testNormalizeEncoders($denormalized)
{
$tb = new TreeBuilder();
$tree = $tb
->root('root_name', 'array')
->fixXmlConfig('encoder')
->children()
->node('encoders', 'array')
->useAttributeAsKey('class')
->prototype('array')
->beforeNormalization()->ifString()->then(function ($v) { return ['algorithm' => $v]; })->end()
->children()
->node('algorithm', 'scalar')->end()
->end()
->end()
->end()
->end()
->end()
->buildTree()
;
$normalized = [
'encoders' => [
'foo' => ['algorithm' => 'plaintext'],
],
];
$this->assertNormalized($tree, $denormalized, $normalized);
}
public function getEncoderTests()
{
$configs = [];
// XML
$configs[] = [
'encoder' => [
['class' => 'foo', 'algorithm' => 'plaintext'],
],
];
// XML when only one element of this type
$configs[] = [
'encoder' => ['class' => 'foo', 'algorithm' => 'plaintext'],
];
// YAML/PHP
$configs[] = [
'encoders' => [
['class' => 'foo', 'algorithm' => 'plaintext'],
],
];
// YAML/PHP
$configs[] = [
'encoders' => [
'foo' => 'plaintext',
],
];
// YAML/PHP
$configs[] = [
'encoders' => [
'foo' => ['algorithm' => 'plaintext'],
],
];
return array_map(function ($v) {
return [$v];
}, $configs);
}
/**
* @dataProvider getAnonymousKeysTests
*/
public function testAnonymousKeysArray($denormalized)
{
$tb = new TreeBuilder();
$tree = $tb
->root('root', 'array')
->children()
->node('logout', 'array')
->fixXmlConfig('handler')
->children()
->node('handlers', 'array')
->prototype('scalar')->end()
->end()
->end()
->end()
->end()
->end()
->buildTree()
;
$normalized = ['logout' => ['handlers' => ['a', 'b', 'c']]];
$this->assertNormalized($tree, $denormalized, $normalized);
}
public function getAnonymousKeysTests()
{
$configs = [];
$configs[] = [
'logout' => [
'handlers' => ['a', 'b', 'c'],
],
];
$configs[] = [
'logout' => [
'handler' => ['a', 'b', 'c'],
],
];
return array_map(function ($v) { return [$v]; }, $configs);
}
/**
* @dataProvider getNumericKeysTests
*/
public function testNumericKeysAsAttributes($denormalized)
{
$normalized = [
'thing' => [42 => ['foo', 'bar'], 1337 => ['baz', 'qux']],
];
$this->assertNormalized($this->getNumericKeysTestTree(), $denormalized, $normalized);
}
public function getNumericKeysTests()
{
$configs = [];
$configs[] = [
'thing' => [
42 => ['foo', 'bar'], 1337 => ['baz', 'qux'],
],
];
$configs[] = [
'thing' => [
['foo', 'bar', 'id' => 42], ['baz', 'qux', 'id' => 1337],
],
];
return array_map(function ($v) { return [$v]; }, $configs);
}
public function testNonAssociativeArrayThrowsExceptionIfAttributeNotSet()
{
$this->expectException('Symfony\Component\Config\Definition\Exception\InvalidConfigurationException');
$this->expectExceptionMessage('The attribute "id" must be set for path "root.thing".');
$denormalized = [
'thing' => [
['foo', 'bar'], ['baz', 'qux'],
],
];
$this->assertNormalized($this->getNumericKeysTestTree(), $denormalized, []);
}
public function testAssociativeArrayPreserveKeys()
{
$tb = new TreeBuilder();
$tree = $tb
->root('root', 'array')
->prototype('array')
->children()
->node('foo', 'scalar')->end()
->end()
->end()
->end()
->buildTree()
;
$data = ['first' => ['foo' => 'bar']];
$this->assertNormalized($tree, $data, $data);
}
public static function assertNormalized(NodeInterface $tree, $denormalized, $normalized)
{
self::assertSame($normalized, $tree->normalize($denormalized));
}
private function getNumericKeysTestTree()
{
$tb = new TreeBuilder();
$tree = $tb
->root('root', 'array')
->children()
->node('thing', 'array')
->useAttributeAsKey('id')
->prototype('array')
->prototype('scalar')->end()
->end()
->end()
->end()
->end()
->buildTree()
;
return $tree;
}
}

View File

@@ -0,0 +1,341 @@
<?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\Config\Tests\Definition;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\Definition\ArrayNode;
use Symfony\Component\Config\Definition\PrototypedArrayNode;
use Symfony\Component\Config\Definition\ScalarNode;
use Symfony\Component\Config\Definition\VariableNode;
class PrototypedArrayNodeTest extends TestCase
{
public function testGetDefaultValueReturnsAnEmptyArrayForPrototypes()
{
$node = new PrototypedArrayNode('root');
$prototype = new ArrayNode(null, $node);
$node->setPrototype($prototype);
$this->assertEmpty($node->getDefaultValue());
}
public function testGetDefaultValueReturnsDefaultValueForPrototypes()
{
$node = new PrototypedArrayNode('root');
$prototype = new ArrayNode(null, $node);
$node->setPrototype($prototype);
$node->setDefaultValue(['test']);
$this->assertEquals(['test'], $node->getDefaultValue());
}
// a remapped key (e.g. "mapping" -> "mappings") should be unset after being used
public function testRemappedKeysAreUnset()
{
$node = new ArrayNode('root');
$mappingsNode = new PrototypedArrayNode('mappings');
$node->addChild($mappingsNode);
// each item under mappings is just a scalar
$prototype = new ScalarNode(null, $mappingsNode);
$mappingsNode->setPrototype($prototype);
$remappings = [];
$remappings[] = ['mapping', 'mappings'];
$node->setXmlRemappings($remappings);
$normalized = $node->normalize(['mapping' => ['foo', 'bar']]);
$this->assertEquals(['mappings' => ['foo', 'bar']], $normalized);
}
/**
* Tests that when a key attribute is mapped, that key is removed from the array.
*
* <things>
* <option id="option1" value="foo">
* <option id="option2" value="bar">
* </things>
*
* The above should finally be mapped to an array that looks like this
* (because "id" is the key attribute).
*
* [
* 'things' => [
* 'option1' => 'foo',
* 'option2' => 'bar',
* ]
* ]
*/
public function testMappedAttributeKeyIsRemoved()
{
$node = new PrototypedArrayNode('root');
$node->setKeyAttribute('id', true);
// each item under the root is an array, with one scalar item
$prototype = new ArrayNode(null, $node);
$prototype->addChild(new ScalarNode('foo'));
$node->setPrototype($prototype);
$children = [];
$children[] = ['id' => 'item_name', 'foo' => 'bar'];
$normalized = $node->normalize($children);
$expected = [];
$expected['item_name'] = ['foo' => 'bar'];
$this->assertEquals($expected, $normalized);
}
/**
* Tests the opposite of the testMappedAttributeKeyIsRemoved because
* the removal can be toggled with an option.
*/
public function testMappedAttributeKeyNotRemoved()
{
$node = new PrototypedArrayNode('root');
$node->setKeyAttribute('id', false);
// each item under the root is an array, with two scalar items
$prototype = new ArrayNode(null, $node);
$prototype->addChild(new ScalarNode('foo'));
$prototype->addChild(new ScalarNode('id')); // the key attribute will remain
$node->setPrototype($prototype);
$children = [];
$children[] = ['id' => 'item_name', 'foo' => 'bar'];
$normalized = $node->normalize($children);
$expected = [];
$expected['item_name'] = ['id' => 'item_name', 'foo' => 'bar'];
$this->assertEquals($expected, $normalized);
}
public function testAddDefaultChildren()
{
$node = $this->getPrototypeNodeWithDefaultChildren();
$node->setAddChildrenIfNoneSet();
$this->assertTrue($node->hasDefaultValue());
$this->assertEquals([['foo' => 'bar']], $node->getDefaultValue());
$node = $this->getPrototypeNodeWithDefaultChildren();
$node->setKeyAttribute('foobar');
$node->setAddChildrenIfNoneSet();
$this->assertTrue($node->hasDefaultValue());
$this->assertEquals(['defaults' => ['foo' => 'bar']], $node->getDefaultValue());
$node = $this->getPrototypeNodeWithDefaultChildren();
$node->setKeyAttribute('foobar');
$node->setAddChildrenIfNoneSet('defaultkey');
$this->assertTrue($node->hasDefaultValue());
$this->assertEquals(['defaultkey' => ['foo' => 'bar']], $node->getDefaultValue());
$node = $this->getPrototypeNodeWithDefaultChildren();
$node->setKeyAttribute('foobar');
$node->setAddChildrenIfNoneSet(['defaultkey']);
$this->assertTrue($node->hasDefaultValue());
$this->assertEquals(['defaultkey' => ['foo' => 'bar']], $node->getDefaultValue());
$node = $this->getPrototypeNodeWithDefaultChildren();
$node->setKeyAttribute('foobar');
$node->setAddChildrenIfNoneSet(['dk1', 'dk2']);
$this->assertTrue($node->hasDefaultValue());
$this->assertEquals(['dk1' => ['foo' => 'bar'], 'dk2' => ['foo' => 'bar']], $node->getDefaultValue());
$node = $this->getPrototypeNodeWithDefaultChildren();
$node->setAddChildrenIfNoneSet([5, 6]);
$this->assertTrue($node->hasDefaultValue());
$this->assertEquals([0 => ['foo' => 'bar'], 1 => ['foo' => 'bar']], $node->getDefaultValue());
$node = $this->getPrototypeNodeWithDefaultChildren();
$node->setAddChildrenIfNoneSet(2);
$this->assertTrue($node->hasDefaultValue());
$this->assertEquals([['foo' => 'bar'], ['foo' => 'bar']], $node->getDefaultValue());
}
public function testDefaultChildrenWinsOverDefaultValue()
{
$node = $this->getPrototypeNodeWithDefaultChildren();
$node->setAddChildrenIfNoneSet();
$node->setDefaultValue(['bar' => 'foo']);
$this->assertTrue($node->hasDefaultValue());
$this->assertEquals([['foo' => 'bar']], $node->getDefaultValue());
}
protected function getPrototypeNodeWithDefaultChildren()
{
$node = new PrototypedArrayNode('root');
$prototype = new ArrayNode(null, $node);
$child = new ScalarNode('foo');
$child->setDefaultValue('bar');
$prototype->addChild($child);
$prototype->setAddIfNotSet(true);
$node->setPrototype($prototype);
return $node;
}
/**
* Tests that when a key attribute is mapped, that key is removed from the array.
* And if only 'value' element is left in the array, it will replace its wrapper array.
*
* <things>
* <option id="option1" value="value1">
* </things>
*
* The above should finally be mapped to an array that looks like this
* (because "id" is the key attribute).
*
* [
* 'things' => [
* 'option1' => 'value1'
* ]
* ]
*
* It's also possible to mix 'value-only' and 'non-value-only' elements in the array.
*
* <things>
* <option id="option1" value="value1">
* <option id="option2" value="value2" foo="foo2">
* </things>
*
* The above should finally be mapped to an array as follows
*
* [
* 'things' => [
* 'option1' => 'value1',
* 'option2' => [
* 'value' => 'value2',
* 'foo' => 'foo2'
* ]
* ]
* ]
*
* The 'value' element can also be ArrayNode:
*
* <things>
* <option id="option1">
* <value>
* <foo>foo1</foo>
* <bar>bar1</bar>
* </value>
* </option>
* </things>
*
* The above should be finally be mapped to an array as follows
*
* [
* 'things' => [
* 'option1' => [
* 'foo' => 'foo1',
* 'bar' => 'bar1'
* ]
* ]
* ]
*
* If using VariableNode for value node, it's also possible to mix different types of value nodes:
*
* <things>
* <option id="option1">
* <value>
* <foo>foo1</foo>
* <bar>bar1</bar>
* </value>
* </option>
* <option id="option2" value="value2">
* </things>
*
* The above should be finally mapped to an array as follows
*
* [
* 'things' => [
* 'option1' => [
* 'foo' => 'foo1',
* 'bar' => 'bar1'
* ],
* 'option2' => 'value2'
* ]
* ]
*
* @dataProvider getDataForKeyRemovedLeftValueOnly
*/
public function testMappedAttributeKeyIsRemovedLeftValueOnly($value, $children, $expected)
{
$node = new PrototypedArrayNode('root');
$node->setKeyAttribute('id', true);
// each item under the root is an array, with one scalar item
$prototype = new ArrayNode(null, $node);
$prototype->addChild(new ScalarNode('id'));
$prototype->addChild(new ScalarNode('foo'));
$prototype->addChild($value);
$node->setPrototype($prototype);
$normalized = $node->normalize($children);
$this->assertEquals($expected, $normalized);
}
public function getDataForKeyRemovedLeftValueOnly()
{
$scalarValue = new ScalarNode('value');
$arrayValue = new ArrayNode('value');
$arrayValue->addChild(new ScalarNode('foo'));
$arrayValue->addChild(new ScalarNode('bar'));
$variableValue = new VariableNode('value');
return [
[
$scalarValue,
[
['id' => 'option1', 'value' => 'value1'],
],
['option1' => 'value1'],
],
[
$scalarValue,
[
['id' => 'option1', 'value' => 'value1'],
['id' => 'option2', 'value' => 'value2', 'foo' => 'foo2'],
],
[
'option1' => 'value1',
'option2' => ['value' => 'value2', 'foo' => 'foo2'],
],
],
[
$arrayValue,
[
[
'id' => 'option1',
'value' => ['foo' => 'foo1', 'bar' => 'bar1'],
],
],
[
'option1' => ['foo' => 'foo1', 'bar' => 'bar1'],
],
],
[$variableValue,
[
[
'id' => 'option1', 'value' => ['foo' => 'foo1', 'bar' => 'bar1'],
],
['id' => 'option2', 'value' => 'value2'],
],
[
'option1' => ['foo' => 'foo1', 'bar' => 'bar1'],
'option2' => 'value2',
],
],
];
}
}

View File

@@ -0,0 +1,161 @@
<?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\Config\Tests\Definition;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\Definition\ArrayNode;
use Symfony\Component\Config\Definition\ScalarNode;
class ScalarNodeTest extends TestCase
{
/**
* @dataProvider getValidValues
*/
public function testNormalize($value)
{
$node = new ScalarNode('test');
$this->assertSame($value, $node->normalize($value));
}
public function getValidValues()
{
return [
[false],
[true],
[null],
[''],
['foo'],
[0],
[1],
[0.0],
[0.1],
];
}
public function testSetDeprecated()
{
$childNode = new ScalarNode('foo');
$childNode->setDeprecated('"%node%" is deprecated');
$this->assertTrue($childNode->isDeprecated());
$this->assertSame('"foo" is deprecated', $childNode->getDeprecationMessage($childNode->getName(), $childNode->getPath()));
$node = new ArrayNode('root');
$node->addChild($childNode);
$deprecationTriggered = 0;
$deprecationHandler = function ($level, $message, $file, $line) use (&$prevErrorHandler, &$deprecationTriggered) {
if (\E_USER_DEPRECATED === $level) {
return ++$deprecationTriggered;
}
return $prevErrorHandler ? $prevErrorHandler($level, $message, $file, $line) : false;
};
$prevErrorHandler = set_error_handler($deprecationHandler);
$node->finalize([]);
restore_error_handler();
$this->assertSame(0, $deprecationTriggered, '->finalize() should not trigger if the deprecated node is not set');
$prevErrorHandler = set_error_handler($deprecationHandler);
$node->finalize(['foo' => '']);
restore_error_handler();
$this->assertSame(1, $deprecationTriggered, '->finalize() should trigger if the deprecated node is set');
}
/**
* @dataProvider getInvalidValues
*/
public function testNormalizeThrowsExceptionOnInvalidValues($value)
{
$this->expectException('Symfony\Component\Config\Definition\Exception\InvalidTypeException');
$node = new ScalarNode('test');
$node->normalize($value);
}
public function getInvalidValues()
{
return [
[[]],
[['foo' => 'bar']],
[new \stdClass()],
];
}
public function testNormalizeThrowsExceptionWithoutHint()
{
$node = new ScalarNode('test');
$this->expectException('Symfony\Component\Config\Definition\Exception\InvalidTypeException');
$this->expectExceptionMessage('Invalid type for path "test". Expected scalar, but got array.');
$node->normalize([]);
}
public function testNormalizeThrowsExceptionWithErrorMessage()
{
$node = new ScalarNode('test');
$node->setInfo('"the test value"');
$this->expectException('Symfony\Component\Config\Definition\Exception\InvalidTypeException');
$this->expectExceptionMessage("Invalid type for path \"test\". Expected scalar, but got array.\nHint: \"the test value\"");
$node->normalize([]);
}
/**
* @dataProvider getValidNonEmptyValues
*
* @param mixed $value
*/
public function testValidNonEmptyValues($value)
{
$node = new ScalarNode('test');
$node->setAllowEmptyValue(false);
$this->assertSame($value, $node->finalize($value));
}
public function getValidNonEmptyValues()
{
return [
[false],
[true],
['foo'],
[0],
[1],
[0.0],
[0.1],
];
}
/**
* @dataProvider getEmptyValues
*
* @param mixed $value
*/
public function testNotAllowedEmptyValuesThrowException($value)
{
$this->expectException('Symfony\Component\Config\Definition\Exception\InvalidConfigurationException');
$node = new ScalarNode('test');
$node->setAllowEmptyValue(false);
$node->finalize($value);
}
public function getEmptyValues()
{
return [
[null],
[''],
];
}
}

View File

@@ -0,0 +1,59 @@
<?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\Config\Tests\DependencyInjection;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\DependencyInjection\ConfigCachePass;
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
/**
* @group legacy
*/
class ConfigCachePassTest extends TestCase
{
public function testThatCheckersAreProcessedInPriorityOrder()
{
$container = new ContainerBuilder();
$definition = $container->register('config_cache_factory')->addArgument(null);
$container->register('checker_2')->addTag('config_cache.resource_checker', ['priority' => 100]);
$container->register('checker_1')->addTag('config_cache.resource_checker', ['priority' => 200]);
$container->register('checker_3')->addTag('config_cache.resource_checker');
$pass = new ConfigCachePass();
$pass->process($container);
$expected = new IteratorArgument([
new Reference('checker_1'),
new Reference('checker_2'),
new Reference('checker_3'),
]);
$this->assertEquals($expected, $definition->getArgument(0));
}
public function testThatCheckersCanBeMissing()
{
$container = new ContainerBuilder();
$definitionsBefore = \count($container->getDefinitions());
$aliasesBefore = \count($container->getAliases());
$pass = new ConfigCachePass();
$pass->process($container);
// the container is untouched (i.e. no new definitions or aliases)
$this->assertCount($definitionsBefore, $container->getDefinitions());
$this->assertCount($aliasesBefore, $container->getAliases());
}
}

View File

@@ -0,0 +1,98 @@
<?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\Config\Tests\Exception;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\Exception\FileLoaderLoadException;
class FileLoaderLoadExceptionTest extends TestCase
{
public function testMessageCannotLoadResource()
{
$exception = new FileLoaderLoadException('resource', null);
$this->assertEquals('Cannot load resource "resource".', $exception->getMessage());
}
public function testMessageCannotLoadResourceWithType()
{
$exception = new FileLoaderLoadException('resource', null, null, null, 'foobar');
$this->assertEquals('Cannot load resource "resource". Make sure there is a loader supporting the "foobar" type.', $exception->getMessage());
}
public function testMessageCannotLoadResourceWithAnnotationType()
{
$exception = new FileLoaderLoadException('resource', null, null, null, 'annotation');
$this->assertEquals('Cannot load resource "resource". Make sure annotations are installed and enabled.', $exception->getMessage());
}
public function testMessageCannotImportResourceFromSource()
{
$exception = new FileLoaderLoadException('resource', 'sourceResource');
$this->assertEquals('Cannot import resource "resource" from "sourceResource".', $exception->getMessage());
}
public function testMessageCannotImportBundleResource()
{
$exception = new FileLoaderLoadException('@resource', 'sourceResource');
$this->assertEquals(
'Cannot import resource "@resource" from "sourceResource". '.
'Make sure the "resource" bundle is correctly registered and loaded in the application kernel class. '.
'If the bundle is registered, make sure the bundle path "@resource" is not empty.',
$exception->getMessage()
);
}
public function testMessageHasPreviousErrorWithDotAndUnableToLoad()
{
$exception = new FileLoaderLoadException(
'resource',
null,
null,
new \Exception('There was a previous error with an ending dot.')
);
$this->assertEquals(
'There was a previous error with an ending dot in resource (which is loaded in resource "resource").',
$exception->getMessage()
);
}
public function testMessageHasPreviousErrorWithoutDotAndUnableToLoad()
{
$exception = new FileLoaderLoadException(
'resource',
null,
null,
new \Exception('There was a previous error with no ending dot')
);
$this->assertEquals(
'There was a previous error with no ending dot in resource (which is loaded in resource "resource").',
$exception->getMessage()
);
}
public function testMessageHasPreviousErrorAndUnableToLoadBundle()
{
$exception = new FileLoaderLoadException(
'@resource',
null,
null,
new \Exception('There was a previous error with an ending dot.')
);
$this->assertEquals(
'There was a previous error with an ending dot in @resource '.
'(which is loaded in resource "@resource"). '.
'Make sure the "resource" bundle is correctly registered and loaded in the application kernel class. '.
'If the bundle is registered, make sure the bundle path "@resource" is not empty.',
$exception->getMessage()
);
}
}

View File

@@ -0,0 +1,114 @@
<?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\Config\Tests;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\FileLocator;
class FileLocatorTest extends TestCase
{
/**
* @dataProvider getIsAbsolutePathTests
*/
public function testIsAbsolutePath($path)
{
$loader = new FileLocator([]);
$r = new \ReflectionObject($loader);
$m = $r->getMethod('isAbsolutePath');
$m->setAccessible(true);
$this->assertTrue($m->invoke($loader, $path), '->isAbsolutePath() returns true for an absolute path');
}
public function getIsAbsolutePathTests()
{
return [
['/foo.xml'],
['c:\\\\foo.xml'],
['c:/foo.xml'],
['\\server\\foo.xml'],
['https://server/foo.xml'],
['phar://server/foo.xml'],
];
}
public function testLocate()
{
$loader = new FileLocator(__DIR__.'/Fixtures');
$this->assertEquals(
__DIR__.\DIRECTORY_SEPARATOR.'FileLocatorTest.php',
$loader->locate('FileLocatorTest.php', __DIR__),
'->locate() returns the absolute filename if the file exists in the given path'
);
$this->assertEquals(
__DIR__.'/Fixtures'.\DIRECTORY_SEPARATOR.'foo.xml',
$loader->locate('foo.xml', __DIR__),
'->locate() returns the absolute filename if the file exists in one of the paths given in the constructor'
);
$this->assertEquals(
__DIR__.'/Fixtures'.\DIRECTORY_SEPARATOR.'foo.xml',
$loader->locate(__DIR__.'/Fixtures'.\DIRECTORY_SEPARATOR.'foo.xml', __DIR__),
'->locate() returns the absolute filename if the file exists in one of the paths given in the constructor'
);
$loader = new FileLocator([__DIR__.'/Fixtures', __DIR__.'/Fixtures/Again']);
$this->assertEquals(
[__DIR__.'/Fixtures'.\DIRECTORY_SEPARATOR.'foo.xml', __DIR__.'/Fixtures/Again'.\DIRECTORY_SEPARATOR.'foo.xml'],
$loader->locate('foo.xml', __DIR__, false),
'->locate() returns an array of absolute filenames'
);
$this->assertEquals(
[__DIR__.'/Fixtures'.\DIRECTORY_SEPARATOR.'foo.xml', __DIR__.'/Fixtures/Again'.\DIRECTORY_SEPARATOR.'foo.xml'],
$loader->locate('foo.xml', __DIR__.'/Fixtures', false),
'->locate() returns an array of absolute filenames'
);
$loader = new FileLocator(__DIR__.'/Fixtures/Again');
$this->assertEquals(
[__DIR__.'/Fixtures'.\DIRECTORY_SEPARATOR.'foo.xml', __DIR__.'/Fixtures/Again'.\DIRECTORY_SEPARATOR.'foo.xml'],
$loader->locate('foo.xml', __DIR__.'/Fixtures', false),
'->locate() returns an array of absolute filenames'
);
}
public function testLocateThrowsAnExceptionIfTheFileDoesNotExists()
{
$this->expectException('Symfony\Component\Config\Exception\FileLocatorFileNotFoundException');
$this->expectExceptionMessage('The file "foobar.xml" does not exist');
$loader = new FileLocator([__DIR__.'/Fixtures']);
$loader->locate('foobar.xml', __DIR__);
}
public function testLocateThrowsAnExceptionIfTheFileDoesNotExistsInAbsolutePath()
{
$this->expectException('Symfony\Component\Config\Exception\FileLocatorFileNotFoundException');
$loader = new FileLocator([__DIR__.'/Fixtures']);
$loader->locate(__DIR__.'/Fixtures/foobar.xml', __DIR__);
}
public function testLocateEmpty()
{
$this->expectException('InvalidArgumentException');
$this->expectExceptionMessage('An empty file name is not valid to be located.');
$loader = new FileLocator([__DIR__.'/Fixtures']);
$loader->locate(null, __DIR__);
}
}

View File

View File

@@ -0,0 +1,9 @@
<?php
namespace Symfony\Component\Config\Tests\Fixtures;
class FileNameMismatchOnPurpose
{
}
throw new \RuntimeException('Mismatch between file name and class name.');

View File

@@ -0,0 +1,7 @@
<?php
namespace Symfony\Component\Config\Tests\Fixtures;
class BadParent extends MissingParent
{
}

View File

@@ -0,0 +1,18 @@
<?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\Config\Tests\Fixtures;
use Symfony\Component\Config\Definition\ArrayNode;
class BarNode extends ArrayNode
{
}

View File

@@ -0,0 +1,23 @@
<?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\Config\Tests\Fixtures\Builder;
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
use Symfony\Component\Config\Tests\Fixtures\BarNode;
class BarNodeDefinition extends NodeDefinition
{
protected function createNode()
{
return new BarNode($this->name);
}
}

View File

@@ -0,0 +1,34 @@
<?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\Config\Tests\Fixtures\Builder;
use Symfony\Component\Config\Definition\Builder\NodeBuilder as BaseNodeBuilder;
class NodeBuilder extends BaseNodeBuilder
{
public function barNode($name)
{
return $this->node($name, 'bar');
}
protected function getNodeClass($type)
{
switch ($type) {
case 'variable':
return __NAMESPACE__.'\\'.ucfirst($type).'NodeDefinition';
case 'bar':
return __NAMESPACE__.'\\'.ucfirst($type).'NodeDefinition';
default:
return parent::getNodeClass($type);
}
}
}

View File

@@ -0,0 +1,18 @@
<?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\Config\Tests\Fixtures\Builder;
use Symfony\Component\Config\Definition\Builder\VariableNodeDefinition as BaseVariableNodeDefinition;
class VariableNodeDefinition extends BaseVariableNodeDefinition
{
}

View File

@@ -0,0 +1,102 @@
<?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\Config\Tests\Fixtures\Configuration;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
class ExampleConfiguration implements ConfigurationInterface
{
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root('acme_root');
$rootNode
->fixXmlConfig('parameter')
->fixXmlConfig('connection')
->fixXmlConfig('cms_page')
->children()
->booleanNode('boolean')->defaultTrue()->end()
->scalarNode('scalar_empty')->end()
->scalarNode('scalar_null')->defaultNull()->end()
->scalarNode('scalar_true')->defaultTrue()->end()
->scalarNode('scalar_false')->defaultFalse()->end()
->scalarNode('scalar_default')->defaultValue('default')->end()
->scalarNode('scalar_array_empty')->defaultValue([])->end()
->scalarNode('scalar_array_defaults')->defaultValue(['elem1', 'elem2'])->end()
->scalarNode('scalar_required')->isRequired()->end()
->scalarNode('scalar_deprecated')->setDeprecated()->end()
->scalarNode('scalar_deprecated_with_message')->setDeprecated('Deprecation custom message for "%node%" at "%path%"')->end()
->scalarNode('node_with_a_looong_name')->end()
->enumNode('enum_with_default')->values(['this', 'that'])->defaultValue('this')->end()
->enumNode('enum')->values(['this', 'that'])->end()
->arrayNode('array')
->info('some info')
->canBeUnset()
->children()
->scalarNode('child1')->end()
->scalarNode('child2')->end()
->scalarNode('child3')
->info(
"this is a long\n".
"multi-line info text\n".
'which should be indented'
)
->example('example setting')
->end()
->end()
->end()
->arrayNode('scalar_prototyped')
->prototype('scalar')->end()
->end()
->arrayNode('parameters')
->useAttributeAsKey('name')
->prototype('scalar')->info('Parameter name')->end()
->end()
->arrayNode('connections')
->prototype('array')
->children()
->scalarNode('user')->end()
->scalarNode('pass')->end()
->end()
->end()
->end()
->arrayNode('cms_pages')
->useAttributeAsKey('page')
->prototype('array')
->useAttributeAsKey('locale')
->prototype('array')
->children()
->scalarNode('title')->isRequired()->end()
->scalarNode('path')->isRequired()->end()
->end()
->end()
->end()
->end()
->arrayNode('pipou')
->useAttributeAsKey('name')
->prototype('array')
->prototype('array')
->children()
->scalarNode('didou')
->end()
->end()
->end()
->end()
->end()
->end()
;
return $treeBuilder;
}
}

View File

@@ -0,0 +1,7 @@
<?php
namespace Symfony\Component\Config\Tests\Fixtures;
class ParseError
{
// missing closing bracket

View File

@@ -0,0 +1,9 @@
<?php
namespace Symfony\Component\Config\Tests\Fixtures\Resource;
if (!class_exists(MissingClass::class)) {
class ConditionalClass
{
}
}

View File

@@ -0,0 +1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE scan [<!ENTITY test SYSTEM "php://filter/read=convert.base64-encode/resource={{ resource }}">]>
<scan></scan>

View File

@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<root>

View File

@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<root2 xmlns="http://example.com/schema" />

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns="http://example.com/schema"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://example.com/schema"
elementFormDefault="qualified">
<xsd:element name="root" />
</xsd:schema>

View File

@@ -0,0 +1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<root xmlns="http://example.com/schema">
</root>

View File

View File

@@ -0,0 +1,69 @@
<?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\Config\Tests\Loader;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\Loader\DelegatingLoader;
use Symfony\Component\Config\Loader\LoaderResolver;
class DelegatingLoaderTest extends TestCase
{
public function testConstructor()
{
new DelegatingLoader($resolver = new LoaderResolver());
$this->assertTrue(true, '__construct() takes a loader resolver as its first argument');
}
public function testGetSetResolver()
{
$resolver = new LoaderResolver();
$loader = new DelegatingLoader($resolver);
$this->assertSame($resolver, $loader->getResolver(), '->getResolver() gets the resolver loader');
$loader->setResolver($resolver = new LoaderResolver());
$this->assertSame($resolver, $loader->getResolver(), '->setResolver() sets the resolver loader');
}
public function testSupports()
{
$loader1 = $this->getMockBuilder('Symfony\Component\Config\Loader\LoaderInterface')->getMock();
$loader1->expects($this->once())->method('supports')->willReturn(true);
$loader = new DelegatingLoader(new LoaderResolver([$loader1]));
$this->assertTrue($loader->supports('foo.xml'), '->supports() returns true if the resource is loadable');
$loader1 = $this->getMockBuilder('Symfony\Component\Config\Loader\LoaderInterface')->getMock();
$loader1->expects($this->once())->method('supports')->willReturn(false);
$loader = new DelegatingLoader(new LoaderResolver([$loader1]));
$this->assertFalse($loader->supports('foo.foo'), '->supports() returns false if the resource is not loadable');
}
public function testLoad()
{
$loader = $this->getMockBuilder('Symfony\Component\Config\Loader\LoaderInterface')->getMock();
$loader->expects($this->once())->method('supports')->willReturn(true);
$loader->expects($this->once())->method('load');
$resolver = new LoaderResolver([$loader]);
$loader = new DelegatingLoader($resolver);
$loader->load('foo');
}
public function testLoadThrowsAnExceptionIfTheResourceCannotBeLoaded()
{
$this->expectException('Symfony\Component\Config\Exception\FileLoaderLoadException');
$loader = $this->getMockBuilder('Symfony\Component\Config\Loader\LoaderInterface')->getMock();
$loader->expects($this->once())->method('supports')->willReturn(false);
$resolver = new LoaderResolver([$loader]);
$loader = new DelegatingLoader($resolver);
$loader->load('foo');
}
}

View File

@@ -0,0 +1,128 @@
<?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\Config\Tests\Loader;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\Config\Loader\FileLoader;
use Symfony\Component\Config\Loader\LoaderResolver;
class FileLoaderTest extends TestCase
{
public function testImportWithFileLocatorDelegation()
{
$locatorMock = $this->getMockBuilder('Symfony\Component\Config\FileLocatorInterface')->getMock();
$locatorMockForAdditionalLoader = $this->getMockBuilder('Symfony\Component\Config\FileLocatorInterface')->getMock();
$locatorMockForAdditionalLoader->expects($this->any())->method('locate')->will($this->onConsecutiveCalls(
['path/to/file1'], // Default
['path/to/file1', 'path/to/file2'], // First is imported
['path/to/file1', 'path/to/file2'], // Second is imported
['path/to/file1'], // Exception
['path/to/file1', 'path/to/file2'] // Exception
));
$fileLoader = new TestFileLoader($locatorMock);
$fileLoader->setSupports(false);
$fileLoader->setCurrentDir('.');
$additionalLoader = new TestFileLoader($locatorMockForAdditionalLoader);
$additionalLoader->setCurrentDir('.');
$fileLoader->setResolver($loaderResolver = new LoaderResolver([$fileLoader, $additionalLoader]));
// Default case
$this->assertSame('path/to/file1', $fileLoader->import('my_resource'));
// Check first file is imported if not already loading
$this->assertSame('path/to/file1', $fileLoader->import('my_resource'));
// Check second file is imported if first is already loading
$fileLoader->addLoading('path/to/file1');
$this->assertSame('path/to/file2', $fileLoader->import('my_resource'));
// Check exception throws if first (and only available) file is already loading
try {
$fileLoader->import('my_resource');
$this->fail('->import() throws a FileLoaderImportCircularReferenceException if the resource is already loading');
} catch (\Exception $e) {
$this->assertInstanceOf('Symfony\Component\Config\Exception\FileLoaderImportCircularReferenceException', $e, '->import() throws a FileLoaderImportCircularReferenceException if the resource is already loading');
}
// Check exception throws if all files are already loading
try {
$fileLoader->addLoading('path/to/file2');
$fileLoader->import('my_resource');
$this->fail('->import() throws a FileLoaderImportCircularReferenceException if the resource is already loading');
} catch (\Exception $e) {
$this->assertInstanceOf('Symfony\Component\Config\Exception\FileLoaderImportCircularReferenceException', $e, '->import() throws a FileLoaderImportCircularReferenceException if the resource is already loading');
}
}
public function testImportWithGlobLikeResource()
{
$locatorMock = $this->getMockBuilder('Symfony\Component\Config\FileLocatorInterface')->getMock();
$loader = new TestFileLoader($locatorMock);
$this->assertSame('[foo]', $loader->import('[foo]'));
}
public function testImportWithNoGlobMatch()
{
$locatorMock = $this->getMockBuilder('Symfony\Component\Config\FileLocatorInterface')->getMock();
$loader = new TestFileLoader($locatorMock);
$this->assertNull($loader->import('./*.abc'));
}
public function testImportWithSimpleGlob()
{
$loader = new TestFileLoader(new FileLocator(__DIR__));
$this->assertSame(__FILE__, strtr($loader->import('FileLoaderTest.*'), '/', \DIRECTORY_SEPARATOR));
}
}
class TestFileLoader extends FileLoader
{
private $supports = true;
public function load($resource, $type = null)
{
return $resource;
}
public function supports($resource, $type = null)
{
return $this->supports;
}
public function addLoading($resource)
{
self::$loading[$resource] = true;
}
public function removeLoading($resource)
{
unset(self::$loading[$resource]);
}
public function clearLoading()
{
self::$loading = [];
}
public function setSupports($supports)
{
$this->supports = $supports;
}
}

View File

@@ -0,0 +1,47 @@
<?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\Config\Tests\Loader;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\Loader\LoaderResolver;
class LoaderResolverTest extends TestCase
{
public function testConstructor()
{
$resolver = new LoaderResolver([
$loader = $this->getMockBuilder('Symfony\Component\Config\Loader\LoaderInterface')->getMock(),
]);
$this->assertEquals([$loader], $resolver->getLoaders(), '__construct() takes an array of loaders as its first argument');
}
public function testResolve()
{
$loader = $this->getMockBuilder('Symfony\Component\Config\Loader\LoaderInterface')->getMock();
$resolver = new LoaderResolver([$loader]);
$this->assertFalse($resolver->resolve('foo.foo'), '->resolve() returns false if no loader is able to load the resource');
$loader = $this->getMockBuilder('Symfony\Component\Config\Loader\LoaderInterface')->getMock();
$loader->expects($this->once())->method('supports')->willReturn(true);
$resolver = new LoaderResolver([$loader]);
$this->assertEquals($loader, $resolver->resolve(function () {}), '->resolve() returns the loader for the given resource');
}
public function testLoaders()
{
$resolver = new LoaderResolver();
$resolver->addLoader($loader = $this->getMockBuilder('Symfony\Component\Config\Loader\LoaderInterface')->getMock());
$this->assertEquals([$loader], $resolver->getLoaders(), 'addLoader() adds a loader');
}
}

View File

@@ -0,0 +1,116 @@
<?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\Config\Tests\Loader;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\Loader\Loader;
class LoaderTest extends TestCase
{
public function testGetSetResolver()
{
$resolver = $this->getMockBuilder('Symfony\Component\Config\Loader\LoaderResolverInterface')->getMock();
$loader = new ProjectLoader1();
$loader->setResolver($resolver);
$this->assertSame($resolver, $loader->getResolver(), '->setResolver() sets the resolver loader');
}
public function testResolve()
{
$resolvedLoader = $this->getMockBuilder('Symfony\Component\Config\Loader\LoaderInterface')->getMock();
$resolver = $this->getMockBuilder('Symfony\Component\Config\Loader\LoaderResolverInterface')->getMock();
$resolver->expects($this->once())
->method('resolve')
->with('foo.xml')
->willReturn($resolvedLoader);
$loader = new ProjectLoader1();
$loader->setResolver($resolver);
$this->assertSame($loader, $loader->resolve('foo.foo'), '->resolve() finds a loader');
$this->assertSame($resolvedLoader, $loader->resolve('foo.xml'), '->resolve() finds a loader');
}
public function testResolveWhenResolverCannotFindLoader()
{
$this->expectException('Symfony\Component\Config\Exception\FileLoaderLoadException');
$resolver = $this->getMockBuilder('Symfony\Component\Config\Loader\LoaderResolverInterface')->getMock();
$resolver->expects($this->once())
->method('resolve')
->with('FOOBAR')
->willReturn(false);
$loader = new ProjectLoader1();
$loader->setResolver($resolver);
$loader->resolve('FOOBAR');
}
public function testImport()
{
$resolvedLoader = $this->getMockBuilder('Symfony\Component\Config\Loader\LoaderInterface')->getMock();
$resolvedLoader->expects($this->once())
->method('load')
->with('foo')
->willReturn('yes');
$resolver = $this->getMockBuilder('Symfony\Component\Config\Loader\LoaderResolverInterface')->getMock();
$resolver->expects($this->once())
->method('resolve')
->with('foo')
->willReturn($resolvedLoader);
$loader = new ProjectLoader1();
$loader->setResolver($resolver);
$this->assertEquals('yes', $loader->import('foo'));
}
public function testImportWithType()
{
$resolvedLoader = $this->getMockBuilder('Symfony\Component\Config\Loader\LoaderInterface')->getMock();
$resolvedLoader->expects($this->once())
->method('load')
->with('foo', 'bar')
->willReturn('yes');
$resolver = $this->getMockBuilder('Symfony\Component\Config\Loader\LoaderResolverInterface')->getMock();
$resolver->expects($this->once())
->method('resolve')
->with('foo', 'bar')
->willReturn($resolvedLoader);
$loader = new ProjectLoader1();
$loader->setResolver($resolver);
$this->assertEquals('yes', $loader->import('foo', 'bar'));
}
}
class ProjectLoader1 extends Loader
{
public function load($resource, $type = null)
{
}
public function supports($resource, $type = null)
{
return \is_string($resource) && 'foo' === pathinfo($resource, \PATHINFO_EXTENSION);
}
public function getType()
{
}
}

View File

@@ -0,0 +1,130 @@
<?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\Config\Tests\Resource;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\Resource\ClassExistenceResource;
use Symfony\Component\Config\Tests\Fixtures\BadFileName;
use Symfony\Component\Config\Tests\Fixtures\BadParent;
use Symfony\Component\Config\Tests\Fixtures\ParseError;
use Symfony\Component\Config\Tests\Fixtures\Resource\ConditionalClass;
class ClassExistenceResourceTest extends TestCase
{
public function testToString()
{
$res = new ClassExistenceResource('BarClass');
$this->assertSame('BarClass', (string) $res);
}
public function testGetResource()
{
$res = new ClassExistenceResource('BarClass');
$this->assertSame('BarClass', $res->getResource());
}
public function testIsFreshWhenClassDoesNotExist()
{
$res = new ClassExistenceResource('Symfony\Component\Config\Tests\Fixtures\BarClass');
$this->assertTrue($res->isFresh(time()));
eval(<<<EOF
namespace Symfony\Component\Config\Tests\Fixtures;
class BarClass
{
}
EOF
);
$this->assertFalse($res->isFresh(time()));
}
public function testIsFreshWhenClassExists()
{
$res = new ClassExistenceResource('Symfony\Component\Config\Tests\Resource\ClassExistenceResourceTest');
$this->assertTrue($res->isFresh(time()));
}
public function testExistsKo()
{
spl_autoload_register($autoloader = function ($class) use (&$loadedClass) { $loadedClass = $class; });
try {
$res = new ClassExistenceResource('MissingFooClass');
$this->assertTrue($res->isFresh(0));
$this->assertSame('MissingFooClass', $loadedClass);
$loadedClass = 123;
new ClassExistenceResource('MissingFooClass', false);
$this->assertSame(123, $loadedClass);
} finally {
spl_autoload_unregister($autoloader);
}
}
public function testBadParentWithTimestamp()
{
$res = new ClassExistenceResource(BadParent::class, false);
$this->assertTrue($res->isFresh(time()));
}
public function testBadParentWithNoTimestamp()
{
$this->expectException('ReflectionException');
$this->expectExceptionMessage('Class "Symfony\Component\Config\Tests\Fixtures\MissingParent" not found while loading "Symfony\Component\Config\Tests\Fixtures\BadParent".');
$res = new ClassExistenceResource(BadParent::class, false);
$res->isFresh(0);
}
public function testBadFileName()
{
$this->expectException('ReflectionException');
$this->expectExceptionMessage('Mismatch between file name and class name.');
$res = new ClassExistenceResource(BadFileName::class, false);
$res->isFresh(0);
}
public function testBadFileNameBis()
{
$this->expectException('ReflectionException');
$this->expectExceptionMessage('Mismatch between file name and class name.');
$res = new ClassExistenceResource(BadFileName::class, false);
$res->isFresh(0);
}
public function testConditionalClass()
{
$res = new ClassExistenceResource(ConditionalClass::class, false);
$this->assertFalse($res->isFresh(0));
}
/**
* @requires PHP 7
*/
public function testParseError()
{
$this->expectException('ParseError');
$res = new ClassExistenceResource(ParseError::class, false);
$res->isFresh(0);
}
}

View File

@@ -0,0 +1,47 @@
<?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\Config\Tests\Resource;
use Composer\Autoload\ClassLoader;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\Resource\ComposerResource;
class ComposerResourceTest extends TestCase
{
public function testGetVendor()
{
$res = new ComposerResource();
$r = new \ReflectionClass(ClassLoader::class);
$found = false;
foreach ($res->getVendors() as $vendor) {
if ($vendor && 0 === strpos($r->getFileName(), $vendor)) {
$found = true;
break;
}
}
$this->assertTrue($found);
}
public function testSerializeUnserialize()
{
$res = new ComposerResource();
$ser = unserialize(serialize($res));
$this->assertTrue($res->isFresh(0));
$this->assertTrue($ser->isFresh(0));
$this->assertEquals($res, $ser);
}
}

View File

@@ -0,0 +1,181 @@
<?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\Config\Tests\Resource;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\Resource\DirectoryResource;
class DirectoryResourceTest extends TestCase
{
protected $directory;
protected function setUp()
{
$this->directory = sys_get_temp_dir().\DIRECTORY_SEPARATOR.'symfonyDirectoryIterator';
if (!file_exists($this->directory)) {
mkdir($this->directory);
}
touch($this->directory.'/tmp.xml');
}
protected function tearDown()
{
if (!is_dir($this->directory)) {
return;
}
$this->removeDirectory($this->directory);
}
protected function removeDirectory($directory)
{
$iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($directory), \RecursiveIteratorIterator::CHILD_FIRST);
foreach ($iterator as $path) {
if (preg_match('#[/\\\\]\.\.?$#', $path->__toString())) {
continue;
}
if ($path->isDir()) {
rmdir($path->__toString());
} else {
unlink($path->__toString());
}
}
rmdir($directory);
}
public function testGetResource()
{
$resource = new DirectoryResource($this->directory);
$this->assertSame(realpath($this->directory), $resource->getResource(), '->getResource() returns the path to the resource');
}
public function testGetPattern()
{
$resource = new DirectoryResource($this->directory, 'bar');
$this->assertEquals('bar', $resource->getPattern());
}
public function testResourceDoesNotExist()
{
$this->expectException('InvalidArgumentException');
$this->expectExceptionMessageMatches('/The directory ".*" does not exist./');
new DirectoryResource('/____foo/foobar'.mt_rand(1, 999999));
}
public function testIsFresh()
{
$resource = new DirectoryResource($this->directory);
$this->assertTrue($resource->isFresh(time() + 10), '->isFresh() returns true if the resource has not changed');
$this->assertFalse($resource->isFresh(time() - 86400), '->isFresh() returns false if the resource has been updated');
}
public function testIsFreshForDeletedResources()
{
$resource = new DirectoryResource($this->directory);
$this->removeDirectory($this->directory);
$this->assertFalse($resource->isFresh(time()), '->isFresh() returns false if the resource does not exist');
}
public function testIsFreshUpdateFile()
{
$resource = new DirectoryResource($this->directory);
touch($this->directory.'/tmp.xml', time() + 20);
$this->assertFalse($resource->isFresh(time() + 10), '->isFresh() returns false if an existing file is modified');
}
public function testIsFreshNewFile()
{
$resource = new DirectoryResource($this->directory);
touch($this->directory.'/new.xml', time() + 20);
$this->assertFalse($resource->isFresh(time() + 10), '->isFresh() returns false if a new file is added');
}
public function testIsFreshNewFileWithDifferentPattern()
{
$resource = new DirectoryResource($this->directory, '/.xml$/');
touch($this->directory.'/new.yaml', time() + 20);
$this->assertTrue($resource->isFresh(time() + 10), '->isFresh() returns true if a new file with a non-matching pattern is added');
}
public function testIsFreshDeleteFile()
{
$resource = new DirectoryResource($this->directory);
$time = time();
sleep(1);
unlink($this->directory.'/tmp.xml');
$this->assertFalse($resource->isFresh($time), '->isFresh() returns false if an existing file is removed');
}
public function testIsFreshDeleteDirectory()
{
$resource = new DirectoryResource($this->directory);
$this->removeDirectory($this->directory);
$this->assertFalse($resource->isFresh(time()), '->isFresh() returns false if the whole resource is removed');
}
public function testIsFreshCreateFileInSubdirectory()
{
$subdirectory = $this->directory.'/subdirectory';
mkdir($subdirectory);
$resource = new DirectoryResource($this->directory);
$this->assertTrue($resource->isFresh(time() + 10), '->isFresh() returns true if an unmodified subdirectory exists');
touch($subdirectory.'/newfile.xml', time() + 20);
$this->assertFalse($resource->isFresh(time() + 10), '->isFresh() returns false if a new file in a subdirectory is added');
}
public function testIsFreshModifySubdirectory()
{
$resource = new DirectoryResource($this->directory);
$subdirectory = $this->directory.'/subdirectory';
mkdir($subdirectory);
touch($subdirectory, time() + 20);
$this->assertFalse($resource->isFresh(time() + 10), '->isFresh() returns false if a subdirectory is modified (e.g. a file gets deleted)');
}
public function testFilterRegexListNoMatch()
{
$resource = new DirectoryResource($this->directory, '/\.(foo|xml)$/');
touch($this->directory.'/new.bar', time() + 20);
$this->assertTrue($resource->isFresh(time() + 10), '->isFresh() returns true if a new file not matching the filter regex is created');
}
public function testFilterRegexListMatch()
{
$resource = new DirectoryResource($this->directory, '/\.(foo|xml)$/');
touch($this->directory.'/new.xml', time() + 20);
$this->assertFalse($resource->isFresh(time() + 10), '->isFresh() returns false if an new file matching the filter regex is created ');
}
public function testSerializeUnserialize()
{
$resource = new DirectoryResource($this->directory, '/\.(foo|xml)$/');
unserialize(serialize($resource));
$this->assertSame(realpath($this->directory), $resource->getResource());
$this->assertSame('/\.(foo|xml)$/', $resource->getPattern());
}
public function testResourcesWithDifferentPatternsAreDifferent()
{
$resourceA = new DirectoryResource($this->directory, '/.xml$/');
$resourceB = new DirectoryResource($this->directory, '/.yaml$/');
$this->assertCount(2, array_unique([$resourceA, $resourceB]));
}
}

View File

@@ -0,0 +1,71 @@
<?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\Config\Tests\Resource;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\Resource\FileExistenceResource;
class FileExistenceResourceTest extends TestCase
{
protected $resource;
protected $file;
protected $time;
protected function setUp()
{
$this->file = realpath(sys_get_temp_dir()).'/tmp.xml';
$this->time = time();
$this->resource = new FileExistenceResource($this->file);
}
protected function tearDown()
{
if (file_exists($this->file)) {
@unlink($this->file);
}
}
public function testToString()
{
$this->assertSame($this->file, (string) $this->resource);
}
public function testGetResource()
{
$this->assertSame($this->file, $this->resource->getResource(), '->getResource() returns the path to the resource');
}
public function testIsFreshWithExistingResource()
{
touch($this->file, $this->time);
$serialized = serialize(new FileExistenceResource($this->file));
$resource = unserialize($serialized);
$this->assertTrue($resource->isFresh($this->time), '->isFresh() returns true if the resource is still present');
unlink($this->file);
$resource = unserialize($serialized);
$this->assertFalse($resource->isFresh($this->time), '->isFresh() returns false if the resource has been deleted');
}
public function testIsFreshWithAbsentResource()
{
$serialized = serialize(new FileExistenceResource($this->file));
$resource = unserialize($serialized);
$this->assertTrue($resource->isFresh($this->time), '->isFresh() returns true if the resource is still absent');
touch($this->file, $this->time);
$resource = unserialize($serialized);
$this->assertFalse($resource->isFresh($this->time), '->isFresh() returns false if the resource has been created');
}
}

View File

@@ -0,0 +1,81 @@
<?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\Config\Tests\Resource;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\Resource\FileResource;
class FileResourceTest extends TestCase
{
protected $resource;
protected $file;
protected $time;
protected function setUp()
{
$this->file = sys_get_temp_dir().'/tmp.xml';
$this->time = time();
touch($this->file, $this->time);
$this->resource = new FileResource($this->file);
}
protected function tearDown()
{
if (file_exists($this->file)) {
@unlink($this->file);
}
}
public function testGetResource()
{
$this->assertSame(realpath($this->file), $this->resource->getResource(), '->getResource() returns the path to the resource');
}
public function testGetResourceWithScheme()
{
$resource = new FileResource('file://'.$this->file);
$this->assertSame('file://'.$this->file, $resource->getResource(), '->getResource() returns the path to the schemed resource');
}
public function testToString()
{
$this->assertSame(realpath($this->file), (string) $this->resource);
}
public function testResourceDoesNotExist()
{
$this->expectException('InvalidArgumentException');
$this->expectExceptionMessageMatches('/The file ".*" does not exist./');
new FileResource('/____foo/foobar'.mt_rand(1, 999999));
}
public function testIsFresh()
{
$this->assertTrue($this->resource->isFresh($this->time), '->isFresh() returns true if the resource has not changed in same second');
$this->assertTrue($this->resource->isFresh($this->time + 10), '->isFresh() returns true if the resource has not changed');
$this->assertFalse($this->resource->isFresh($this->time - 86400), '->isFresh() returns false if the resource has been updated');
}
public function testIsFreshForDeletedResources()
{
unlink($this->file);
$this->assertFalse($this->resource->isFresh($this->time), '->isFresh() returns false if the resource does not exist');
}
public function testSerializeUnserialize()
{
unserialize(serialize($this->resource));
$this->assertSame(realpath($this->file), $this->resource->getResource());
}
}

View File

@@ -0,0 +1,114 @@
<?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\Config\Tests\Resource;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\Resource\GlobResource;
class GlobResourceTest extends TestCase
{
protected function tearDown()
{
$dir = \dirname(__DIR__).'/Fixtures';
@rmdir($dir.'/TmpGlob');
@unlink($dir.'/TmpGlob');
@unlink($dir.'/Resource/TmpGlob');
touch($dir.'/Resource/.hiddenFile');
}
public function testIterator()
{
$dir = \dirname(__DIR__).\DIRECTORY_SEPARATOR.'Fixtures';
$resource = new GlobResource($dir, '/Resource', true);
$paths = iterator_to_array($resource);
$file = $dir.'/Resource'.\DIRECTORY_SEPARATOR.'ConditionalClass.php';
$this->assertEquals([$file => new \SplFileInfo($file)], $paths);
$this->assertInstanceOf('SplFileInfo', current($paths));
$this->assertSame($dir, $resource->getPrefix());
$resource = new GlobResource($dir, '/**/Resource', true);
$paths = iterator_to_array($resource);
$file = $dir.\DIRECTORY_SEPARATOR.'Resource'.\DIRECTORY_SEPARATOR.'ConditionalClass.php';
$this->assertEquals([$file => $file], $paths);
$this->assertInstanceOf('SplFileInfo', current($paths));
$this->assertSame($dir, $resource->getPrefix());
}
public function testIsFreshNonRecursiveDetectsNewFile()
{
$dir = \dirname(__DIR__).'/Fixtures';
$resource = new GlobResource($dir, '/*', false);
$this->assertTrue($resource->isFresh(0));
mkdir($dir.'/TmpGlob');
$this->assertTrue($resource->isFresh(0));
rmdir($dir.'/TmpGlob');
$this->assertTrue($resource->isFresh(0));
touch($dir.'/TmpGlob');
$this->assertFalse($resource->isFresh(0));
unlink($dir.'/TmpGlob');
$this->assertTrue($resource->isFresh(0));
}
public function testIsFreshNonRecursiveDetectsRemovedFile()
{
$dir = \dirname(__DIR__).'/Fixtures';
$resource = new GlobResource($dir, '/*', false);
touch($dir.'/TmpGlob');
touch($dir.'/.TmpGlob');
$this->assertTrue($resource->isFresh(0));
unlink($dir.'/.TmpGlob');
$this->assertTrue($resource->isFresh(0));
unlink($dir.'/TmpGlob');
$this->assertFalse($resource->isFresh(0));
}
public function testIsFreshRecursiveDetectsRemovedFile()
{
$dir = \dirname(__DIR__).'/Fixtures';
$resource = new GlobResource($dir, '/*', true);
touch($dir.'/Resource/TmpGlob');
$this->assertTrue($resource->isFresh(0));
unlink($dir.'/Resource/TmpGlob');
$this->assertFalse($resource->isFresh(0));
touch($dir.'/Resource/TmpGlob');
$this->assertTrue($resource->isFresh(0));
unlink($dir.'/Resource/.hiddenFile');
$this->assertTrue($resource->isFresh(0));
}
public function testIsFreshRecursiveDetectsNewFile()
{
$dir = \dirname(__DIR__).'/Fixtures';
$resource = new GlobResource($dir, '/*', true);
$this->assertTrue($resource->isFresh(0));
touch($dir.'/Resource/TmpGlob');
$this->assertFalse($resource->isFresh(0));
}
}

View File

@@ -0,0 +1,223 @@
<?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\Config\Tests\Resource;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\Resource\ReflectionClassResource;
use Symfony\Component\DependencyInjection\ServiceSubscriberInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class ReflectionClassResourceTest extends TestCase
{
public function testToString()
{
$res = new ReflectionClassResource(new \ReflectionClass('ErrorException'));
$this->assertSame('reflection.ErrorException', (string) $res);
}
public function testSerializeUnserialize()
{
$res = new ReflectionClassResource(new \ReflectionClass(DummyInterface::class));
$ser = unserialize(serialize($res));
$this->assertTrue($res->isFresh(0));
$this->assertTrue($ser->isFresh(0));
$this->assertSame((string) $res, (string) $ser);
}
public function testIsFresh()
{
$res = new ReflectionClassResource(new \ReflectionClass(__CLASS__));
$mtime = filemtime(__FILE__);
$this->assertTrue($res->isFresh($mtime), '->isFresh() returns true if the resource has not changed in same second');
$this->assertTrue($res->isFresh($mtime + 10), '->isFresh() returns true if the resource has not changed');
$this->assertTrue($res->isFresh($mtime - 86400), '->isFresh() returns true if the resource has not changed');
}
public function testIsFreshForDeletedResources()
{
$now = time();
$tmp = sys_get_temp_dir().'/tmp.php';
file_put_contents($tmp, '<?php class ReflectionClassResourceTestClass {}');
require $tmp;
$res = new ReflectionClassResource(new \ReflectionClass('ReflectionClassResourceTestClass'));
$this->assertTrue($res->isFresh($now));
unlink($tmp);
$this->assertFalse($res->isFresh($now), '->isFresh() returns false if the resource does not exist');
}
/**
* @dataProvider provideHashedSignature
*/
public function testHashedSignature($changeExpected, $changedLine, $changedCode, $setContext = null)
{
if ($setContext) {
$setContext();
}
$code = <<<'EOPHP'
/* 0*/
/* 1*/ class %s extends ErrorException
/* 2*/ {
/* 3*/ const FOO = 123;
/* 4*/
/* 5*/ public $pub = [];
/* 6*/
/* 7*/ protected $prot;
/* 8*/
/* 9*/ private $priv;
/*10*/
/*11*/ public function pub($arg = null) {}
/*12*/
/*13*/ protected function prot($a = []) {}
/*14*/
/*15*/ private function priv() {}
/*16*/
/*17*/ public function ccc($bar = A_CONSTANT_THAT_FOR_SURE_WILL_NEVER_BE_DEFINED_CCCCCC) {}
/*18*/ }
EOPHP;
static $expectedSignature, $generateSignature;
if (null === $expectedSignature) {
eval(sprintf($code, $class = 'Foo'.str_replace('.', '_', uniqid('', true))));
$r = new \ReflectionClass(ReflectionClassResource::class);
$generateSignature = $r->getMethod('generateSignature');
$generateSignature->setAccessible(true);
$generateSignature = $generateSignature->getClosure($r->newInstanceWithoutConstructor());
$expectedSignature = implode("\n", iterator_to_array($generateSignature(new \ReflectionClass($class))));
}
$code = explode("\n", $code);
if (null !== $changedCode) {
$code[$changedLine] = $changedCode;
}
eval(sprintf(implode("\n", $code), $class = 'Foo'.str_replace('.', '_', uniqid('', true))));
$signature = implode("\n", iterator_to_array($generateSignature(new \ReflectionClass($class))));
if ($changeExpected) {
$this->assertNotSame($expectedSignature, $signature);
} else {
$this->assertSame($expectedSignature, $signature);
}
}
public function provideHashedSignature()
{
yield [0, 0, "// line change\n\n"];
yield [1, 0, '/** class docblock */'];
yield [1, 1, 'abstract class %s'];
yield [1, 1, 'final class %s'];
yield [1, 1, 'class %s extends Exception'];
yield [1, 1, 'class %s implements '.DummyInterface::class];
yield [1, 3, 'const FOO = 456;'];
yield [1, 3, 'const BAR = 123;'];
yield [1, 4, '/** pub docblock */'];
yield [1, 5, 'protected $pub = [];'];
yield [1, 5, 'public $pub = [123];'];
yield [1, 6, '/** prot docblock */'];
yield [1, 7, 'private $prot;'];
yield [0, 8, '/** priv docblock */'];
yield [0, 9, 'private $priv = 123;'];
yield [1, 10, '/** pub docblock */'];
if (\PHP_VERSION_ID >= 50600) {
yield [1, 11, 'public function pub(...$arg) {}'];
}
if (\PHP_VERSION_ID >= 70000) {
yield [1, 11, 'public function pub($arg = null): Foo {}'];
}
yield [0, 11, "public function pub(\$arg = null) {\nreturn 123;\n}"];
yield [1, 12, '/** prot docblock */'];
yield [1, 13, 'protected function prot($a = [123]) {}'];
yield [0, 14, '/** priv docblock */'];
yield [0, 15, ''];
if (\PHP_VERSION_ID >= 70400) {
// PHP7.4 typed properties without default value are
// undefined, make sure this doesn't throw an error
yield [1, 5, 'public array $pub;'];
yield [0, 7, 'protected int $prot;'];
yield [0, 9, 'private string $priv;'];
}
yield [1, 17, 'public function ccc($bar = 187) {}'];
yield [1, 17, 'public function ccc($bar = ANOTHER_ONE_THAT_WILL_NEVER_BE_DEFINED_CCCCCCCCC) {}'];
yield [1, 17, null, static function () { \define('A_CONSTANT_THAT_FOR_SURE_WILL_NEVER_BE_DEFINED_CCCCCC', 'foo'); }];
}
public function testEventSubscriber()
{
$res = new ReflectionClassResource(new \ReflectionClass(TestEventSubscriber::class));
$this->assertTrue($res->isFresh(0));
TestEventSubscriber::$subscribedEvents = [123];
$this->assertFalse($res->isFresh(0));
$res = new ReflectionClassResource(new \ReflectionClass(TestEventSubscriber::class));
$this->assertTrue($res->isFresh(0));
}
public function testServiceSubscriber()
{
$res = new ReflectionClassResource(new \ReflectionClass(TestServiceSubscriber::class));
$this->assertTrue($res->isFresh(0));
TestServiceSubscriber::$subscribedServices = [123];
$this->assertFalse($res->isFresh(0));
$res = new ReflectionClassResource(new \ReflectionClass(TestServiceSubscriber::class));
$this->assertTrue($res->isFresh(0));
}
public function testIgnoresObjectsInSignature()
{
$res = new ReflectionClassResource(new \ReflectionClass(TestServiceWithStaticProperty::class));
$this->assertTrue($res->isFresh(0));
TestServiceWithStaticProperty::$initializedObject = new TestServiceWithStaticProperty();
$this->assertTrue($res->isFresh(0));
}
}
interface DummyInterface
{
}
class TestEventSubscriber implements EventSubscriberInterface
{
public static $subscribedEvents = [];
public static function getSubscribedEvents()
{
return self::$subscribedEvents;
}
}
class TestServiceSubscriber implements ServiceSubscriberInterface
{
public static $subscribedServices = [];
public static function getSubscribedServices()
{
return self::$subscribedServices;
}
}
class TestServiceWithStaticProperty
{
public static $initializedObject;
}

View File

@@ -0,0 +1,34 @@
<?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\Config\Tests\Resource;
use Symfony\Component\Config\Resource\SelfCheckingResourceInterface;
class ResourceStub implements SelfCheckingResourceInterface
{
private $fresh = true;
public function setFresh($isFresh)
{
$this->fresh = $isFresh;
}
public function __toString()
{
return 'stub';
}
public function isFresh($timestamp)
{
return $this->fresh;
}
}

View File

@@ -0,0 +1,150 @@
<?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\Config\Tests;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\Resource\FileResource;
use Symfony\Component\Config\ResourceCheckerConfigCache;
use Symfony\Component\Config\Tests\Resource\ResourceStub;
class ResourceCheckerConfigCacheTest extends TestCase
{
private $cacheFile = null;
protected function setUp()
{
$this->cacheFile = tempnam(sys_get_temp_dir(), 'config_');
}
protected function tearDown()
{
$files = [$this->cacheFile, "{$this->cacheFile}.meta"];
foreach ($files as $file) {
if (file_exists($file)) {
@unlink($file);
}
}
}
public function testGetPath()
{
$cache = new ResourceCheckerConfigCache($this->cacheFile);
$this->assertSame($this->cacheFile, $cache->getPath());
}
public function testCacheIsNotFreshIfEmpty()
{
$checker = $this->getMockBuilder('\Symfony\Component\Config\ResourceCheckerInterface')->getMock()
->expects($this->never())->method('supports');
/* If there is nothing in the cache, it needs to be filled (and thus it's not fresh).
It does not matter if you provide checkers or not. */
unlink($this->cacheFile); // remove tempnam() side effect
$cache = new ResourceCheckerConfigCache($this->cacheFile, [$checker]);
$this->assertFalse($cache->isFresh());
}
public function testCacheIsFreshIfNoCheckerProvided()
{
/* For example in prod mode, you may choose not to run any checkers
at all. In that case, the cache should always be considered fresh. */
$cache = new ResourceCheckerConfigCache($this->cacheFile);
$this->assertTrue($cache->isFresh());
}
public function testCacheIsFreshIfEmptyCheckerIteratorProvided()
{
$cache = new ResourceCheckerConfigCache($this->cacheFile, new \ArrayIterator([]));
$this->assertTrue($cache->isFresh());
}
public function testResourcesWithoutcheckersAreIgnoredAndConsideredFresh()
{
/* As in the previous test, but this time we have a resource. */
$cache = new ResourceCheckerConfigCache($this->cacheFile);
$cache->write('', [new ResourceStub()]);
$this->assertTrue($cache->isFresh()); // no (matching) ResourceChecker passed
}
public function testIsFreshWithchecker()
{
$checker = $this->getMockBuilder('\Symfony\Component\Config\ResourceCheckerInterface')->getMock();
$checker->expects($this->once())
->method('supports')
->willReturn(true);
$checker->expects($this->once())
->method('isFresh')
->willReturn(true);
$cache = new ResourceCheckerConfigCache($this->cacheFile, [$checker]);
$cache->write('', [new ResourceStub()]);
$this->assertTrue($cache->isFresh());
}
public function testIsNotFreshWithchecker()
{
$checker = $this->getMockBuilder('\Symfony\Component\Config\ResourceCheckerInterface')->getMock();
$checker->expects($this->once())
->method('supports')
->willReturn(true);
$checker->expects($this->once())
->method('isFresh')
->willReturn(false);
$cache = new ResourceCheckerConfigCache($this->cacheFile, [$checker]);
$cache->write('', [new ResourceStub()]);
$this->assertFalse($cache->isFresh());
}
public function testCacheIsNotFreshWhenUnserializeFails()
{
$checker = $this->getMockBuilder('\Symfony\Component\Config\ResourceCheckerInterface')->getMock();
$cache = new ResourceCheckerConfigCache($this->cacheFile, [$checker]);
$cache->write('foo', [new FileResource(__FILE__)]);
$metaFile = "{$this->cacheFile}.meta";
file_put_contents($metaFile, str_replace('FileResource', 'ClassNotHere', file_get_contents($metaFile)));
$this->assertFalse($cache->isFresh());
}
public function testCacheKeepsContent()
{
$cache = new ResourceCheckerConfigCache($this->cacheFile);
$cache->write('FOOBAR');
$this->assertSame('FOOBAR', file_get_contents($cache->getPath()));
}
public function testCacheIsNotFreshIfNotExistsMetaFile()
{
$checker = $this->getMockBuilder('\Symfony\Component\Config\ResourceCheckerInterface')->getMock();
$cache = new ResourceCheckerConfigCache($this->cacheFile, [$checker]);
$cache->write('foo', [new FileResource(__FILE__)]);
$metaFile = "{$this->cacheFile}.meta";
unlink($metaFile);
$this->assertFalse($cache->isFresh());
}
}

View File

@@ -0,0 +1,240 @@
<?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\Config\Tests\Util;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\Util\XmlUtils;
class XmlUtilsTest extends TestCase
{
public function testLoadFile()
{
$fixtures = __DIR__.'/../Fixtures/Util/';
try {
XmlUtils::loadFile($fixtures);
$this->fail();
} catch (\InvalidArgumentException $e) {
$this->assertStringContainsString('is not a file', $e->getMessage());
}
try {
XmlUtils::loadFile($fixtures.'non_existing.xml');
$this->fail();
} catch (\InvalidArgumentException $e) {
$this->assertStringContainsString('is not a file', $e->getMessage());
}
try {
if ('\\' === \DIRECTORY_SEPARATOR) {
$this->markTestSkipped('chmod is not supported on Windows');
}
chmod($fixtures.'not_readable.xml', 000);
XmlUtils::loadFile($fixtures.'not_readable.xml');
$this->fail();
} catch (\InvalidArgumentException $e) {
chmod($fixtures.'not_readable.xml', 0644);
$this->assertStringContainsString('is not readable', $e->getMessage());
}
try {
XmlUtils::loadFile($fixtures.'invalid.xml');
$this->fail();
} catch (\InvalidArgumentException $e) {
$this->assertStringContainsString('ERROR ', $e->getMessage());
}
try {
XmlUtils::loadFile($fixtures.'document_type.xml');
$this->fail();
} catch (\InvalidArgumentException $e) {
$this->assertStringContainsString('Document types are not allowed', $e->getMessage());
}
try {
XmlUtils::loadFile($fixtures.'invalid_schema.xml', $fixtures.'schema.xsd');
$this->fail();
} catch (\InvalidArgumentException $e) {
$this->assertStringContainsString('ERROR 1845', $e->getMessage());
}
try {
XmlUtils::loadFile($fixtures.'invalid_schema.xml', 'invalid_callback_or_file');
$this->fail();
} catch (\InvalidArgumentException $e) {
$this->assertStringContainsString('XSD file or callable', $e->getMessage());
}
$mock = $this->getMockBuilder(Validator::class)->getMock();
$mock->expects($this->exactly(2))->method('validate')->will($this->onConsecutiveCalls(false, true));
try {
XmlUtils::loadFile($fixtures.'valid.xml', [$mock, 'validate']);
$this->fail();
} catch (\InvalidArgumentException $e) {
$this->assertMatchesRegularExpression('/The XML file ".+" is not valid\./', $e->getMessage());
}
$this->assertInstanceOf('DOMDocument', XmlUtils::loadFile($fixtures.'valid.xml', [$mock, 'validate']));
$this->assertSame([], libxml_get_errors());
}
public function testParseWithInvalidValidatorCallable()
{
$this->expectException('Symfony\Component\Config\Util\Exception\InvalidXmlException');
$this->expectExceptionMessage('The XML is not valid');
$fixtures = __DIR__.'/../Fixtures/Util/';
$mock = $this->getMockBuilder(Validator::class)->getMock();
$mock->expects($this->once())->method('validate')->willReturn(false);
XmlUtils::parse(file_get_contents($fixtures.'valid.xml'), [$mock, 'validate']);
}
public function testLoadFileWithInternalErrorsEnabled()
{
$internalErrors = libxml_use_internal_errors(true);
$this->assertSame([], libxml_get_errors());
$this->assertInstanceOf('DOMDocument', XmlUtils::loadFile(__DIR__.'/../Fixtures/Util/invalid_schema.xml'));
$this->assertSame([], libxml_get_errors());
libxml_clear_errors();
libxml_use_internal_errors($internalErrors);
}
/**
* @dataProvider getDataForConvertDomToArray
*/
public function testConvertDomToArray($expected, $xml, $root = false, $checkPrefix = true)
{
$dom = new \DOMDocument();
$dom->loadXML($root ? $xml : '<root>'.$xml.'</root>');
$this->assertSame($expected, XmlUtils::convertDomElementToArray($dom->documentElement, $checkPrefix));
}
public function getDataForConvertDomToArray()
{
return [
[null, ''],
['bar', 'bar'],
[['bar' => 'foobar'], '<foo bar="foobar" />', true],
[['foo' => null], '<foo />'],
[['foo' => 'bar'], '<foo>bar</foo>'],
[['foo' => ['foo' => 'bar']], '<foo foo="bar"/>'],
[['foo' => ['foo' => 0]], '<foo><foo>0</foo></foo>'],
[['foo' => ['foo' => 'bar']], '<foo><foo>bar</foo></foo>'],
[['foo' => ['foo' => 'bar', 'value' => 'text']], '<foo foo="bar">text</foo>'],
[['foo' => ['attr' => 'bar', 'foo' => 'text']], '<foo attr="bar"><foo>text</foo></foo>'],
[['foo' => ['bar', 'text']], '<foo>bar</foo><foo>text</foo>'],
[['foo' => [['foo' => 'bar'], ['foo' => 'text']]], '<foo foo="bar"/><foo foo="text" />'],
[['foo' => ['foo' => ['bar', 'text']]], '<foo foo="bar"><foo>text</foo></foo>'],
[['foo' => 'bar'], '<foo><!-- Comment -->bar</foo>'],
[['foo' => 'text'], '<foo xmlns:h="http://www.example.org/bar" h:bar="bar">text</foo>'],
[['foo' => ['bar' => 'bar', 'value' => 'text']], '<foo xmlns:h="http://www.example.org/bar" h:bar="bar">text</foo>', false, false],
[['attr' => 1, 'b' => 'hello'], '<foo:a xmlns:foo="http://www.example.org/foo" xmlns:h="http://www.example.org/bar" attr="1" h:bar="bar"><foo:b>hello</foo:b><h:c>2</h:c></foo:a>', true],
];
}
/**
* @dataProvider getDataForPhpize
*/
public function testPhpize($expected, $value)
{
$this->assertSame($expected, XmlUtils::phpize($value));
}
public function getDataForPhpize()
{
return [
['', ''],
[null, 'null'],
[true, 'true'],
[false, 'false'],
[null, 'Null'],
[true, 'True'],
[false, 'False'],
[0, '0'],
[1, '1'],
[-1, '-1'],
[0777, '0777'],
[255, '0xFF'],
[100.0, '1e2'],
[-120.0, '-1.2E2'],
[-10100.1, '-10100.1'],
['-10,100.1', '-10,100.1'],
['1234 5678 9101 1121 3141', '1234 5678 9101 1121 3141'],
['1,2,3,4', '1,2,3,4'],
['11,22,33,44', '11,22,33,44'],
['11,222,333,4', '11,222,333,4'],
['1,222,333,444', '1,222,333,444'],
['11,222,333,444', '11,222,333,444'],
['111,222,333,444', '111,222,333,444'],
['1111,2222,3333,4444,5555', '1111,2222,3333,4444,5555'],
['foo', 'foo'],
[6, '0b0110'],
];
}
public function testLoadEmptyXmlFile()
{
$file = __DIR__.'/../Fixtures/foo.xml';
$this->expectException('InvalidArgumentException');
$this->expectExceptionMessage(sprintf('File "%s" does not contain valid XML, it is empty.', $file));
XmlUtils::loadFile($file);
}
// test for issue https://github.com/symfony/symfony/issues/9731
public function testLoadWrongEmptyXMLWithErrorHandler()
{
if (\LIBXML_VERSION < 20900) {
$originalDisableEntities = libxml_disable_entity_loader(false);
}
$errorReporting = error_reporting(-1);
set_error_handler(function ($errno, $errstr) {
throw new \Exception($errstr, $errno);
});
$file = __DIR__.'/../Fixtures/foo.xml';
try {
try {
XmlUtils::loadFile($file);
$this->fail('An exception should have been raised');
} catch (\InvalidArgumentException $e) {
$this->assertEquals(sprintf('File "%s" does not contain valid XML, it is empty.', $file), $e->getMessage());
}
} finally {
restore_error_handler();
error_reporting($errorReporting);
}
if (\LIBXML_VERSION < 20900) {
$disableEntities = libxml_disable_entity_loader(true);
libxml_disable_entity_loader($disableEntities);
libxml_disable_entity_loader($originalDisableEntities);
$this->assertFalse($disableEntities);
}
// should not throw an exception
XmlUtils::loadFile(__DIR__.'/../Fixtures/Util/valid.xml', __DIR__.'/../Fixtures/Util/schema.xsd');
}
}
interface Validator
{
public function validate();
}