Merge branch '3.4'
* 3.4: use the parseFile() method of the YAML parser [Yaml] support parsing files Adding Definition::addError() and a compiler pass to throw errors as exceptions [DI] Add AutowireRequiredMethodsPass to fix bindings for `@required` methods [Cache] Add ResettableInterface to allow resetting any pool's local state [DI][DX] Throw exception on some ContainerBuilder methods used from extensions added missing @author tag for new class allow forms without translations and validator [VarDumper] Make `dump()` a little bit more easier to use [Form] Add ambiguous & exception debug:form tests Reset the authentication token between requests. [Serializer] Getter for extra attributes in ExtraAttributesException [DI] Dont use JSON_BIGINT_AS_STRING # Conflicts: # src/Symfony/Component/Routing/composer.json # src/Symfony/Component/Translation/composer.json # src/Symfony/Component/Yaml/Inline.php # src/Symfony/Component/Yaml/Tests/ParserTest.php # src/Symfony/Component/Yaml/Yaml.php
This commit is contained in:
commit
3640585bfd
@ -43,6 +43,7 @@ class CachePoolPass implements CompilerPassInterface
|
||||
'provider',
|
||||
'namespace',
|
||||
'default_lifetime',
|
||||
'reset',
|
||||
);
|
||||
foreach ($container->findTaggedServiceIds('cache.pool') as $id => $tags) {
|
||||
$adapter = $pool = $container->getDefinition($id);
|
||||
@ -73,13 +74,19 @@ class CachePoolPass implements CompilerPassInterface
|
||||
}
|
||||
$i = 0;
|
||||
foreach ($attributes as $attr) {
|
||||
if (isset($tags[0][$attr]) && ('namespace' !== $attr || ArrayAdapter::class !== $adapter->getClass())) {
|
||||
if (!isset($tags[0][$attr])) {
|
||||
// no-op
|
||||
} elseif ('reset' === $attr) {
|
||||
if ($tags[0][$attr]) {
|
||||
$pool->addTag('kernel.reset', array('method' => $tags[0][$attr]));
|
||||
}
|
||||
} elseif ('namespace' !== $attr || ArrayAdapter::class !== $adapter->getClass()) {
|
||||
$pool->replaceArgument($i++, $tags[0][$attr]);
|
||||
}
|
||||
unset($tags[0][$attr]);
|
||||
}
|
||||
if (!empty($tags[0])) {
|
||||
throw new InvalidArgumentException(sprintf('Invalid "cache.pool" tag for service "%s": accepted attributes are "clearer", "provider", "namespace" and "default_lifetime", found "%s".', $id, implode('", "', array_keys($tags[0]))));
|
||||
throw new InvalidArgumentException(sprintf('Invalid "cache.pool" tag for service "%s": accepted attributes are "clearer", "provider", "namespace", "default_lifetime" and "reset", found "%s".', $id, implode('", "', array_keys($tags[0]))));
|
||||
}
|
||||
|
||||
if (null !== $clearer) {
|
||||
|
@ -25,6 +25,7 @@ use Symfony\Bundle\FrameworkBundle\Controller\Controller;
|
||||
use Symfony\Bundle\FrameworkBundle\Routing\AnnotatedRouteControllerLoader;
|
||||
use Symfony\Component\Cache\Adapter\AdapterInterface;
|
||||
use Symfony\Component\Cache\Adapter\ArrayAdapter;
|
||||
use Symfony\Component\Cache\ResettableInterface;
|
||||
use Symfony\Component\Config\FileLocator;
|
||||
use Symfony\Component\Config\Loader\LoaderInterface;
|
||||
use Symfony\Component\Config\Resource\DirectoryResource;
|
||||
@ -72,6 +73,7 @@ use Symfony\Component\Serializer\Normalizer\JsonSerializableNormalizer;
|
||||
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
|
||||
use Symfony\Component\Stopwatch\Stopwatch;
|
||||
use Symfony\Component\Translation\Command\XliffLintCommand as BaseXliffLintCommand;
|
||||
use Symfony\Component\Translation\Translator;
|
||||
use Symfony\Component\Validator\ConstraintValidatorInterface;
|
||||
use Symfony\Component\Validator\ObjectInitializerInterface;
|
||||
use Symfony\Component\WebLink\HttpHeaderSerializer;
|
||||
@ -146,16 +148,14 @@ class FrameworkExtension extends Extension
|
||||
throw new LogicException('Translation support cannot be enabled as the Translation component is not installed.');
|
||||
}
|
||||
|
||||
if (!class_exists('Symfony\Component\Translation\Translator') && $this->isConfigEnabled($container, $config['form'])) {
|
||||
throw new LogicException('Form support cannot be enabled as the Translation component is not installed.');
|
||||
}
|
||||
|
||||
if (!class_exists('Symfony\Component\Translation\Translator') && $this->isConfigEnabled($container, $config['validation'])) {
|
||||
throw new LogicException('Validation support cannot be enabled as the Translation component is not installed.');
|
||||
}
|
||||
|
||||
if (class_exists(Translator::class)) {
|
||||
$loader->load('identity_translator.xml');
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($config['secret'])) {
|
||||
$container->setParameter('kernel.secret', $config['secret']);
|
||||
@ -200,7 +200,10 @@ class FrameworkExtension extends Extension
|
||||
$config['validation']['enabled'] = true;
|
||||
|
||||
if (!class_exists('Symfony\Component\Validator\Validation')) {
|
||||
throw new LogicException('The Validator component is required to use the Form component.');
|
||||
$container->setParameter('validator.translation_domain', 'validators');
|
||||
|
||||
$container->removeDefinition('form.type_extension.form.validator');
|
||||
$container->removeDefinition('form.type_guesser.validator');
|
||||
}
|
||||
} else {
|
||||
$container->removeDefinition('Symfony\Component\Form\Command\DebugCommand');
|
||||
@ -287,6 +290,8 @@ class FrameworkExtension extends Extension
|
||||
->addTag('kernel.cache_warmer');
|
||||
$container->registerForAutoconfiguration(EventSubscriberInterface::class)
|
||||
->addTag('kernel.event_subscriber');
|
||||
$container->registerForAutoconfiguration(ResettableInterface::class)
|
||||
->addTag('kernel.reset', array('method' => 'reset'));
|
||||
$container->registerForAutoconfiguration(PropertyListExtractorInterface::class)
|
||||
->addTag('property_info.list_extractor');
|
||||
$container->registerForAutoconfiguration(PropertyTypeExtractorInterface::class)
|
||||
@ -342,6 +347,10 @@ class FrameworkExtension extends Extension
|
||||
} else {
|
||||
$container->setParameter('form.type_extension.csrf.enabled', false);
|
||||
}
|
||||
|
||||
if (!class_exists(Translator::class)) {
|
||||
$container->removeDefinition('form.type_extension.upload.validator');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -8,7 +8,7 @@
|
||||
<defaults public="false" />
|
||||
|
||||
<service id="cache.app" parent="cache.adapter.filesystem" public="true">
|
||||
<tag name="cache.pool" clearer="cache.app_clearer" />
|
||||
<tag name="cache.pool" clearer="cache.app_clearer" reset="reset" />
|
||||
</service>
|
||||
|
||||
<service id="cache.system" parent="cache.adapter.system" public="true">
|
||||
@ -90,7 +90,7 @@
|
||||
</service>
|
||||
|
||||
<service id="cache.adapter.memcached" class="Symfony\Component\Cache\Adapter\MemcachedAdapter" abstract="true">
|
||||
<tag name="cache.pool" provider="cache.default_memcached_provider" clearer="cache.default_clearer" />
|
||||
<tag name="cache.pool" provider="cache.default_memcached_provider" clearer="cache.default_clearer" reset="reset" />
|
||||
<tag name="monolog.logger" channel="cache" />
|
||||
<argument /> <!-- Memcached connection service -->
|
||||
<argument /> <!-- namespace -->
|
||||
|
@ -12,7 +12,7 @@
|
||||
<argument type="service" id="security.csrf.token_manager" />
|
||||
<argument>%form.type_extension.csrf.enabled%</argument>
|
||||
<argument>%form.type_extension.csrf.field_name%</argument>
|
||||
<argument type="service" id="translator" />
|
||||
<argument type="service" id="translator" on-invalid="null" />
|
||||
<argument>%validator.translation_domain%</argument>
|
||||
<argument type="service" id="form.server_params" />
|
||||
</service>
|
||||
|
@ -21,7 +21,9 @@
|
||||
</service>
|
||||
<service id="Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface" alias="security.authorization_checker" />
|
||||
|
||||
<service id="security.token_storage" class="Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage" public="true" />
|
||||
<service id="security.token_storage" class="Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage" public="true">
|
||||
<tag name="kernel.reset" method="setToken" />
|
||||
</service>
|
||||
<service id="Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface" alias="security.token_storage" />
|
||||
|
||||
<service id="security.user_value_resolver" class="Symfony\Bundle\SecurityBundle\SecurityUserValueResolver">
|
||||
|
@ -17,12 +17,13 @@ use Psr\Log\LoggerInterface;
|
||||
use Psr\Log\NullLogger;
|
||||
use Symfony\Component\Cache\CacheItem;
|
||||
use Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Cache\ResettableInterface;
|
||||
use Symfony\Component\Cache\Traits\AbstractTrait;
|
||||
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
abstract class AbstractAdapter implements AdapterInterface, LoggerAwareInterface
|
||||
abstract class AbstractAdapter implements AdapterInterface, LoggerAwareInterface, ResettableInterface
|
||||
{
|
||||
use AbstractTrait;
|
||||
|
||||
|
@ -14,12 +14,13 @@ namespace Symfony\Component\Cache\Adapter;
|
||||
use Psr\Cache\CacheItemInterface;
|
||||
use Psr\Log\LoggerAwareInterface;
|
||||
use Symfony\Component\Cache\CacheItem;
|
||||
use Symfony\Component\Cache\ResettableInterface;
|
||||
use Symfony\Component\Cache\Traits\ArrayTrait;
|
||||
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class ArrayAdapter implements AdapterInterface, LoggerAwareInterface
|
||||
class ArrayAdapter implements AdapterInterface, LoggerAwareInterface, ResettableInterface
|
||||
{
|
||||
use ArrayTrait;
|
||||
|
||||
|
@ -16,6 +16,7 @@ use Psr\Cache\CacheItemPoolInterface;
|
||||
use Symfony\Component\Cache\CacheItem;
|
||||
use Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Cache\PruneableInterface;
|
||||
use Symfony\Component\Cache\ResettableInterface;
|
||||
|
||||
/**
|
||||
* Chains several adapters together.
|
||||
@ -25,7 +26,7 @@ use Symfony\Component\Cache\PruneableInterface;
|
||||
*
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
class ChainAdapter implements AdapterInterface, PruneableInterface
|
||||
class ChainAdapter implements AdapterInterface, PruneableInterface, ResettableInterface
|
||||
{
|
||||
private $adapters = array();
|
||||
private $adapterCount;
|
||||
@ -248,4 +249,16 @@ class ChainAdapter implements AdapterInterface, PruneableInterface
|
||||
|
||||
return $pruned;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
foreach ($this->adapters as $adapter) {
|
||||
if ($adapter instanceof ResettableInterface) {
|
||||
$adapter->reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,8 @@ use Psr\Cache\CacheItemInterface;
|
||||
use Psr\Cache\CacheItemPoolInterface;
|
||||
use Symfony\Component\Cache\CacheItem;
|
||||
use Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Cache\PruneableInterface;
|
||||
use Symfony\Component\Cache\ResettableInterface;
|
||||
use Symfony\Component\Cache\Traits\PhpArrayTrait;
|
||||
|
||||
/**
|
||||
@ -24,7 +26,7 @@ use Symfony\Component\Cache\Traits\PhpArrayTrait;
|
||||
* @author Titouan Galopin <galopintitouan@gmail.com>
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class PhpArrayAdapter implements AdapterInterface
|
||||
class PhpArrayAdapter implements AdapterInterface, PruneableInterface, ResettableInterface
|
||||
{
|
||||
use PhpArrayTrait;
|
||||
|
||||
@ -37,7 +39,7 @@ class PhpArrayAdapter implements AdapterInterface
|
||||
public function __construct($file, AdapterInterface $fallbackPool)
|
||||
{
|
||||
$this->file = $file;
|
||||
$this->fallbackPool = $fallbackPool;
|
||||
$this->pool = $fallbackPool;
|
||||
$this->zendDetectUnicode = ini_get('zend.detect_unicode');
|
||||
$this->createCacheItem = \Closure::bind(
|
||||
function ($key, $value, $isHit) {
|
||||
@ -87,7 +89,7 @@ class PhpArrayAdapter implements AdapterInterface
|
||||
$this->initialize();
|
||||
}
|
||||
if (!isset($this->values[$key])) {
|
||||
return $this->fallbackPool->getItem($key);
|
||||
return $this->pool->getItem($key);
|
||||
}
|
||||
|
||||
$value = $this->values[$key];
|
||||
@ -142,7 +144,7 @@ class PhpArrayAdapter implements AdapterInterface
|
||||
$this->initialize();
|
||||
}
|
||||
|
||||
return isset($this->values[$key]) || $this->fallbackPool->hasItem($key);
|
||||
return isset($this->values[$key]) || $this->pool->hasItem($key);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -157,7 +159,7 @@ class PhpArrayAdapter implements AdapterInterface
|
||||
$this->initialize();
|
||||
}
|
||||
|
||||
return !isset($this->values[$key]) && $this->fallbackPool->deleteItem($key);
|
||||
return !isset($this->values[$key]) && $this->pool->deleteItem($key);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -184,7 +186,7 @@ class PhpArrayAdapter implements AdapterInterface
|
||||
}
|
||||
|
||||
if ($fallbackKeys) {
|
||||
$deleted = $this->fallbackPool->deleteItems($fallbackKeys) && $deleted;
|
||||
$deleted = $this->pool->deleteItems($fallbackKeys) && $deleted;
|
||||
}
|
||||
|
||||
return $deleted;
|
||||
@ -199,7 +201,7 @@ class PhpArrayAdapter implements AdapterInterface
|
||||
$this->initialize();
|
||||
}
|
||||
|
||||
return !isset($this->values[$item->getKey()]) && $this->fallbackPool->save($item);
|
||||
return !isset($this->values[$item->getKey()]) && $this->pool->save($item);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -211,7 +213,7 @@ class PhpArrayAdapter implements AdapterInterface
|
||||
$this->initialize();
|
||||
}
|
||||
|
||||
return !isset($this->values[$item->getKey()]) && $this->fallbackPool->saveDeferred($item);
|
||||
return !isset($this->values[$item->getKey()]) && $this->pool->saveDeferred($item);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -219,7 +221,7 @@ class PhpArrayAdapter implements AdapterInterface
|
||||
*/
|
||||
public function commit()
|
||||
{
|
||||
return $this->fallbackPool->commit();
|
||||
return $this->pool->commit();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -257,7 +259,7 @@ class PhpArrayAdapter implements AdapterInterface
|
||||
}
|
||||
|
||||
if ($fallbackKeys) {
|
||||
foreach ($this->fallbackPool->getItems($fallbackKeys) as $key => $item) {
|
||||
foreach ($this->pool->getItems($fallbackKeys) as $key => $item) {
|
||||
yield $key => $item;
|
||||
}
|
||||
}
|
||||
|
@ -14,13 +14,17 @@ namespace Symfony\Component\Cache\Adapter;
|
||||
use Psr\Cache\CacheItemInterface;
|
||||
use Psr\Cache\CacheItemPoolInterface;
|
||||
use Symfony\Component\Cache\CacheItem;
|
||||
use Symfony\Component\Cache\PruneableInterface;
|
||||
use Symfony\Component\Cache\ResettableInterface;
|
||||
use Symfony\Component\Cache\Traits\ProxyTrait;
|
||||
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class ProxyAdapter implements AdapterInterface
|
||||
class ProxyAdapter implements AdapterInterface, PruneableInterface, ResettableInterface
|
||||
{
|
||||
private $pool;
|
||||
use ProxyTrait;
|
||||
|
||||
private $namespace;
|
||||
private $namespaceLen;
|
||||
private $createCacheItem;
|
||||
|
@ -12,13 +12,17 @@
|
||||
namespace Symfony\Component\Cache\Adapter;
|
||||
|
||||
use Psr\SimpleCache\CacheInterface;
|
||||
use Symfony\Component\Cache\PruneableInterface;
|
||||
use Symfony\Component\Cache\ResettableInterface;
|
||||
use Symfony\Component\Cache\Traits\ProxyTrait;
|
||||
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class SimpleCacheAdapter extends AbstractAdapter
|
||||
class SimpleCacheAdapter extends AbstractAdapter implements PruneableInterface, ResettableInterface
|
||||
{
|
||||
private $pool;
|
||||
use ProxyTrait;
|
||||
|
||||
private $miss;
|
||||
|
||||
public function __construct(CacheInterface $pool, $namespace = '', $defaultLifetime = 0)
|
||||
|
@ -15,26 +15,29 @@ use Psr\Cache\CacheItemInterface;
|
||||
use Psr\Cache\InvalidArgumentException;
|
||||
use Symfony\Component\Cache\CacheItem;
|
||||
use Symfony\Component\Cache\PruneableInterface;
|
||||
use Symfony\Component\Cache\ResettableInterface;
|
||||
use Symfony\Component\Cache\Traits\ProxyTrait;
|
||||
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class TagAwareAdapter implements TagAwareAdapterInterface, PruneableInterface
|
||||
class TagAwareAdapter implements TagAwareAdapterInterface, PruneableInterface, ResettableInterface
|
||||
{
|
||||
const TAGS_PREFIX = "\0tags\0";
|
||||
|
||||
private $itemsAdapter;
|
||||
use ProxyTrait;
|
||||
|
||||
private $deferred = array();
|
||||
private $createCacheItem;
|
||||
private $setCacheItemTags;
|
||||
private $getTagsByKey;
|
||||
private $invalidateTags;
|
||||
private $tagsAdapter;
|
||||
private $tagsPool;
|
||||
|
||||
public function __construct(AdapterInterface $itemsAdapter, AdapterInterface $tagsAdapter = null)
|
||||
public function __construct(AdapterInterface $itemsPool, AdapterInterface $tagsPool = null)
|
||||
{
|
||||
$this->itemsAdapter = $itemsAdapter;
|
||||
$this->tagsAdapter = $tagsAdapter ?: $itemsAdapter;
|
||||
$this->pool = $itemsPool;
|
||||
$this->tags = $tagsPool ?: $itemsPool;
|
||||
$this->createCacheItem = \Closure::bind(
|
||||
function ($key, $value, CacheItem $protoItem) {
|
||||
$item = new CacheItem();
|
||||
@ -110,7 +113,7 @@ class TagAwareAdapter implements TagAwareAdapterInterface, PruneableInterface
|
||||
}
|
||||
$f = $this->invalidateTags;
|
||||
|
||||
return $f($this->tagsAdapter, $tags);
|
||||
return $f($this->tags, $tags);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -121,10 +124,10 @@ class TagAwareAdapter implements TagAwareAdapterInterface, PruneableInterface
|
||||
if ($this->deferred) {
|
||||
$this->commit();
|
||||
}
|
||||
if (!$this->itemsAdapter->hasItem($key)) {
|
||||
if (!$this->pool->hasItem($key)) {
|
||||
return false;
|
||||
}
|
||||
if (!$itemTags = $this->itemsAdapter->getItem(static::TAGS_PREFIX.$key)->get()) {
|
||||
if (!$itemTags = $this->pool->getItem(static::TAGS_PREFIX.$key)->get()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -165,9 +168,9 @@ class TagAwareAdapter implements TagAwareAdapterInterface, PruneableInterface
|
||||
}
|
||||
|
||||
try {
|
||||
$items = $this->itemsAdapter->getItems($tagKeys + $keys);
|
||||
$items = $this->pool->getItems($tagKeys + $keys);
|
||||
} catch (InvalidArgumentException $e) {
|
||||
$this->itemsAdapter->getItems($keys); // Should throw an exception
|
||||
$this->pool->getItems($keys); // Should throw an exception
|
||||
|
||||
throw $e;
|
||||
}
|
||||
@ -182,7 +185,7 @@ class TagAwareAdapter implements TagAwareAdapterInterface, PruneableInterface
|
||||
{
|
||||
$this->deferred = array();
|
||||
|
||||
return $this->itemsAdapter->clear();
|
||||
return $this->pool->clear();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -204,7 +207,7 @@ class TagAwareAdapter implements TagAwareAdapterInterface, PruneableInterface
|
||||
}
|
||||
}
|
||||
|
||||
return $this->itemsAdapter->deleteItems($keys);
|
||||
return $this->pool->deleteItems($keys);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -243,7 +246,7 @@ class TagAwareAdapter implements TagAwareAdapterInterface, PruneableInterface
|
||||
if ($this->deferred) {
|
||||
$items = $this->deferred;
|
||||
foreach ($items as $key => $item) {
|
||||
if (!$this->itemsAdapter->saveDeferred($item)) {
|
||||
if (!$this->pool->saveDeferred($item)) {
|
||||
unset($this->deferred[$key]);
|
||||
$ok = false;
|
||||
}
|
||||
@ -257,17 +260,17 @@ class TagAwareAdapter implements TagAwareAdapterInterface, PruneableInterface
|
||||
|
||||
foreach ($tagsByKey as $key => $tags) {
|
||||
if ($tags) {
|
||||
$this->itemsAdapter->saveDeferred($f(static::TAGS_PREFIX.$key, array_intersect_key($tagVersions, $tags), $items[$key]));
|
||||
$this->pool->saveDeferred($f(static::TAGS_PREFIX.$key, array_intersect_key($tagVersions, $tags), $items[$key]));
|
||||
} else {
|
||||
$deletedTags[] = static::TAGS_PREFIX.$key;
|
||||
}
|
||||
}
|
||||
if ($deletedTags) {
|
||||
$this->itemsAdapter->deleteItems($deletedTags);
|
||||
$this->pool->deleteItems($deletedTags);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->itemsAdapter->commit() && $ok;
|
||||
return $this->pool->commit() && $ok;
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
@ -328,23 +331,11 @@ class TagAwareAdapter implements TagAwareAdapterInterface, PruneableInterface
|
||||
$tagVersions[$tag] = $tag.static::TAGS_PREFIX;
|
||||
$tags[$tag.static::TAGS_PREFIX] = $tag;
|
||||
}
|
||||
foreach ($this->tagsAdapter->getItems($tagVersions) as $tag => $version) {
|
||||
foreach ($this->tags->getItems($tagVersions) as $tag => $version) {
|
||||
$tagVersions[$tags[$tag]] = $version->get() ?: 0;
|
||||
}
|
||||
}
|
||||
|
||||
return $tagVersions;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function prune()
|
||||
{
|
||||
if ($this->itemsAdapter instanceof PruneableInterface) {
|
||||
return $this->itemsAdapter->prune();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,8 @@
|
||||
namespace Symfony\Component\Cache\Adapter;
|
||||
|
||||
use Psr\Cache\CacheItemInterface;
|
||||
use Symfony\Component\Cache\PruneableInterface;
|
||||
use Symfony\Component\Cache\ResettableInterface;
|
||||
|
||||
/**
|
||||
* An adapter that collects data about all cache calls.
|
||||
@ -20,7 +22,7 @@ use Psr\Cache\CacheItemInterface;
|
||||
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class TraceableAdapter implements AdapterInterface
|
||||
class TraceableAdapter implements AdapterInterface, PruneableInterface, ResettableInterface
|
||||
{
|
||||
protected $pool;
|
||||
private $calls = array();
|
||||
@ -168,6 +170,38 @@ class TraceableAdapter implements AdapterInterface
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function prune()
|
||||
{
|
||||
if (!$this->pool instanceof PruneableInterface) {
|
||||
return false;
|
||||
}
|
||||
$event = $this->start(__FUNCTION__);
|
||||
try {
|
||||
return $event->result = $this->pool->prune();
|
||||
} finally {
|
||||
$event->end = microtime(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
if (!$this->pool instanceof ResettableInterface) {
|
||||
return;
|
||||
}
|
||||
$event = $this->start(__FUNCTION__);
|
||||
try {
|
||||
$this->pool->reset();
|
||||
} finally {
|
||||
$event->end = microtime(true);
|
||||
}
|
||||
}
|
||||
|
||||
public function getCalls()
|
||||
{
|
||||
try {
|
||||
|
@ -17,7 +17,7 @@ use Psr\Cache\CacheItemPoolInterface;
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class DoctrineProvider extends CacheProvider
|
||||
class DoctrineProvider extends CacheProvider implements PruneableInterface, ResettableInterface
|
||||
{
|
||||
private $pool;
|
||||
|
||||
@ -26,6 +26,25 @@ class DoctrineProvider extends CacheProvider
|
||||
$this->pool = $pool;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function prune()
|
||||
{
|
||||
return $this->pool instanceof PruneableInterface && $this->pool->prune();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
if ($this->pool instanceof ResettableInterface) {
|
||||
$this->pool->reset();
|
||||
}
|
||||
$this->setNamespace($this->getNamespace());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
20
src/Symfony/Component/Cache/ResettableInterface.php
Normal file
20
src/Symfony/Component/Cache/ResettableInterface.php
Normal file
@ -0,0 +1,20 @@
|
||||
<?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\Cache;
|
||||
|
||||
/**
|
||||
* Resets a pool's local state.
|
||||
*/
|
||||
interface ResettableInterface
|
||||
{
|
||||
public function reset();
|
||||
}
|
@ -16,11 +16,12 @@ use Psr\SimpleCache\CacheInterface;
|
||||
use Symfony\Component\Cache\CacheItem;
|
||||
use Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Cache\Traits\AbstractTrait;
|
||||
use Symfony\Component\Cache\ResettableInterface;
|
||||
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
abstract class AbstractCache implements CacheInterface, LoggerAwareInterface
|
||||
abstract class AbstractCache implements CacheInterface, LoggerAwareInterface, ResettableInterface
|
||||
{
|
||||
use AbstractTrait {
|
||||
deleteItems as private;
|
||||
|
@ -15,12 +15,13 @@ use Psr\Log\LoggerAwareInterface;
|
||||
use Psr\SimpleCache\CacheInterface;
|
||||
use Symfony\Component\Cache\CacheItem;
|
||||
use Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Cache\ResettableInterface;
|
||||
use Symfony\Component\Cache\Traits\ArrayTrait;
|
||||
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class ArrayCache implements CacheInterface, LoggerAwareInterface
|
||||
class ArrayCache implements CacheInterface, LoggerAwareInterface, ResettableInterface
|
||||
{
|
||||
use ArrayTrait {
|
||||
ArrayTrait::deleteItem as delete;
|
||||
|
@ -14,6 +14,7 @@ namespace Symfony\Component\Cache\Simple;
|
||||
use Psr\SimpleCache\CacheInterface;
|
||||
use Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Cache\PruneableInterface;
|
||||
use Symfony\Component\Cache\ResettableInterface;
|
||||
|
||||
/**
|
||||
* Chains several caches together.
|
||||
@ -23,7 +24,7 @@ use Symfony\Component\Cache\PruneableInterface;
|
||||
*
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class ChainCache implements CacheInterface, PruneableInterface
|
||||
class ChainCache implements CacheInterface, PruneableInterface, ResettableInterface
|
||||
{
|
||||
private $miss;
|
||||
private $caches = array();
|
||||
@ -236,4 +237,16 @@ class ChainCache implements CacheInterface, PruneableInterface
|
||||
|
||||
return $pruned;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
foreach ($this->caches as $cache) {
|
||||
if ($cache instanceof ResettableInterface) {
|
||||
$cache->reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,8 @@ namespace Symfony\Component\Cache\Simple;
|
||||
use Psr\SimpleCache\CacheInterface;
|
||||
use Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Cache\Traits\PhpArrayTrait;
|
||||
use Symfony\Component\Cache\PruneableInterface;
|
||||
use Symfony\Component\Cache\ResettableInterface;
|
||||
|
||||
/**
|
||||
* Caches items at warm up time using a PHP array that is stored in shared memory by OPCache since PHP 7.0.
|
||||
@ -22,7 +24,7 @@ use Symfony\Component\Cache\Traits\PhpArrayTrait;
|
||||
* @author Titouan Galopin <galopintitouan@gmail.com>
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class PhpArrayCache implements CacheInterface
|
||||
class PhpArrayCache implements CacheInterface, PruneableInterface, ResettableInterface
|
||||
{
|
||||
use PhpArrayTrait;
|
||||
|
||||
@ -33,7 +35,7 @@ class PhpArrayCache implements CacheInterface
|
||||
public function __construct($file, CacheInterface $fallbackPool)
|
||||
{
|
||||
$this->file = $file;
|
||||
$this->fallbackPool = $fallbackPool;
|
||||
$this->pool = $fallbackPool;
|
||||
$this->zendDetectUnicode = ini_get('zend.detect_unicode');
|
||||
}
|
||||
|
||||
@ -66,7 +68,7 @@ class PhpArrayCache implements CacheInterface
|
||||
$this->initialize();
|
||||
}
|
||||
if (!isset($this->values[$key])) {
|
||||
return $this->fallbackPool->get($key, $default);
|
||||
return $this->pool->get($key, $default);
|
||||
}
|
||||
|
||||
$value = $this->values[$key];
|
||||
@ -122,7 +124,7 @@ class PhpArrayCache implements CacheInterface
|
||||
$this->initialize();
|
||||
}
|
||||
|
||||
return isset($this->values[$key]) || $this->fallbackPool->has($key);
|
||||
return isset($this->values[$key]) || $this->pool->has($key);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -137,7 +139,7 @@ class PhpArrayCache implements CacheInterface
|
||||
$this->initialize();
|
||||
}
|
||||
|
||||
return !isset($this->values[$key]) && $this->fallbackPool->delete($key);
|
||||
return !isset($this->values[$key]) && $this->pool->delete($key);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -168,7 +170,7 @@ class PhpArrayCache implements CacheInterface
|
||||
}
|
||||
|
||||
if ($fallbackKeys) {
|
||||
$deleted = $this->fallbackPool->deleteMultiple($fallbackKeys) && $deleted;
|
||||
$deleted = $this->pool->deleteMultiple($fallbackKeys) && $deleted;
|
||||
}
|
||||
|
||||
return $deleted;
|
||||
@ -186,7 +188,7 @@ class PhpArrayCache implements CacheInterface
|
||||
$this->initialize();
|
||||
}
|
||||
|
||||
return !isset($this->values[$key]) && $this->fallbackPool->set($key, $value, $ttl);
|
||||
return !isset($this->values[$key]) && $this->pool->set($key, $value, $ttl);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -214,7 +216,7 @@ class PhpArrayCache implements CacheInterface
|
||||
}
|
||||
|
||||
if ($fallbackValues) {
|
||||
$saved = $this->fallbackPool->setMultiple($fallbackValues, $ttl) && $saved;
|
||||
$saved = $this->pool->setMultiple($fallbackValues, $ttl) && $saved;
|
||||
}
|
||||
|
||||
return $saved;
|
||||
@ -247,7 +249,7 @@ class PhpArrayCache implements CacheInterface
|
||||
}
|
||||
|
||||
if ($fallbackKeys) {
|
||||
foreach ($this->fallbackPool->getMultiple($fallbackKeys, $default) as $key => $item) {
|
||||
foreach ($this->pool->getMultiple($fallbackKeys, $default) as $key => $item) {
|
||||
yield $key => $item;
|
||||
}
|
||||
}
|
||||
|
@ -18,13 +18,17 @@ use Psr\SimpleCache\CacheException as SimpleCacheException;
|
||||
use Symfony\Component\Cache\Adapter\AbstractAdapter;
|
||||
use Symfony\Component\Cache\CacheItem;
|
||||
use Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Cache\PruneableInterface;
|
||||
use Symfony\Component\Cache\ResettableInterface;
|
||||
use Symfony\Component\Cache\Traits\ProxyTrait;
|
||||
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class Psr6Cache implements CacheInterface
|
||||
class Psr6Cache implements CacheInterface, PruneableInterface, ResettableInterface
|
||||
{
|
||||
private $pool;
|
||||
use ProxyTrait;
|
||||
|
||||
private $createCacheItem;
|
||||
|
||||
public function __construct(CacheItemPoolInterface $pool)
|
||||
|
@ -12,13 +12,15 @@
|
||||
namespace Symfony\Component\Cache\Simple;
|
||||
|
||||
use Psr\SimpleCache\CacheInterface;
|
||||
use Symfony\Component\Cache\PruneableInterface;
|
||||
use Symfony\Component\Cache\ResettableInterface;
|
||||
|
||||
/**
|
||||
* An adapter that collects data about all cache calls.
|
||||
*
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class TraceableCache implements CacheInterface
|
||||
class TraceableCache implements CacheInterface, PruneableInterface, ResettableInterface
|
||||
{
|
||||
private $pool;
|
||||
private $miss;
|
||||
@ -177,6 +179,38 @@ class TraceableCache implements CacheInterface
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function prune()
|
||||
{
|
||||
if (!$this->pool instanceof PruneableInterface) {
|
||||
return false;
|
||||
}
|
||||
$event = $this->start(__FUNCTION__);
|
||||
try {
|
||||
return $event->result = $this->pool->prune();
|
||||
} finally {
|
||||
$event->end = microtime(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
if (!$this->pool instanceof ResettableInterface) {
|
||||
return;
|
||||
}
|
||||
$event = $this->start(__FUNCTION__);
|
||||
try {
|
||||
$this->pool->reset();
|
||||
} finally {
|
||||
$event->end = microtime(true);
|
||||
}
|
||||
}
|
||||
|
||||
public function getCalls()
|
||||
{
|
||||
try {
|
||||
|
@ -50,6 +50,7 @@ class PhpArrayAdapterTest extends AdapterTestCase
|
||||
'testDeleteItemsInvalidKeys' => 'PhpArrayAdapter does not throw exceptions on invalid key.',
|
||||
|
||||
'testDefaultLifeTime' => 'PhpArrayAdapter does not allow configuring a default lifetime.',
|
||||
'testPrune' => 'PhpArrayAdapter just proxies',
|
||||
);
|
||||
|
||||
protected static $file;
|
||||
|
@ -25,6 +25,7 @@ class PhpArrayAdapterWithFallbackTest extends AdapterTestCase
|
||||
'testHasItemInvalidKeys' => 'PhpArrayAdapter does not throw exceptions on invalid key.',
|
||||
'testDeleteItemInvalidKeys' => 'PhpArrayAdapter does not throw exceptions on invalid key.',
|
||||
'testDeleteItemsInvalidKeys' => 'PhpArrayAdapter does not throw exceptions on invalid key.',
|
||||
'testPrune' => 'PhpArrayAdapter just proxies',
|
||||
);
|
||||
|
||||
protected static $file;
|
||||
|
@ -24,6 +24,7 @@ class ProxyAdapterTest extends AdapterTestCase
|
||||
protected $skippedTests = array(
|
||||
'testDeferredSaveWithoutCommit' => 'Assumes a shared cache which ArrayAdapter is not.',
|
||||
'testSaveWithoutExpire' => 'Assumes a shared cache which ArrayAdapter is not.',
|
||||
'testPrune' => 'ProxyAdapter just proxies',
|
||||
);
|
||||
|
||||
public function createCachePool($defaultLifetime = 0)
|
||||
|
@ -19,6 +19,10 @@ use Symfony\Component\Cache\Adapter\SimpleCacheAdapter;
|
||||
*/
|
||||
class SimpleCacheAdapterTest extends AdapterTestCase
|
||||
{
|
||||
protected $skippedTests = array(
|
||||
'testPrune' => 'SimpleCache just proxies',
|
||||
);
|
||||
|
||||
public function createCachePool($defaultLifetime = 0)
|
||||
{
|
||||
return new SimpleCacheAdapter(new FilesystemCache(), '', $defaultLifetime);
|
||||
|
@ -19,6 +19,10 @@ use Symfony\Component\Cache\Adapter\TraceableAdapter;
|
||||
*/
|
||||
class TraceableAdapterTest extends AdapterTestCase
|
||||
{
|
||||
protected $skippedTests = array(
|
||||
'testPrune' => 'TraceableAdapter just proxies',
|
||||
);
|
||||
|
||||
public function createCachePool($defaultLifetime = 0)
|
||||
{
|
||||
return new TraceableAdapter(new FilesystemAdapter('', $defaultLifetime));
|
||||
|
@ -44,6 +44,7 @@ class PhpArrayCacheTest extends CacheTestCase
|
||||
'testSetValidData' => 'PhpArrayCache does no validation',
|
||||
|
||||
'testDefaultLifeTime' => 'PhpArrayCache does not allow configuring a default lifetime.',
|
||||
'testPrune' => 'PhpArrayCache just proxies',
|
||||
);
|
||||
|
||||
protected static $file;
|
||||
|
@ -31,6 +31,7 @@ class PhpArrayCacheWithFallbackTest extends CacheTestCase
|
||||
'testSetMultipleInvalidKeys' => 'PhpArrayCache does no validation',
|
||||
'testSetMultipleInvalidTtl' => 'PhpArrayCache does no validation',
|
||||
'testHasInvalidKeys' => 'PhpArrayCache does no validation',
|
||||
'testPrune' => 'PhpArrayCache just proxies',
|
||||
);
|
||||
|
||||
protected static $file;
|
||||
|
@ -19,6 +19,10 @@ use Symfony\Component\Cache\Simple\Psr6Cache;
|
||||
*/
|
||||
class Psr6CacheTest extends CacheTestCase
|
||||
{
|
||||
protected $skippedTests = array(
|
||||
'testPrune' => 'Psr6Cache just proxies',
|
||||
);
|
||||
|
||||
public function createSimpleCache($defaultLifetime = 0)
|
||||
{
|
||||
return new Psr6Cache(new FilesystemAdapter('', $defaultLifetime));
|
||||
|
@ -19,6 +19,10 @@ use Symfony\Component\Cache\Simple\TraceableCache;
|
||||
*/
|
||||
class TraceableCacheTest extends CacheTestCase
|
||||
{
|
||||
protected $skippedTests = array(
|
||||
'testPrune' => 'TraceableCache just proxies',
|
||||
);
|
||||
|
||||
public function createSimpleCache($defaultLifetime = 0)
|
||||
{
|
||||
return new TraceableCache(new FilesystemCache('', $defaultLifetime));
|
||||
|
@ -189,6 +189,17 @@ trait AbstractTrait
|
||||
return $wasEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
if ($this->deferred) {
|
||||
$this->commit();
|
||||
}
|
||||
$this->namespaceVersion = '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Like the native unserialize() function but throws an exception if anything goes wrong.
|
||||
*
|
||||
|
@ -69,6 +69,14 @@ trait ArrayTrait
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
$this->clear();
|
||||
}
|
||||
|
||||
private function generateItems(array $keys, $now, $f)
|
||||
{
|
||||
foreach ($keys as $i => $key) {
|
||||
|
@ -20,6 +20,15 @@ trait DoctrineTrait
|
||||
{
|
||||
private $provider;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
parent::reset();
|
||||
$this->provider->setNamespace($this->provider->getNamespace());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@ -22,9 +22,10 @@ use Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||
*/
|
||||
trait PhpArrayTrait
|
||||
{
|
||||
use ProxyTrait;
|
||||
|
||||
private $file;
|
||||
private $values;
|
||||
private $fallbackPool;
|
||||
private $zendDetectUnicode;
|
||||
|
||||
/**
|
||||
@ -119,7 +120,7 @@ EOF;
|
||||
|
||||
$cleared = @unlink($this->file) || !file_exists($this->file);
|
||||
|
||||
return $this->fallbackPool->clear() && $cleared;
|
||||
return $this->pool->clear() && $cleared;
|
||||
}
|
||||
|
||||
/**
|
||||
|
41
src/Symfony/Component/Cache/Traits/ProxyTrait.php
Normal file
41
src/Symfony/Component/Cache/Traits/ProxyTrait.php
Normal file
@ -0,0 +1,41 @@
|
||||
<?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\Cache\Traits;
|
||||
|
||||
use Symfony\Component\Cache\PruneableInterface;
|
||||
use Symfony\Component\Cache\ResettableInterface;
|
||||
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
trait ProxyTrait
|
||||
{
|
||||
private $pool;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function prune()
|
||||
{
|
||||
return $this->pool instanceof PruneableInterface && $this->pool->prune();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
if ($this->pool instanceof ResettableInterface) {
|
||||
$this->pool->reset();
|
||||
}
|
||||
}
|
||||
}
|
@ -22,6 +22,9 @@ use Symfony\Component\DependencyInjection\Reference;
|
||||
*/
|
||||
abstract class AbstractRecursivePass implements CompilerPassInterface
|
||||
{
|
||||
/**
|
||||
* @var ContainerBuilder
|
||||
*/
|
||||
protected $container;
|
||||
protected $currentId;
|
||||
|
||||
@ -90,7 +93,7 @@ abstract class AbstractRecursivePass implements CompilerPassInterface
|
||||
{
|
||||
if (is_string($factory = $definition->getFactory())) {
|
||||
if (!function_exists($factory)) {
|
||||
throw new RuntimeException(sprintf('Unable to resolve service "%s": function "%s" does not exist.', $this->currentId, $factory));
|
||||
throw new RuntimeException(sprintf('Invalid service "%s": function "%s" does not exist.', $this->currentId, $factory));
|
||||
}
|
||||
$r = new \ReflectionFunction($factory);
|
||||
if (false !== $r->getFileName() && file_exists($r->getFileName())) {
|
||||
@ -108,7 +111,7 @@ abstract class AbstractRecursivePass implements CompilerPassInterface
|
||||
$class = $definition->getClass();
|
||||
}
|
||||
if ('__construct' === $method) {
|
||||
throw new RuntimeException(sprintf('Unable to resolve service "%s": "__construct()" cannot be used as a factory method.', $this->currentId));
|
||||
throw new RuntimeException(sprintf('Invalid service "%s": "__construct()" cannot be used as a factory method.', $this->currentId));
|
||||
}
|
||||
|
||||
return $this->getReflectionMethod(new Definition($class), $method);
|
||||
@ -117,14 +120,14 @@ abstract class AbstractRecursivePass implements CompilerPassInterface
|
||||
$class = $definition->getClass();
|
||||
|
||||
if (!$r = $this->container->getReflectionClass($class)) {
|
||||
throw new RuntimeException(sprintf('Unable to resolve service "%s": class "%s" does not exist.', $this->currentId, $class));
|
||||
throw new RuntimeException(sprintf('Invalid service "%s": class "%s" does not exist.', $this->currentId, $class));
|
||||
}
|
||||
if (!$r = $r->getConstructor()) {
|
||||
if ($required) {
|
||||
throw new RuntimeException(sprintf('Unable to resolve service "%s": class%s has no constructor.', $this->currentId, sprintf($class !== $this->currentId ? ' "%s"' : '', $class)));
|
||||
throw new RuntimeException(sprintf('Invalid service "%s": class%s has no constructor.', $this->currentId, sprintf($class !== $this->currentId ? ' "%s"' : '', $class)));
|
||||
}
|
||||
} elseif (!$r->isPublic()) {
|
||||
throw new RuntimeException(sprintf('Unable to resolve service "%s": %s must be public.', $this->currentId, sprintf($class !== $this->currentId ? 'constructor of class "%s"' : 'its constructor', $class)));
|
||||
throw new RuntimeException(sprintf('Invalid service "%s": %s must be public.', $this->currentId, sprintf($class !== $this->currentId ? 'constructor of class "%s"' : 'its constructor', $class)));
|
||||
}
|
||||
|
||||
return $r;
|
||||
@ -145,20 +148,20 @@ abstract class AbstractRecursivePass implements CompilerPassInterface
|
||||
}
|
||||
|
||||
if (!$class = $definition->getClass()) {
|
||||
throw new RuntimeException(sprintf('Unable to resolve service "%s": the class is not set.', $this->currentId));
|
||||
throw new RuntimeException(sprintf('Invalid service "%s": the class is not set.', $this->currentId));
|
||||
}
|
||||
|
||||
if (!$r = $this->container->getReflectionClass($class)) {
|
||||
throw new RuntimeException(sprintf('Unable to resolve service "%s": class "%s" does not exist.', $this->currentId, $class));
|
||||
throw new RuntimeException(sprintf('Invalid service "%s": class "%s" does not exist.', $this->currentId, $class));
|
||||
}
|
||||
|
||||
if (!$r->hasMethod($method)) {
|
||||
throw new RuntimeException(sprintf('Unable to resolve service "%s": method "%s()" does not exist.', $this->currentId, $class !== $this->currentId ? $class.'::'.$method : $method));
|
||||
throw new RuntimeException(sprintf('Invalid service "%s": method "%s()" does not exist.', $this->currentId, $class !== $this->currentId ? $class.'::'.$method : $method));
|
||||
}
|
||||
|
||||
$r = $r->getMethod($method);
|
||||
if (!$r->isPublic()) {
|
||||
throw new RuntimeException(sprintf('Unable to resolve service "%s": method "%s()" must be public.', $this->currentId, $class !== $this->currentId ? $class.'::'.$method : $method));
|
||||
throw new RuntimeException(sprintf('Invalid service "%s": method "%s()" must be public.', $this->currentId, $class !== $this->currentId ? $class.'::'.$method : $method));
|
||||
}
|
||||
|
||||
return $r;
|
||||
|
@ -11,11 +11,15 @@
|
||||
|
||||
namespace Symfony\Component\DependencyInjection\Compiler;
|
||||
|
||||
@trigger_error('The '.__NAMESPACE__.'\AutowireExceptionPass class is deprecated since version 3.4 and will be removed in 4.0. Use the DefinitionErrorExceptionPass class instead.', E_USER_DEPRECATED);
|
||||
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
|
||||
/**
|
||||
* Throws autowire exceptions from AutowirePass for definitions that still exist.
|
||||
*
|
||||
* @deprecated since version 3.4, will be removed in 4.0.
|
||||
*
|
||||
* @author Ryan Weaver <ryan@knpuniversity.com>
|
||||
*/
|
||||
class AutowireExceptionPass implements CompilerPassInterface
|
||||
|
@ -34,7 +34,7 @@ class AutowirePass extends AbstractRecursivePass
|
||||
private $autowiringExceptions = array();
|
||||
|
||||
/**
|
||||
* @param bool $throwOnAutowireException If false, retrieved errors via getAutowiringExceptions
|
||||
* @param bool $throwOnAutowireException Errors can be retrieved via Definition::getErrors()
|
||||
*/
|
||||
public function __construct($throwOnAutowireException = true)
|
||||
{
|
||||
@ -42,10 +42,14 @@ class AutowirePass extends AbstractRecursivePass
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since version 3.4, to be removed in 4.0.
|
||||
*
|
||||
* @return AutowiringFailedException[]
|
||||
*/
|
||||
public function getAutowiringExceptions()
|
||||
{
|
||||
@trigger_error('Calling AutowirePass::getAutowiringExceptions() is deprecated since Symfony 3.4 and will be removed in 4.0. Use Definition::getErrors() instead.', E_USER_DEPRECATED);
|
||||
|
||||
return $this->autowiringExceptions;
|
||||
}
|
||||
|
||||
@ -79,6 +83,7 @@ class AutowirePass extends AbstractRecursivePass
|
||||
}
|
||||
|
||||
$this->autowiringExceptions[] = $e;
|
||||
$this->container->getDefinition($this->currentId)->addError($e->getMessage());
|
||||
|
||||
return parent::processValue($value, $isRoot);
|
||||
}
|
||||
@ -103,7 +108,6 @@ class AutowirePass extends AbstractRecursivePass
|
||||
return $value;
|
||||
}
|
||||
|
||||
$autowiredMethods = $this->getMethodsToAutowire($reflectionClass);
|
||||
$methodCalls = $value->getMethodCalls();
|
||||
|
||||
try {
|
||||
@ -116,7 +120,7 @@ class AutowirePass extends AbstractRecursivePass
|
||||
array_unshift($methodCalls, array($constructor, $value->getArguments()));
|
||||
}
|
||||
|
||||
$methodCalls = $this->autowireCalls($reflectionClass, $methodCalls, $autowiredMethods);
|
||||
$methodCalls = $this->autowireCalls($reflectionClass, $methodCalls);
|
||||
|
||||
if ($constructor) {
|
||||
list(, $arguments) = array_shift($methodCalls);
|
||||
@ -133,62 +137,19 @@ class AutowirePass extends AbstractRecursivePass
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of methods to autowire.
|
||||
*
|
||||
* @param \ReflectionClass $reflectionClass
|
||||
*
|
||||
* @return \ReflectionMethod[]
|
||||
*/
|
||||
private function getMethodsToAutowire(\ReflectionClass $reflectionClass)
|
||||
{
|
||||
$methodsToAutowire = array();
|
||||
|
||||
foreach ($reflectionClass->getMethods() as $reflectionMethod) {
|
||||
$r = $reflectionMethod;
|
||||
|
||||
if ($r->isConstructor()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
if (false !== $doc = $r->getDocComment()) {
|
||||
if (false !== stripos($doc, '@required') && preg_match('#(?:^/\*\*|\n\s*+\*)\s*+@required(?:\s|\*/$)#i', $doc)) {
|
||||
$methodsToAutowire[strtolower($reflectionMethod->name)] = $reflectionMethod;
|
||||
break;
|
||||
}
|
||||
if (false === stripos($doc, '@inheritdoc') || !preg_match('#(?:^/\*\*|\n\s*+\*)\s*+(?:\{@inheritdoc\}|@inheritdoc)(?:\s|\*/$)#i', $doc)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
try {
|
||||
$r = $r->getPrototype();
|
||||
} catch (\ReflectionException $e) {
|
||||
break; // method has no prototype
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $methodsToAutowire;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \ReflectionClass $reflectionClass
|
||||
* @param array $methodCalls
|
||||
* @param \ReflectionMethod[] $autowiredMethods
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function autowireCalls(\ReflectionClass $reflectionClass, array $methodCalls, array $autowiredMethods)
|
||||
private function autowireCalls(\ReflectionClass $reflectionClass, array $methodCalls)
|
||||
{
|
||||
foreach ($methodCalls as $i => $call) {
|
||||
list($method, $arguments) = $call;
|
||||
|
||||
if ($method instanceof \ReflectionFunctionAbstract) {
|
||||
$reflectionMethod = $method;
|
||||
} elseif (isset($autowiredMethods[$lcMethod = strtolower($method)]) && $autowiredMethods[$lcMethod]->isPublic()) {
|
||||
$reflectionMethod = $autowiredMethods[$lcMethod];
|
||||
unset($autowiredMethods[$lcMethod]);
|
||||
} else {
|
||||
$reflectionMethod = $this->getReflectionMethod(new Definition($reflectionClass->name), $method);
|
||||
}
|
||||
@ -200,16 +161,6 @@ class AutowirePass extends AbstractRecursivePass
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($autowiredMethods as $lcMethod => $reflectionMethod) {
|
||||
$method = $reflectionMethod->name;
|
||||
|
||||
if (!$reflectionMethod->isPublic()) {
|
||||
$class = $reflectionClass->name;
|
||||
throw new AutowiringFailedException($this->currentId, sprintf('Cannot autowire service "%s": method "%s()" must be public.', $this->currentId, $class !== $this->currentId ? $class.'::'.$method : $method));
|
||||
}
|
||||
$methodCalls[] = array($method, $this->autowireMethod($reflectionMethod, array()));
|
||||
}
|
||||
|
||||
return $methodCalls;
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\DependencyInjection\Compiler;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Definition;
|
||||
|
||||
/**
|
||||
* Looks for definitions with autowiring enabled and registers their corresponding "@required" methods as setters.
|
||||
*
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class AutowireRequiredMethodsPass extends AbstractRecursivePass
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function processValue($value, $isRoot = false)
|
||||
{
|
||||
$value = parent::processValue($value, $isRoot);
|
||||
|
||||
if (!$value instanceof Definition || !$value->isAutowired() || $value->isAbstract() || !$value->getClass()) {
|
||||
return $value;
|
||||
}
|
||||
if (!$reflectionClass = $this->container->getReflectionClass($value->getClass(), false)) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
$alreadyCalledMethods = array();
|
||||
|
||||
foreach ($value->getMethodCalls() as list($method)) {
|
||||
$alreadyCalledMethods[strtolower($method)] = true;
|
||||
}
|
||||
|
||||
foreach ($reflectionClass->getMethods() as $reflectionMethod) {
|
||||
$r = $reflectionMethod;
|
||||
|
||||
if ($r->isConstructor() || isset($alreadyCalledMethods[strtolower($r->name)])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
if (false !== $doc = $r->getDocComment()) {
|
||||
if (false !== stripos($doc, '@required') && preg_match('#(?:^/\*\*|\n\s*+\*)\s*+@required(?:\s|\*/$)#i', $doc)) {
|
||||
$value->addMethodCall($reflectionMethod->name);
|
||||
break;
|
||||
}
|
||||
if (false === stripos($doc, '@inheritdoc') || !preg_match('#(?:^/\*\*|\n\s*+\*)\s*+(?:\{@inheritdoc\}|@inheritdoc)(?:\s|\*/$)#i', $doc)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
try {
|
||||
$r = $r->getPrototype();
|
||||
} catch (\ReflectionException $e) {
|
||||
break; // method has no prototype
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
}
|
@ -22,6 +22,13 @@ use Symfony\Component\DependencyInjection\Exception\RuntimeException;
|
||||
*/
|
||||
class CheckArgumentsValidityPass extends AbstractRecursivePass
|
||||
{
|
||||
private $throwExceptions;
|
||||
|
||||
public function __construct($throwExceptions = true)
|
||||
{
|
||||
$this->throwExceptions = $throwExceptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
@ -35,10 +42,20 @@ class CheckArgumentsValidityPass extends AbstractRecursivePass
|
||||
foreach ($value->getArguments() as $k => $v) {
|
||||
if ($k !== $i++) {
|
||||
if (!is_int($k)) {
|
||||
throw new RuntimeException(sprintf('Invalid constructor argument for service "%s": integer expected but found string "%s". Check your service definition.', $this->currentId, $k));
|
||||
$msg = sprintf('Invalid constructor argument for service "%s": integer expected but found string "%s". Check your service definition.', $this->currentId, $k);
|
||||
$value->addError($msg);
|
||||
if ($this->throwExceptions) {
|
||||
throw new RuntimeException($msg);
|
||||
}
|
||||
|
||||
throw new RuntimeException(sprintf('Invalid constructor argument %d for service "%s": argument %d must be defined before. Check your service definition.', 1 + $k, $this->currentId, $i));
|
||||
break;
|
||||
}
|
||||
|
||||
$msg = sprintf('Invalid constructor argument %d for service "%s": argument %d must be defined before. Check your service definition.', 1 + $k, $this->currentId, $i);
|
||||
$value->addError($msg);
|
||||
if ($this->throwExceptions) {
|
||||
throw new RuntimeException($msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -47,10 +64,20 @@ class CheckArgumentsValidityPass extends AbstractRecursivePass
|
||||
foreach ($methodCall[1] as $k => $v) {
|
||||
if ($k !== $i++) {
|
||||
if (!is_int($k)) {
|
||||
throw new RuntimeException(sprintf('Invalid argument for method call "%s" of service "%s": integer expected but found string "%s". Check your service definition.', $methodCall[0], $this->currentId, $k));
|
||||
$msg = sprintf('Invalid argument for method call "%s" of service "%s": integer expected but found string "%s". Check your service definition.', $methodCall[0], $this->currentId, $k);
|
||||
$value->addError($msg);
|
||||
if ($this->throwExceptions) {
|
||||
throw new RuntimeException($msg);
|
||||
}
|
||||
|
||||
throw new RuntimeException(sprintf('Invalid argument %d for method call "%s" of service "%s": argument %d must be defined before. Check your service definition.', 1 + $k, $methodCall[0], $this->currentId, $i));
|
||||
break;
|
||||
}
|
||||
|
||||
$msg = sprintf('Invalid argument %d for method call "%s" of service "%s": argument %d must be defined before. Check your service definition.', 1 + $k, $methodCall[0], $this->currentId, $i);
|
||||
$value->addError($msg);
|
||||
if ($this->throwExceptions) {
|
||||
throw new RuntimeException($msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\DependencyInjection\Compiler;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Definition;
|
||||
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
|
||||
|
||||
/**
|
||||
* Throws an exception for any Definitions that have errors and still exist.
|
||||
*
|
||||
* @author Ryan Weaver <ryan@knpuniversity.com>
|
||||
*/
|
||||
class DefinitionErrorExceptionPass extends AbstractRecursivePass
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function processValue($value, $isRoot = false)
|
||||
{
|
||||
if (!$value instanceof Definition || empty($value->getErrors())) {
|
||||
return parent::processValue($value, $isRoot);
|
||||
}
|
||||
|
||||
// only show the first error so they user can focus on it
|
||||
$errors = $value->getErrors();
|
||||
$message = reset($errors);
|
||||
|
||||
throw new RuntimeException($message);
|
||||
}
|
||||
}
|
@ -38,10 +38,14 @@ class InlineServiceDefinitionsPass extends AbstractRecursivePass implements Repe
|
||||
*
|
||||
* The key is the inlined service id and its value is the list of services it was inlined into.
|
||||
*
|
||||
* @deprecated since version 3.4, to be removed in 4.0.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getInlinedServiceIds()
|
||||
{
|
||||
@trigger_error('Calling InlineServiceDefinitionsPass::getInlinedServiceIds() is deprecated since Symfony 3.4 and will be removed in 4.0.', E_USER_DEPRECATED);
|
||||
|
||||
return $this->inlinedServiceIds;
|
||||
}
|
||||
|
||||
|
@ -12,10 +12,13 @@
|
||||
namespace Symfony\Component\DependencyInjection\Compiler;
|
||||
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Exception\LogicException;
|
||||
use Symfony\Component\DependencyInjection\Extension\ConfigurationExtensionInterface;
|
||||
use Symfony\Component\DependencyInjection\Extension\Extension;
|
||||
use Symfony\Component\DependencyInjection\Extension\ExtensionInterface;
|
||||
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
|
||||
|
||||
/**
|
||||
* Merges extension configs into the container builder.
|
||||
@ -52,7 +55,7 @@ class MergeExtensionConfigurationPass implements CompilerPassInterface
|
||||
}
|
||||
$config = $resolvingBag->resolveValue($config);
|
||||
|
||||
$tmpContainer = new ContainerBuilder($resolvingBag);
|
||||
$tmpContainer = new MergeExtensionConfigurationContainerBuilder($extension, $resolvingBag);
|
||||
$tmpContainer->setResourceTracking($container->isTrackingResources());
|
||||
$tmpContainer->addObjectResource($extension);
|
||||
if ($extension instanceof ConfigurationExtensionInterface && null !== $configuration = $extension->getConfiguration($config, $tmpContainer)) {
|
||||
@ -121,3 +124,44 @@ class MergeExtensionConfigurationParameterBag extends EnvPlaceholderParameterBag
|
||||
return null !== $this->processedEnvPlaceholders ? $this->processedEnvPlaceholders : parent::getEnvPlaceholders();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A container builder preventing using methods that wouldn't have any effect from extensions.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class MergeExtensionConfigurationContainerBuilder extends ContainerBuilder
|
||||
{
|
||||
private $extensionClass;
|
||||
|
||||
public function __construct(ExtensionInterface $extension, ParameterBagInterface $parameterBag = null)
|
||||
{
|
||||
parent::__construct($parameterBag);
|
||||
|
||||
$this->extensionClass = get_class($extension);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addCompilerPass(CompilerPassInterface $pass, $type = PassConfig::TYPE_BEFORE_OPTIMIZATION/*, int $priority = 0*/)
|
||||
{
|
||||
throw new LogicException(sprintf('You cannot add compiler pass "%s" from extension "%s". Compiler passes must be registered before the container is compiled.', get_class($pass), $this->extensionClass));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function registerExtension(ExtensionInterface $extension)
|
||||
{
|
||||
throw new LogicException(sprintf('You cannot register extension "%s" from "%s". Extensions must be registered before the container is compiled.', get_class($extension), $this->extensionClass));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function compile($resolveEnvPlaceholders = false)
|
||||
{
|
||||
throw new LogicException(sprintf('Cannot compile the container in extension "%s".', $this->extensionClass));
|
||||
}
|
||||
}
|
||||
|
@ -57,15 +57,16 @@ class PassConfig
|
||||
new CheckDefinitionValidityPass(),
|
||||
new RegisterServiceSubscribersPass(),
|
||||
new ResolveNamedArgumentsPass(),
|
||||
new AutowireRequiredMethodsPass(),
|
||||
new ResolveBindingsPass(),
|
||||
$autowirePass = new AutowirePass(false),
|
||||
new AutowirePass(false),
|
||||
new ResolveServiceSubscribersPass(),
|
||||
new ResolveReferencesToAliasesPass(),
|
||||
new ResolveInvalidReferencesPass(),
|
||||
new AnalyzeServiceReferencesPass(true),
|
||||
new CheckCircularReferencesPass(),
|
||||
new CheckReferenceValidityPass(),
|
||||
new CheckArgumentsValidityPass(),
|
||||
new CheckArgumentsValidityPass(false),
|
||||
));
|
||||
|
||||
$this->removingPasses = array(array(
|
||||
@ -74,11 +75,11 @@ class PassConfig
|
||||
new RemoveAbstractDefinitionsPass(),
|
||||
new RepeatedPass(array(
|
||||
new AnalyzeServiceReferencesPass(),
|
||||
$inlinedServicePass = new InlineServiceDefinitionsPass(),
|
||||
new InlineServiceDefinitionsPass(),
|
||||
new AnalyzeServiceReferencesPass(),
|
||||
new RemoveUnusedDefinitionsPass(),
|
||||
)),
|
||||
new AutowireExceptionPass($autowirePass, $inlinedServicePass),
|
||||
new DefinitionErrorExceptionPass(),
|
||||
new CheckExceptionOnInvalidReferenceBehaviorPass(),
|
||||
));
|
||||
}
|
||||
|
@ -61,11 +61,11 @@ class ResolveNamedArgumentsPass extends AbstractRecursivePass
|
||||
}
|
||||
}
|
||||
|
||||
throw new InvalidArgumentException(sprintf('Unable to resolve service "%s": method "%s()" has no argument named "%s". Check your service definition.', $this->currentId, $class !== $this->currentId ? $class.'::'.$method : $method, $key));
|
||||
throw new InvalidArgumentException(sprintf('Invalid service "%s": method "%s()" has no argument named "%s". Check your service definition.', $this->currentId, $class !== $this->currentId ? $class.'::'.$method : $method, $key));
|
||||
}
|
||||
|
||||
if (null !== $argument && !$argument instanceof Reference && !$argument instanceof Definition) {
|
||||
throw new InvalidArgumentException(sprintf('Unable to resolve service "%s": the value of argument "%s" of method "%s()" must be null, an instance of %s or an instance of %s, %s given.', $this->currentId, $key, $class !== $this->currentId ? $class.'::'.$method : $method, Reference::class, Definition::class, gettype($argument)));
|
||||
throw new InvalidArgumentException(sprintf('Invalid service "%s": the value of argument "%s" of method "%s()" must be null, an instance of %s or an instance of %s, %s given.', $this->currentId, $key, $class !== $this->currentId ? $class.'::'.$method : $method, Reference::class, Definition::class, gettype($argument)));
|
||||
}
|
||||
|
||||
foreach ($parameters as $j => $p) {
|
||||
@ -76,7 +76,7 @@ class ResolveNamedArgumentsPass extends AbstractRecursivePass
|
||||
}
|
||||
}
|
||||
|
||||
throw new InvalidArgumentException(sprintf('Unable to resolve service "%s": method "%s()" has no argument type-hinted as "%s". Check your service definition.', $this->currentId, $class !== $this->currentId ? $class.'::'.$method : $method, $key));
|
||||
throw new InvalidArgumentException(sprintf('Invalid service "%s": method "%s()" has no argument type-hinted as "%s". Check your service definition.', $this->currentId, $class !== $this->currentId ? $class.'::'.$method : $method, $key));
|
||||
}
|
||||
|
||||
if ($resolvedArguments !== $call[1]) {
|
||||
|
@ -43,6 +43,7 @@ class Definition
|
||||
private $autowired = false;
|
||||
private $changes = array();
|
||||
private $bindings = array();
|
||||
private $errors = array();
|
||||
|
||||
protected $arguments = array();
|
||||
|
||||
@ -868,4 +869,24 @@ class Definition
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an error that occurred when building this Definition.
|
||||
*
|
||||
* @param string $error
|
||||
*/
|
||||
public function addError($error)
|
||||
{
|
||||
$this->errors[] = $error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns any errors that occurred while building this Definition.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getErrors()
|
||||
{
|
||||
return $this->errors;
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,9 @@ use Symfony\Component\Config\Util\XmlUtils;
|
||||
use Symfony\Component\DependencyInjection\Exception\EnvNotFoundException;
|
||||
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
|
||||
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class EnvVarProcessor implements EnvVarProcessorInterface
|
||||
{
|
||||
private $container;
|
||||
@ -119,7 +122,7 @@ class EnvVarProcessor implements EnvVarProcessorInterface
|
||||
}
|
||||
|
||||
if ('json' === $prefix) {
|
||||
$env = json_decode($env, true, JSON_BIGINT_AS_STRING);
|
||||
$env = json_decode($env, true);
|
||||
|
||||
if (JSON_ERROR_NONE !== json_last_error()) {
|
||||
throw new RuntimeException(sprintf('Invalid JSON in env var "%s": '.json_last_error_msg(), $name));
|
||||
|
@ -636,7 +636,7 @@ class YamlFileLoader extends FileLoader
|
||||
}
|
||||
|
||||
try {
|
||||
$configuration = $this->yamlParser->parse(file_get_contents($file), Yaml::PARSE_CONSTANT | Yaml::PARSE_CUSTOM_TAGS);
|
||||
$configuration = $this->yamlParser->parseFile($file, Yaml::PARSE_CONSTANT | Yaml::PARSE_CUSTOM_TAGS);
|
||||
} catch (ParseException $e) {
|
||||
throw new InvalidArgumentException(sprintf('The file "%s" does not contain valid YAML.', $file), 0, $e);
|
||||
}
|
||||
|
@ -18,6 +18,9 @@ use Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Exception\AutowiringFailedException;
|
||||
|
||||
/**
|
||||
* @group legacy
|
||||
*/
|
||||
class AutowireExceptionPassTest extends TestCase
|
||||
{
|
||||
public function testThrowsException()
|
||||
|
@ -12,6 +12,7 @@
|
||||
namespace Symfony\Component\DependencyInjection\Tests\Compiler;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\DependencyInjection\Compiler\AutowireRequiredMethodsPass;
|
||||
use Symfony\Component\DependencyInjection\Compiler\AutowirePass;
|
||||
use Symfony\Component\DependencyInjection\Compiler\ResolveClassPass;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
@ -20,6 +21,8 @@ use Symfony\Component\DependencyInjection\Reference;
|
||||
use Symfony\Component\DependencyInjection\Tests\Fixtures\includes\FooVariadic;
|
||||
use Symfony\Component\DependencyInjection\TypedReference;
|
||||
|
||||
require_once __DIR__.'/../Fixtures/includes/autowiring_classes.php';
|
||||
|
||||
/**
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
@ -128,6 +131,9 @@ class AutowirePassTest extends TestCase
|
||||
$this->assertEquals(DInterface::class, (string) $container->getDefinition('h')->getArgument(1));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group legacy
|
||||
*/
|
||||
public function testExceptionsAreStored()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
@ -145,7 +151,7 @@ class AutowirePassTest extends TestCase
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\DependencyInjection\Exception\AutowiringFailedException
|
||||
* @expectedExceptionMessage Unable to resolve service "private_service": constructor of class "Symfony\Component\DependencyInjection\Tests\Compiler\PrivateConstructor" must be public.
|
||||
* @expectedExceptionMessage Invalid service "private_service": constructor of class "Symfony\Component\DependencyInjection\Tests\Compiler\PrivateConstructor" must be public.
|
||||
*/
|
||||
public function testPrivateConstructorThrowsAutowireException()
|
||||
{
|
||||
@ -523,6 +529,7 @@ class AutowirePassTest extends TestCase
|
||||
;
|
||||
|
||||
(new ResolveClassPass())->process($container);
|
||||
(new AutowireRequiredMethodsPass())->process($container);
|
||||
(new AutowirePass())->process($container);
|
||||
|
||||
$methodCalls = $container->getDefinition('setter_injection')->getMethodCalls();
|
||||
@ -559,6 +566,7 @@ class AutowirePassTest extends TestCase
|
||||
;
|
||||
|
||||
(new ResolveClassPass())->process($container);
|
||||
(new AutowireRequiredMethodsPass())->process($container);
|
||||
(new AutowirePass())->process($container);
|
||||
|
||||
$methodCalls = $container->getDefinition('setter_injection')->getMethodCalls();
|
||||
@ -630,6 +638,8 @@ class AutowirePassTest extends TestCase
|
||||
$aDefinition = $container->register('setter_injection_collision', SetterInjectionCollision::class);
|
||||
$aDefinition->setAutowired(true);
|
||||
|
||||
(new AutowireRequiredMethodsPass())->process($container);
|
||||
|
||||
$pass = new AutowirePass();
|
||||
$pass->process($container);
|
||||
}
|
||||
@ -711,6 +721,7 @@ class AutowirePassTest extends TestCase
|
||||
}
|
||||
|
||||
(new ResolveClassPass())->process($container);
|
||||
(new AutowireRequiredMethodsPass())->process($container);
|
||||
(new AutowirePass())->process($container);
|
||||
}
|
||||
|
||||
@ -719,7 +730,7 @@ class AutowirePassTest extends TestCase
|
||||
return array(
|
||||
array('setNotAutowireable', 'Cannot autowire service "foo": argument "$n" of method "Symfony\Component\DependencyInjection\Tests\Compiler\NotWireable::setNotAutowireable()" has type "Symfony\Component\DependencyInjection\Tests\Compiler\NotARealClass" but this class cannot be loaded.'),
|
||||
array('setDifferentNamespace', 'Cannot autowire service "foo": argument "$n" of method "Symfony\Component\DependencyInjection\Tests\Compiler\NotWireable::setDifferentNamespace()" references class "stdClass" but no such service exists. It cannot be auto-registered because it is from a different root namespace.'),
|
||||
array(null, 'Cannot autowire service "foo": method "Symfony\Component\DependencyInjection\Tests\Compiler\NotWireable::setProtectedMethod()" must be public.'),
|
||||
array(null, 'Invalid service "foo": method "Symfony\Component\DependencyInjection\Tests\Compiler\NotWireable::setProtectedMethod()" must be public.'),
|
||||
);
|
||||
}
|
||||
|
||||
@ -779,333 +790,3 @@ class AutowirePassTest extends TestCase
|
||||
$pass->process($container);
|
||||
}
|
||||
}
|
||||
|
||||
class Foo
|
||||
{
|
||||
}
|
||||
|
||||
class Bar
|
||||
{
|
||||
public function __construct(Foo $foo)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
interface AInterface
|
||||
{
|
||||
}
|
||||
|
||||
class A implements AInterface
|
||||
{
|
||||
public static function create(Foo $foo)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class B extends A
|
||||
{
|
||||
}
|
||||
|
||||
class C
|
||||
{
|
||||
public function __construct(A $a)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
interface DInterface
|
||||
{
|
||||
}
|
||||
|
||||
interface EInterface extends DInterface
|
||||
{
|
||||
}
|
||||
|
||||
interface IInterface
|
||||
{
|
||||
}
|
||||
|
||||
class I implements IInterface
|
||||
{
|
||||
}
|
||||
|
||||
class F extends I implements EInterface
|
||||
{
|
||||
}
|
||||
|
||||
class G
|
||||
{
|
||||
public function __construct(DInterface $d, EInterface $e, IInterface $i)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class H
|
||||
{
|
||||
public function __construct(B $b, DInterface $d)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class D
|
||||
{
|
||||
public function __construct(A $a, DInterface $d)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class E
|
||||
{
|
||||
public function __construct(D $d = null)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class J
|
||||
{
|
||||
public function __construct(I $i)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
interface CollisionInterface
|
||||
{
|
||||
}
|
||||
|
||||
class CollisionA implements CollisionInterface
|
||||
{
|
||||
}
|
||||
|
||||
class CollisionB implements CollisionInterface
|
||||
{
|
||||
}
|
||||
|
||||
class CannotBeAutowired
|
||||
{
|
||||
public function __construct(CollisionInterface $collision)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class CannotBeAutowiredForwardOrder
|
||||
{
|
||||
public function __construct(CollisionA $a, CollisionInterface $b, CollisionB $c)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class CannotBeAutowiredReverseOrder
|
||||
{
|
||||
public function __construct(CollisionA $a, CollisionB $c, CollisionInterface $b)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class Lille
|
||||
{
|
||||
}
|
||||
|
||||
class Dunglas
|
||||
{
|
||||
public function __construct(Lille $l)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class LesTilleuls
|
||||
{
|
||||
public function __construct(Dunglas $j, Dunglas $k)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class OptionalParameter
|
||||
{
|
||||
public function __construct(CollisionInterface $c = null, A $a, Foo $f = null)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class BadTypeHintedArgument
|
||||
{
|
||||
public function __construct(Dunglas $k, NotARealClass $r)
|
||||
{
|
||||
}
|
||||
}
|
||||
class BadParentTypeHintedArgument
|
||||
{
|
||||
public function __construct(Dunglas $k, OptionalServiceClass $r)
|
||||
{
|
||||
}
|
||||
}
|
||||
class NotGuessableArgument
|
||||
{
|
||||
public function __construct(Foo $k)
|
||||
{
|
||||
}
|
||||
}
|
||||
class NotGuessableArgumentForSubclass
|
||||
{
|
||||
public function __construct(A $k)
|
||||
{
|
||||
}
|
||||
}
|
||||
class MultipleArguments
|
||||
{
|
||||
public function __construct(A $k, $foo, Dunglas $dunglas)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class MultipleArgumentsOptionalScalar
|
||||
{
|
||||
public function __construct(A $a, $foo = 'default_val', Lille $lille = null)
|
||||
{
|
||||
}
|
||||
}
|
||||
class MultipleArgumentsOptionalScalarLast
|
||||
{
|
||||
public function __construct(A $a, Lille $lille, $foo = 'some_val')
|
||||
{
|
||||
}
|
||||
}
|
||||
class MultipleArgumentsOptionalScalarNotReallyOptional
|
||||
{
|
||||
public function __construct(A $a, $foo = 'default_val', Lille $lille)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Classes used for testing createResourceForClass
|
||||
*/
|
||||
class ClassForResource
|
||||
{
|
||||
public function __construct($foo, Bar $bar = null)
|
||||
{
|
||||
}
|
||||
|
||||
public function setBar(Bar $bar)
|
||||
{
|
||||
}
|
||||
}
|
||||
class IdenticalClassResource extends ClassForResource
|
||||
{
|
||||
}
|
||||
|
||||
class ClassChangedConstructorArgs extends ClassForResource
|
||||
{
|
||||
public function __construct($foo, Bar $bar, $baz)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class SetterInjection extends SetterInjectionParent
|
||||
{
|
||||
/**
|
||||
* @required
|
||||
*/
|
||||
public function setFoo(Foo $foo)
|
||||
{
|
||||
// should be called
|
||||
}
|
||||
|
||||
/** @inheritdoc*/
|
||||
public function setDependencies(Foo $foo, A $a)
|
||||
{
|
||||
// should be called
|
||||
}
|
||||
|
||||
/** {@inheritdoc} */
|
||||
public function setWithCallsConfigured(A $a)
|
||||
{
|
||||
// this method has a calls configured on it
|
||||
}
|
||||
|
||||
public function notASetter(A $a)
|
||||
{
|
||||
// should be called only when explicitly specified
|
||||
}
|
||||
|
||||
/**
|
||||
* @required*/
|
||||
public function setChildMethodWithoutDocBlock(A $a)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class SetterInjectionParent
|
||||
{
|
||||
/** @required*/
|
||||
public function setDependencies(Foo $foo, A $a)
|
||||
{
|
||||
// should be called
|
||||
}
|
||||
|
||||
public function notASetter(A $a)
|
||||
{
|
||||
// @required should be ignored when the child does not add @inheritdoc
|
||||
}
|
||||
|
||||
/** @required <tab> prefix is on purpose */
|
||||
public function setWithCallsConfigured(A $a)
|
||||
{
|
||||
}
|
||||
|
||||
/** @required */
|
||||
public function setChildMethodWithoutDocBlock(A $a)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class SetterInjectionCollision
|
||||
{
|
||||
/**
|
||||
* @required
|
||||
*/
|
||||
public function setMultipleInstancesForOneArg(CollisionInterface $collision)
|
||||
{
|
||||
// The CollisionInterface cannot be autowired - there are multiple
|
||||
|
||||
// should throw an exception
|
||||
}
|
||||
}
|
||||
|
||||
class NotWireable
|
||||
{
|
||||
public function setNotAutowireable(NotARealClass $n)
|
||||
{
|
||||
}
|
||||
|
||||
public function setBar()
|
||||
{
|
||||
}
|
||||
|
||||
public function setOptionalNotAutowireable(NotARealClass $n = null)
|
||||
{
|
||||
}
|
||||
|
||||
public function setDifferentNamespace(\stdClass $n)
|
||||
{
|
||||
}
|
||||
|
||||
public function setOptionalNoTypeHint($foo = null)
|
||||
{
|
||||
}
|
||||
|
||||
public function setOptionalArgNoAutowireable($other = 'default_val')
|
||||
{
|
||||
}
|
||||
|
||||
/** @required */
|
||||
protected function setProtectedMethod(A $a)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class PrivateConstructor
|
||||
{
|
||||
private function __construct()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\DependencyInjection\Tests\Compiler;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\DependencyInjection\Compiler\ResolveClassPass;
|
||||
use Symfony\Component\DependencyInjection\Compiler\AutowireRequiredMethodsPass;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
|
||||
require_once __DIR__.'/../Fixtures/includes/autowiring_classes.php';
|
||||
|
||||
class AutowireRequiredMethodsPassTest extends TestCase
|
||||
{
|
||||
public function testSetterInjection()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
$container->register(Foo::class);
|
||||
$container->register(A::class);
|
||||
$container->register(CollisionA::class);
|
||||
$container->register(CollisionB::class);
|
||||
|
||||
// manually configure *one* call, to override autowiring
|
||||
$container
|
||||
->register('setter_injection', SetterInjection::class)
|
||||
->setAutowired(true)
|
||||
->addMethodCall('setWithCallsConfigured', array('manual_arg1', 'manual_arg2'));
|
||||
|
||||
(new ResolveClassPass())->process($container);
|
||||
(new AutowireRequiredMethodsPass())->process($container);
|
||||
|
||||
$methodCalls = $container->getDefinition('setter_injection')->getMethodCalls();
|
||||
|
||||
$this->assertEquals(
|
||||
array('setWithCallsConfigured', 'setFoo', 'setDependencies', 'setChildMethodWithoutDocBlock'),
|
||||
array_column($methodCalls, 0)
|
||||
);
|
||||
|
||||
// test setWithCallsConfigured args
|
||||
$this->assertEquals(
|
||||
array('manual_arg1', 'manual_arg2'),
|
||||
$methodCalls[0][1]
|
||||
);
|
||||
// test setFoo args
|
||||
$this->assertEquals(array(), $methodCalls[1][1]);
|
||||
}
|
||||
|
||||
public function testExplicitMethodInjection()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
$container->register(Foo::class);
|
||||
$container->register(A::class);
|
||||
$container->register(CollisionA::class);
|
||||
$container->register(CollisionB::class);
|
||||
|
||||
$container
|
||||
->register('setter_injection', SetterInjection::class)
|
||||
->setAutowired(true)
|
||||
->addMethodCall('notASetter', array());
|
||||
|
||||
(new ResolveClassPass())->process($container);
|
||||
(new AutowireRequiredMethodsPass())->process($container);
|
||||
|
||||
$methodCalls = $container->getDefinition('setter_injection')->getMethodCalls();
|
||||
|
||||
$this->assertEquals(
|
||||
array('notASetter', 'setFoo', 'setDependencies', 'setWithCallsConfigured', 'setChildMethodWithoutDocBlock'),
|
||||
array_column($methodCalls, 0)
|
||||
);
|
||||
$this->assertEquals(array(), $methodCalls[0][1]);
|
||||
}
|
||||
}
|
@ -64,4 +64,15 @@ class CheckArgumentsValidityPassTest extends TestCase
|
||||
array(array(), array(array('baz', array(1 => 1)))),
|
||||
);
|
||||
}
|
||||
|
||||
public function testNoException()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
$definition = $container->register('foo');
|
||||
$definition->setArguments(array(null, 'a' => 'a'));
|
||||
|
||||
$pass = new CheckArgumentsValidityPass(false);
|
||||
$pass->process($container);
|
||||
$this->assertCount(1, $definition->getErrors());
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\DependencyInjection\Tests\Compiler;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\DependencyInjection\Compiler\DefinitionErrorExceptionPass;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Definition;
|
||||
|
||||
class DefinitionErrorExceptionPassTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException
|
||||
* @expectedExceptionMessage Things went wrong!
|
||||
*/
|
||||
public function testThrowsException()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
$def = new Definition();
|
||||
$def->addError('Things went wrong!');
|
||||
$def->addError('Now something else!');
|
||||
$container->register('foo_service_id')
|
||||
->setArguments(array(
|
||||
$def,
|
||||
));
|
||||
|
||||
$pass = new DefinitionErrorExceptionPass();
|
||||
$pass->process($container);
|
||||
}
|
||||
|
||||
public function testNoExceptionThrown()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
$def = new Definition();
|
||||
$container->register('foo_service_id')
|
||||
->setArguments(array(
|
||||
$def,
|
||||
));
|
||||
|
||||
$pass = new DefinitionErrorExceptionPass();
|
||||
$pass->process($container);
|
||||
$this->assertSame($def, $container->getDefinition('foo_service_id')->getArgument(0));
|
||||
}
|
||||
}
|
@ -252,6 +252,9 @@ class InlineServiceDefinitionsPassTest extends TestCase
|
||||
$this->assertSame('inline', (string) $values[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group legacy
|
||||
*/
|
||||
public function testGetInlinedServiceIdData()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
|
@ -16,6 +16,7 @@ use Symfony\Component\Config\Definition\Builder\TreeBuilder;
|
||||
use Symfony\Component\Config\Definition\ConfigurationInterface;
|
||||
use Symfony\Component\Config\Resource\FileResource;
|
||||
use Symfony\Component\DependencyInjection\Compiler\MergeExtensionConfigurationPass;
|
||||
use Symfony\Component\DependencyInjection\Compiler\MergeExtensionConfigurationContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Extension\Extension;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
|
||||
@ -54,6 +55,22 @@ class MergeExtensionConfigurationPassTest extends TestCase
|
||||
$this->assertEquals(array($provider), $tmpProviders);
|
||||
}
|
||||
|
||||
public function testExtensionLoadGetAMergeExtensionConfigurationContainerBuilderInstance()
|
||||
{
|
||||
$extension = $this->getMockBuilder(FooExtension::class)->setMethods(array('load'))->getMock();
|
||||
$extension->expects($this->once())
|
||||
->method('load')
|
||||
->with($this->isType('array'), $this->isInstanceOf(MergeExtensionConfigurationContainerBuilder::class))
|
||||
;
|
||||
|
||||
$container = new ContainerBuilder(new ParameterBag());
|
||||
$container->registerExtension($extension);
|
||||
$container->prependExtensionConfig('foo', array());
|
||||
|
||||
$pass = new MergeExtensionConfigurationPass();
|
||||
$pass->process($container);
|
||||
}
|
||||
|
||||
public function testExtensionConfigurationIsTrackedByDefault()
|
||||
{
|
||||
$extension = $this->getMockBuilder(FooExtension::class)->setMethods(array('getConfiguration'))->getMock();
|
||||
|
@ -13,6 +13,7 @@ namespace Symfony\Component\DependencyInjection\Tests\Compiler;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\DependencyInjection\Argument\BoundArgument;
|
||||
use Symfony\Component\DependencyInjection\Compiler\AutowireRequiredMethodsPass;
|
||||
use Symfony\Component\DependencyInjection\Compiler\ResolveBindingsPass;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
@ -20,6 +21,8 @@ use Symfony\Component\DependencyInjection\Tests\Fixtures\NamedArgumentsDummy;
|
||||
use Symfony\Component\DependencyInjection\Tests\Fixtures\CaseSensitiveClass;
|
||||
use Symfony\Component\DependencyInjection\TypedReference;
|
||||
|
||||
require_once __DIR__.'/../Fixtures/includes/autowiring_classes.php';
|
||||
|
||||
class ResolveBindingsPassTest extends TestCase
|
||||
{
|
||||
public function testProcess()
|
||||
@ -79,4 +82,17 @@ class ResolveBindingsPassTest extends TestCase
|
||||
$this->assertEquals(array($typedRef), $container->getDefinition('def1')->getArguments());
|
||||
$this->assertEquals(array(new Reference('foo')), $container->getDefinition('def2')->getArguments());
|
||||
}
|
||||
|
||||
public function testScalarSetter()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
|
||||
$definition = $container->autowire('foo', ScalarSetter::class);
|
||||
$definition->setBindings(array('$defaultLocale' => 'fr'));
|
||||
|
||||
(new AutowireRequiredMethodsPass())->process($container);
|
||||
(new ResolveBindingsPass())->process($container);
|
||||
|
||||
$this->assertEquals(array(array('setDefaultLocale', array('fr'))), $definition->getMethodCalls());
|
||||
}
|
||||
}
|
||||
|
@ -371,4 +371,13 @@ class DefinitionTest extends TestCase
|
||||
$def->setAutoconfigured(true);
|
||||
$this->assertTrue($def->isAutoconfigured());
|
||||
}
|
||||
|
||||
public function testAddError()
|
||||
{
|
||||
$def = new Definition('stdClass');
|
||||
$this->assertEmpty($def->getErrors());
|
||||
$def->addError('First error');
|
||||
$def->addError('Second error');
|
||||
$this->assertSame(array('First error', 'Second error'), $def->getErrors());
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,343 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\DependencyInjection\Tests\Compiler;
|
||||
|
||||
class Foo
|
||||
{
|
||||
}
|
||||
|
||||
class Bar
|
||||
{
|
||||
public function __construct(Foo $foo)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
interface AInterface
|
||||
{
|
||||
}
|
||||
|
||||
class A implements AInterface
|
||||
{
|
||||
public static function create(Foo $foo)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class B extends A
|
||||
{
|
||||
}
|
||||
|
||||
class C
|
||||
{
|
||||
public function __construct(A $a)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
interface DInterface
|
||||
{
|
||||
}
|
||||
|
||||
interface EInterface extends DInterface
|
||||
{
|
||||
}
|
||||
|
||||
interface IInterface
|
||||
{
|
||||
}
|
||||
|
||||
class I implements IInterface
|
||||
{
|
||||
}
|
||||
|
||||
class F extends I implements EInterface
|
||||
{
|
||||
}
|
||||
|
||||
class G
|
||||
{
|
||||
public function __construct(DInterface $d, EInterface $e, IInterface $i)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class H
|
||||
{
|
||||
public function __construct(B $b, DInterface $d)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class D
|
||||
{
|
||||
public function __construct(A $a, DInterface $d)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class E
|
||||
{
|
||||
public function __construct(D $d = null)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class J
|
||||
{
|
||||
public function __construct(I $i)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
interface CollisionInterface
|
||||
{
|
||||
}
|
||||
|
||||
class CollisionA implements CollisionInterface
|
||||
{
|
||||
}
|
||||
|
||||
class CollisionB implements CollisionInterface
|
||||
{
|
||||
}
|
||||
|
||||
class CannotBeAutowired
|
||||
{
|
||||
public function __construct(CollisionInterface $collision)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class CannotBeAutowiredForwardOrder
|
||||
{
|
||||
public function __construct(CollisionA $a, CollisionInterface $b, CollisionB $c)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class CannotBeAutowiredReverseOrder
|
||||
{
|
||||
public function __construct(CollisionA $a, CollisionB $c, CollisionInterface $b)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class Lille
|
||||
{
|
||||
}
|
||||
|
||||
class Dunglas
|
||||
{
|
||||
public function __construct(Lille $l)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class LesTilleuls
|
||||
{
|
||||
public function __construct(Dunglas $j, Dunglas $k)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class OptionalParameter
|
||||
{
|
||||
public function __construct(CollisionInterface $c = null, A $a, Foo $f = null)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class BadTypeHintedArgument
|
||||
{
|
||||
public function __construct(Dunglas $k, NotARealClass $r)
|
||||
{
|
||||
}
|
||||
}
|
||||
class BadParentTypeHintedArgument
|
||||
{
|
||||
public function __construct(Dunglas $k, OptionalServiceClass $r)
|
||||
{
|
||||
}
|
||||
}
|
||||
class NotGuessableArgument
|
||||
{
|
||||
public function __construct(Foo $k)
|
||||
{
|
||||
}
|
||||
}
|
||||
class NotGuessableArgumentForSubclass
|
||||
{
|
||||
public function __construct(A $k)
|
||||
{
|
||||
}
|
||||
}
|
||||
class MultipleArguments
|
||||
{
|
||||
public function __construct(A $k, $foo, Dunglas $dunglas)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class MultipleArgumentsOptionalScalar
|
||||
{
|
||||
public function __construct(A $a, $foo = 'default_val', Lille $lille = null)
|
||||
{
|
||||
}
|
||||
}
|
||||
class MultipleArgumentsOptionalScalarLast
|
||||
{
|
||||
public function __construct(A $a, Lille $lille, $foo = 'some_val')
|
||||
{
|
||||
}
|
||||
}
|
||||
class MultipleArgumentsOptionalScalarNotReallyOptional
|
||||
{
|
||||
public function __construct(A $a, $foo = 'default_val', Lille $lille)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Classes used for testing createResourceForClass
|
||||
*/
|
||||
class ClassForResource
|
||||
{
|
||||
public function __construct($foo, Bar $bar = null)
|
||||
{
|
||||
}
|
||||
|
||||
public function setBar(Bar $bar)
|
||||
{
|
||||
}
|
||||
}
|
||||
class IdenticalClassResource extends ClassForResource
|
||||
{
|
||||
}
|
||||
|
||||
class ClassChangedConstructorArgs extends ClassForResource
|
||||
{
|
||||
public function __construct($foo, Bar $bar, $baz)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class SetterInjectionCollision
|
||||
{
|
||||
/**
|
||||
* @required
|
||||
*/
|
||||
public function setMultipleInstancesForOneArg(CollisionInterface $collision)
|
||||
{
|
||||
// The CollisionInterface cannot be autowired - there are multiple
|
||||
|
||||
// should throw an exception
|
||||
}
|
||||
}
|
||||
|
||||
class SetterInjection extends SetterInjectionParent
|
||||
{
|
||||
/**
|
||||
* @required
|
||||
*/
|
||||
public function setFoo(Foo $foo)
|
||||
{
|
||||
// should be called
|
||||
}
|
||||
|
||||
/** @inheritdoc*/ // <- brackets are missing on purpose
|
||||
public function setDependencies(Foo $foo, A $a)
|
||||
{
|
||||
// should be called
|
||||
}
|
||||
|
||||
/** {@inheritdoc} */
|
||||
public function setWithCallsConfigured(A $a)
|
||||
{
|
||||
// this method has a calls configured on it
|
||||
}
|
||||
|
||||
public function notASetter(A $a)
|
||||
{
|
||||
// should be called only when explicitly specified
|
||||
}
|
||||
|
||||
/**
|
||||
* @required*/
|
||||
public function setChildMethodWithoutDocBlock(A $a)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class SetterInjectionParent
|
||||
{
|
||||
/** @required*/
|
||||
public function setDependencies(Foo $foo, A $a)
|
||||
{
|
||||
// should be called
|
||||
}
|
||||
|
||||
public function notASetter(A $a)
|
||||
{
|
||||
// @required should be ignored when the child does not add @inheritdoc
|
||||
}
|
||||
|
||||
/** @required <tab> prefix is on purpose */
|
||||
public function setWithCallsConfigured(A $a)
|
||||
{
|
||||
}
|
||||
|
||||
/** @required */
|
||||
public function setChildMethodWithoutDocBlock(A $a)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class NotWireable
|
||||
{
|
||||
public function setNotAutowireable(NotARealClass $n)
|
||||
{
|
||||
}
|
||||
|
||||
public function setBar()
|
||||
{
|
||||
}
|
||||
|
||||
public function setOptionalNotAutowireable(NotARealClass $n = null)
|
||||
{
|
||||
}
|
||||
|
||||
public function setDifferentNamespace(\stdClass $n)
|
||||
{
|
||||
}
|
||||
|
||||
public function setOptionalNoTypeHint($foo = null)
|
||||
{
|
||||
}
|
||||
|
||||
public function setOptionalArgNoAutowireable($other = 'default_val')
|
||||
{
|
||||
}
|
||||
|
||||
/** @required */
|
||||
protected function setProtectedMethod(A $a)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class PrivateConstructor
|
||||
{
|
||||
private function __construct()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class ScalarSetter
|
||||
{
|
||||
/**
|
||||
* @required
|
||||
*/
|
||||
public function setDefaultLocale($defaultLocale)
|
||||
{
|
||||
}
|
||||
}
|
@ -118,7 +118,7 @@ EOF
|
||||
return $classes[0];
|
||||
}
|
||||
if (!$input->isInteractive()) {
|
||||
throw new InvalidArgumentException(sprintf("The type \"%s\" is ambiguous.\nDid you mean one of these?\n %s", $shortClassName, implode("\n ", $classes)));
|
||||
throw new InvalidArgumentException(sprintf("The type \"%s\" is ambiguous.\n\nDid you mean one of these?\n %s", $shortClassName, implode("\n ", $classes)));
|
||||
}
|
||||
|
||||
return $io->choice(sprintf("The type \"%s\" is ambiguous.\n\n Select one of the following form types to display its information:", $shortClassName), $classes, $classes[0]);
|
||||
|
@ -13,11 +13,11 @@ namespace Symfony\Component\Form\Tests\Command;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\Console\Application;
|
||||
use Symfony\Component\Console\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Console\Tester\CommandTester;
|
||||
use Symfony\Component\Form\Command\DebugCommand;
|
||||
use Symfony\Component\Form\Extension\Core\Type\FormType;
|
||||
use Symfony\Component\Form\FormRegistryInterface;
|
||||
use Symfony\Component\Form\ResolvedFormTypeInterface;
|
||||
use Symfony\Component\Form\FormRegistry;
|
||||
use Symfony\Component\Form\ResolvedFormTypeFactory;
|
||||
|
||||
class DebugCommandTest extends TestCase
|
||||
{
|
||||
@ -39,6 +39,67 @@ class DebugCommandTest extends TestCase
|
||||
$this->assertContains('Symfony\Component\Form\Extension\Core\Type\FormType (Block prefix: "form")', $tester->getDisplay());
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Console\Exception\InvalidArgumentException
|
||||
* @expectedExceptionMessage Could not find type "NonExistentType"
|
||||
*/
|
||||
public function testDebugSingleFormTypeNotFound()
|
||||
{
|
||||
$tester = $this->createCommandTester();
|
||||
$tester->execute(array('class' => 'NonExistentType'), array('decorated' => false, 'interactive' => false));
|
||||
}
|
||||
|
||||
public function testDebugAmbiguousFormType()
|
||||
{
|
||||
$expectedMessage = <<<TXT
|
||||
The type "AmbiguousType" is ambiguous.
|
||||
|
||||
Did you mean one of these?
|
||||
Symfony\Component\Form\Tests\Fixtures\Debug\A\AmbiguousType
|
||||
Symfony\Component\Form\Tests\Fixtures\Debug\B\AmbiguousType
|
||||
TXT;
|
||||
|
||||
if (method_exists($this, 'expectException')) {
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage($expectedMessage);
|
||||
} else {
|
||||
$this->setExpectedException(InvalidArgumentException::class, $expectedMessage);
|
||||
}
|
||||
|
||||
$tester = $this->createCommandTester(array(
|
||||
'Symfony\Component\Form\Tests\Fixtures\Debug\A',
|
||||
'Symfony\Component\Form\Tests\Fixtures\Debug\B',
|
||||
));
|
||||
|
||||
$tester->execute(array('class' => 'AmbiguousType'), array('decorated' => false, 'interactive' => false));
|
||||
}
|
||||
|
||||
public function testDebugAmbiguousFormTypeInteractive()
|
||||
{
|
||||
$tester = $this->createCommandTester(array(
|
||||
'Symfony\Component\Form\Tests\Fixtures\Debug\A',
|
||||
'Symfony\Component\Form\Tests\Fixtures\Debug\B',
|
||||
));
|
||||
|
||||
$tester->setInputs(array(0));
|
||||
$tester->execute(array('class' => 'AmbiguousType'), array('decorated' => false, 'interactive' => true));
|
||||
|
||||
$this->assertEquals(0, $tester->getStatusCode(), 'Returns 0 in case of success');
|
||||
$output = $tester->getDisplay(true);
|
||||
$this->assertStringMatchesFormat(<<<TXT
|
||||
|
||||
The type "AmbiguousType" is ambiguous.
|
||||
|
||||
Select one of the following form types to display its information: [%A\A\AmbiguousType]:
|
||||
[0] %A\A\AmbiguousType
|
||||
[1] %A\B\AmbiguousType
|
||||
%A
|
||||
%A\A\AmbiguousType (Block prefix: "ambiguous")
|
||||
%A
|
||||
TXT
|
||||
, $output);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \InvalidArgumentException
|
||||
*/
|
||||
@ -47,36 +108,10 @@ class DebugCommandTest extends TestCase
|
||||
$this->createCommandTester()->execute(array('class' => 'test'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return CommandTester
|
||||
*/
|
||||
private function createCommandTester()
|
||||
private function createCommandTester(array $namespaces = null)
|
||||
{
|
||||
$resolvedFormType = $this->getMockBuilder(ResolvedFormTypeInterface::class)->getMock();
|
||||
$resolvedFormType
|
||||
->expects($this->any())
|
||||
->method('getParent')
|
||||
->willReturn(null)
|
||||
;
|
||||
$resolvedFormType
|
||||
->expects($this->any())
|
||||
->method('getInnerType')
|
||||
->willReturn(new FormType())
|
||||
;
|
||||
$resolvedFormType
|
||||
->expects($this->any())
|
||||
->method('getTypeExtensions')
|
||||
->willReturn(array())
|
||||
;
|
||||
|
||||
$formRegistry = $this->getMockBuilder(FormRegistryInterface::class)->getMock();
|
||||
$formRegistry
|
||||
->expects($this->any())
|
||||
->method('getType')
|
||||
->will($this->returnValue($resolvedFormType))
|
||||
;
|
||||
|
||||
$command = new DebugCommand($formRegistry);
|
||||
$formRegistry = new FormRegistry(array(), new ResolvedFormTypeFactory());
|
||||
$command = null === $namespaces ? new DebugCommand($formRegistry) : new DebugCommand($formRegistry, $namespaces);
|
||||
$application = new Application();
|
||||
$application->add($command);
|
||||
|
||||
|
@ -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\Form\Tests\Fixtures\Debug\A;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
|
||||
class AmbiguousType extends AbstractType
|
||||
{
|
||||
}
|
@ -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\Form\Tests\Fixtures\Debug\B;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
|
||||
class AmbiguousType extends AbstractType
|
||||
{
|
||||
}
|
@ -58,7 +58,7 @@ class YamlFileLoader extends FileLoader
|
||||
}
|
||||
|
||||
try {
|
||||
$parsedConfig = $this->yamlParser->parse(file_get_contents($path));
|
||||
$parsedConfig = $this->yamlParser->parseFile($path);
|
||||
} catch (ParseException $e) {
|
||||
throw new \InvalidArgumentException(sprintf('The file "%s" does not contain valid YAML.', $path), 0, $e);
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ CHANGELOG
|
||||
* added `AbstractObjectNormalizer::DISABLE_TYPE_ENFORCEMENT` context option
|
||||
to disable throwing an `UnexpectedValueException` on a type mismatch
|
||||
* added support for serializing `DateInterval` objects
|
||||
* added getter for extra attributes in `ExtraAttributesException`
|
||||
|
||||
3.3.0
|
||||
-----
|
||||
|
@ -18,10 +18,24 @@ namespace Symfony\Component\Serializer\Exception;
|
||||
*/
|
||||
class ExtraAttributesException extends RuntimeException
|
||||
{
|
||||
private $extraAttributes;
|
||||
|
||||
public function __construct(array $extraAttributes, \Exception $previous = null)
|
||||
{
|
||||
$msg = sprintf('Extra attributes are not allowed ("%s" are unknown).', implode('", "', $extraAttributes));
|
||||
|
||||
$this->extraAttributes = $extraAttributes;
|
||||
|
||||
parent::__construct($msg, 0, $previous);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the extra attributes that are not allowed.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getExtraAttributes()
|
||||
{
|
||||
return $this->extraAttributes;
|
||||
}
|
||||
}
|
||||
|
@ -113,7 +113,7 @@ class YamlFileLoader extends FileLoader
|
||||
$this->yamlParser = new Parser();
|
||||
}
|
||||
|
||||
$classes = $this->yamlParser->parse(file_get_contents($this->file));
|
||||
$classes = $this->yamlParser->parseFile($this->file);
|
||||
|
||||
if (empty($classes)) {
|
||||
return array();
|
||||
|
@ -39,7 +39,7 @@ class YamlFileLoader extends FileLoader
|
||||
}
|
||||
|
||||
try {
|
||||
$messages = $this->yamlParser->parse(file_get_contents($resource));
|
||||
$messages = $this->yamlParser->parseFile($resource);
|
||||
} catch (ParseException $e) {
|
||||
throw new InvalidResourceException(sprintf('Error parsing YAML, invalid file "%s"', $resource), 0, $e);
|
||||
}
|
||||
|
@ -116,7 +116,7 @@ class YamlFileLoader extends FileLoader
|
||||
private function parseFile($path)
|
||||
{
|
||||
try {
|
||||
$classes = $this->yamlParser->parse(file_get_contents($path), Yaml::PARSE_CONSTANT);
|
||||
$classes = $this->yamlParser->parseFile($path, Yaml::PARSE_CONSTANT);
|
||||
} catch (ParseException $e) {
|
||||
throw new \InvalidArgumentException(sprintf('The file "%s" does not contain valid YAML.', $path), 0, $e);
|
||||
}
|
||||
|
@ -22,5 +22,11 @@ if (!function_exists('dump')) {
|
||||
foreach ($moreVars as $var) {
|
||||
VarDumper::dump($var);
|
||||
}
|
||||
|
||||
if (1 < func_num_args()) {
|
||||
return func_get_args();
|
||||
}
|
||||
|
||||
return $var;
|
||||
}
|
||||
}
|
||||
|
@ -34,6 +34,8 @@ CHANGELOG
|
||||
3.4.0
|
||||
-----
|
||||
|
||||
* added support for parsing YAML files using the `Yaml::parseFile()` or `Parser::parseFile()` method
|
||||
|
||||
* the `Dumper`, `Parser`, and `Yaml` classes are marked as final
|
||||
|
||||
* Deprecated the `!php/object:` tag which will be replaced by the
|
||||
|
@ -26,7 +26,8 @@ class Inline
|
||||
{
|
||||
const REGEX_QUOTED_STRING = '(?:"([^"\\\\]*+(?:\\\\.[^"\\\\]*+)*+)"|\'([^\']*+(?:\'\'[^\']*+)*+)\')';
|
||||
|
||||
public static $parsedLineNumber;
|
||||
public static $parsedLineNumber = -1;
|
||||
public static $parsedFilename;
|
||||
|
||||
private static $exceptionOnInvalidType = false;
|
||||
private static $objectSupport = false;
|
||||
@ -36,17 +37,16 @@ class Inline
|
||||
/**
|
||||
* @param int $flags
|
||||
* @param int|null $parsedLineNumber
|
||||
* @param string|null $parsedFilename
|
||||
*/
|
||||
public static function initialize($flags, $parsedLineNumber = null)
|
||||
public static function initialize($flags, $parsedLineNumber = null, $parsedFilename = null)
|
||||
{
|
||||
self::$exceptionOnInvalidType = (bool) (Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE & $flags);
|
||||
self::$objectSupport = (bool) (Yaml::PARSE_OBJECT & $flags);
|
||||
self::$objectForMap = (bool) (Yaml::PARSE_OBJECT_FOR_MAP & $flags);
|
||||
self::$constantSupport = (bool) (Yaml::PARSE_CONSTANT & $flags);
|
||||
|
||||
if (null !== $parsedLineNumber) {
|
||||
self::$parsedLineNumber = $parsedLineNumber;
|
||||
}
|
||||
self::$parsedFilename = $parsedFilename;
|
||||
self::$parsedLineNumber = null !== $parsedLineNumber ? $parsedLineNumber : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -96,7 +96,7 @@ class Inline
|
||||
|
||||
// some comments are allowed at the end
|
||||
if (preg_replace('/\s+#.*$/A', '', substr($value, $i))) {
|
||||
throw new ParseException(sprintf('Unexpected characters near "%s".', substr($value, $i)));
|
||||
throw new ParseException(sprintf('Unexpected characters near "%s".', substr($value, $i)), self::$parsedLineNumber, $value, self::$parsedFilename);
|
||||
}
|
||||
|
||||
if (isset($mbEncoding)) {
|
||||
@ -274,7 +274,7 @@ class Inline
|
||||
if (null !== $delimiters) {
|
||||
$tmp = ltrim(substr($scalar, $i), ' ');
|
||||
if (!in_array($tmp[0], $delimiters)) {
|
||||
throw new ParseException(sprintf('Unexpected characters (%s).', substr($scalar, $i)));
|
||||
throw new ParseException(sprintf('Unexpected characters (%s).', substr($scalar, $i)), self::$parsedLineNumber, $scalar, self::$parsedFilename);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -291,12 +291,12 @@ class Inline
|
||||
$output = $match[1];
|
||||
$i += strlen($output);
|
||||
} else {
|
||||
throw new ParseException(sprintf('Malformed inline YAML string: %s.', $scalar));
|
||||
throw new ParseException(sprintf('Malformed inline YAML string: %s.', $scalar), self::$parsedLineNumber, null, self::$parsedFilename);
|
||||
}
|
||||
|
||||
// a non-quoted string cannot start with @ or ` (reserved) nor with a scalar indicator (| or >)
|
||||
if ($output && ('@' === $output[0] || '`' === $output[0] || '|' === $output[0] || '>' === $output[0] || '%' === $output[0])) {
|
||||
throw new ParseException(sprintf('The reserved indicator "%s" cannot start a plain scalar; you need to quote the scalar.', $output[0]));
|
||||
throw new ParseException(sprintf('The reserved indicator "%s" cannot start a plain scalar; you need to quote the scalar.', $output[0]), self::$parsedLineNumber, $output, self::$parsedFilename);
|
||||
}
|
||||
|
||||
if ($evaluate) {
|
||||
@ -320,7 +320,7 @@ class Inline
|
||||
private static function parseQuotedScalar(string $scalar, int &$i): string
|
||||
{
|
||||
if (!Parser::preg_match('/'.self::REGEX_QUOTED_STRING.'/Au', substr($scalar, $i), $match)) {
|
||||
throw new ParseException(sprintf('Malformed inline YAML string: %s.', substr($scalar, $i)));
|
||||
throw new ParseException(sprintf('Malformed inline YAML string: %s.', substr($scalar, $i)), self::$parsedLineNumber, $scalar, self::$parsedFilename);
|
||||
}
|
||||
|
||||
$output = substr($match[0], 1, strlen($match[0]) - 2);
|
||||
@ -403,7 +403,7 @@ class Inline
|
||||
++$i;
|
||||
}
|
||||
|
||||
throw new ParseException(sprintf('Malformed inline YAML string: %s.', $sequence));
|
||||
throw new ParseException(sprintf('Malformed inline YAML string: %s.', $sequence), self::$parsedLineNumber, null, self::$parsedFilename);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -516,7 +516,7 @@ class Inline
|
||||
}
|
||||
}
|
||||
|
||||
throw new ParseException(sprintf('Malformed inline YAML string: %s.', $mapping));
|
||||
throw new ParseException(sprintf('Malformed inline YAML string: %s.', $mapping), self::$parsedLineNumber, null, self::$parsedFilename);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -544,11 +544,11 @@ class Inline
|
||||
|
||||
// an unquoted *
|
||||
if (false === $value || '' === $value) {
|
||||
throw new ParseException('A reference must contain at least one character.');
|
||||
throw new ParseException('A reference must contain at least one character.', self::$parsedLineNumber, $value, self::$parsedFilename);
|
||||
}
|
||||
|
||||
if (!array_key_exists($value, $references)) {
|
||||
throw new ParseException(sprintf('Reference "%s" does not exist.', $value));
|
||||
throw new ParseException(sprintf('Reference "%s" does not exist.', $value), self::$parsedLineNumber, $value, self::$parsedFilename);
|
||||
}
|
||||
|
||||
return $references[$value];
|
||||
@ -575,7 +575,7 @@ class Inline
|
||||
}
|
||||
|
||||
if (self::$exceptionOnInvalidType) {
|
||||
throw new ParseException('Object support when parsing a YAML file has been disabled.');
|
||||
throw new ParseException('Object support when parsing a YAML file has been disabled.', self::$parsedLineNumber, $scalar, self::$parsedFilename);
|
||||
}
|
||||
|
||||
return;
|
||||
@ -585,10 +585,10 @@ class Inline
|
||||
return constant($const);
|
||||
}
|
||||
|
||||
throw new ParseException(sprintf('The constant "%s" is not defined.', $const));
|
||||
throw new ParseException(sprintf('The constant "%s" is not defined.', $const), self::$parsedLineNumber, $scalar, self::$parsedFilename);
|
||||
}
|
||||
if (self::$exceptionOnInvalidType) {
|
||||
throw new ParseException(sprintf('The string "%s" could not be parsed as a constant. Have you forgotten to pass the "Yaml::PARSE_CONSTANT" flag to the parser?', $scalar));
|
||||
throw new ParseException(sprintf('The string "%s" could not be parsed as a constant. Have you forgotten to pass the "Yaml::PARSE_CONSTANT" flag to the parser?', $scalar), self::$parsedLineNumber, $scalar, self::$parsedFilename);
|
||||
}
|
||||
|
||||
return;
|
||||
@ -597,7 +597,7 @@ class Inline
|
||||
case 0 === strpos($scalar, '!!binary '):
|
||||
return self::evaluateBinaryScalar(substr($scalar, 9));
|
||||
default:
|
||||
throw new ParseException(sprintf('The string "%s" could not be parsed as it uses an unsupported built-in tag.', $scalar));
|
||||
throw new ParseException(sprintf('The string "%s" could not be parsed as it uses an unsupported built-in tag.', $scalar), self::$parsedLineNumber, $scalar, self::$parsedFilename);
|
||||
}
|
||||
|
||||
// Optimize for returning strings.
|
||||
@ -677,14 +677,14 @@ class Inline
|
||||
|
||||
// Built-in tags
|
||||
if ($tag && '!' === $tag[0]) {
|
||||
throw new ParseException(sprintf('The built-in tag "!%s" is not implemented.', $tag));
|
||||
throw new ParseException(sprintf('The built-in tag "!%s" is not implemented.', $tag), self::$parsedLineNumber, $value, self::$parsedFilename);
|
||||
}
|
||||
|
||||
if ('' === $tag || Yaml::PARSE_CUSTOM_TAGS & $flags) {
|
||||
return $tag;
|
||||
}
|
||||
|
||||
throw new ParseException(sprintf('Tags support is not enabled. Enable the `Yaml::PARSE_CUSTOM_TAGS` flag to use "!%s".', $tag));
|
||||
throw new ParseException(sprintf('Tags support is not enabled. Enable the `Yaml::PARSE_CUSTOM_TAGS` flag to use "!%s".', $tag), self::$parsedLineNumber, $value, self::$parsedFilename);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -697,11 +697,11 @@ class Inline
|
||||
$parsedBinaryData = self::parseScalar(preg_replace('/\s/', '', $scalar));
|
||||
|
||||
if (0 !== (strlen($parsedBinaryData) % 4)) {
|
||||
throw new ParseException(sprintf('The normalized base64 encoded data (data without whitespace characters) length must be a multiple of four (%d bytes given).', strlen($parsedBinaryData)));
|
||||
throw new ParseException(sprintf('The normalized base64 encoded data (data without whitespace characters) length must be a multiple of four (%d bytes given).', strlen($parsedBinaryData)), self::$parsedLineNumber, $scalar, self::$parsedFilename);
|
||||
}
|
||||
|
||||
if (!Parser::preg_match('#^[A-Z0-9+/]+={0,2}$#i', $parsedBinaryData)) {
|
||||
throw new ParseException(sprintf('The base64 encoded data (%s) contains invalid characters.', $parsedBinaryData));
|
||||
throw new ParseException(sprintf('The base64 encoded data (%s) contains invalid characters.', $parsedBinaryData), self::$parsedLineNumber, $scalar, self::$parsedFilename);
|
||||
}
|
||||
|
||||
return base64_decode($parsedBinaryData, true);
|
||||
|
@ -26,6 +26,7 @@ class Parser
|
||||
const TAG_PATTERN = '(?P<tag>![\w!.\/:-]+)';
|
||||
const BLOCK_SCALAR_HEADER_PATTERN = '(?P<separator>\||>)(?P<modifiers>\+|\-|\d+|\+\d+|\-\d+|\d+\+|\d+\-)?(?P<comments> +#.*)?';
|
||||
|
||||
private $filename;
|
||||
private $offset = 0;
|
||||
private $totalNumberOfLines;
|
||||
private $lines = array();
|
||||
@ -35,6 +36,35 @@ class Parser
|
||||
private $skippedLineNumbers = array();
|
||||
private $locallySkippedLineNumbers = array();
|
||||
|
||||
/**
|
||||
* Parses a YAML file into a PHP value.
|
||||
*
|
||||
* @param string $filename The path to the YAML file to be parsed
|
||||
* @param int $flags A bit field of PARSE_* constants to customize the YAML parser behavior
|
||||
*
|
||||
* @return mixed The YAML converted to a PHP value
|
||||
*
|
||||
* @throws ParseException If the file could not be read or the YAML is not valid
|
||||
*/
|
||||
public function parseFile($filename, $flags = 0)
|
||||
{
|
||||
if (!is_file($filename)) {
|
||||
throw new ParseException(sprintf('File "%s" does not exist.', $filename));
|
||||
}
|
||||
|
||||
if (!is_readable($filename)) {
|
||||
throw new ParseException(sprintf('File "%s" cannot be read.', $filename));
|
||||
}
|
||||
|
||||
$this->filename = $filename;
|
||||
|
||||
try {
|
||||
return $this->parse(file_get_contents($filename), $flags);
|
||||
} finally {
|
||||
$this->filename = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a YAML string to a PHP value.
|
||||
*
|
||||
@ -48,7 +78,7 @@ class Parser
|
||||
public function parse(string $value, int $flags = 0)
|
||||
{
|
||||
if (false === preg_match('//u', $value)) {
|
||||
throw new ParseException('The YAML value does not appear to be valid UTF-8.');
|
||||
throw new ParseException('The YAML value does not appear to be valid UTF-8.', -1, null, $this->filename);
|
||||
}
|
||||
|
||||
$this->refs = array();
|
||||
@ -125,13 +155,13 @@ class Parser
|
||||
|
||||
// tab?
|
||||
if ("\t" === $this->currentLine[0]) {
|
||||
throw new ParseException('A YAML file cannot contain tabs as indentation.', $this->getRealCurrentLineNb() + 1, $this->currentLine);
|
||||
throw new ParseException('A YAML file cannot contain tabs as indentation.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename);
|
||||
}
|
||||
|
||||
$isRef = $mergeNode = false;
|
||||
if (self::preg_match('#^\-((?P<leadspaces>\s+)(?P<value>.+))?$#u', rtrim($this->currentLine), $values)) {
|
||||
if ($context && 'mapping' == $context) {
|
||||
throw new ParseException('You cannot define a sequence item when in a mapping', $this->getRealCurrentLineNb() + 1, $this->currentLine);
|
||||
throw new ParseException('You cannot define a sequence item when in a mapping', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename);
|
||||
}
|
||||
$context = 'sequence';
|
||||
|
||||
@ -175,11 +205,11 @@ class Parser
|
||||
&& (false === strpos($values['key'], ' #') || in_array($values['key'][0], array('"', "'")))
|
||||
) {
|
||||
if ($context && 'sequence' == $context) {
|
||||
throw new ParseException('You cannot define a mapping item when in a sequence', $this->currentLineNb + 1, $this->currentLine);
|
||||
throw new ParseException('You cannot define a mapping item when in a sequence', $this->currentLineNb + 1, $this->currentLine, $this->filename);
|
||||
}
|
||||
$context = 'mapping';
|
||||
|
||||
Inline::initialize($flags, $this->getRealCurrentLineNb());
|
||||
Inline::initialize($flags, $this->getRealCurrentLineNb(), $this->filename);
|
||||
try {
|
||||
$key = Inline::parseScalar($values['key']);
|
||||
} catch (ParseException $e) {
|
||||
@ -204,13 +234,13 @@ class Parser
|
||||
if (isset($values['value']) && 0 === strpos($values['value'], '*')) {
|
||||
$refName = substr(rtrim($values['value']), 1);
|
||||
if (!array_key_exists($refName, $this->refs)) {
|
||||
throw new ParseException(sprintf('Reference "%s" does not exist.', $refName), $this->getRealCurrentLineNb() + 1, $this->currentLine);
|
||||
throw new ParseException(sprintf('Reference "%s" does not exist.', $refName), $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename);
|
||||
}
|
||||
|
||||
$refValue = $this->refs[$refName];
|
||||
|
||||
if (!is_array($refValue)) {
|
||||
throw new ParseException('YAML merge keys used with a scalar value instead of an array.', $this->getRealCurrentLineNb() + 1, $this->currentLine);
|
||||
throw new ParseException('YAML merge keys used with a scalar value instead of an array.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename);
|
||||
}
|
||||
|
||||
$data += $refValue; // array union
|
||||
@ -223,7 +253,7 @@ class Parser
|
||||
$parsed = $this->parseBlock($this->getRealCurrentLineNb() + 1, $value, $flags);
|
||||
|
||||
if (!is_array($parsed)) {
|
||||
throw new ParseException('YAML merge keys used with a scalar value instead of an array.', $this->getRealCurrentLineNb() + 1, $this->currentLine);
|
||||
throw new ParseException('YAML merge keys used with a scalar value instead of an array.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename);
|
||||
}
|
||||
|
||||
if (isset($parsed[0])) {
|
||||
@ -232,7 +262,7 @@ class Parser
|
||||
// in the sequence override keys specified in later mapping nodes.
|
||||
foreach ($parsed as $parsedItem) {
|
||||
if (!is_array($parsedItem)) {
|
||||
throw new ParseException('Merge items must be arrays.', $this->getRealCurrentLineNb() + 1, $parsedItem);
|
||||
throw new ParseException('Merge items must be arrays.', $this->getRealCurrentLineNb() + 1, $parsedItem, $this->filename);
|
||||
}
|
||||
|
||||
$data += $parsedItem; // array union
|
||||
@ -298,7 +328,7 @@ class Parser
|
||||
} else {
|
||||
// multiple documents are not supported
|
||||
if ('---' === $this->currentLine) {
|
||||
throw new ParseException('Multiple documents are not supported.', $this->currentLineNb + 1, $this->currentLine);
|
||||
throw new ParseException('Multiple documents are not supported.', $this->currentLineNb + 1, $this->currentLine, $this->filename);
|
||||
}
|
||||
|
||||
if (isset($this->currentLine[1]) && '?' === $this->currentLine[0] && ' ' === $this->currentLine[1]) {
|
||||
@ -360,7 +390,7 @@ class Parser
|
||||
}
|
||||
}
|
||||
|
||||
throw new ParseException('Unable to parse.', $this->getRealCurrentLineNb() + 1, $this->currentLine);
|
||||
throw new ParseException('Unable to parse.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename);
|
||||
}
|
||||
} while ($this->moveToNextLine());
|
||||
|
||||
@ -463,7 +493,7 @@ class Parser
|
||||
$unindentedEmbedBlock = $this->isStringUnIndentedCollectionItem();
|
||||
|
||||
if (!$this->isCurrentLineEmpty() && 0 === $newIndent && !$unindentedEmbedBlock) {
|
||||
throw new ParseException('Indentation problem.', $this->getRealCurrentLineNb() + 1, $this->currentLine);
|
||||
throw new ParseException('Indentation problem.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename);
|
||||
}
|
||||
} else {
|
||||
$newIndent = $indentation;
|
||||
@ -542,7 +572,7 @@ class Parser
|
||||
|
||||
break;
|
||||
} else {
|
||||
throw new ParseException('Indentation problem.', $this->getRealCurrentLineNb() + 1, $this->currentLine);
|
||||
throw new ParseException('Indentation problem.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename);
|
||||
}
|
||||
}
|
||||
|
||||
@ -602,7 +632,7 @@ class Parser
|
||||
}
|
||||
|
||||
if (!array_key_exists($value, $this->refs)) {
|
||||
throw new ParseException(sprintf('Reference "%s" does not exist.', $value), $this->currentLineNb + 1, $this->currentLine);
|
||||
throw new ParseException(sprintf('Reference "%s" does not exist.', $value), $this->currentLineNb + 1, $this->currentLine, $this->filename);
|
||||
}
|
||||
|
||||
return $this->refs[$value];
|
||||
@ -652,7 +682,7 @@ class Parser
|
||||
$parsedValue = Inline::parse($value, $flags, $this->refs);
|
||||
|
||||
if ('mapping' === $context && is_string($parsedValue) && '"' !== $value[0] && "'" !== $value[0] && '[' !== $value[0] && '{' !== $value[0] && '!' !== $value[0] && false !== strpos($parsedValue, ': ')) {
|
||||
throw new ParseException('A colon cannot be used in an unquoted mapping value.');
|
||||
throw new ParseException('A colon cannot be used in an unquoted mapping value.', $this->getRealCurrentLineNb() + 1, $value, $this->filename);
|
||||
}
|
||||
|
||||
return $parsedValue;
|
||||
@ -992,13 +1022,13 @@ class Parser
|
||||
|
||||
// Built-in tags
|
||||
if ($tag && '!' === $tag[0]) {
|
||||
throw new ParseException(sprintf('The built-in tag "!%s" is not implemented.', $tag));
|
||||
throw new ParseException(sprintf('The built-in tag "!%s" is not implemented.', $tag), $this->getRealCurrentLineNb() + 1, $value, $this->filename);
|
||||
}
|
||||
|
||||
if (Yaml::PARSE_CUSTOM_TAGS & $flags) {
|
||||
return $tag;
|
||||
}
|
||||
|
||||
throw new ParseException(sprintf('Tags support is not enabled. You must use the flag `Yaml::PARSE_CUSTOM_TAGS` to use "%s".', $matches['tag']));
|
||||
throw new ParseException(sprintf('Tags support is not enabled. You must use the flag `Yaml::PARSE_CUSTOM_TAGS` to use "%s".', $matches['tag']), $this->getRealCurrentLineNb() + 1, $value, $this->filename);
|
||||
}
|
||||
}
|
||||
|
18
src/Symfony/Component/Yaml/Tests/Fixtures/not_readable.yml
Normal file
18
src/Symfony/Component/Yaml/Tests/Fixtures/not_readable.yml
Normal file
@ -0,0 +1,18 @@
|
||||
- escapedCharacters
|
||||
- sfComments
|
||||
- sfCompact
|
||||
- sfTests
|
||||
- sfObjects
|
||||
- sfMergeKey
|
||||
- sfQuotes
|
||||
- YtsAnchorAlias
|
||||
- YtsBasicTests
|
||||
- YtsBlockMapping
|
||||
- YtsDocumentSeparator
|
||||
- YtsErrorTests
|
||||
- YtsFlowCollections
|
||||
- YtsFoldedScalars
|
||||
- YtsNullsAndEmpties
|
||||
- YtsSpecificationExamples
|
||||
- YtsTypeTransfers
|
||||
- unindentedCollections
|
@ -19,6 +19,11 @@ use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
class InlineTest extends TestCase
|
||||
{
|
||||
protected function setUp()
|
||||
{
|
||||
Inline::initialize(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getTestsForParse
|
||||
*/
|
||||
@ -241,11 +246,16 @@ class InlineTest extends TestCase
|
||||
|
||||
/**
|
||||
* @dataProvider getReservedIndicators
|
||||
* @expectedException \Symfony\Component\Yaml\Exception\ParseException
|
||||
* @expectedExceptionMessage cannot start a plain scalar; you need to quote the scalar.
|
||||
*/
|
||||
public function testParseUnquotedScalarStartingWithReservedIndicator($indicator)
|
||||
{
|
||||
if (method_exists($this, 'expectExceptionMessage')) {
|
||||
$this->expectException(ParseException::class);
|
||||
$this->expectExceptionMessage(sprintf('cannot start a plain scalar; you need to quote the scalar (near "%sfoo ").', $indicator));
|
||||
} else {
|
||||
$this->setExpectedException(ParseException::class, sprintf('cannot start a plain scalar; you need to quote the scalar (near "%sfoo ").', $indicator));
|
||||
}
|
||||
|
||||
Inline::parse(sprintf('{ foo: %sfoo }', $indicator));
|
||||
}
|
||||
|
||||
@ -256,11 +266,16 @@ class InlineTest extends TestCase
|
||||
|
||||
/**
|
||||
* @dataProvider getScalarIndicators
|
||||
* @expectedException \Symfony\Component\Yaml\Exception\ParseException
|
||||
* @expectedExceptionMessage cannot start a plain scalar; you need to quote the scalar.
|
||||
*/
|
||||
public function testParseUnquotedScalarStartingWithScalarIndicator($indicator)
|
||||
{
|
||||
if (method_exists($this, 'expectExceptionMessage')) {
|
||||
$this->expectException(ParseException::class);
|
||||
$this->expectExceptionMessage(sprintf('cannot start a plain scalar; you need to quote the scalar (near "%sfoo ").', $indicator));
|
||||
} else {
|
||||
$this->setExpectedException(ParseException::class, sprintf('cannot start a plain scalar; you need to quote the scalar (near "%sfoo ").', $indicator));
|
||||
}
|
||||
|
||||
Inline::parse(sprintf('{ foo: %sfoo }', $indicator));
|
||||
}
|
||||
|
||||
|
@ -30,6 +30,8 @@ class ParserTest extends TestCase
|
||||
protected function tearDown()
|
||||
{
|
||||
$this->parser = null;
|
||||
|
||||
chmod(__DIR__.'/Fixtures/not_readable.yml', 0644);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1594,7 +1596,7 @@ YAML
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Yaml\Exception\ParseException
|
||||
* @expectedExceptionMessage The built-in tag "!!foo" is not implemented.
|
||||
* @expectedExceptionMessage The built-in tag "!!foo" is not implemented at line 1 (near "!!foo").
|
||||
*/
|
||||
public function testExceptionWhenUsingUnsuportedBuiltInTags()
|
||||
{
|
||||
@ -1745,6 +1747,43 @@ YAML;
|
||||
|
||||
$this->assertSame($expected, $this->parser->parse($yaml, Yaml::PARSE_CONSTANT));
|
||||
}
|
||||
|
||||
public function testFilenamesAreParsedAsStringsWithoutFlag()
|
||||
{
|
||||
$file = __DIR__.'/Fixtures/index.yml';
|
||||
|
||||
$this->assertSame($file, $this->parser->parse($file));
|
||||
}
|
||||
|
||||
public function testParseFile()
|
||||
{
|
||||
$this->assertInternalType('array', $this->parser->parseFile(__DIR__.'/Fixtures/index.yml'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Yaml\Exception\ParseException
|
||||
* @expectedExceptionMessageRegExp #^File ".+/Fixtures/nonexistent.yml" does not exist\.$#
|
||||
*/
|
||||
public function testParsingNonExistentFilesThrowsException()
|
||||
{
|
||||
$this->parser->parseFile(__DIR__.'/Fixtures/nonexistent.yml');
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Yaml\Exception\ParseException
|
||||
* @expectedExceptionMessageRegExp #^File ".+/Fixtures/not_readable.yml" cannot be read\.$#
|
||||
*/
|
||||
public function testParsingNotReadableFilesThrowsException()
|
||||
{
|
||||
if ('\\' === DIRECTORY_SEPARATOR) {
|
||||
$this->markTestSkipped('chmod is not supported on Windows');
|
||||
}
|
||||
|
||||
$file = __DIR__.'/Fixtures/not_readable.yml';
|
||||
chmod($file, 0200);
|
||||
|
||||
$this->parser->parseFile($file);
|
||||
}
|
||||
}
|
||||
|
||||
class B
|
||||
|
@ -34,6 +34,29 @@ class Yaml
|
||||
const PARSE_CUSTOM_TAGS = 512;
|
||||
const DUMP_EMPTY_ARRAY_AS_SEQUENCE = 1024;
|
||||
|
||||
/**
|
||||
* Parses a YAML file into a PHP value.
|
||||
*
|
||||
* Usage:
|
||||
* <code>
|
||||
* $array = Yaml::parseFile('config.yml');
|
||||
* print_r($array);
|
||||
* </code>
|
||||
*
|
||||
* @param string $filename The path to the YAML file to be parsed
|
||||
* @param int $flags A bit field of PARSE_* constants to customize the YAML parser behavior
|
||||
*
|
||||
* @return mixed The YAML converted to a PHP value
|
||||
*
|
||||
* @throws ParseException If the file could not be read or the YAML is not valid
|
||||
*/
|
||||
public static function parseFile($filename, $flags = 0)
|
||||
{
|
||||
$yaml = new Parser();
|
||||
|
||||
return $yaml->parseFile($filename, $flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses YAML into a PHP value.
|
||||
*
|
||||
|
Reference in New Issue
Block a user