Augmentation vers version 3.3.0

This commit is contained in:
Gauvain Boiché
2020-03-31 15:31:03 +02:00
parent d926806907
commit a1864c0414
2618 changed files with 406015 additions and 31377 deletions

View File

@@ -11,7 +11,6 @@
namespace Symfony\Component\Config;
use Symfony\Component\Config\Resource\BCResourceInterfaceChecker;
use Symfony\Component\Config\Resource\SelfCheckingResourceChecker;
/**
@@ -21,11 +20,6 @@ use Symfony\Component\Config\Resource\SelfCheckingResourceChecker;
* \Symfony\Component\Config\Resource\SelfCheckingResourceInterface will
* be used to check cache freshness.
*
* During a transition period, also instances of
* \Symfony\Component\Config\Resource\ResourceInterface will be checked
* by means of the isFresh() method. This behaviour is deprecated since 2.8
* and will be removed in 3.0.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Matthias Pigulla <mp@webfactory.de>
*/
@@ -39,25 +33,14 @@ class ConfigCache extends ResourceCheckerConfigCache
*/
public function __construct($file, $debug)
{
parent::__construct($file, array(
new SelfCheckingResourceChecker(),
new BCResourceInterfaceChecker(),
));
$this->debug = (bool) $debug;
}
/**
* Gets the cache file path.
*
* @return string The cache file path
*
* @deprecated since 2.7, to be removed in 3.0. Use getPath() instead.
*/
public function __toString()
{
@trigger_error('ConfigCache::__toString() is deprecated since Symfony 2.7 and will be removed in 3.0. Use the getPath() method instead.', E_USER_DEPRECATED);
$checkers = [];
if (true === $this->debug) {
$checkers = [new SelfCheckingResourceChecker()];
}
return $this->getPath();
parent::__construct($file, $checkers);
}
/**

View File

@@ -22,8 +22,8 @@ use Symfony\Component\Config\Definition\Exception\UnsetKeyException;
*/
class ArrayNode extends BaseNode implements PrototypeNodeInterface
{
protected $xmlRemappings = array();
protected $children = array();
protected $xmlRemappings = [];
protected $children = [];
protected $allowFalse = false;
protected $allowNewKeys = true;
protected $addIfNotSet = false;
@@ -38,17 +38,13 @@ class ArrayNode extends BaseNode implements PrototypeNodeInterface
}
/**
* Normalizes keys between the different configuration formats.
* {@inheritdoc}
*
* Namely, you mostly have foo_bar in YAML while you have foo-bar in XML.
* After running this method, all keys are normalized to foo_bar.
*
* If you have a mixed key like foo-bar_moo, it will not be altered.
* The key will also not be altered if the target key already exists.
*
* @param mixed $value
*
* @return array The value with normalized keys
*/
protected function preNormalize($value)
{
@@ -56,10 +52,10 @@ class ArrayNode extends BaseNode implements PrototypeNodeInterface
return $value;
}
$normalized = array();
$normalized = [];
foreach ($value as $k => $v) {
if (false !== strpos($k, '-') && false === strpos($k, '_') && !array_key_exists($normalizedKey = str_replace('-', '_', $k), $value)) {
if (false !== strpos($k, '-') && false === strpos($k, '_') && !\array_key_exists($normalizedKey = str_replace('-', '_', $k), $value)) {
$normalized[$normalizedKey] = $v;
} else {
$normalized[$k] = $v;
@@ -82,7 +78,7 @@ class ArrayNode extends BaseNode implements PrototypeNodeInterface
/**
* Sets the xml remappings that should be performed.
*
* @param array $remappings An array of the form array(array(string, string))
* @param array $remappings An array of the form [[string, string]]
*/
public function setXmlRemappings(array $remappings)
{
@@ -92,7 +88,7 @@ class ArrayNode extends BaseNode implements PrototypeNodeInterface
/**
* Gets the xml remappings that should be performed.
*
* @return array an array of the form array(array(string, string))
* @return array an array of the form [[string, string]]
*/
public function getXmlRemappings()
{
@@ -141,7 +137,7 @@ class ArrayNode extends BaseNode implements PrototypeNodeInterface
}
/**
* Whether extra keys should just be ignore without an exception.
* Whether extra keys should just be ignored without an exception.
*
* @param bool $boolean To allow extra keys
* @param bool $remove To remove extra keys
@@ -177,7 +173,7 @@ class ArrayNode extends BaseNode implements PrototypeNodeInterface
throw new \RuntimeException(sprintf('The node at path "%s" has no default value.', $this->getPath()));
}
$defaults = array();
$defaults = [];
foreach ($this->children as $name => $child) {
if ($child->hasDefaultValue()) {
$defaults[$name] = $child->getDefaultValue();
@@ -223,7 +219,7 @@ class ArrayNode extends BaseNode implements PrototypeNodeInterface
}
foreach ($this->children as $name => $child) {
if (!array_key_exists($name, $value)) {
if (!\array_key_exists($name, $value)) {
if ($child->isRequired()) {
$ex = new InvalidConfigurationException(sprintf('The child node "%s" at path "%s" must be configured.', $name, $this->getPath()));
$ex->setPath($this->getPath());
@@ -238,6 +234,10 @@ class ArrayNode extends BaseNode implements PrototypeNodeInterface
continue;
}
if ($child->isDeprecated()) {
@trigger_error($child->getDeprecationMessage($name, $this->getPath()), E_USER_DEPRECATED);
}
try {
$value[$name] = $child->finalize($value[$name]);
} catch (UnsetKeyException $e) {
@@ -285,7 +285,7 @@ class ArrayNode extends BaseNode implements PrototypeNodeInterface
$value = $this->remapXml($value);
$normalized = array();
$normalized = [];
foreach ($value as $name => $val) {
if (isset($this->children[$name])) {
try {
@@ -318,9 +318,7 @@ class ArrayNode extends BaseNode implements PrototypeNodeInterface
*/
protected function remapXml($value)
{
foreach ($this->xmlRemappings as $transformation) {
list($singular, $plural) = $transformation;
foreach ($this->xmlRemappings as list($singular, $plural)) {
if (!isset($value[$singular])) {
continue;
}
@@ -357,7 +355,7 @@ class ArrayNode extends BaseNode implements PrototypeNodeInterface
foreach ($rightSide as $k => $v) {
// no conflict
if (!array_key_exists($k, $leftSide)) {
if (!\array_key_exists($k, $leftSide)) {
if (!$this->allowNewKeys) {
$ex = new InvalidConfigurationException(sprintf('You are not allowed to define new elements for path "%s". Please define all elements for this path in one config file. If you are trying to overwrite an element, make sure you redefine it with the same name.', $this->getPath()));
$ex->setPath($this->getPath());

View File

@@ -25,12 +25,13 @@ abstract class BaseNode implements NodeInterface
{
protected $name;
protected $parent;
protected $normalizationClosures = array();
protected $finalValidationClosures = array();
protected $normalizationClosures = [];
protected $finalValidationClosures = [];
protected $allowOverwrite = true;
protected $required = false;
protected $equivalentValues = array();
protected $attributes = array();
protected $deprecationMessage = null;
protected $equivalentValues = [];
protected $attributes = [];
/**
* @param string|null $name The name of the node
@@ -48,21 +49,37 @@ abstract class BaseNode implements NodeInterface
$this->parent = $parent;
}
/**
* @param string $key
*/
public function setAttribute($key, $value)
{
$this->attributes[$key] = $value;
}
/**
* @param string $key
*
* @return mixed
*/
public function getAttribute($key, $default = null)
{
return isset($this->attributes[$key]) ? $this->attributes[$key] : $default;
}
/**
* @param string $key
*
* @return bool
*/
public function hasAttribute($key)
{
return isset($this->attributes[$key]);
}
/**
* @return array
*/
public function getAttributes()
{
return $this->attributes;
@@ -73,6 +90,9 @@ abstract class BaseNode implements NodeInterface
$this->attributes = $attributes;
}
/**
* @param string $key
*/
public function removeAttribute($key)
{
unset($this->attributes[$key]);
@@ -91,7 +111,7 @@ abstract class BaseNode implements NodeInterface
/**
* Returns info message.
*
* @return string The info text
* @return string|null The info text
*/
public function getInfo()
{
@@ -111,7 +131,7 @@ abstract class BaseNode implements NodeInterface
/**
* Retrieves the example configuration for this node.
*
* @return string|array The example
* @return string|array|null The example
*/
public function getExample()
{
@@ -126,7 +146,7 @@ abstract class BaseNode implements NodeInterface
*/
public function addEquivalentValue($originalValue, $equivalentValue)
{
$this->equivalentValues[] = array($originalValue, $equivalentValue);
$this->equivalentValues[] = [$originalValue, $equivalentValue];
}
/**
@@ -139,6 +159,19 @@ abstract class BaseNode implements NodeInterface
$this->required = (bool) $boolean;
}
/**
* Sets this node as deprecated.
*
* You can use %node% and %path% placeholders in your message to display,
* respectively, the node name and its complete path.
*
* @param string|null $message Deprecated message
*/
public function setDeprecated($message)
{
$this->deprecationMessage = $message;
}
/**
* Sets if this node can be overridden.
*
@@ -177,6 +210,29 @@ abstract class BaseNode implements NodeInterface
return $this->required;
}
/**
* Checks if this node is deprecated.
*
* @return bool
*/
public function isDeprecated()
{
return null !== $this->deprecationMessage;
}
/**
* Returns the deprecated message.
*
* @param string $node the configuration node name
* @param string $path the path of the node
*
* @return string
*/
public function getDeprecationMessage($node, $path)
{
return strtr($this->deprecationMessage, ['%node%' => $node, '%path%' => $path]);
}
/**
* {@inheritdoc}
*/
@@ -243,9 +299,9 @@ abstract class BaseNode implements NodeInterface
/**
* Normalizes the value before any other normalization is applied.
*
* @param $value
* @param mixed $value
*
* @return The normalized array value
* @return mixed The normalized array value
*/
protected function preNormalize($value)
{

View File

@@ -25,7 +25,7 @@ class ArrayNodeDefinition extends NodeDefinition implements ParentNodeDefinition
protected $performDeepMerging = true;
protected $ignoreExtraKeys = false;
protected $removeExtraKeys = true;
protected $children = array();
protected $children = [];
protected $prototype;
protected $atLeastOne = false;
protected $allowNewKeys = true;
@@ -43,8 +43,8 @@ class ArrayNodeDefinition extends NodeDefinition implements ParentNodeDefinition
{
parent::__construct($name, $parent);
$this->nullEquivalent = array();
$this->trueEquivalent = array();
$this->nullEquivalent = [];
$this->trueEquivalent = [];
}
/**
@@ -75,6 +75,62 @@ class ArrayNodeDefinition extends NodeDefinition implements ParentNodeDefinition
return $this->prototype = $this->getNodeBuilder()->node(null, $type)->setParent($this);
}
/**
* @return VariableNodeDefinition
*/
public function variablePrototype()
{
return $this->prototype('variable');
}
/**
* @return ScalarNodeDefinition
*/
public function scalarPrototype()
{
return $this->prototype('scalar');
}
/**
* @return BooleanNodeDefinition
*/
public function booleanPrototype()
{
return $this->prototype('boolean');
}
/**
* @return IntegerNodeDefinition
*/
public function integerPrototype()
{
return $this->prototype('integer');
}
/**
* @return FloatNodeDefinition
*/
public function floatPrototype()
{
return $this->prototype('float');
}
/**
* @return ArrayNodeDefinition
*/
public function arrayPrototype()
{
return $this->prototype('array');
}
/**
* @return EnumNodeDefinition
*/
public function enumPrototype()
{
return $this->prototype('enum');
}
/**
* Adds the default value if the node is not set in the configuration.
*
@@ -158,15 +214,15 @@ class ArrayNodeDefinition extends NodeDefinition implements ParentNodeDefinition
* to be the key of the particular item. For example, if "id" is the
* "key", then:
*
* array(
* array('id' => 'my_name', 'foo' => 'bar'),
* );
* [
* ['id' => 'my_name', 'foo' => 'bar'],
* ];
*
* becomes
*
* array(
* 'my_name' => array('foo' => 'bar'),
* );
* [
* 'my_name' => ['foo' => 'bar'],
* ];
*
* If you'd like "'id' => 'my_name'" to still be present in the resulting
* array, then you can set the second argument of this method to false.
@@ -219,9 +275,9 @@ class ArrayNodeDefinition extends NodeDefinition implements ParentNodeDefinition
{
$this
->addDefaultsIfNotSet()
->treatFalseLike(array('enabled' => false))
->treatTrueLike(array('enabled' => true))
->treatNullLike(array('enabled' => true))
->treatFalseLike(['enabled' => false])
->treatTrueLike(['enabled' => true])
->treatNullLike(['enabled' => true])
->beforeNormalization()
->ifArray()
->then(function ($v) {
@@ -249,9 +305,9 @@ class ArrayNodeDefinition extends NodeDefinition implements ParentNodeDefinition
{
$this
->addDefaultsIfNotSet()
->treatFalseLike(array('enabled' => false))
->treatTrueLike(array('enabled' => true))
->treatNullLike(array('enabled' => true))
->treatFalseLike(['enabled' => false])
->treatTrueLike(['enabled' => true])
->treatNullLike(['enabled' => true])
->children()
->booleanNode('enabled')
->defaultTrue()
@@ -276,10 +332,10 @@ class ArrayNodeDefinition extends NodeDefinition implements ParentNodeDefinition
* Allows extra config keys to be specified under an array without
* throwing an exception.
*
* Those config values are simply ignored and removed from the
* resulting array. This should be used only in special cases where
* you want to send an entire configuration array through a special
* tree that processes only part of the array.
* Those config values are ignored and removed from the resulting
* array. This should be used only in special cases where you want
* to send an entire configuration array through a special tree that
* processes only part of the array.
*
* @param bool $remove Whether to remove the extra keys
*
@@ -356,6 +412,10 @@ class ArrayNodeDefinition extends NodeDefinition implements ParentNodeDefinition
$node->setKeyAttribute($this->key, $this->removeKeyItem);
}
if (false === $this->allowEmptyValue) {
@trigger_error(sprintf('Using %s::cannotBeEmpty() at path "%s" has no effect, consider requiresAtLeastOneElement() instead. In 4.0 both methods will behave the same.', __CLASS__, $node->getPath()), E_USER_DEPRECATED);
}
if (true === $this->atLeastOne) {
$node->setMinNumberOfElements(1);
}
@@ -381,6 +441,7 @@ class ArrayNodeDefinition extends NodeDefinition implements ParentNodeDefinition
$node->addEquivalentValue(false, $this->falseEquivalent);
$node->setPerformDeepMerging($this->performDeepMerging);
$node->setRequired($this->required);
$node->setDeprecated($this->deprecationMessage);
$node->setIgnoreExtraKeys($this->ignoreExtraKeys, $this->removeExtraKeys);
$node->setNormalizeKeys($this->normalizeKeys);
@@ -414,6 +475,10 @@ class ArrayNodeDefinition extends NodeDefinition implements ParentNodeDefinition
throw new InvalidDefinitionException(sprintf('->useAttributeAsKey() is not applicable to concrete nodes at path "%s"', $path));
}
if (false === $this->allowEmptyValue) {
@trigger_error(sprintf('->cannotBeEmpty() is not applicable to concrete nodes at path "%s". In 4.0 it will throw an exception.', $path), E_USER_DEPRECATED);
}
if (true === $this->atLeastOne) {
throw new InvalidDefinitionException(sprintf('->requiresAtLeastOneElement() is not applicable to concrete nodes at path "%s"', $path));
}

View File

@@ -12,6 +12,7 @@
namespace Symfony\Component\Config\Definition\Builder;
use Symfony\Component\Config\Definition\BooleanNode;
use Symfony\Component\Config\Definition\Exception\InvalidDefinitionException;
/**
* This class provides a fluent interface for defining a node.
@@ -30,18 +31,6 @@ class BooleanNodeDefinition extends ScalarNodeDefinition
$this->nullEquivalent = true;
}
/**
* {@inheritdoc}
*
* @deprecated Deprecated since version 2.8, to be removed in 3.0.
*/
public function cannotBeEmpty()
{
@trigger_error('The '.__METHOD__.' method is deprecated since Symfony 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
return parent::cannotBeEmpty();
}
/**
* Instantiate a Node.
*
@@ -51,4 +40,14 @@ class BooleanNodeDefinition extends ScalarNodeDefinition
{
return new BooleanNode($this->name, $this->parent);
}
/**
* {@inheritdoc}
*
* @throws InvalidDefinitionException
*/
public function cannotBeEmpty()
{
throw new InvalidDefinitionException('->cannotBeEmpty() is not applicable to BooleanNodeDefinition.');
}
}

View File

@@ -88,6 +88,18 @@ class ExprBuilder
return $this;
}
/**
* Tests if the value is empty.
*
* @return ExprBuilder
*/
public function ifEmpty()
{
$this->ifPart = function ($v) { return empty($v); };
return $this;
}
/**
* Tests if the value is an array.
*
@@ -124,6 +136,19 @@ class ExprBuilder
return $this;
}
/**
* Transforms variables of any type into an array.
*
* @return $this
*/
public function castToArray()
{
$this->ifPart = function ($v) { return !\is_array($v); };
$this->thenPart = function ($v) { return [$v]; };
return $this;
}
/**
* Sets the closure to run if the test pass.
*
@@ -143,7 +168,7 @@ class ExprBuilder
*/
public function thenEmptyArray()
{
$this->thenPart = function ($v) { return array(); };
$this->thenPart = function ($v) { return []; };
return $this;
}

View File

@@ -23,7 +23,7 @@ class NodeBuilder implements NodeParentInterface
public function __construct()
{
$this->nodeMapping = array(
$this->nodeMapping = [
'variable' => __NAMESPACE__.'\\VariableNodeDefinition',
'scalar' => __NAMESPACE__.'\\ScalarNodeDefinition',
'boolean' => __NAMESPACE__.'\\BooleanNodeDefinition',
@@ -31,7 +31,7 @@ class NodeBuilder implements NodeParentInterface
'float' => __NAMESPACE__.'\\FloatNodeDefinition',
'array' => __NAMESPACE__.'\\ArrayNodeDefinition',
'enum' => __NAMESPACE__.'\\EnumNodeDefinition',
);
];
}
/**

View File

@@ -27,13 +27,14 @@ abstract class NodeDefinition implements NodeParentInterface
protected $defaultValue;
protected $default = false;
protected $required = false;
protected $deprecationMessage = null;
protected $merge;
protected $allowEmptyValue = true;
protected $nullEquivalent;
protected $trueEquivalent = true;
protected $falseEquivalent = false;
protected $parent;
protected $attributes = array();
protected $attributes = [];
/**
* @param string|null $name The name of the node
@@ -160,6 +161,23 @@ abstract class NodeDefinition implements NodeParentInterface
return $this;
}
/**
* Sets the node as deprecated.
*
* You can use %node% and %path% placeholders in your message to display,
* respectively, the node name and its complete path.
*
* @param string $message Deprecation message
*
* @return $this
*/
public function setDeprecated($message = 'The child node "%node%" at path "%path%" is deprecated.')
{
$this->deprecationMessage = $message;
return $this;
}
/**
* Sets the equivalent value used when the node contains null.
*

View File

@@ -19,8 +19,8 @@ namespace Symfony\Component\Config\Definition\Builder;
class NormalizationBuilder
{
protected $node;
public $before = array();
public $remappings = array();
public $before = [];
public $remappings = [];
public function __construct(NodeDefinition $node)
{
@@ -37,7 +37,7 @@ class NormalizationBuilder
*/
public function remap($key, $plural = null)
{
$this->remappings[] = array($key, null === $plural ? $key.'s' : $plural);
$this->remappings[] = [$key, null === $plural ? $key.'s' : $plural];
return $this;
}

View File

@@ -11,6 +11,8 @@
namespace Symfony\Component\Config\Definition\Builder;
use Symfony\Component\Config\Definition\Exception\InvalidDefinitionException;
/**
* Abstract class that contains common code of integer and float node definitions.
*
@@ -62,12 +64,10 @@ abstract class NumericNodeDefinition extends ScalarNodeDefinition
/**
* {@inheritdoc}
*
* @deprecated Deprecated since version 2.8, to be removed in 3.0.
* @throws InvalidDefinitionException
*/
public function cannotBeEmpty()
{
@trigger_error('The '.__METHOD__.' method is deprecated since Symfony 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
return parent::cannotBeEmpty();
throw new InvalidDefinitionException('->cannotBeEmpty() is not applicable to NumericNodeDefinition.');
}
}

View File

@@ -22,6 +22,10 @@ class TreeBuilder implements NodeParentInterface
{
protected $tree;
protected $root;
/**
* @deprecated since 3.4. To be removed in 4.0
*/
protected $builder;
/**

View File

@@ -19,7 +19,7 @@ namespace Symfony\Component\Config\Definition\Builder;
class ValidationBuilder
{
protected $node;
public $rules = array();
public $rules = [];
public function __construct(NodeDefinition $node)
{

View File

@@ -54,6 +54,7 @@ class VariableNodeDefinition extends NodeDefinition
$node->addEquivalentValue(true, $this->trueEquivalent);
$node->addEquivalentValue(false, $this->falseEquivalent);
$node->setRequired($this->required);
$node->setDeprecated($this->deprecationMessage);
if (null !== $this->validation) {
$node->setFinalValidationClosures($this->validation->rules);

View File

@@ -42,10 +42,9 @@ class XmlReferenceDumper
}
/**
* @param NodeInterface $node
* @param int $depth
* @param bool $root If the node is the root node
* @param string $namespace The namespace of the node
* @param int $depth
* @param bool $root If the node is the root node
* @param string $namespace The namespace of the node
*/
private function writeNode(NodeInterface $node, $depth = 0, $root = false, $namespace = null)
{
@@ -65,10 +64,10 @@ class XmlReferenceDumper
}
$rootName = str_replace('_', '-', $rootName);
$rootAttributes = array();
$rootAttributeComments = array();
$rootChildren = array();
$rootComments = array();
$rootAttributes = [];
$rootAttributeComments = [];
$rootChildren = [];
$rootComments = [];
if ($node instanceof ArrayNode) {
$children = $node->getChildren();
@@ -96,7 +95,10 @@ class XmlReferenceDumper
$rootAttributes[$key] = str_replace('-', ' ', $rootName).' '.$key;
}
if ($prototype instanceof ArrayNode) {
if ($prototype instanceof PrototypedArrayNode) {
$prototype->setName($key);
$children = [$key => $prototype];
} elseif ($prototype instanceof ArrayNode) {
$children = $prototype->getChildren();
} else {
if ($prototype->hasDefaultValue()) {
@@ -137,7 +139,7 @@ class XmlReferenceDumper
$value = '%%%%not_defined%%%%'; // use a string which isn't used in the normal world
// comments
$comments = array();
$comments = [];
if ($info = $child->getInfo()) {
$comments[] = $info;
}
@@ -150,6 +152,10 @@ class XmlReferenceDumper
$comments[] = 'Required';
}
if ($child->isDeprecated()) {
$comments[] = sprintf('Deprecated (%s)', $child->getDeprecationMessage($child->getName(), $node->getPath()));
}
if ($child instanceof EnumNode) {
$comments[] = 'One of '.implode('; ', array_map('json_encode', $child->getValues()));
}
@@ -300,5 +306,7 @@ class XmlReferenceDumper
if (\is_array($value)) {
return implode(',', $value);
}
return '';
}
}

View File

@@ -16,6 +16,7 @@ use Symfony\Component\Config\Definition\ConfigurationInterface;
use Symfony\Component\Config\Definition\EnumNode;
use Symfony\Component\Config\Definition\NodeInterface;
use Symfony\Component\Config\Definition\PrototypedArrayNode;
use Symfony\Component\Config\Definition\ScalarNode;
use Symfony\Component\Yaml\Inline;
/**
@@ -32,6 +33,32 @@ class YamlReferenceDumper
return $this->dumpNode($configuration->getConfigTreeBuilder()->buildTree());
}
public function dumpAtPath(ConfigurationInterface $configuration, $path)
{
$rootNode = $node = $configuration->getConfigTreeBuilder()->buildTree();
foreach (explode('.', $path) as $step) {
if (!$node instanceof ArrayNode) {
throw new \UnexpectedValueException(sprintf('Unable to find node at path "%s.%s"', $rootNode->getName(), $path));
}
/** @var NodeInterface[] $children */
$children = $node instanceof PrototypedArrayNode ? $this->getPrototypeChildren($node) : $node->getChildren();
foreach ($children as $child) {
if ($child->getName() === $step) {
$node = $child;
continue 2;
}
}
throw new \UnexpectedValueException(sprintf('Unable to find node at path "%s.%s"', $rootNode->getName(), $path));
}
return $this->dumpNode($node);
}
public function dumpNode(NodeInterface $node)
{
$this->reference = '';
@@ -43,12 +70,12 @@ class YamlReferenceDumper
}
/**
* @param NodeInterface $node
* @param int $depth
* @param int $depth
* @param bool $prototypedArray
*/
private function writeNode(NodeInterface $node, $depth = 0)
private function writeNode(NodeInterface $node, NodeInterface $parentNode = null, $depth = 0, $prototypedArray = false)
{
$comments = array();
$comments = [];
$default = '';
$defaultArray = null;
$children = null;
@@ -59,29 +86,7 @@ class YamlReferenceDumper
$children = $node->getChildren();
if ($node instanceof PrototypedArrayNode) {
$prototype = $node->getPrototype();
if ($prototype instanceof ArrayNode) {
$children = $prototype->getChildren();
}
// check for attribute as key
if ($key = $node->getKeyAttribute()) {
$keyNodeClass = 'Symfony\Component\Config\Definition\\'.($prototype instanceof ArrayNode ? 'ArrayNode' : 'ScalarNode');
$keyNode = new $keyNodeClass($key, $node);
$info = 'Prototype';
if (null !== $prototype->getInfo()) {
$info .= ': '.$prototype->getInfo();
}
$keyNode->setInfo($info);
// add children
foreach ($children as $childNode) {
$keyNode->addChild($childNode);
}
$children = array($key => $keyNode);
}
$children = $this->getPrototypeChildren($node);
}
if (!$children) {
@@ -117,6 +122,11 @@ class YamlReferenceDumper
$comments[] = 'Required';
}
// deprecated?
if ($node->isDeprecated()) {
$comments[] = sprintf('Deprecated (%s)', $node->getDeprecationMessage($node->getName(), $parentNode ? $parentNode->getPath() : $node->getPath()));
}
// example
if ($example && !\is_array($example)) {
$comments[] = 'Example: '.$example;
@@ -125,7 +135,8 @@ class YamlReferenceDumper
$default = '' != (string) $default ? ' '.$default : '';
$comments = \count($comments) ? '# '.implode(', ', $comments) : '';
$text = rtrim(sprintf('%-21s%s %s', $node->getName().':', $default, $comments), ' ');
$key = $prototypedArray ? '-' : $node->getName().':';
$text = rtrim(sprintf('%-21s%s %s', $key, $default, $comments), ' ');
if ($info = $node->getInfo()) {
$this->writeLine('');
@@ -159,7 +170,7 @@ class YamlReferenceDumper
if ($children) {
foreach ($children as $childNode) {
$this->writeNode($childNode, $depth + 1);
$this->writeNode($childNode, $node, $depth + 1, $node instanceof PrototypedArrayNode && !$node->getKeyAttribute());
}
}
}
@@ -200,4 +211,42 @@ class YamlReferenceDumper
}
}
}
/**
* @return array
*/
private function getPrototypeChildren(PrototypedArrayNode $node)
{
$prototype = $node->getPrototype();
$key = $node->getKeyAttribute();
// Do not expand prototype if it isn't an array node nor uses attribute as key
if (!$key && !$prototype instanceof ArrayNode) {
return $node->getChildren();
}
if ($prototype instanceof ArrayNode) {
$keyNode = new ArrayNode($key, $node);
$children = $prototype->getChildren();
if ($prototype instanceof PrototypedArrayNode && $prototype->getKeyAttribute()) {
$children = $this->getPrototypeChildren($prototype);
}
// add children
foreach ($children as $childNode) {
$keyNode->addChild($childNode);
}
} else {
$keyNode = new ScalarNode($key, $node);
}
$info = 'Prototype';
if (null !== $prototype->getInfo()) {
$info .= ': '.$prototype->getInfo();
}
$keyNode->setInfo($info);
return [$key => $keyNode];
}
}

View File

@@ -22,7 +22,7 @@ class EnumNode extends ScalarNode
{
private $values;
public function __construct($name, NodeInterface $parent = null, array $values = array())
public function __construct($name, NodeInterface $parent = null, array $values = [])
{
$values = array_unique($values);
if (empty($values)) {

View File

@@ -28,7 +28,7 @@ class Processor
*/
public function process(NodeInterface $configTree, array $configs)
{
$currentConfig = array();
$currentConfig = [];
foreach ($configs as $config) {
$config = $configTree->normalize($config);
$currentConfig = $configTree->merge($currentConfig, $config);
@@ -86,12 +86,12 @@ class Processor
if (isset($config[$key])) {
if (\is_string($config[$key]) || !\is_int(key($config[$key]))) {
// only one
return array($config[$key]);
return [$config[$key]];
}
return $config[$key];
}
return array();
return [];
}
}

View File

@@ -27,12 +27,12 @@ class PrototypedArrayNode extends ArrayNode
protected $keyAttribute;
protected $removeKeyAttribute = false;
protected $minNumberOfElements = 0;
protected $defaultValue = array();
protected $defaultValue = [];
protected $defaultChildren;
/**
* @var NodeInterface[] An array of the prototypes of the simplified value children
*/
private $valuePrototypes = array();
private $valuePrototypes = [];
/**
* Sets the minimum number of elements that a prototype based node must
@@ -53,15 +53,15 @@ class PrototypedArrayNode extends ArrayNode
* to be the key of the particular item. For example, if "id" is the
* "key", then:
*
* array(
* array('id' => 'my_name', 'foo' => 'bar'),
* );
* [
* ['id' => 'my_name', 'foo' => 'bar'],
* ];
*
* becomes
*
* array(
* 'my_name' => array('foo' => 'bar'),
* );
* [
* 'my_name' => ['foo' => 'bar'],
* ];
*
* If you'd like "'id' => 'my_name'" to still be present in the resulting
* array, then you can set the second argument of this method to false.
@@ -78,7 +78,7 @@ class PrototypedArrayNode extends ArrayNode
/**
* Retrieves the name of the attribute which value should be used as key.
*
* @return string The name of the attribute
* @return string|null The name of the attribute
*/
public function getKeyAttribute()
{
@@ -114,10 +114,10 @@ class PrototypedArrayNode extends ArrayNode
*
* @param int|string|array|null $children The number of children|The child name|The children names to be added
*/
public function setAddChildrenIfNoneSet($children = array('defaults'))
public function setAddChildrenIfNoneSet($children = ['defaults'])
{
if (null === $children) {
$this->defaultChildren = array('defaults');
$this->defaultChildren = ['defaults'];
} else {
$this->defaultChildren = \is_int($children) && $children > 0 ? range(1, $children) : (array) $children;
}
@@ -132,8 +132,8 @@ class PrototypedArrayNode extends ArrayNode
public function getDefaultValue()
{
if (null !== $this->defaultChildren) {
$default = $this->prototype->hasDefaultValue() ? $this->prototype->getDefaultValue() : array();
$defaults = array();
$default = $this->prototype->hasDefaultValue() ? $this->prototype->getDefaultValue() : [];
$defaults = [];
foreach (array_values($this->defaultChildren) as $i => $name) {
$defaults[null === $this->keyAttribute ? $i : $name] = $default;
}
@@ -226,7 +226,7 @@ class PrototypedArrayNode extends ArrayNode
$value = $this->remapXml($value);
$isAssoc = array_keys($value) !== range(0, \count($value) - 1);
$normalized = array();
$normalized = [];
foreach ($value as $k => $v) {
if (null !== $this->keyAttribute && \is_array($v)) {
if (!isset($v[$this->keyAttribute]) && \is_int($k) && !$isAssoc) {
@@ -243,9 +243,9 @@ class PrototypedArrayNode extends ArrayNode
}
// if only "value" is left
if (array_keys($v) === array('value')) {
if (array_keys($v) === ['value']) {
$v = $v['value'];
if ($this->prototype instanceof ArrayNode && ($children = $this->prototype->getChildren()) && array_key_exists('value', $children)) {
if ($this->prototype instanceof ArrayNode && ($children = $this->prototype->getChildren()) && \array_key_exists('value', $children)) {
$valuePrototype = current($this->valuePrototypes) ?: clone $children['value'];
$valuePrototype->parent = $this;
$originalClosures = $this->prototype->normalizationClosures;
@@ -258,7 +258,7 @@ class PrototypedArrayNode extends ArrayNode
}
}
if (array_key_exists($k, $normalized)) {
if (\array_key_exists($k, $normalized)) {
$ex = new DuplicateKeyException(sprintf('Duplicate key "%s" for path "%s".', $k, $this->getPath()));
$ex->setPath($this->getPath());
@@ -301,14 +301,14 @@ class PrototypedArrayNode extends ArrayNode
}
foreach ($rightSide as $k => $v) {
// prototype, and key is irrelevant, so simply append the element
// prototype, and key is irrelevant, append the element
if (null === $this->keyAttribute) {
$leftSide[] = $v;
continue;
}
// no conflict
if (!array_key_exists($k, $leftSide)) {
if (!\array_key_exists($k, $leftSide)) {
if (!$this->allowNewKeys) {
$ex = new InvalidConfigurationException(sprintf('You are not allowed to define new elements for path "%s". Please define all elements for this path in one config file.', $this->getPath()));
$ex->setPath($this->getPath());
@@ -335,30 +335,30 @@ class PrototypedArrayNode extends ArrayNode
*
* For example, assume $this->keyAttribute is 'name' and the value array is as follows:
*
* array(
* array(
* [
* [
* 'name' => 'name001',
* 'value' => 'value001'
* )
* )
* ]
* ]
*
* Now, the key is 0 and the child node is:
*
* array(
* [
* 'name' => 'name001',
* 'value' => 'value001'
* )
* ]
*
* When normalizing the value array, the 'name' element will removed from the child node
* and its value becomes the new key of the child node:
*
* array(
* 'name001' => array('value' => 'value001')
* )
* [
* 'name001' => ['value' => 'value001']
* ]
*
* Now only 'value' element is left in the child node which can be further simplified into a string:
*
* array('name001' => 'value001')
* ['name001' => 'value001']
*
* Now, the key becomes 'name001' and the child node becomes 'value001' and
* the prototype of child node 'name001' should be a ScalarNode instead of an ArrayNode instance.

View File

@@ -0,0 +1,52 @@
<?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\DependencyInjection;
@trigger_error(sprintf('The %s class is deprecated since Symfony 3.4 and will be removed in 4.0. Use tagged iterator arguments instead.', ConfigCachePass::class), E_USER_DEPRECATED);
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Compiler\PriorityTaggedServiceTrait;
use Symfony\Component\DependencyInjection\ContainerBuilder;
/**
* Adds services tagged config_cache.resource_checker to the config_cache_factory service, ordering them by priority.
*
* @author Matthias Pigulla <mp@webfactory.de>
* @author Benjamin Klotz <bk@webfactory.de>
*
* @deprecated since version 3.4, to be removed in 4.0. Use tagged iterator arguments instead.
*/
class ConfigCachePass implements CompilerPassInterface
{
use PriorityTaggedServiceTrait;
private $factoryServiceId;
private $resourceCheckerTag;
public function __construct($factoryServiceId = 'config_cache_factory', $resourceCheckerTag = 'config_cache.resource_checker')
{
$this->factoryServiceId = $factoryServiceId;
$this->resourceCheckerTag = $resourceCheckerTag;
}
public function process(ContainerBuilder $container)
{
$resourceCheckers = $this->findAndSortTaggedServices($this->resourceCheckerTag, $container);
if (empty($resourceCheckers)) {
return;
}
$container->getDefinition($this->factoryServiceId)->replaceArgument(0, new IteratorArgument($resourceCheckers));
}
}

View File

@@ -23,8 +23,9 @@ class FileLoaderLoadException extends \Exception
* @param string $sourceResource The original resource importing the new resource
* @param int $code The error code
* @param \Exception $previous A previous exception
* @param string $type The type of resource
*/
public function __construct($resource, $sourceResource = null, $code = null, $previous = null)
public function __construct($resource, $sourceResource = null, $code = null, $previous = null, $type = null)
{
$message = '';
if ($previous) {
@@ -60,6 +61,13 @@ class FileLoaderLoadException extends \Exception
$bundle = substr($parts[0], 1);
$message .= sprintf(' Make sure the "%s" bundle is correctly registered and loaded in the application kernel class.', $bundle);
$message .= sprintf(' If the bundle is registered, make sure the bundle path "%s" is not empty.', $resource);
} elseif (null !== $type) {
// maybe there is no loader for this specific type
if ('annotation' === $type) {
$message .= ' Make sure annotations are installed and enabled.';
} else {
$message .= sprintf(' Make sure there is a loader supporting the "%s" type.', $type);
}
}
parent::__construct($message, $code, $previous);
@@ -72,7 +80,7 @@ class FileLoaderLoadException extends \Exception
}
if (\is_array($var)) {
$a = array();
$a = [];
foreach ($var as $k => $v) {
$a[] = sprintf('%s => %s', $k, $this->varToString($v));
}

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\Exception;
/**
* File locator exception if a file does not exist.
*
* @author Leo Feyer <https://github.com/leofeyer>
*/
class FileLocatorFileNotFoundException extends \InvalidArgumentException
{
private $paths;
public function __construct($message = '', $code = 0, $previous = null, array $paths = [])
{
parent::__construct($message, $code, $previous);
$this->paths = $paths;
}
public function getPaths()
{
return $this->paths;
}
}

View File

@@ -11,6 +11,8 @@
namespace Symfony\Component\Config;
use Symfony\Component\Config\Exception\FileLocatorFileNotFoundException;
/**
* FileLocator uses an array of pre-defined paths to find files.
*
@@ -23,7 +25,7 @@ class FileLocator implements FileLocatorInterface
/**
* @param string|array $paths A path or an array of paths where to look for resources
*/
public function __construct($paths = array())
public function __construct($paths = [])
{
$this->paths = (array) $paths;
}
@@ -39,7 +41,7 @@ class FileLocator implements FileLocatorInterface
if ($this->isAbsolutePath($name)) {
if (!file_exists($name)) {
throw new \InvalidArgumentException(sprintf('The file "%s" does not exist.', $name));
throw new FileLocatorFileNotFoundException(sprintf('The file "%s" does not exist.', $name), 0, null, [$name]);
}
return $name;
@@ -52,7 +54,7 @@ class FileLocator implements FileLocatorInterface
}
$paths = array_unique($paths);
$filepaths = array();
$filepaths = $notfound = [];
foreach ($paths as $path) {
if (@file_exists($file = $path.\DIRECTORY_SEPARATOR.$name)) {
@@ -60,11 +62,13 @@ class FileLocator implements FileLocatorInterface
return $file;
}
$filepaths[] = $file;
} else {
$notfound[] = $file;
}
}
if (!$filepaths) {
throw new \InvalidArgumentException(sprintf('The file "%s" does not exist (in: %s).', $name, implode(', ', $paths)));
throw new FileLocatorFileNotFoundException(sprintf('The file "%s" does not exist (in: %s).', $name, implode(', ', $paths)), 0, null, $notfound);
}
return $filepaths;

View File

@@ -11,6 +11,8 @@
namespace Symfony\Component\Config;
use Symfony\Component\Config\Exception\FileLocatorFileNotFoundException;
/**
* @author Fabien Potencier <fabien@symfony.com>
*/
@@ -25,7 +27,8 @@ interface FileLocatorInterface
*
* @return string|array The full path to the file or an array of file paths
*
* @throws \InvalidArgumentException When file is not found
* @throws \InvalidArgumentException If $name is empty
* @throws FileLocatorFileNotFoundException If a file is not found
*/
public function locate($name, $currentPath = null, $first = true);
}

View File

@@ -1,4 +1,4 @@
Copyright (c) 2004-2018 Fabien Potencier
Copyright (c) 2004-2019 Fabien Potencier
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@@ -34,7 +34,7 @@ class DelegatingLoader extends Loader
public function load($resource, $type = null)
{
if (false === $loader = $this->resolver->resolve($resource, $type)) {
throw new FileLoaderLoadException($resource);
throw new FileLoaderLoadException($resource, null, null, null, $type);
}
return $loader->load($resource, $type);

View File

@@ -13,7 +13,10 @@ namespace Symfony\Component\Config\Loader;
use Symfony\Component\Config\Exception\FileLoaderImportCircularReferenceException;
use Symfony\Component\Config\Exception\FileLoaderLoadException;
use Symfony\Component\Config\Exception\FileLocatorFileNotFoundException;
use Symfony\Component\Config\FileLocatorInterface;
use Symfony\Component\Config\Resource\FileExistenceResource;
use Symfony\Component\Config\Resource\GlobResource;
/**
* FileLoader is the abstract class used by all built-in loaders that are file based.
@@ -22,7 +25,7 @@ use Symfony\Component\Config\FileLocatorInterface;
*/
abstract class FileLoader extends Loader
{
protected static $loading = array();
protected static $loading = [];
protected $locator;
@@ -65,26 +68,75 @@ abstract class FileLoader extends Loader
*
* @throws FileLoaderLoadException
* @throws FileLoaderImportCircularReferenceException
* @throws FileLocatorFileNotFoundException
*/
public function import($resource, $type = null, $ignoreErrors = false, $sourceResource = null)
{
if (\is_string($resource) && \strlen($resource) !== $i = strcspn($resource, '*?{[')) {
$ret = [];
$isSubpath = 0 !== $i && false !== strpos(substr($resource, 0, $i), '/');
foreach ($this->glob($resource, false, $_, $ignoreErrors || !$isSubpath) as $path => $info) {
if (null !== $res = $this->doImport($path, $type, $ignoreErrors, $sourceResource)) {
$ret[] = $res;
}
$isSubpath = true;
}
if ($isSubpath) {
return isset($ret[1]) ? $ret : (isset($ret[0]) ? $ret[0] : null);
}
}
return $this->doImport($resource, $type, $ignoreErrors, $sourceResource);
}
/**
* @internal
*/
protected function glob($pattern, $recursive, &$resource = null, $ignoreErrors = false)
{
if (\strlen($pattern) === $i = strcspn($pattern, '*?{[')) {
$prefix = $pattern;
$pattern = '';
} elseif (0 === $i || false === strpos(substr($pattern, 0, $i), '/')) {
$prefix = '.';
$pattern = '/'.$pattern;
} else {
$prefix = \dirname(substr($pattern, 0, 1 + $i));
$pattern = substr($pattern, \strlen($prefix));
}
try {
$prefix = $this->locator->locate($prefix, $this->currentDir, true);
} catch (FileLocatorFileNotFoundException $e) {
if (!$ignoreErrors) {
throw $e;
}
$resource = [];
foreach ($e->getPaths() as $path) {
$resource[] = new FileExistenceResource($path);
}
return;
}
$resource = new GlobResource($prefix, $pattern, $recursive);
foreach ($resource as $path => $info) {
yield $path => $info;
}
}
private function doImport($resource, $type = null, $ignoreErrors = false, $sourceResource = null)
{
try {
$loader = $this->resolve($resource, $type);
if ($loader instanceof self && null !== $this->currentDir) {
// we fallback to the current locator to keep BC
// as some some loaders do not call the parent __construct()
// @deprecated should be removed in 3.0
$locator = $loader->getLocator();
if (null === $locator) {
@trigger_error('Not calling the parent constructor in '.\get_class($loader).' which extends '.__CLASS__.' is deprecated since Symfony 2.7 and will not be supported anymore in 3.0.', E_USER_DEPRECATED);
$locator = $this->locator;
}
$resource = $locator->locate($resource, $this->currentDir, false);
$resource = $loader->getLocator()->locate($resource, $this->currentDir, false);
}
$resources = \is_array($resource) ? $resource : array($resource);
$resources = \is_array($resource) ? $resource : [$resource];
for ($i = 0; $i < $resourcesCount = \count($resources); ++$i) {
if (isset(self::$loading[$resources[$i]])) {
if ($i == $resourcesCount - 1) {
@@ -99,16 +151,10 @@ abstract class FileLoader extends Loader
try {
$ret = $loader->load($resource, $type);
} catch (\Exception $e) {
} finally {
unset(self::$loading[$resource]);
throw $e;
} catch (\Throwable $e) {
unset(self::$loading[$resource]);
throw $e;
}
unset(self::$loading[$resource]);
return $ret;
} catch (FileLoaderImportCircularReferenceException $e) {
throw $e;
@@ -119,8 +165,10 @@ abstract class FileLoader extends Loader
throw $e;
}
throw new FileLoaderLoadException($resource, $sourceResource, null, $e);
throw new FileLoaderLoadException($resource, $sourceResource, null, $e, $type);
}
}
return null;
}
}

View File

@@ -0,0 +1,36 @@
<?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\Loader;
/**
* GlobFileLoader loads files from a glob pattern.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class GlobFileLoader extends FileLoader
{
/**
* {@inheritdoc}
*/
public function load($resource, $type = null)
{
return $this->import($resource);
}
/**
* {@inheritdoc}
*/
public function supports($resource, $type = null)
{
return 'glob' === $type;
}
}

View File

@@ -70,7 +70,7 @@ abstract class Loader implements LoaderInterface
$loader = null === $this->resolver ? false : $this->resolver->resolve($resource, $type);
if (false === $loader) {
throw new FileLoaderLoadException($resource);
throw new FileLoaderLoadException($resource, null, null, null, $type);
}
return $loader;

View File

@@ -24,12 +24,12 @@ class LoaderResolver implements LoaderResolverInterface
/**
* @var LoaderInterface[] An array of LoaderInterface objects
*/
private $loaders = array();
private $loaders = [];
/**
* @param LoaderInterface[] $loaders An array of loaders
*/
public function __construct(array $loaders = array())
public function __construct(array $loaders = [])
{
foreach ($loaders as $loader) {
$this->addLoader($loader);

View File

@@ -0,0 +1,210 @@
<?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\Resource;
/**
* ClassExistenceResource represents a class existence.
* Freshness is only evaluated against resource existence.
*
* The resource must be a fully-qualified class name.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class ClassExistenceResource implements SelfCheckingResourceInterface, \Serializable
{
private $resource;
private $exists;
private static $autoloadLevel = 0;
private static $autoloadedClass;
private static $existsCache = [];
/**
* @param string $resource The fully-qualified class name
* @param bool|null $exists Boolean when the existency check has already been done
*/
public function __construct($resource, $exists = null)
{
$this->resource = $resource;
if (null !== $exists) {
$this->exists = (bool) $exists;
}
}
/**
* {@inheritdoc}
*/
public function __toString()
{
return $this->resource;
}
/**
* @return string The file path to the resource
*/
public function getResource()
{
return $this->resource;
}
/**
* {@inheritdoc}
*
* @throws \ReflectionException when a parent class/interface/trait is not found
*/
public function isFresh($timestamp)
{
$loaded = class_exists($this->resource, false) || interface_exists($this->resource, false) || trait_exists($this->resource, false);
if (null !== $exists = &self::$existsCache[(int) (0 >= $timestamp)][$this->resource]) {
$exists = $exists || $loaded;
} elseif (!$exists = $loaded) {
if (!self::$autoloadLevel++) {
spl_autoload_register(__CLASS__.'::throwOnRequiredClass');
}
$autoloadedClass = self::$autoloadedClass;
self::$autoloadedClass = ltrim($this->resource, '\\');
try {
$exists = class_exists($this->resource) || interface_exists($this->resource, false) || trait_exists($this->resource, false);
} catch (\Exception $e) {
try {
self::throwOnRequiredClass($this->resource, $e);
} catch (\ReflectionException $e) {
if (0 >= $timestamp) {
unset(self::$existsCache[1][$this->resource]);
throw $e;
}
}
} finally {
self::$autoloadedClass = $autoloadedClass;
if (!--self::$autoloadLevel) {
spl_autoload_unregister(__CLASS__.'::throwOnRequiredClass');
}
}
}
if (null === $this->exists) {
$this->exists = $exists;
}
return $this->exists xor !$exists;
}
/**
* @internal
*/
public function serialize()
{
if (null === $this->exists) {
$this->isFresh(0);
}
return serialize([$this->resource, $this->exists]);
}
/**
* @internal
*/
public function unserialize($serialized)
{
list($this->resource, $this->exists) = unserialize($serialized);
}
/**
* Throws a reflection exception when the passed class does not exist but is required.
*
* A class is considered "not required" when it's loaded as part of a "class_exists" or similar check.
*
* This function can be used as an autoload function to throw a reflection
* exception if the class was not found by previous autoload functions.
*
* A previous exception can be passed. In this case, the class is considered as being
* required totally, so if it doesn't exist, a reflection exception is always thrown.
* If it exists, the previous exception is rethrown.
*
* @throws \ReflectionException
*
* @internal
*/
public static function throwOnRequiredClass($class, \Exception $previous = null)
{
// If the passed class is the resource being checked, we shouldn't throw.
if (null === $previous && self::$autoloadedClass === $class) {
return;
}
if (class_exists($class, false) || interface_exists($class, false) || trait_exists($class, false)) {
if (null !== $previous) {
throw $previous;
}
return;
}
if ($previous instanceof \ReflectionException) {
throw $previous;
}
$e = new \ReflectionException(sprintf('Class "%s" not found while loading "%s".', $class, self::$autoloadedClass), 0, $previous);
if (null !== $previous) {
throw $e;
}
$trace = debug_backtrace();
$autoloadFrame = [
'function' => 'spl_autoload_call',
'args' => [$class],
];
if (false === $i = array_search($autoloadFrame, $trace, true)) {
throw $e;
}
if (isset($trace[++$i]['function']) && !isset($trace[$i]['class'])) {
switch ($trace[$i]['function']) {
case 'get_class_methods':
case 'get_class_vars':
case 'get_parent_class':
case 'is_a':
case 'is_subclass_of':
case 'class_exists':
case 'class_implements':
case 'class_parents':
case 'trait_exists':
case 'defined':
case 'interface_exists':
case 'method_exists':
case 'property_exists':
case 'is_callable':
return;
}
$props = [
'file' => isset($trace[$i]['file']) ? $trace[$i]['file'] : null,
'line' => isset($trace[$i]['line']) ? $trace[$i]['line'] : null,
'trace' => \array_slice($trace, 1 + $i),
];
foreach ($props as $p => $v) {
if (null !== $v) {
$r = new \ReflectionProperty('Exception', $p);
$r->setAccessible(true);
$r->setValue($e, $v);
}
}
}
throw $e;
}
}

View File

@@ -0,0 +1,84 @@
<?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\Resource;
/**
* ComposerResource tracks the PHP version and Composer dependencies.
*
* @author Nicolas Grekas <p@tchwork.com>
*/
class ComposerResource implements SelfCheckingResourceInterface, \Serializable
{
private $vendors;
private static $runtimeVendors;
public function __construct()
{
self::refresh();
$this->vendors = self::$runtimeVendors;
}
public function getVendors()
{
return array_keys($this->vendors);
}
/**
* {@inheritdoc}
*/
public function __toString()
{
return __CLASS__;
}
/**
* {@inheritdoc}
*/
public function isFresh($timestamp)
{
self::refresh();
return array_values(self::$runtimeVendors) === array_values($this->vendors);
}
/**
* @internal
*/
public function serialize()
{
return serialize($this->vendors);
}
/**
* @internal
*/
public function unserialize($serialized)
{
$this->vendors = unserialize($serialized);
}
private static function refresh()
{
self::$runtimeVendors = [];
foreach (get_declared_classes() as $class) {
if ('C' === $class[0] && 0 === strpos($class, 'ComposerAutoloaderInit')) {
$r = new \ReflectionClass($class);
$v = \dirname(\dirname($r->getFileName()));
if (file_exists($v.'/composer/installed.json')) {
self::$runtimeVendors[$v] = @filemtime($v.'/composer/installed.json');
}
}
}
}
}

View File

@@ -24,11 +24,17 @@ class DirectoryResource implements SelfCheckingResourceInterface, \Serializable
/**
* @param string $resource The file path to the resource
* @param string|null $pattern A pattern to restrict monitored files
*
* @throws \InvalidArgumentException
*/
public function __construct($resource, $pattern = null)
{
$this->resource = $resource;
$this->resource = realpath($resource) ?: (file_exists($resource) ? $resource : false);
$this->pattern = $pattern;
if (false === $this->resource || !is_dir($this->resource)) {
throw new \InvalidArgumentException(sprintf('The directory "%s" does not exist.', $resource));
}
}
/**
@@ -36,11 +42,11 @@ class DirectoryResource implements SelfCheckingResourceInterface, \Serializable
*/
public function __toString()
{
return md5(serialize(array($this->resource, $this->pattern)));
return md5(serialize([$this->resource, $this->pattern]));
}
/**
* {@inheritdoc}
* @return string The file path to the resource
*/
public function getResource()
{
@@ -98,11 +104,17 @@ class DirectoryResource implements SelfCheckingResourceInterface, \Serializable
return true;
}
/**
* @internal
*/
public function serialize()
{
return serialize(array($this->resource, $this->pattern));
return serialize([$this->resource, $this->pattern]);
}
/**
* @internal
*/
public function unserialize($serialized)
{
list($this->resource, $this->pattern) = unserialize($serialized);

View File

@@ -43,7 +43,7 @@ class FileExistenceResource implements SelfCheckingResourceInterface, \Serializa
}
/**
* {@inheritdoc}
* @return string The file path to the resource
*/
public function getResource()
{
@@ -59,15 +59,15 @@ class FileExistenceResource implements SelfCheckingResourceInterface, \Serializa
}
/**
* {@inheritdoc}
* @internal
*/
public function serialize()
{
return serialize(array($this->resource, $this->exists));
return serialize([$this->resource, $this->exists]);
}
/**
* {@inheritdoc}
* @internal
*/
public function unserialize($serialized)
{

View File

@@ -27,10 +27,16 @@ class FileResource implements SelfCheckingResourceInterface, \Serializable
/**
* @param string $resource The file path to the resource
*
* @throws \InvalidArgumentException
*/
public function __construct($resource)
{
$this->resource = realpath($resource) ?: (file_exists($resource) ? $resource : false);
if (false === $this->resource) {
throw new \InvalidArgumentException(sprintf('The file "%s" does not exist.', $resource));
}
}
/**
@@ -38,11 +44,11 @@ class FileResource implements SelfCheckingResourceInterface, \Serializable
*/
public function __toString()
{
return (string) $this->resource;
return $this->resource;
}
/**
* {@inheritdoc}
* @return string The canonicalized, absolute path to the resource
*/
public function getResource()
{
@@ -54,18 +60,20 @@ class FileResource implements SelfCheckingResourceInterface, \Serializable
*/
public function isFresh($timestamp)
{
if (false === $this->resource || !file_exists($this->resource)) {
return false;
}
return filemtime($this->resource) <= $timestamp;
return false !== ($filemtime = @filemtime($this->resource)) && $filemtime <= $timestamp;
}
/**
* @internal
*/
public function serialize()
{
return serialize($this->resource);
}
/**
* @internal
*/
public function unserialize($serialized)
{
$this->resource = unserialize($serialized);

View File

@@ -0,0 +1,159 @@
<?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\Resource;
use Symfony\Component\Finder\Finder;
use Symfony\Component\Finder\Glob;
/**
* GlobResource represents a set of resources stored on the filesystem.
*
* Only existence/removal is tracked (not mtimes.)
*
* @author Nicolas Grekas <p@tchwork.com>
*/
class GlobResource implements \IteratorAggregate, SelfCheckingResourceInterface, \Serializable
{
private $prefix;
private $pattern;
private $recursive;
private $hash;
/**
* @param string $prefix A directory prefix
* @param string $pattern A glob pattern
* @param bool $recursive Whether directories should be scanned recursively or not
*
* @throws \InvalidArgumentException
*/
public function __construct($prefix, $pattern, $recursive)
{
$this->prefix = realpath($prefix) ?: (file_exists($prefix) ? $prefix : false);
$this->pattern = $pattern;
$this->recursive = $recursive;
if (false === $this->prefix) {
throw new \InvalidArgumentException(sprintf('The path "%s" does not exist.', $prefix));
}
}
public function getPrefix()
{
return $this->prefix;
}
/**
* {@inheritdoc}
*/
public function __toString()
{
return 'glob.'.$this->prefix.$this->pattern.(int) $this->recursive;
}
/**
* {@inheritdoc}
*/
public function isFresh($timestamp)
{
$hash = $this->computeHash();
if (null === $this->hash) {
$this->hash = $hash;
}
return $this->hash === $hash;
}
/**
* @internal
*/
public function serialize()
{
if (null === $this->hash) {
$this->hash = $this->computeHash();
}
return serialize([$this->prefix, $this->pattern, $this->recursive, $this->hash]);
}
/**
* @internal
*/
public function unserialize($serialized)
{
list($this->prefix, $this->pattern, $this->recursive, $this->hash) = unserialize($serialized);
}
public function getIterator()
{
if (!file_exists($this->prefix) || (!$this->recursive && '' === $this->pattern)) {
return;
}
if (0 !== strpos($this->prefix, 'phar://') && false === strpos($this->pattern, '/**/') && (\defined('GLOB_BRACE') || false === strpos($this->pattern, '{'))) {
$paths = glob($this->prefix.$this->pattern, GLOB_NOSORT | (\defined('GLOB_BRACE') ? GLOB_BRACE : 0));
sort($paths);
foreach ($paths as $path) {
if ($this->recursive && is_dir($path)) {
$files = iterator_to_array(new \RecursiveIteratorIterator(
new \RecursiveCallbackFilterIterator(
new \RecursiveDirectoryIterator($path, \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS),
function (\SplFileInfo $file) { return '.' !== $file->getBasename()[0]; }
),
\RecursiveIteratorIterator::LEAVES_ONLY
));
uasort($files, function (\SplFileInfo $a, \SplFileInfo $b) {
return (string) $a > (string) $b ? 1 : -1;
});
foreach ($files as $path => $info) {
if ($info->isFile()) {
yield $path => $info;
}
}
} elseif (is_file($path)) {
yield $path => new \SplFileInfo($path);
}
}
return;
}
if (!class_exists(Finder::class)) {
throw new \LogicException(sprintf('Extended glob pattern "%s" cannot be used as the Finder component is not installed.', $this->pattern));
}
$finder = new Finder();
$regex = Glob::toRegex($this->pattern);
if ($this->recursive) {
$regex = substr_replace($regex, '(/|$)', -2, 1);
}
$prefixLen = \strlen($this->prefix);
foreach ($finder->followLinks()->sortByName()->in($this->prefix) as $path => $info) {
if (preg_match($regex, substr('\\' === \DIRECTORY_SEPARATOR ? str_replace('\\', '/', $path) : $path, $prefixLen)) && $info->isFile()) {
yield $path => $info;
}
}
}
private function computeHash()
{
$hash = hash_init('md5');
foreach ($this->getIterator() as $path => $info) {
hash_update($hash, $path."\n");
}
return hash_final($hash);
}
}

View File

@@ -0,0 +1,250 @@
<?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\Resource;
use Symfony\Component\DependencyInjection\ServiceSubscriberInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* @author Nicolas Grekas <p@tchwork.com>
*/
class ReflectionClassResource implements SelfCheckingResourceInterface, \Serializable
{
private $files = [];
private $className;
private $classReflector;
private $excludedVendors = [];
private $hash;
public function __construct(\ReflectionClass $classReflector, $excludedVendors = [])
{
$this->className = $classReflector->name;
$this->classReflector = $classReflector;
$this->excludedVendors = $excludedVendors;
}
public function isFresh($timestamp)
{
if (null === $this->hash) {
$this->hash = $this->computeHash();
$this->loadFiles($this->classReflector);
}
foreach ($this->files as $file => $v) {
if (false === $filemtime = @filemtime($file)) {
return false;
}
if ($filemtime > $timestamp) {
return $this->hash === $this->computeHash();
}
}
return true;
}
public function __toString()
{
return 'reflection.'.$this->className;
}
/**
* @internal
*/
public function serialize()
{
if (null === $this->hash) {
$this->hash = $this->computeHash();
$this->loadFiles($this->classReflector);
}
return serialize([$this->files, $this->className, $this->hash]);
}
/**
* @internal
*/
public function unserialize($serialized)
{
list($this->files, $this->className, $this->hash) = unserialize($serialized);
}
private function loadFiles(\ReflectionClass $class)
{
foreach ($class->getInterfaces() as $v) {
$this->loadFiles($v);
}
do {
$file = $class->getFileName();
if (false !== $file && file_exists($file)) {
foreach ($this->excludedVendors as $vendor) {
if (0 === strpos($file, $vendor) && false !== strpbrk(substr($file, \strlen($vendor), 1), '/'.\DIRECTORY_SEPARATOR)) {
$file = false;
break;
}
}
if ($file) {
$this->files[$file] = null;
}
}
foreach ($class->getTraits() as $v) {
$this->loadFiles($v);
}
} while ($class = $class->getParentClass());
}
private function computeHash()
{
if (null === $this->classReflector) {
try {
$this->classReflector = new \ReflectionClass($this->className);
} catch (\ReflectionException $e) {
// the class does not exist anymore
return false;
}
}
$hash = hash_init('md5');
foreach ($this->generateSignature($this->classReflector) as $info) {
hash_update($hash, $info);
}
return hash_final($hash);
}
private function generateSignature(\ReflectionClass $class)
{
yield $class->getDocComment();
yield (int) $class->isFinal();
yield (int) $class->isAbstract();
if ($class->isTrait()) {
yield print_r(class_uses($class->name), true);
} else {
yield print_r(class_parents($class->name), true);
yield print_r(class_implements($class->name), true);
yield print_r($class->getConstants(), true);
}
if (!$class->isInterface()) {
$defaults = $class->getDefaultProperties();
foreach ($class->getProperties(\ReflectionProperty::IS_PUBLIC | \ReflectionProperty::IS_PROTECTED) as $p) {
yield $p->getDocComment().$p;
yield print_r(isset($defaults[$p->name]) && !\is_object($defaults[$p->name]) ? $defaults[$p->name] : null, true);
}
}
if (\defined('HHVM_VERSION')) {
foreach ($class->getMethods(\ReflectionMethod::IS_PUBLIC | \ReflectionMethod::IS_PROTECTED) as $m) {
// workaround HHVM bug with variadics, see https://github.com/facebook/hhvm/issues/5762
yield preg_replace('/^ @@.*/m', '', new ReflectionMethodHhvmWrapper($m->class, $m->name));
}
} else {
foreach ($class->getMethods(\ReflectionMethod::IS_PUBLIC | \ReflectionMethod::IS_PROTECTED) as $m) {
$defaults = [];
$parametersWithUndefinedConstants = [];
foreach ($m->getParameters() as $p) {
if (!$p->isDefaultValueAvailable()) {
$defaults[$p->name] = null;
continue;
}
if (!$p->isDefaultValueConstant() || \defined($p->getDefaultValueConstantName())) {
$defaults[$p->name] = $p->getDefaultValue();
continue;
}
$defaults[$p->name] = $p->getDefaultValueConstantName();
$parametersWithUndefinedConstants[$p->name] = true;
}
if (!$parametersWithUndefinedConstants) {
yield preg_replace('/^ @@.*/m', '', $m);
} else {
$stack = [
$m->getDocComment(),
$m->getName(),
$m->isAbstract(),
$m->isFinal(),
$m->isStatic(),
$m->isPublic(),
$m->isPrivate(),
$m->isProtected(),
$m->returnsReference(),
\PHP_VERSION_ID >= 70000 && $m->hasReturnType() ? (\PHP_VERSION_ID >= 70100 ? $m->getReturnType()->getName() : (string) $m->getReturnType()) : '',
];
foreach ($m->getParameters() as $p) {
if (!isset($parametersWithUndefinedConstants[$p->name])) {
$stack[] = (string) $p;
} else {
$stack[] = $p->isOptional();
$stack[] = \PHP_VERSION_ID >= 70000 && $p->hasType() ? (\PHP_VERSION_ID >= 70100 ? $p->getType()->getName() : (string) $p->getType()) : '';
$stack[] = $p->isPassedByReference();
$stack[] = \PHP_VERSION_ID >= 50600 ? $p->isVariadic() : '';
$stack[] = $p->getName();
}
}
yield implode(',', $stack);
}
yield print_r($defaults, true);
}
}
if ($class->isAbstract() || $class->isInterface() || $class->isTrait()) {
return;
}
if (interface_exists(EventSubscriberInterface::class, false) && $class->isSubclassOf(EventSubscriberInterface::class)) {
yield EventSubscriberInterface::class;
yield print_r(\call_user_func([$class->name, 'getSubscribedEvents']), true);
}
if (interface_exists(ServiceSubscriberInterface::class, false) && $class->isSubclassOf(ServiceSubscriberInterface::class)) {
yield ServiceSubscriberInterface::class;
yield print_r(\call_user_func([$class->name, 'getSubscribedServices']), true);
}
}
}
/**
* @internal
*/
class ReflectionMethodHhvmWrapper extends \ReflectionMethod
{
public function getParameters()
{
$params = [];
foreach (parent::getParameters() as $i => $p) {
$params[] = new ReflectionParameterHhvmWrapper([$this->class, $this->name], $i);
}
return $params;
}
}
/**
* @internal
*/
class ReflectionParameterHhvmWrapper extends \ReflectionParameter
{
public function getDefaultValue()
{
return [$this->isVariadic(), $this->isDefaultValueAvailable() ? parent::getDefaultValue() : null];
}
}

View File

@@ -30,29 +30,4 @@ interface ResourceInterface
* @return string A string representation unique to the underlying Resource
*/
public function __toString();
/**
* Returns true if the resource has not been updated since the given timestamp.
*
* @param int $timestamp The last time the resource was loaded
*
* @return bool True if the resource has not been updated, false otherwise
*
* @deprecated since 2.8, to be removed in 3.0. If your resource can check itself for
* freshness implement the SelfCheckingResourceInterface instead.
*/
public function isFresh($timestamp);
/**
* Returns the tied resource.
*
* @return mixed The resource
*
* @deprecated since 2.8, to be removed in 3.0. As there are many different kinds of resource,
* a single getResource() method does not make sense at the interface level. You
* can still call getResource() on implementing classes, probably after performing
* a type check. If you know the concrete type of Resource at hand, the return value
* of this method may make sense to you.
*/
public function getResource();
}

View File

@@ -29,15 +29,15 @@ class ResourceCheckerConfigCache implements ConfigCacheInterface
private $file;
/**
* @var ResourceCheckerInterface[]
* @var iterable|ResourceCheckerInterface[]
*/
private $resourceCheckers;
/**
* @param string $file The absolute cache path
* @param ResourceCheckerInterface[] $resourceCheckers The ResourceCheckers to use for the freshness check
* @param string $file The absolute cache path
* @param iterable|ResourceCheckerInterface[] $resourceCheckers The ResourceCheckers to use for the freshness check
*/
public function __construct($file, array $resourceCheckers = array())
public function __construct($file, $resourceCheckers = [])
{
$this->file = $file;
$this->resourceCheckers = $resourceCheckers;
@@ -68,42 +68,28 @@ class ResourceCheckerConfigCache implements ConfigCacheInterface
return false;
}
if (!$this->resourceCheckers) {
if ($this->resourceCheckers instanceof \Traversable && !$this->resourceCheckers instanceof \Countable) {
$this->resourceCheckers = iterator_to_array($this->resourceCheckers);
}
if (!\count($this->resourceCheckers)) {
return true; // shortcut - if we don't have any checkers we don't need to bother with the meta file at all
}
$metadata = $this->getMetaFile();
if (!is_file($metadata)) {
return false;
}
$e = null;
$meta = false;
$time = filemtime($this->file);
$signalingException = new \UnexpectedValueException();
$prevUnserializeHandler = ini_set('unserialize_callback_func', '');
$prevErrorHandler = set_error_handler(function ($type, $msg, $file, $line, $context) use (&$prevErrorHandler, $signalingException) {
if (E_WARNING === $type && 'Class __PHP_Incomplete_Class has no unserializer' === $msg) {
throw $signalingException;
}
$meta = $this->safelyUnserialize($metadata);
return $prevErrorHandler ? $prevErrorHandler($type, $msg, $file, $line, $context) : false;
});
try {
$meta = unserialize(file_get_contents($metadata));
} catch (\Error $e) {
} catch (\Exception $e) {
}
restore_error_handler();
ini_set('unserialize_callback_func', $prevUnserializeHandler);
if (null !== $e && $e !== $signalingException) {
throw $e;
}
if (false === $meta) {
return false;
}
$time = filemtime($this->file);
foreach ($meta as $resource) {
/* @var ResourceInterface $resource */
foreach ($this->resourceCheckers as $checker) {
@@ -135,7 +121,7 @@ class ResourceCheckerConfigCache implements ConfigCacheInterface
$mode = 0666;
$umask = umask();
$filesystem = new Filesystem();
$filesystem->dumpFile($this->file, $content, null);
$filesystem->dumpFile($this->file, $content);
try {
$filesystem->chmod($this->file, $mode, $umask);
} catch (IOException $e) {
@@ -143,7 +129,7 @@ class ResourceCheckerConfigCache implements ConfigCacheInterface
}
if (null !== $metadata) {
$filesystem->dumpFile($this->getMetaFile(), serialize($metadata), null);
$filesystem->dumpFile($this->getMetaFile(), serialize($metadata));
try {
$filesystem->chmod($this->getMetaFile(), $mode, $umask);
} catch (IOException $e) {
@@ -165,4 +151,33 @@ class ResourceCheckerConfigCache implements ConfigCacheInterface
{
return $this->file.'.meta';
}
private function safelyUnserialize($file)
{
$e = null;
$meta = false;
$content = file_get_contents($file);
$signalingException = new \UnexpectedValueException();
$prevUnserializeHandler = ini_set('unserialize_callback_func', '');
$prevErrorHandler = set_error_handler(function ($type, $msg, $file, $line, $context = []) use (&$prevErrorHandler, $signalingException) {
if (__FILE__ === $file) {
throw $signalingException;
}
return $prevErrorHandler ? $prevErrorHandler($type, $msg, $file, $line, $context) : false;
});
try {
$meta = unserialize($content);
} catch (\Error $e) {
} catch (\Exception $e) {
}
restore_error_handler();
ini_set('unserialize_callback_func', $prevUnserializeHandler);
if (null !== $e && $e !== $signalingException) {
throw $e;
}
return $meta;
}
}

View File

@@ -19,15 +19,12 @@ namespace Symfony\Component\Config;
*/
class ResourceCheckerConfigCacheFactory implements ConfigCacheFactoryInterface
{
/**
* @var ResourceCheckerInterface[]
*/
private $resourceCheckers = array();
private $resourceCheckers = [];
/**
* @param ResourceCheckerInterface[] $resourceCheckers
* @param iterable|ResourceCheckerInterface[] $resourceCheckers
*/
public function __construct(array $resourceCheckers = array())
public function __construct($resourceCheckers = [])
{
$this->resourceCheckers = $resourceCheckers;
}

View File

@@ -0,0 +1,21 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Config\Util\Exception;
/**
* Exception class for when XML parsing with an XSD schema file path or a callable validator produces errors unrelated
* to the actual XML parsing.
*
* @author Ole Rößner <ole@roessner.it>
*/
class InvalidXmlException extends XmlParsingException
{
}

View File

@@ -0,0 +1,21 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Config\Util\Exception;
/**
* Exception class for when XML cannot be parsed properly.
*
* @author Ole Rößner <ole@roessner.it>
*/
class XmlParsingException extends \InvalidArgumentException
{
}

View File

@@ -11,6 +11,9 @@
namespace Symfony\Component\Config\Util;
use Symfony\Component\Config\Util\Exception\InvalidXmlException;
use Symfony\Component\Config\Util\Exception\XmlParsingException;
/**
* XMLUtils is a bunch of utility methods to XML operations.
*
@@ -18,6 +21,7 @@ namespace Symfony\Component\Config\Util;
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Martin Hasoň <martin.hason@gmail.com>
* @author Ole Rößner <ole@roessner.it>
*/
class XmlUtils
{
@@ -29,27 +33,23 @@ class XmlUtils
}
/**
* Loads an XML file.
* Parses an XML string.
*
* @param string $file An XML file path
* @param string $content An XML string
* @param string|callable|null $schemaOrCallable An XSD schema file path, a callable, or null to disable validation
*
* @return \DOMDocument
*
* @throws \InvalidArgumentException When loading of XML file returns error
* @throws \RuntimeException When DOM extension is missing
* @throws XmlParsingException When parsing of XML file returns error
* @throws InvalidXmlException When parsing of XML with schema or callable produces any errors unrelated to the XML parsing itself
* @throws \RuntimeException When DOM extension is missing
*/
public static function loadFile($file, $schemaOrCallable = null)
public static function parse($content, $schemaOrCallable = null)
{
if (!\extension_loaded('dom')) {
throw new \RuntimeException('Extension DOM is required.');
}
$content = @file_get_contents($file);
if ('' === trim($content)) {
throw new \InvalidArgumentException(sprintf('File %s does not contain valid XML, it is empty.', $file));
}
$internalErrors = libxml_use_internal_errors(true);
$disableEntities = libxml_disable_entity_loader(true);
libxml_clear_errors();
@@ -59,7 +59,7 @@ class XmlUtils
if (!$dom->loadXML($content, LIBXML_NONET | (\defined('LIBXML_COMPACT') ? LIBXML_COMPACT : 0))) {
libxml_disable_entity_loader($disableEntities);
throw new \InvalidArgumentException(implode("\n", static::getXmlErrors($internalErrors)));
throw new XmlParsingException(implode("\n", static::getXmlErrors($internalErrors)));
}
$dom->normalizeDocument();
@@ -69,7 +69,7 @@ class XmlUtils
foreach ($dom->childNodes as $child) {
if (XML_DOCUMENT_TYPE_NODE === $child->nodeType) {
throw new \InvalidArgumentException('Document types are not allowed.');
throw new XmlParsingException('Document types are not allowed.');
}
}
@@ -90,15 +90,15 @@ class XmlUtils
} else {
libxml_use_internal_errors($internalErrors);
throw new \InvalidArgumentException('The schemaOrCallable argument has to be a valid path to XSD file or callable.');
throw new XmlParsingException('The schemaOrCallable argument has to be a valid path to XSD file or callable.');
}
if (!$valid) {
$messages = static::getXmlErrors($internalErrors);
if (empty($messages)) {
$messages = array(sprintf('The XML file "%s" is not valid.', $file));
throw new InvalidXmlException('The XML is not valid.', 0, $e);
}
throw new \InvalidArgumentException(implode("\n", $messages), 0, $e);
throw new XmlParsingException(implode("\n", $messages), 0, $e);
}
}
@@ -108,6 +108,32 @@ class XmlUtils
return $dom;
}
/**
* Loads an XML file.
*
* @param string $file An XML file path
* @param string|callable|null $schemaOrCallable An XSD schema file path, a callable, or null to disable validation
*
* @return \DOMDocument
*
* @throws \InvalidArgumentException When loading of XML file returns error
* @throws XmlParsingException When XML parsing returns any errors
* @throws \RuntimeException When DOM extension is missing
*/
public static function loadFile($file, $schemaOrCallable = null)
{
$content = @file_get_contents($file);
if ('' === trim($content)) {
throw new \InvalidArgumentException(sprintf('File %s does not contain valid XML, it is empty.', $file));
}
try {
return static::parse($content, $schemaOrCallable);
} catch (InvalidXmlException $e) {
throw new XmlParsingException(sprintf('The XML file "%s" is not valid.', $file), 0, $e->getPrevious());
}
}
/**
* Converts a \DOMElement object to a PHP array.
*
@@ -126,15 +152,15 @@ class XmlUtils
* @param \DOMElement $element A \DOMElement instance
* @param bool $checkPrefix Check prefix in an element or an attribute name
*
* @return array A PHP array
* @return mixed
*/
public static function convertDomElementToArray(\DOMElement $element, $checkPrefix = true)
{
$prefix = (string) $element->prefix;
$empty = true;
$config = array();
$config = [];
foreach ($element->attributes as $name => $node) {
if ($checkPrefix && !\in_array((string) $node->prefix, array('', $prefix), true)) {
if ($checkPrefix && !\in_array((string) $node->prefix, ['', $prefix], true)) {
continue;
}
$config[$name] = static::phpize($node->value);
@@ -156,7 +182,7 @@ class XmlUtils
$key = $node->localName;
if (isset($config[$key])) {
if (!\is_array($config[$key]) || !\is_int(key($config[$key]))) {
$config[$key] = array($config[$key]);
$config[$key] = [$config[$key]];
}
$config[$key][] = $value;
} else {
@@ -193,7 +219,7 @@ class XmlUtils
switch (true) {
case 'null' === $lowercaseValue:
return;
return null;
case ctype_digit($value):
$raw = $value;
$cast = (int) $value;
@@ -208,13 +234,13 @@ class XmlUtils
return true;
case 'false' === $lowercaseValue:
return false;
case isset($value[1]) && '0b' == $value[0].$value[1]:
case isset($value[1]) && '0b' == $value[0].$value[1] && preg_match('/^0b[01]*$/', $value):
return bindec($value);
case is_numeric($value):
return '0x' === $value[0].$value[1] ? hexdec($value) : (float) $value;
case preg_match('/^0x[0-9a-f]++$/i', $value):
return hexdec($value);
case preg_match('/^(-|\+)?[0-9]+(\.[0-9]+)?$/', $value):
case preg_match('/^[+-]?[0-9]+(\.[0-9]+)?$/', $value):
return (float) $value;
default:
return $value;
@@ -223,7 +249,7 @@ class XmlUtils
protected static function getXmlErrors($internalErrors)
{
$errors = array();
$errors = [];
foreach (libxml_get_errors() as $error) {
$errors[] = sprintf('[%s %s] %s (in %s - line %d, column %d)',
LIBXML_ERR_WARNING == $error->level ? 'WARNING' : 'ERROR',

View File

@@ -16,12 +16,19 @@
}
],
"require": {
"php": ">=5.3.9",
"symfony/filesystem": "~2.3|~3.0.0",
"php": "^5.5.9|>=7.0.8",
"symfony/filesystem": "~2.8|~3.0|~4.0",
"symfony/polyfill-ctype": "~1.8"
},
"require-dev": {
"symfony/yaml": "~2.7|~3.0.0"
"symfony/finder": "~3.3|~4.0",
"symfony/yaml": "~3.0|~4.0",
"symfony/dependency-injection": "~3.3|~4.0",
"symfony/event-dispatcher": "~3.3|~4.0"
},
"conflict": {
"symfony/finder": "<3.3",
"symfony/dependency-injection": "<3.3"
},
"suggest": {
"symfony/yaml": "To use the yaml reference dumper"
@@ -35,7 +42,7 @@
"minimum-stability": "dev",
"extra": {
"branch-alias": {
"dev-master": "2.8-dev"
"dev-master": "3.4-dev"
}
}
}