diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php index 62313cb441..aa5fae01cb 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php @@ -683,7 +683,9 @@ class Configuration implements ConfigurationInterface ->{!class_exists(FullStack::class) && class_exists(Serializer::class) ? 'canBeDisabled' : 'canBeEnabled'}() ->children() ->booleanNode('enable_annotations')->{!class_exists(FullStack::class) && class_exists(Annotation::class) ? 'defaultTrue' : 'defaultFalse'}()->end() - ->scalarNode('cache')->end() + ->scalarNode('cache') + ->setDeprecated('The "%path%.%node%" option is deprecated since Symfony 3.1 and will be removed in 4.0. Configure the "cache.serializer" service under "framework.cache.pools" instead.') + ->end() ->scalarNode('name_converter')->end() ->scalarNode('circular_reference_handler')->end() ->arrayNode('mapping') diff --git a/src/Symfony/Component/Cache/Adapter/TagAwareAdapter.php b/src/Symfony/Component/Cache/Adapter/TagAwareAdapter.php index 1e0617ebe2..96df1128bb 100644 --- a/src/Symfony/Component/Cache/Adapter/TagAwareAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/TagAwareAdapter.php @@ -14,11 +14,12 @@ namespace Symfony\Component\Cache\Adapter; use Psr\Cache\CacheItemInterface; use Psr\Cache\InvalidArgumentException; use Symfony\Component\Cache\CacheItem; +use Symfony\Component\Cache\PruneableInterface; /** * @author Nicolas Grekas
*/
-class TagAwareAdapter implements TagAwareAdapterInterface
+class TagAwareAdapter implements TagAwareAdapterInterface, PruneableInterface
{
const TAGS_PREFIX = "\0tags\0";
@@ -334,4 +335,16 @@ class TagAwareAdapter implements TagAwareAdapterInterface
return $tagVersions;
}
+
+ /**
+ * {@inheritdoc}
+ */
+ public function prune()
+ {
+ if ($this->itemsAdapter instanceof PruneableInterface) {
+ return $this->itemsAdapter->prune();
+ }
+
+ return false;
+ }
}
diff --git a/src/Symfony/Component/Cache/CHANGELOG.md b/src/Symfony/Component/Cache/CHANGELOG.md
index bd637f10df..7389a7edcc 100644
--- a/src/Symfony/Component/Cache/CHANGELOG.md
+++ b/src/Symfony/Component/Cache/CHANGELOG.md
@@ -5,7 +5,7 @@ CHANGELOG
-----
* added PruneableInterface so PSR-6 or PSR-16 cache implementations can declare support for manual stale cache pruning
- * added prune logic to FilesystemTrait, PhpFilesTrait, PdoTrait, and ChainTrait
+ * added prune logic to FilesystemTrait, PhpFilesTrait, PdoTrait, TagAwareAdapter and ChainTrait
* now FilesystemAdapter, PhpFilesAdapter, FilesystemCache, PhpFilesCache, PdoAdapter, PdoCache, ChainAdapter, and
ChainCache implement PruneableInterface and support manual stale cache pruning
diff --git a/src/Symfony/Component/Cache/Tests/Adapter/TagAwareAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/TagAwareAdapterTest.php
index deca227c47..0e4e07a16d 100644
--- a/src/Symfony/Component/Cache/Tests/Adapter/TagAwareAdapterTest.php
+++ b/src/Symfony/Component/Cache/Tests/Adapter/TagAwareAdapterTest.php
@@ -11,6 +11,7 @@
namespace Symfony\Component\Cache\Tests\Adapter;
+use Symfony\Component\Cache\Adapter\AdapterInterface;
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
use Symfony\Component\Cache\Adapter\TagAwareAdapter;
@@ -125,4 +126,60 @@ class TagAwareAdapterTest extends AdapterTestCase
$i = $pool->getItem('k');
$this->assertSame(array('foo' => 'foo'), $i->getPreviousTags());
}
+
+ public function testPrune()
+ {
+ $cache = new TagAwareAdapter($this->getPruneableMock());
+ $this->assertTrue($cache->prune());
+
+ $cache = new TagAwareAdapter($this->getNonPruneableMock());
+ $this->assertFalse($cache->prune());
+
+ $cache = new TagAwareAdapter($this->getFailingPruneableMock());
+ $this->assertFalse($cache->prune());
+ }
+
+ /**
+ * @return \PHPUnit_Framework_MockObject_MockObject|PruneableCacheInterface
+ */
+ private function getPruneableMock()
+ {
+ $pruneable = $this
+ ->getMockBuilder(PruneableCacheInterface::class)
+ ->getMock();
+
+ $pruneable
+ ->expects($this->atLeastOnce())
+ ->method('prune')
+ ->will($this->returnValue(true));
+
+ return $pruneable;
+ }
+
+ /**
+ * @return \PHPUnit_Framework_MockObject_MockObject|PruneableCacheInterface
+ */
+ private function getFailingPruneableMock()
+ {
+ $pruneable = $this
+ ->getMockBuilder(PruneableCacheInterface::class)
+ ->getMock();
+
+ $pruneable
+ ->expects($this->atLeastOnce())
+ ->method('prune')
+ ->will($this->returnValue(false));
+
+ return $pruneable;
+ }
+
+ /**
+ * @return \PHPUnit_Framework_MockObject_MockObject|AdapterInterface
+ */
+ private function getNonPruneableMock()
+ {
+ return $this
+ ->getMockBuilder(AdapterInterface::class)
+ ->getMock();
+ }
}
diff --git a/src/Symfony/Component/Config/Definition/ArrayNode.php b/src/Symfony/Component/Config/Definition/ArrayNode.php
index 2ffa2a2137..ef068c9f34 100644
--- a/src/Symfony/Component/Config/Definition/ArrayNode.php
+++ b/src/Symfony/Component/Config/Definition/ArrayNode.php
@@ -234,10 +234,6 @@ class ArrayNode extends BaseNode implements PrototypeNodeInterface
}
foreach ($this->children as $name => $child) {
- if ($child->isDeprecated()) {
- @trigger_error($child->getDeprecationMessage($name, $this->getPath()), E_USER_DEPRECATED);
- }
-
if (!array_key_exists($name, $value)) {
if ($child->isRequired()) {
$msg = sprintf('The child node "%s" at path "%s" must be configured.', $name, $this->getPath());
@@ -254,6 +250,10 @@ class ArrayNode extends BaseNode implements PrototypeNodeInterface
continue;
}
+ if ($child->isDeprecated()) {
+ @trigger_error($child->getDeprecationMessage($name, $this->getPath()), E_USER_DEPRECATED);
+ }
+
try {
$value[$name] = $child->finalize($value[$name]);
} catch (UnsetKeyException $e) {
diff --git a/src/Symfony/Component/Config/Definition/VariableNode.php b/src/Symfony/Component/Config/Definition/VariableNode.php
index f37ced901b..a9c35284cd 100644
--- a/src/Symfony/Component/Config/Definition/VariableNode.php
+++ b/src/Symfony/Component/Config/Definition/VariableNode.php
@@ -84,10 +84,6 @@ class VariableNode extends BaseNode implements PrototypeNodeInterface
*/
protected function finalizeValue($value)
{
- if ($this->deprecationMessage) {
- @trigger_error($this->getDeprecationMessage($this->getName(), $this->getPath()), E_USER_DEPRECATED);
- }
-
if (!$this->allowEmptyValue && $this->isValueEmpty($value)) {
$ex = new InvalidConfigurationException(sprintf(
'The path "%s" cannot contain an empty value, but got %s.',
diff --git a/src/Symfony/Component/Config/Tests/Definition/ArrayNodeTest.php b/src/Symfony/Component/Config/Tests/Definition/ArrayNodeTest.php
index 7a5bf30373..58d2939300 100644
--- a/src/Symfony/Component/Config/Tests/Definition/ArrayNodeTest.php
+++ b/src/Symfony/Component/Config/Tests/Definition/ArrayNodeTest.php
@@ -219,10 +219,33 @@ class ArrayNodeTest extends TestCase
public function testSetDeprecated()
{
- $node = new ArrayNode('foo');
- $node->setDeprecated('"%node%" is deprecated');
+ $childNode = new ArrayNode('foo');
+ $childNode->setDeprecated('"%node%" is deprecated');
- $this->assertTrue($node->isDeprecated());
- $this->assertSame('"foo" is deprecated', $node->getDeprecationMessage($node->getName(), $node->getPath()));
+ $this->assertTrue($childNode->isDeprecated());
+ $this->assertSame('"foo" is deprecated', $childNode->getDeprecationMessage($childNode->getName(), $childNode->getPath()));
+
+ $node = new ArrayNode('root');
+ $node->addChild($childNode);
+
+ $deprecationTriggered = false;
+ $deprecationHandler = function ($level, $message, $file, $line) use (&$prevErrorHandler, &$deprecationTriggered) {
+ if (E_USER_DEPRECATED === $level) {
+ return $deprecationTriggered = true;
+ }
+
+ return $prevErrorHandler ? $prevErrorHandler($level, $message, $file, $line) : false;
+ };
+
+ $prevErrorHandler = set_error_handler($deprecationHandler);
+ $node->finalize(array());
+ restore_error_handler();
+
+ $this->assertFalse($deprecationTriggered, '->finalize() should not trigger if the deprecated node is not set');
+
+ $prevErrorHandler = set_error_handler($deprecationHandler);
+ $node->finalize(array('foo' => array()));
+ restore_error_handler();
+ $this->assertTrue($deprecationTriggered, '->finalize() should trigger if the deprecated node is set');
}
}
diff --git a/src/Symfony/Component/Config/Tests/Definition/ScalarNodeTest.php b/src/Symfony/Component/Config/Tests/Definition/ScalarNodeTest.php
index 1981ac9459..481ef3f496 100644
--- a/src/Symfony/Component/Config/Tests/Definition/ScalarNodeTest.php
+++ b/src/Symfony/Component/Config/Tests/Definition/ScalarNodeTest.php
@@ -12,6 +12,7 @@
namespace Symfony\Component\Config\Tests\Definition;
use PHPUnit\Framework\TestCase;
+use Symfony\Component\Config\Definition\ArrayNode;
use Symfony\Component\Config\Definition\ScalarNode;
class ScalarNodeTest extends TestCase
@@ -42,11 +43,33 @@ class ScalarNodeTest extends TestCase
public function testSetDeprecated()
{
- $node = new ScalarNode('foo');
- $node->setDeprecated('"%node%" is deprecated');
+ $childNode = new ScalarNode('foo');
+ $childNode->setDeprecated('"%node%" is deprecated');
- $this->assertTrue($node->isDeprecated());
- $this->assertSame('"foo" is deprecated', $node->getDeprecationMessage($node->getName(), $node->getPath()));
+ $this->assertTrue($childNode->isDeprecated());
+ $this->assertSame('"foo" is deprecated', $childNode->getDeprecationMessage($childNode->getName(), $childNode->getPath()));
+
+ $node = new ArrayNode('root');
+ $node->addChild($childNode);
+
+ $deprecationTriggered = 0;
+ $deprecationHandler = function ($level, $message, $file, $line) use (&$prevErrorHandler, &$deprecationTriggered) {
+ if (E_USER_DEPRECATED === $level) {
+ return ++$deprecationTriggered;
+ }
+
+ return $prevErrorHandler ? $prevErrorHandler($level, $message, $file, $line) : false;
+ };
+
+ $prevErrorHandler = set_error_handler($deprecationHandler);
+ $node->finalize(array());
+ restore_error_handler();
+ $this->assertSame(0, $deprecationTriggered, '->finalize() should not trigger if the deprecated node is not set');
+
+ $prevErrorHandler = set_error_handler($deprecationHandler);
+ $node->finalize(array('foo' => ''));
+ restore_error_handler();
+ $this->assertSame(1, $deprecationTriggered, '->finalize() should trigger if the deprecated node is set');
}
/**
diff --git a/src/Symfony/Component/VarDumper/Caster/DateCaster.php b/src/Symfony/Component/VarDumper/Caster/DateCaster.php
index b39b1f9b05..e2868ff718 100644
--- a/src/Symfony/Component/VarDumper/Caster/DateCaster.php
+++ b/src/Symfony/Component/VarDumper/Caster/DateCaster.php
@@ -34,7 +34,7 @@ class DateCaster
;
$a = array();
- $a[$prefix.'date'] = new ConstStub($d->format('Y-m-d H:i:'.self::formatSeconds($d->format('s'), $d->format('u')).($location ? ' e (P)' : ' P')), $title);
+ $a[$prefix.'date'] = new ConstStub(self::formatDateTime($d, $location ? ' e (P)' : ' P'), $title);
$stub->class .= $d->format(' @U');
@@ -63,7 +63,7 @@ class DateCaster
$format .= ($i->y ? '%yy ' : '').($i->m ? '%mm ' : '').($i->d ? '%dd ' : '');
}
- $format .= $i->h || $i->i || $i->s || $i->f ? '%H:%I:'.self::formatSeconds($i->s, $i->f) : '';
+ $format .= $i->h || $i->i || $i->s || $i->f ? '%H:%I:'.self::formatSeconds($i->s, substr($i->f, 2)) : '';
$format = '%R ' === $format ? '0s' : $format;
return $i->format(rtrim($format));
@@ -93,16 +93,16 @@ class DateCaster
);
break;
}
- $dates[] = sprintf('%s) %s', $i + 1, $d->format('Y-m-d H:i:s'));
+ $dates[] = sprintf('%s) %s', $i + 1, self::formatDateTime($d));
}
}
$period = sprintf(
'every %s, from %s (%s) %s',
self::formatInterval($p->getDateInterval()),
- $p->getStartDate()->format('Y-m-d H:i:s'),
+ self::formatDateTime($p->getStartDate()),
$p->include_start_date ? 'included' : 'excluded',
- ($end = $p->getEndDate()) ? 'to '.$end->format('Y-m-d H:i:s') : 'recurring '.$p->recurrences.' time/s'
+ ($end = $p->getEndDate()) ? 'to '.self::formatDateTime($end) : 'recurring '.$p->recurrences.' time/s'
);
$p = array(Caster::PREFIX_VIRTUAL.'period' => new ConstStub($period, implode("\n", $dates)));
@@ -110,6 +110,11 @@ class DateCaster
return $filter & Caster::EXCLUDE_VERBOSE ? $p : $p + $a;
}
+ private static function formatDateTime(\DateTimeInterface $d, $extra = '')
+ {
+ return $d->format('Y-m-d H:i:'.self::formatSeconds($d->format('s'), $d->format('u')).$extra);
+ }
+
private static function formatSeconds($s, $us)
{
return sprintf('%02d.%s', $s, 0 === ($len = strlen($t = rtrim($us, '0'))) ? '0' : ($len <= 3 ? str_pad($t, 3, '0') : $us));
diff --git a/src/Symfony/Component/VarDumper/Tests/Caster/DateCasterTest.php b/src/Symfony/Component/VarDumper/Tests/Caster/DateCasterTest.php
index a3bc4867a8..cb6b6a6b16 100644
--- a/src/Symfony/Component/VarDumper/Tests/Caster/DateCasterTest.php
+++ b/src/Symfony/Component/VarDumper/Tests/Caster/DateCasterTest.php
@@ -92,10 +92,9 @@ EODUMP;
/**
* @dataProvider provideIntervals
*/
- public function testDumpInterval($intervalSpec, $invert, $expected)
+ public function testDumpInterval($intervalSpec, $ms, $invert, $expected)
{
- $interval = new \DateInterval($intervalSpec);
- $interval->invert = $invert;
+ $interval = $this->createInterval($intervalSpec, $ms, $invert);
$xDump = <<