+ *
+ * @final since Symfony 4.3
*/
-class ComposerResource implements SelfCheckingResourceInterface, \Serializable
+class ComposerResource implements SelfCheckingResourceInterface
{
private $vendors;
@@ -51,22 +53,6 @@ public function isFresh($timestamp)
return self::$runtimeVendors === $this->vendors;
}
- /**
- * @internal
- */
- public function serialize()
- {
- return serialize($this->vendors);
- }
-
- /**
- * @internal
- */
- public function unserialize($serialized)
- {
- $this->vendors = unserialize($serialized);
- }
-
private static function refresh()
{
self::$runtimeVendors = [];
diff --git a/vendor/symfony/config/Resource/DirectoryResource.php b/vendor/symfony/config/Resource/DirectoryResource.php
index d657abcbae..3d703db7f6 100644
--- a/vendor/symfony/config/Resource/DirectoryResource.php
+++ b/vendor/symfony/config/Resource/DirectoryResource.php
@@ -15,8 +15,10 @@
* DirectoryResource represents a resources stored in a subdirectory tree.
*
* @author Fabien Potencier
+ *
+ * @final since Symfony 4.3
*/
-class DirectoryResource implements SelfCheckingResourceInterface, \Serializable
+class DirectoryResource implements SelfCheckingResourceInterface
{
private $resource;
private $pattern;
@@ -103,20 +105,4 @@ public function isFresh($timestamp)
return true;
}
-
- /**
- * @internal
- */
- public function serialize()
- {
- return serialize([$this->resource, $this->pattern]);
- }
-
- /**
- * @internal
- */
- public function unserialize($serialized)
- {
- list($this->resource, $this->pattern) = unserialize($serialized);
- }
}
diff --git a/vendor/symfony/config/Resource/FileExistenceResource.php b/vendor/symfony/config/Resource/FileExistenceResource.php
index 8c65729c45..5723416158 100644
--- a/vendor/symfony/config/Resource/FileExistenceResource.php
+++ b/vendor/symfony/config/Resource/FileExistenceResource.php
@@ -18,8 +18,10 @@
* The resource can be a file or a directory.
*
* @author Charles-Henri Bruyand
+ *
+ * @final since Symfony 4.3
*/
-class FileExistenceResource implements SelfCheckingResourceInterface, \Serializable
+class FileExistenceResource implements SelfCheckingResourceInterface
{
private $resource;
@@ -57,20 +59,4 @@ public function isFresh($timestamp)
{
return file_exists($this->resource) === $this->exists;
}
-
- /**
- * @internal
- */
- public function serialize()
- {
- return serialize([$this->resource, $this->exists]);
- }
-
- /**
- * @internal
- */
- public function unserialize($serialized)
- {
- list($this->resource, $this->exists) = unserialize($serialized);
- }
}
diff --git a/vendor/symfony/config/Resource/FileResource.php b/vendor/symfony/config/Resource/FileResource.php
index 94ec2c729a..95fe8a0bf8 100644
--- a/vendor/symfony/config/Resource/FileResource.php
+++ b/vendor/symfony/config/Resource/FileResource.php
@@ -17,8 +17,10 @@
* The resource can be a file or a directory.
*
* @author Fabien Potencier
+ *
+ * @final since Symfony 4.3
*/
-class FileResource implements SelfCheckingResourceInterface, \Serializable
+class FileResource implements SelfCheckingResourceInterface
{
/**
* @var string|false
@@ -62,20 +64,4 @@ public function isFresh($timestamp)
{
return false !== ($filemtime = @filemtime($this->resource)) && $filemtime <= $timestamp;
}
-
- /**
- * @internal
- */
- public function serialize()
- {
- return serialize($this->resource);
- }
-
- /**
- * @internal
- */
- public function unserialize($serialized)
- {
- $this->resource = unserialize($serialized);
- }
}
diff --git a/vendor/symfony/config/Resource/GlobResource.php b/vendor/symfony/config/Resource/GlobResource.php
index 5581e67eca..fce8f6e206 100644
--- a/vendor/symfony/config/Resource/GlobResource.php
+++ b/vendor/symfony/config/Resource/GlobResource.php
@@ -20,8 +20,10 @@
* Only existence/removal is tracked (not mtimes.)
*
* @author Nicolas Grekas
+ *
+ * @final since Symfony 4.3
*/
-class GlobResource implements \IteratorAggregate, SelfCheckingResourceInterface, \Serializable
+class GlobResource implements \IteratorAggregate, SelfCheckingResourceInterface
{
private $prefix;
private $pattern;
@@ -80,21 +82,13 @@ public function isFresh($timestamp)
/**
* @internal
*/
- public function serialize()
+ public function __sleep(): array
{
if (null === $this->hash) {
$this->hash = $this->computeHash();
}
- return serialize([$this->prefix, $this->pattern, $this->recursive, $this->hash, $this->forExclusion, $this->excludedPrefixes]);
- }
-
- /**
- * @internal
- */
- public function unserialize($serialized)
- {
- list($this->prefix, $this->pattern, $this->recursive, $this->hash, $this->forExclusion, $this->excludedPrefixes) = unserialize($serialized) + [4 => false, []];
+ return ['prefix', 'pattern', 'recursive', 'hash', 'forExclusion', 'excludedPrefixes'];
}
public function getIterator()
diff --git a/vendor/symfony/config/Resource/ReflectionClassResource.php b/vendor/symfony/config/Resource/ReflectionClassResource.php
index a7e73becf5..940b24d79b 100644
--- a/vendor/symfony/config/Resource/ReflectionClassResource.php
+++ b/vendor/symfony/config/Resource/ReflectionClassResource.php
@@ -13,12 +13,15 @@
use Symfony\Component\DependencyInjection\ServiceSubscriberInterface as LegacyServiceSubscriberInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+use Symfony\Component\Messenger\Handler\MessageSubscriberInterface;
use Symfony\Contracts\Service\ServiceSubscriberInterface;
/**
* @author Nicolas Grekas
+ *
+ * @final since Symfony 4.3
*/
-class ReflectionClassResource implements SelfCheckingResourceInterface, \Serializable
+class ReflectionClassResource implements SelfCheckingResourceInterface
{
private $files = [];
private $className;
@@ -61,22 +64,14 @@ public function __toString()
/**
* @internal
*/
- public function serialize()
+ public function __sleep(): array
{
if (null === $this->hash) {
$this->hash = $this->computeHash();
$this->loadFiles($this->classReflector);
}
- return serialize([$this->files, $this->className, $this->hash]);
- }
-
- /**
- * @internal
- */
- public function unserialize($serialized)
- {
- list($this->files, $this->className, $this->hash) = unserialize($serialized);
+ return ['files', 'className', 'hash'];
}
private function loadFiles(\ReflectionClass $class)
@@ -164,6 +159,13 @@ private function generateSignature(\ReflectionClass $class)
yield print_r($class->name::getSubscribedEvents(), true);
}
+ if (interface_exists(MessageSubscriberInterface::class, false) && $class->isSubclassOf(MessageSubscriberInterface::class)) {
+ yield MessageSubscriberInterface::class;
+ foreach ($class->name::getHandledMessages() as $key => $value) {
+ yield $key.print_r($value, true);
+ }
+ }
+
if (interface_exists(LegacyServiceSubscriberInterface::class, false) && $class->isSubclassOf(LegacyServiceSubscriberInterface::class)) {
yield LegacyServiceSubscriberInterface::class;
yield print_r([$class->name, 'getSubscribedServices'](), true);
diff --git a/vendor/symfony/config/ResourceCheckerConfigCache.php b/vendor/symfony/config/ResourceCheckerConfigCache.php
index ef18072844..34dc35d5f5 100644
--- a/vendor/symfony/config/ResourceCheckerConfigCache.php
+++ b/vendor/symfony/config/ResourceCheckerConfigCache.php
@@ -158,7 +158,7 @@ private function safelyUnserialize($file)
$meta = false;
$content = file_get_contents($file);
$signalingException = new \UnexpectedValueException();
- $prevUnserializeHandler = ini_set('unserialize_callback_func', '');
+ $prevUnserializeHandler = ini_set('unserialize_callback_func', self::class.'::handleUnserializeCallback');
$prevErrorHandler = set_error_handler(function ($type, $msg, $file, $line, $context = []) use (&$prevErrorHandler, $signalingException) {
if (__FILE__ === $file) {
throw $signalingException;
@@ -180,4 +180,12 @@ private function safelyUnserialize($file)
return $meta;
}
+
+ /**
+ * @internal
+ */
+ public static function handleUnserializeCallback($class)
+ {
+ trigger_error('Class not found: '.$class);
+ }
}
diff --git a/vendor/symfony/config/Tests/Definition/Builder/TreeBuilderTest.php b/vendor/symfony/config/Tests/Definition/Builder/TreeBuilderTest.php
index 0dae83be41..d0e3d2d52d 100644
--- a/vendor/symfony/config/Tests/Definition/Builder/TreeBuilderTest.php
+++ b/vendor/symfony/config/Tests/Definition/Builder/TreeBuilderTest.php
@@ -197,4 +197,14 @@ public function testInitializingTreeBuildersWithoutRootNode()
{
new TreeBuilder();
}
+
+ /**
+ * @group legacy
+ * @expectedDeprecation The "Symfony\Component\Config\Definition\Builder\TreeBuilder::root()" method called for the "foo" configuration is deprecated since Symfony 4.3, pass the root name to the constructor instead.
+ */
+ public function testRoot()
+ {
+ $builder = new TreeBuilder('foo');
+ $builder->root('foo');
+ }
}
diff --git a/vendor/symfony/config/Tests/Loader/DelegatingLoaderTest.php b/vendor/symfony/config/Tests/Loader/DelegatingLoaderTest.php
index 3517cacfd5..daff5288ef 100644
--- a/vendor/symfony/config/Tests/Loader/DelegatingLoaderTest.php
+++ b/vendor/symfony/config/Tests/Loader/DelegatingLoaderTest.php
@@ -35,12 +35,12 @@ public function testGetSetResolver()
public function testSupports()
{
$loader1 = $this->getMockBuilder('Symfony\Component\Config\Loader\LoaderInterface')->getMock();
- $loader1->expects($this->once())->method('supports')->will($this->returnValue(true));
+ $loader1->expects($this->once())->method('supports')->willReturn(true);
$loader = new DelegatingLoader(new LoaderResolver([$loader1]));
$this->assertTrue($loader->supports('foo.xml'), '->supports() returns true if the resource is loadable');
$loader1 = $this->getMockBuilder('Symfony\Component\Config\Loader\LoaderInterface')->getMock();
- $loader1->expects($this->once())->method('supports')->will($this->returnValue(false));
+ $loader1->expects($this->once())->method('supports')->willReturn(false);
$loader = new DelegatingLoader(new LoaderResolver([$loader1]));
$this->assertFalse($loader->supports('foo.foo'), '->supports() returns false if the resource is not loadable');
}
@@ -48,7 +48,7 @@ public function testSupports()
public function testLoad()
{
$loader = $this->getMockBuilder('Symfony\Component\Config\Loader\LoaderInterface')->getMock();
- $loader->expects($this->once())->method('supports')->will($this->returnValue(true));
+ $loader->expects($this->once())->method('supports')->willReturn(true);
$loader->expects($this->once())->method('load');
$resolver = new LoaderResolver([$loader]);
$loader = new DelegatingLoader($resolver);
@@ -62,7 +62,7 @@ public function testLoad()
public function testLoadThrowsAnExceptionIfTheResourceCannotBeLoaded()
{
$loader = $this->getMockBuilder('Symfony\Component\Config\Loader\LoaderInterface')->getMock();
- $loader->expects($this->once())->method('supports')->will($this->returnValue(false));
+ $loader->expects($this->once())->method('supports')->willReturn(false);
$resolver = new LoaderResolver([$loader]);
$loader = new DelegatingLoader($resolver);
diff --git a/vendor/symfony/config/Tests/Loader/LoaderResolverTest.php b/vendor/symfony/config/Tests/Loader/LoaderResolverTest.php
index 487dc43adc..aabc2a600d 100644
--- a/vendor/symfony/config/Tests/Loader/LoaderResolverTest.php
+++ b/vendor/symfony/config/Tests/Loader/LoaderResolverTest.php
@@ -32,7 +32,7 @@ public function testResolve()
$this->assertFalse($resolver->resolve('foo.foo'), '->resolve() returns false if no loader is able to load the resource');
$loader = $this->getMockBuilder('Symfony\Component\Config\Loader\LoaderInterface')->getMock();
- $loader->expects($this->once())->method('supports')->will($this->returnValue(true));
+ $loader->expects($this->once())->method('supports')->willReturn(true);
$resolver = new LoaderResolver([$loader]);
$this->assertEquals($loader, $resolver->resolve(function () {}), '->resolve() returns the loader for the given resource');
}
diff --git a/vendor/symfony/config/Tests/Loader/LoaderTest.php b/vendor/symfony/config/Tests/Loader/LoaderTest.php
index 926cec8333..cd14d58fe5 100644
--- a/vendor/symfony/config/Tests/Loader/LoaderTest.php
+++ b/vendor/symfony/config/Tests/Loader/LoaderTest.php
@@ -34,7 +34,7 @@ public function testResolve()
$resolver->expects($this->once())
->method('resolve')
->with('foo.xml')
- ->will($this->returnValue($resolvedLoader));
+ ->willReturn($resolvedLoader);
$loader = new ProjectLoader1();
$loader->setResolver($resolver);
@@ -52,7 +52,7 @@ public function testResolveWhenResolverCannotFindLoader()
$resolver->expects($this->once())
->method('resolve')
->with('FOOBAR')
- ->will($this->returnValue(false));
+ ->willReturn(false);
$loader = new ProjectLoader1();
$loader->setResolver($resolver);
@@ -66,13 +66,13 @@ public function testImport()
$resolvedLoader->expects($this->once())
->method('load')
->with('foo')
- ->will($this->returnValue('yes'));
+ ->willReturn('yes');
$resolver = $this->getMockBuilder('Symfony\Component\Config\Loader\LoaderResolverInterface')->getMock();
$resolver->expects($this->once())
->method('resolve')
->with('foo')
- ->will($this->returnValue($resolvedLoader));
+ ->willReturn($resolvedLoader);
$loader = new ProjectLoader1();
$loader->setResolver($resolver);
@@ -86,13 +86,13 @@ public function testImportWithType()
$resolvedLoader->expects($this->once())
->method('load')
->with('foo', 'bar')
- ->will($this->returnValue('yes'));
+ ->willReturn('yes');
$resolver = $this->getMockBuilder('Symfony\Component\Config\Loader\LoaderResolverInterface')->getMock();
$resolver->expects($this->once())
->method('resolve')
->with('foo', 'bar')
- ->will($this->returnValue($resolvedLoader));
+ ->willReturn($resolvedLoader);
$loader = new ProjectLoader1();
$loader->setResolver($resolver);
diff --git a/vendor/symfony/config/Tests/Resource/ReflectionClassResourceTest.php b/vendor/symfony/config/Tests/Resource/ReflectionClassResourceTest.php
index abc461cd7c..e22933245d 100644
--- a/vendor/symfony/config/Tests/Resource/ReflectionClassResourceTest.php
+++ b/vendor/symfony/config/Tests/Resource/ReflectionClassResourceTest.php
@@ -15,6 +15,7 @@
use Symfony\Component\Config\Resource\ReflectionClassResource;
use Symfony\Component\DependencyInjection\ServiceSubscriberInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+use Symfony\Component\Messenger\Handler\MessageSubscriberInterface;
class ReflectionClassResourceTest extends TestCase
{
@@ -147,6 +148,24 @@ public function testEventSubscriber()
$this->assertTrue($res->isFresh(0));
}
+ public function testMessageSubscriber()
+ {
+ $res = new ReflectionClassResource(new \ReflectionClass(TestMessageSubscriber::class));
+ $this->assertTrue($res->isFresh(0));
+
+ TestMessageSubscriberConfigHolder::$handledMessages = ['SomeMessageClass' => []];
+ $this->assertFalse($res->isFresh(0));
+
+ $res = new ReflectionClassResource(new \ReflectionClass(TestMessageSubscriber::class));
+ $this->assertTrue($res->isFresh(0));
+
+ TestMessageSubscriberConfigHolder::$handledMessages = ['OtherMessageClass' => []];
+ $this->assertFalse($res->isFresh(0));
+
+ $res = new ReflectionClassResource(new \ReflectionClass(TestMessageSubscriber::class));
+ $this->assertTrue($res->isFresh(0));
+ }
+
public function testServiceSubscriber()
{
$res = new ReflectionClassResource(new \ReflectionClass(TestServiceSubscriber::class));
@@ -174,6 +193,20 @@ public static function getSubscribedEvents()
}
}
+class TestMessageSubscriber implements MessageSubscriberInterface
+{
+ public static function getHandledMessages(): iterable
+ {
+ foreach (TestMessageSubscriberConfigHolder::$handledMessages as $key => $subscribedMessage) {
+ yield $key => $subscribedMessage;
+ }
+ }
+}
+class TestMessageSubscriberConfigHolder
+{
+ public static $handledMessages = [];
+}
+
class TestServiceSubscriber implements ServiceSubscriberInterface
{
public static $subscribedServices = [];
diff --git a/vendor/symfony/config/composer.json b/vendor/symfony/config/composer.json
index 35fbffdb69..c1f2338e5a 100644
--- a/vendor/symfony/config/composer.json
+++ b/vendor/symfony/config/composer.json
@@ -24,6 +24,7 @@
"symfony/dependency-injection": "~3.4|~4.0",
"symfony/event-dispatcher": "~3.4|~4.0",
"symfony/finder": "~3.4|~4.0",
+ "symfony/messenger": "~4.1",
"symfony/yaml": "~3.4|~4.0"
},
"conflict": {
@@ -41,7 +42,7 @@
"minimum-stability": "dev",
"extra": {
"branch-alias": {
- "dev-master": "4.2-dev"
+ "dev-master": "4.3-dev"
}
}
}
diff --git a/vendor/symfony/console/Application.php b/vendor/symfony/console/Application.php
index 17df91f994..5a9b185519 100644
--- a/vendor/symfony/console/Application.php
+++ b/vendor/symfony/console/Application.php
@@ -44,6 +44,7 @@
use Symfony\Component\Debug\ErrorHandler;
use Symfony\Component\Debug\Exception\FatalThrowableError;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
+use Symfony\Component\EventDispatcher\LegacyEventDispatcherProxy;
/**
* An Application is the container for a collection of commands.
@@ -90,9 +91,12 @@ public function __construct(string $name = 'UNKNOWN', string $version = 'UNKNOWN
$this->defaultCommand = 'list';
}
+ /**
+ * @final since Symfony 4.3, the type-hint will be updated to the interface from symfony/contracts in 5.0
+ */
public function setDispatcher(EventDispatcherInterface $dispatcher)
{
- $this->dispatcher = $dispatcher;
+ $this->dispatcher = LegacyEventDispatcherProxy::decorate($dispatcher);
}
public function setCommandLoader(CommandLoaderInterface $commandLoader)
@@ -235,7 +239,7 @@ public function doRun(InputInterface $input, OutputInterface $output)
if (!($e instanceof CommandNotFoundException && !$e instanceof NamespaceNotFoundException) || 1 !== \count($alternatives = $e->getAlternatives()) || !$input->isInteractive()) {
if (null !== $this->dispatcher) {
$event = new ConsoleErrorEvent($input, $output, $e);
- $this->dispatcher->dispatch(ConsoleEvents::ERROR, $event);
+ $this->dispatcher->dispatch($event, ConsoleEvents::ERROR);
if (0 === $event->getExitCode()) {
return 0;
@@ -254,7 +258,7 @@ public function doRun(InputInterface $input, OutputInterface $output)
if (!$style->confirm(sprintf('Do you want to run "%s" instead? ', $alternative), false)) {
if (null !== $this->dispatcher) {
$event = new ConsoleErrorEvent($input, $output, $e);
- $this->dispatcher->dispatch(ConsoleEvents::ERROR, $event);
+ $this->dispatcher->dispatch($event, ConsoleEvents::ERROR);
return $event->getExitCode();
}
@@ -610,6 +614,15 @@ public function find($name)
$this->init();
$aliases = [];
+
+ foreach ($this->commands as $command) {
+ foreach ($command->getAliases() as $alias) {
+ if (!$this->has($alias)) {
+ $this->commands[$alias] = $command;
+ }
+ }
+ }
+
$allCommands = $this->commandLoader ? array_merge($this->commandLoader->getNames(), array_keys($this->commands)) : array_keys($this->commands);
$expr = preg_replace_callback('{([^:]+|)}', function ($matches) { return preg_quote($matches[1]).'[^:]*'; }, $name);
$commands = preg_grep('{^'.$expr.'}', $allCommands);
@@ -920,7 +933,7 @@ protected function doRunCommand(Command $command, InputInterface $input, OutputI
$e = null;
try {
- $this->dispatcher->dispatch(ConsoleEvents::COMMAND, $event);
+ $this->dispatcher->dispatch($event, ConsoleEvents::COMMAND);
if ($event->commandShouldRun()) {
$exitCode = $command->run($input, $output);
@@ -929,7 +942,7 @@ protected function doRunCommand(Command $command, InputInterface $input, OutputI
}
} catch (\Throwable $e) {
$event = new ConsoleErrorEvent($input, $output, $e, $command);
- $this->dispatcher->dispatch(ConsoleEvents::ERROR, $event);
+ $this->dispatcher->dispatch($event, ConsoleEvents::ERROR);
$e = $event->getError();
if (0 === $exitCode = $event->getExitCode()) {
@@ -938,7 +951,7 @@ protected function doRunCommand(Command $command, InputInterface $input, OutputI
}
$event = new ConsoleTerminateEvent($command, $input, $output, $exitCode);
- $this->dispatcher->dispatch(ConsoleEvents::TERMINATE, $event);
+ $this->dispatcher->dispatch($event, ConsoleEvents::TERMINATE);
if (null !== $e) {
throw $e;
diff --git a/vendor/symfony/console/CHANGELOG.md b/vendor/symfony/console/CHANGELOG.md
index 399bbc2213..67decd30be 100644
--- a/vendor/symfony/console/CHANGELOG.md
+++ b/vendor/symfony/console/CHANGELOG.md
@@ -1,6 +1,14 @@
CHANGELOG
=========
+4.3.0
+-----
+
+ * added support for hyperlinks
+ * added `ProgressBar::iterate()` method that simplify updating the progress bar when iterating
+ * added `Question::setAutocompleterCallback()` to provide a callback function
+ that dynamically generates suggestions as the user types
+
4.2.0
-----
diff --git a/vendor/symfony/console/Descriptor/JsonDescriptor.php b/vendor/symfony/console/Descriptor/JsonDescriptor.php
index 197b843d4b..f5a143800b 100644
--- a/vendor/symfony/console/Descriptor/JsonDescriptor.php
+++ b/vendor/symfony/console/Descriptor/JsonDescriptor.php
@@ -97,7 +97,9 @@ protected function describeApplication(Application $application, array $options
*/
private function writeData(array $data, array $options)
{
- $this->write(json_encode($data, isset($options['json_encoding']) ? $options['json_encoding'] : 0));
+ $flags = isset($options['json_encoding']) ? $options['json_encoding'] : 0;
+
+ $this->write(json_encode($data, $flags));
}
/**
diff --git a/vendor/symfony/console/Exception/CommandNotFoundException.php b/vendor/symfony/console/Exception/CommandNotFoundException.php
index 15ac522c67..69d5cb996a 100644
--- a/vendor/symfony/console/Exception/CommandNotFoundException.php
+++ b/vendor/symfony/console/Exception/CommandNotFoundException.php
@@ -24,9 +24,9 @@ class CommandNotFoundException extends \InvalidArgumentException implements Exce
* @param string $message Exception message to throw
* @param array $alternatives List of similar defined names
* @param int $code Exception code
- * @param \Exception $previous Previous exception used for the exception chaining
+ * @param \Throwable $previous Previous exception used for the exception chaining
*/
- public function __construct(string $message, array $alternatives = [], int $code = 0, \Exception $previous = null)
+ public function __construct(string $message, array $alternatives = [], int $code = 0, \Throwable $previous = null)
{
parent::__construct($message, $code, $previous);
diff --git a/vendor/symfony/console/Formatter/OutputFormatter.php b/vendor/symfony/console/Formatter/OutputFormatter.php
index 2b6db373d8..83f16d7731 100644
--- a/vendor/symfony/console/Formatter/OutputFormatter.php
+++ b/vendor/symfony/console/Formatter/OutputFormatter.php
@@ -141,7 +141,7 @@ public function formatAndWrap(string $message, int $width)
{
$offset = 0;
$output = '';
- $tagRegex = '[a-z][a-z0-9,_=;-]*+';
+ $tagRegex = '[a-z][^<>]*+';
$currentLineLength = 0;
preg_match_all("#<(($tagRegex) | /($tagRegex)?)>#ix", $message, $matches, PREG_OFFSET_CAPTURE);
foreach ($matches[0] as $i => $match) {
@@ -216,6 +216,8 @@ private function createStyleFromString(string $string)
$style->setForeground(strtolower($match[1]));
} elseif ('bg' == $match[0]) {
$style->setBackground(strtolower($match[1]));
+ } elseif ('href' === $match[0]) {
+ $style->setHref($match[1]);
} elseif ('options' === $match[0]) {
preg_match_all('([^,;]+)', strtolower($match[1]), $options);
$options = array_shift($options);
diff --git a/vendor/symfony/console/Formatter/OutputFormatterStyle.php b/vendor/symfony/console/Formatter/OutputFormatterStyle.php
index dcc1beb46a..655bdd083e 100644
--- a/vendor/symfony/console/Formatter/OutputFormatterStyle.php
+++ b/vendor/symfony/console/Formatter/OutputFormatterStyle.php
@@ -52,7 +52,9 @@ class OutputFormatterStyle implements OutputFormatterStyleInterface
private $foreground;
private $background;
+ private $href;
private $options = [];
+ private $handlesHrefGracefully;
/**
* Initializes output formatter style.
@@ -118,6 +120,11 @@ public function setBackground($color = null)
$this->background = static::$availableBackgroundColors[$color];
}
+ public function setHref(string $url): void
+ {
+ $this->href = $url;
+ }
+
/**
* Sets some specific style option.
*
@@ -179,6 +186,10 @@ public function apply($text)
$setCodes = [];
$unsetCodes = [];
+ if (null === $this->handlesHrefGracefully) {
+ $this->handlesHrefGracefully = 'JetBrains-JediTerm' !== getenv('TERMINAL_EMULATOR') && !getenv('KONSOLE_VERSION');
+ }
+
if (null !== $this->foreground) {
$setCodes[] = $this->foreground['set'];
$unsetCodes[] = $this->foreground['unset'];
@@ -187,11 +198,14 @@ public function apply($text)
$setCodes[] = $this->background['set'];
$unsetCodes[] = $this->background['unset'];
}
- if (\count($this->options)) {
- foreach ($this->options as $option) {
- $setCodes[] = $option['set'];
- $unsetCodes[] = $option['unset'];
- }
+
+ foreach ($this->options as $option) {
+ $setCodes[] = $option['set'];
+ $unsetCodes[] = $option['unset'];
+ }
+
+ if (null !== $this->href && $this->handlesHrefGracefully) {
+ $text = "\033]8;;$this->href\033\\$text\033]8;;\033\\";
}
if (0 === \count($setCodes)) {
diff --git a/vendor/symfony/console/Helper/Dumper.php b/vendor/symfony/console/Helper/Dumper.php
new file mode 100644
index 0000000000..b013b6c527
--- /dev/null
+++ b/vendor/symfony/console/Helper/Dumper.php
@@ -0,0 +1,64 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Console\Helper;
+
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\VarDumper\Cloner\ClonerInterface;
+use Symfony\Component\VarDumper\Cloner\VarCloner;
+use Symfony\Component\VarDumper\Dumper\CliDumper;
+
+/**
+ * @author Roland Franssen
+ */
+final class Dumper
+{
+ private $output;
+ private $dumper;
+ private $cloner;
+ private $handler;
+
+ public function __construct(OutputInterface $output, CliDumper $dumper = null, ClonerInterface $cloner = null)
+ {
+ $this->output = $output;
+ $this->dumper = $dumper;
+ $this->cloner = $cloner;
+
+ if (class_exists(CliDumper::class)) {
+ $this->handler = function ($var): string {
+ $dumper = $this->dumper ?? $this->dumper = new CliDumper(null, null, CliDumper::DUMP_LIGHT_ARRAY | CliDumper::DUMP_COMMA_SEPARATOR);
+ $dumper->setColors($this->output->isDecorated());
+
+ return rtrim($dumper->dump(($this->cloner ?? $this->cloner = new VarCloner())->cloneVar($var)->withRefHandles(false), true));
+ };
+ } else {
+ $this->handler = function ($var): string {
+ switch (true) {
+ case null === $var:
+ return 'null';
+ case true === $var:
+ return 'true';
+ case false === $var:
+ return 'false';
+ case \is_string($var):
+ return '"'.$var.'"';
+ default:
+ return rtrim(print_r($var, true));
+ }
+ };
+ }
+ }
+
+ public function __invoke($var): string
+ {
+ return ($this->handler)($var);
+ }
+}
diff --git a/vendor/symfony/console/Helper/ProgressBar.php b/vendor/symfony/console/Helper/ProgressBar.php
index 12a6cf2245..34e6aa5d2b 100644
--- a/vendor/symfony/console/Helper/ProgressBar.php
+++ b/vendor/symfony/console/Helper/ProgressBar.php
@@ -243,6 +243,24 @@ public function setRedrawFrequency(int $freq)
$this->redrawFreq = max($freq, 1);
}
+ /**
+ * Returns an iterator that will automatically update the progress bar when iterated.
+ *
+ * @param int|null $max Number of steps to complete the bar (0 if indeterminate), if null it will be inferred from $iterable
+ */
+ public function iterate(iterable $iterable, ?int $max = null): iterable
+ {
+ $this->start($max ?? (\is_countable($iterable) ? \count($iterable) : 0));
+
+ foreach ($iterable as $key => $value) {
+ yield $key => $value;
+
+ $this->advance();
+ }
+
+ $this->finish();
+ }
+
/**
* Starts the progress output.
*
diff --git a/vendor/symfony/console/Helper/QuestionHelper.php b/vendor/symfony/console/Helper/QuestionHelper.php
index b8b76833a6..e6a700aa45 100644
--- a/vendor/symfony/console/Helper/QuestionHelper.php
+++ b/vendor/symfony/console/Helper/QuestionHelper.php
@@ -115,7 +115,7 @@ private function doAsk(OutputInterface $output, Question $question)
$this->writePrompt($output, $question);
$inputStream = $this->inputStream ?: STDIN;
- $autocomplete = $question->getAutocompleterValues();
+ $autocomplete = $question->getAutocompleterCallback();
if (null === $autocomplete || !$this->hasSttyAvailable()) {
$ret = false;
@@ -137,7 +137,7 @@ private function doAsk(OutputInterface $output, Question $question)
$ret = trim($ret);
}
} else {
- $ret = trim($this->autocomplete($output, $question, $inputStream, \is_array($autocomplete) ? $autocomplete : iterator_to_array($autocomplete, false)));
+ $ret = trim($this->autocomplete($output, $question, $inputStream, $autocomplete));
}
if ($output instanceof ConsoleSectionOutput) {
@@ -194,17 +194,16 @@ protected function writeError(OutputInterface $output, \Exception $error)
/**
* Autocompletes a question.
*
- * @param OutputInterface $output
- * @param Question $question
- * @param resource $inputStream
+ * @param resource $inputStream
*/
- private function autocomplete(OutputInterface $output, Question $question, $inputStream, array $autocomplete): string
+ private function autocomplete(OutputInterface $output, Question $question, $inputStream, callable $autocomplete): string
{
+ $fullChoice = '';
$ret = '';
$i = 0;
$ofs = -1;
- $matches = $autocomplete;
+ $matches = $autocomplete($ret);
$numMatches = \count($matches);
$sttyMode = shell_exec('stty -g');
@@ -226,13 +225,14 @@ private function autocomplete(OutputInterface $output, Question $question, $inpu
} elseif ("\177" === $c) { // Backspace Character
if (0 === $numMatches && 0 !== $i) {
--$i;
+ $fullChoice = substr($fullChoice, 0, -1);
// Move cursor backwards
$output->write("\033[1D");
}
if (0 === $i) {
$ofs = -1;
- $matches = $autocomplete;
+ $matches = $autocomplete($ret);
$numMatches = \count($matches);
} else {
$numMatches = 0;
@@ -260,18 +260,27 @@ private function autocomplete(OutputInterface $output, Question $question, $inpu
} elseif (\ord($c) < 32) {
if ("\t" === $c || "\n" === $c) {
if ($numMatches > 0 && -1 !== $ofs) {
- $ret = $matches[$ofs];
+ $ret = (string) $matches[$ofs];
// Echo out remaining chars for current match
- $output->write(substr($ret, $i));
- $i = \strlen($ret);
+ $remainingCharacters = substr($ret, \strlen(trim($this->mostRecentlyEnteredValue($fullChoice))));
+ $output->write($remainingCharacters);
+ $fullChoice .= $remainingCharacters;
+ $i = \strlen($fullChoice);
+
+ $matches = array_filter(
+ $autocomplete($ret),
+ function ($match) use ($ret) {
+ return '' === $ret || 0 === strpos($match, $ret);
+ }
+ );
+ $numMatches = \count($matches);
+ $ofs = -1;
}
if ("\n" === $c) {
$output->write($c);
break;
}
-
- $numMatches = 0;
}
continue;
@@ -282,14 +291,21 @@ private function autocomplete(OutputInterface $output, Question $question, $inpu
$output->write($c);
$ret .= $c;
+ $fullChoice .= $c;
++$i;
+ $tempRet = $ret;
+
+ if ($question instanceof ChoiceQuestion && $question->isMultiselect()) {
+ $tempRet = $this->mostRecentlyEnteredValue($fullChoice);
+ }
+
$numMatches = 0;
$ofs = 0;
- foreach ($autocomplete as $value) {
+ foreach ($autocomplete($ret) as $value) {
// If typed characters match the beginning chunk of value (e.g. [AcmeDe]moBundle)
- if (0 === strpos($value, $ret)) {
+ if (0 === strpos($value, $tempRet)) {
$matches[$numMatches++] = $value;
}
}
@@ -301,8 +317,9 @@ private function autocomplete(OutputInterface $output, Question $question, $inpu
if ($numMatches > 0 && -1 !== $ofs) {
// Save cursor position
$output->write("\0337");
- // Write highlighted text
- $output->write(''.OutputFormatter::escapeTrailingBackslash(substr($matches[$ofs], $i)).'');
+ // Write highlighted text, complete the partially entered response
+ $charactersEntered = \strlen(trim($this->mostRecentlyEnteredValue($fullChoice)));
+ $output->write(''.OutputFormatter::escapeTrailingBackslash(substr($matches[$ofs], $charactersEntered)).'');
// Restore cursor position
$output->write("\0338");
}
@@ -311,7 +328,22 @@ private function autocomplete(OutputInterface $output, Question $question, $inpu
// Reset stty so it behaves normally again
shell_exec(sprintf('stty %s', $sttyMode));
- return $ret;
+ return $fullChoice;
+ }
+
+ private function mostRecentlyEnteredValue($entered)
+ {
+ // Determine the most recent value that the user entered
+ if (false === strpos($entered, ',')) {
+ return $entered;
+ }
+
+ $choices = explode(',', $entered);
+ if (\strlen($lastChoice = trim($choices[\count($choices) - 1])) > 0) {
+ return $lastChoice;
+ }
+
+ return $entered;
}
/**
diff --git a/vendor/symfony/console/Question/Question.php b/vendor/symfony/console/Question/Question.php
index eac82cfad3..9201af2fd5 100644
--- a/vendor/symfony/console/Question/Question.php
+++ b/vendor/symfony/console/Question/Question.php
@@ -25,7 +25,7 @@ class Question
private $attempts;
private $hidden = false;
private $hiddenFallback = true;
- private $autocompleterValues;
+ private $autocompleterCallback;
private $validator;
private $default;
private $normalizer;
@@ -81,7 +81,7 @@ public function isHidden()
*/
public function setHidden($hidden)
{
- if ($this->autocompleterValues) {
+ if ($this->autocompleterCallback) {
throw new LogicException('A hidden question cannot use the autocompleter.');
}
@@ -121,7 +121,9 @@ public function setHiddenFallback($fallback)
*/
public function getAutocompleterValues()
{
- return $this->autocompleterValues;
+ $callback = $this->getAutocompleterCallback();
+
+ return $callback ? $callback('') : null;
}
/**
@@ -138,17 +140,46 @@ public function setAutocompleterValues($values)
{
if (\is_array($values)) {
$values = $this->isAssoc($values) ? array_merge(array_keys($values), array_values($values)) : array_values($values);
- }
- if (null !== $values && !\is_array($values) && !$values instanceof \Traversable) {
+ $callback = static function () use ($values) {
+ return $values;
+ };
+ } elseif ($values instanceof \Traversable) {
+ $valueCache = null;
+ $callback = static function () use ($values, &$valueCache) {
+ return $valueCache ?? $valueCache = iterator_to_array($values, false);
+ };
+ } elseif (null === $values) {
+ $callback = null;
+ } else {
throw new InvalidArgumentException('Autocompleter values can be either an array, "null" or a "Traversable" object.');
}
- if ($this->hidden) {
+ return $this->setAutocompleterCallback($callback);
+ }
+
+ /**
+ * Gets the callback function used for the autocompleter.
+ */
+ public function getAutocompleterCallback(): ?callable
+ {
+ return $this->autocompleterCallback;
+ }
+
+ /**
+ * Sets the callback function used for the autocompleter.
+ *
+ * The callback is passed the user input as argument and should return an iterable of corresponding suggestions.
+ *
+ * @return $this
+ */
+ public function setAutocompleterCallback(callable $callback = null): self
+ {
+ if ($this->hidden && null !== $callback) {
throw new LogicException('A hidden question cannot use the autocompleter.');
}
- $this->autocompleterValues = $values;
+ $this->autocompleterCallback = $callback;
return $this;
}
diff --git a/vendor/symfony/console/Style/SymfonyStyle.php b/vendor/symfony/console/Style/SymfonyStyle.php
index b46162de68..962ba923f3 100644
--- a/vendor/symfony/console/Style/SymfonyStyle.php
+++ b/vendor/symfony/console/Style/SymfonyStyle.php
@@ -154,7 +154,7 @@ public function error($message)
*/
public function warning($message)
{
- $this->block($message, 'WARNING', 'fg=white;bg=red', ' ', true);
+ $this->block($message, 'WARNING', 'fg=black;bg=yellow', ' ', true);
}
/**
diff --git a/vendor/symfony/console/Tests/ApplicationTest.php b/vendor/symfony/console/Tests/ApplicationTest.php
index efbe2a81fd..e153e5609a 100644
--- a/vendor/symfony/console/Tests/ApplicationTest.php
+++ b/vendor/symfony/console/Tests/ApplicationTest.php
@@ -73,8 +73,8 @@ public static function setUpBeforeClass()
require_once self::$fixturesPath.'/FooSubnamespaced1Command.php';
require_once self::$fixturesPath.'/FooSubnamespaced2Command.php';
require_once self::$fixturesPath.'/FooWithoutAliasCommand.php';
- require_once self::$fixturesPath.'/TestTiti.php';
- require_once self::$fixturesPath.'/TestToto.php';
+ require_once self::$fixturesPath.'/TestAmbiguousCommandRegistering.php';
+ require_once self::$fixturesPath.'/TestAmbiguousCommandRegistering2.php';
}
protected function normalizeLineBreaks($text)
@@ -165,6 +165,28 @@ public function testRegister()
$this->assertEquals('foo', $command->getName(), '->register() registers a new command');
}
+ public function testRegisterAmbiguous()
+ {
+ $code = function (InputInterface $input, OutputInterface $output) {
+ $output->writeln('It works!');
+ };
+
+ $application = new Application();
+ $application->setAutoExit(false);
+ $application
+ ->register('test-foo')
+ ->setAliases(['test'])
+ ->setCode($code);
+
+ $application
+ ->register('test-bar')
+ ->setCode($code);
+
+ $tester = new ApplicationTester($application);
+ $tester->run(['test']);
+ $this->assertContains('It works!', $tester->getDisplay(true));
+ }
+
public function testAdd()
{
$application = new Application();
@@ -304,9 +326,9 @@ public function testFindAmbiguousNamespace()
public function testFindNonAmbiguous()
{
$application = new Application();
- $application->add(new \TestTiti());
- $application->add(new \TestToto());
- $this->assertEquals('test-toto', $application->find('test')->getName());
+ $application->add(new \TestAmbiguousCommandRegistering());
+ $application->add(new \TestAmbiguousCommandRegistering2());
+ $this->assertEquals('test-ambiguous', $application->find('test')->getName());
}
/**
@@ -687,7 +709,7 @@ public function testFindNamespaceDoesNotFailOnDeepSimilarNamespaces()
$application = $this->getMockBuilder('Symfony\Component\Console\Application')->setMethods(['getNamespaces'])->getMock();
$application->expects($this->once())
->method('getNamespaces')
- ->will($this->returnValue(['foo:sublong', 'bar:sub']));
+ ->willReturn(['foo:sublong', 'bar:sub']);
$this->assertEquals('foo:sublong', $application->findNamespace('f:sub'));
}
@@ -831,7 +853,7 @@ public function testRenderExceptionLineBreaks()
$application->setAutoExit(false);
$application->expects($this->any())
->method('getTerminalWidth')
- ->will($this->returnValue(120));
+ ->willReturn(120);
$application->register('foo')->setCode(function () {
throw new \InvalidArgumentException("\n\nline 1 with extra spaces \nline 2\n\nline 4\n");
});
diff --git a/vendor/symfony/console/Tests/Command/CommandTest.php b/vendor/symfony/console/Tests/Command/CommandTest.php
index 512feca706..be73e5c941 100644
--- a/vendor/symfony/console/Tests/Command/CommandTest.php
+++ b/vendor/symfony/console/Tests/Command/CommandTest.php
@@ -188,7 +188,7 @@ public function testGetSetAliases()
public function testSetAliasesNull()
{
$command = new \TestCommand();
- $this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('InvalidArgumentException');
+ $this->expectException('InvalidArgumentException');
$command->setAliases(null);
}
@@ -321,7 +321,7 @@ public function testRunReturnsIntegerExitCode()
$command = $this->getMockBuilder('TestCommand')->setMethods(['execute'])->getMock();
$command->expects($this->once())
->method('execute')
- ->will($this->returnValue('2.3'));
+ ->willReturn('2.3');
$exitCode = $command->run(new StringInput(''), new NullOutput());
$this->assertSame(2, $exitCode, '->run() returns integer exit code (casts numeric to int)');
}
diff --git a/vendor/symfony/console/Tests/Fixtures/TestAmbiguousCommandRegistering.php b/vendor/symfony/console/Tests/Fixtures/TestAmbiguousCommandRegistering.php
new file mode 100644
index 0000000000..bece09fcdd
--- /dev/null
+++ b/vendor/symfony/console/Tests/Fixtures/TestAmbiguousCommandRegistering.php
@@ -0,0 +1,22 @@
+setName('test-ambiguous')
+ ->setDescription('The test-ambiguous command')
+ ->setAliases(['test'])
+ ;
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output)
+ {
+ $output->write('test-ambiguous');
+ }
+}
diff --git a/vendor/symfony/console/Tests/Fixtures/TestAmbiguousCommandRegistering2.php b/vendor/symfony/console/Tests/Fixtures/TestAmbiguousCommandRegistering2.php
new file mode 100644
index 0000000000..9dde486245
--- /dev/null
+++ b/vendor/symfony/console/Tests/Fixtures/TestAmbiguousCommandRegistering2.php
@@ -0,0 +1,21 @@
+setName('test-ambiguous2')
+ ->setDescription('The test-ambiguous2 command')
+ ;
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output)
+ {
+ $output->write('test-ambiguous2');
+ }
+}
diff --git a/vendor/symfony/console/Tests/Formatter/OutputFormatterStyleTest.php b/vendor/symfony/console/Tests/Formatter/OutputFormatterStyleTest.php
index d47760fe4e..d4559e8def 100644
--- a/vendor/symfony/console/Tests/Formatter/OutputFormatterStyleTest.php
+++ b/vendor/symfony/console/Tests/Formatter/OutputFormatterStyleTest.php
@@ -41,7 +41,7 @@ public function testForeground()
$style->setForeground('default');
$this->assertEquals("\033[39mfoo\033[39m", $style->apply('foo'));
- $this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('InvalidArgumentException');
+ $this->expectException('InvalidArgumentException');
$style->setForeground('undefined-color');
}
@@ -58,7 +58,7 @@ public function testBackground()
$style->setBackground('default');
$this->assertEquals("\033[49mfoo\033[49m", $style->apply('foo'));
- $this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('InvalidArgumentException');
+ $this->expectException('InvalidArgumentException');
$style->setBackground('undefined-color');
}
@@ -97,4 +97,19 @@ public function testOptions()
$this->assertContains('Invalid option specified: "foo"', $e->getMessage(), '->unsetOption() throws an \InvalidArgumentException when the option does not exist in the available options');
}
}
+
+ public function testHref()
+ {
+ $prevTerminalEmulator = getenv('TERMINAL_EMULATOR');
+ putenv('TERMINAL_EMULATOR');
+
+ $style = new OutputFormatterStyle();
+
+ try {
+ $style->setHref('idea://open/?file=/path/SomeFile.php&line=12');
+ $this->assertSame("\e]8;;idea://open/?file=/path/SomeFile.php&line=12\e\\some URL\e]8;;\e\\", $style->apply('some URL'));
+ } finally {
+ putenv('TERMINAL_EMULATOR'.($prevTerminalEmulator ? "=$prevTerminalEmulator" : ''));
+ }
+ }
}
diff --git a/vendor/symfony/console/Tests/Formatter/OutputFormatterTest.php b/vendor/symfony/console/Tests/Formatter/OutputFormatterTest.php
index ce81c353f0..86473b3a8f 100644
--- a/vendor/symfony/console/Tests/Formatter/OutputFormatterTest.php
+++ b/vendor/symfony/console/Tests/Formatter/OutputFormatterTest.php
@@ -228,7 +228,7 @@ public function testFormatToStringObject()
);
}
- public function testNotDecoratedFormatter()
+ public function testFormatterHasStyles()
{
$formatter = new OutputFormatter(false);
@@ -236,40 +236,35 @@ public function testNotDecoratedFormatter()
$this->assertTrue($formatter->hasStyle('info'));
$this->assertTrue($formatter->hasStyle('comment'));
$this->assertTrue($formatter->hasStyle('question'));
+ }
- $this->assertEquals(
- 'some error', $formatter->format('some error')
- );
- $this->assertEquals(
- 'some info', $formatter->format('some info')
- );
- $this->assertEquals(
- 'some comment', $formatter->format('some comment')
- );
- $this->assertEquals(
- 'some question', $formatter->format('some question')
- );
- $this->assertEquals(
- 'some text with inline style', $formatter->format('some text with inline style>')
- );
-
- $formatter->setDecorated(true);
+ /**
+ * @dataProvider provideDecoratedAndNonDecoratedOutput
+ */
+ public function testNotDecoratedFormatter(string $input, string $expectedNonDecoratedOutput, string $expectedDecoratedOutput, string $terminalEmulator = 'foo')
+ {
+ $prevTerminalEmulator = getenv('TERMINAL_EMULATOR');
+ putenv('TERMINAL_EMULATOR='.$terminalEmulator);
+
+ try {
+ $this->assertEquals($expectedDecoratedOutput, (new OutputFormatter(true))->format($input));
+ $this->assertEquals($expectedNonDecoratedOutput, (new OutputFormatter(false))->format($input));
+ } finally {
+ putenv('TERMINAL_EMULATOR'.($prevTerminalEmulator ? "=$prevTerminalEmulator" : ''));
+ }
+ }
- $this->assertEquals(
- "\033[37;41msome error\033[39;49m", $formatter->format('some error')
- );
- $this->assertEquals(
- "\033[32msome info\033[39m", $formatter->format('some info')
- );
- $this->assertEquals(
- "\033[33msome comment\033[39m", $formatter->format('some comment')
- );
- $this->assertEquals(
- "\033[30;46msome question\033[39;49m", $formatter->format('some question')
- );
- $this->assertEquals(
- "\033[31msome text with inline style\033[39m", $formatter->format('some text with inline style>')
- );
+ public function provideDecoratedAndNonDecoratedOutput()
+ {
+ return [
+ ['some error', 'some error', "\033[37;41msome error\033[39;49m"],
+ ['some info', 'some info', "\033[32msome info\033[39m"],
+ ['some comment', 'some comment', "\033[33msome comment\033[39m"],
+ ['some question', 'some question', "\033[30;46msome question\033[39;49m"],
+ ['some text with inline style>', 'some text with inline style', "\033[31msome text with inline style\033[39m"],
+ ['some URL>', 'some URL', "\033]8;;idea://open/?file=/path/SomeFile.php&line=12\033\\some URL\033]8;;\033\\"],
+ ['some URL>', 'some URL', 'some URL', 'JetBrains-JediTerm'],
+ ];
}
public function testContentWithLineBreaks()
diff --git a/vendor/symfony/console/Tests/Helper/AbstractQuestionHelperTest.php b/vendor/symfony/console/Tests/Helper/AbstractQuestionHelperTest.php
index 56dd65f6b4..f12566dedd 100644
--- a/vendor/symfony/console/Tests/Helper/AbstractQuestionHelperTest.php
+++ b/vendor/symfony/console/Tests/Helper/AbstractQuestionHelperTest.php
@@ -21,7 +21,7 @@ protected function createStreamableInputInterfaceMock($stream = null, $interacti
$mock = $this->getMockBuilder(StreamableInputInterface::class)->getMock();
$mock->expects($this->any())
->method('isInteractive')
- ->will($this->returnValue($interactive));
+ ->willReturn($interactive);
if ($stream) {
$mock->expects($this->any())
diff --git a/vendor/symfony/console/Tests/Helper/DumperNativeFallbackTest.php b/vendor/symfony/console/Tests/Helper/DumperNativeFallbackTest.php
new file mode 100644
index 0000000000..b9fa2dc294
--- /dev/null
+++ b/vendor/symfony/console/Tests/Helper/DumperNativeFallbackTest.php
@@ -0,0 +1,58 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Console\Tests\Helper;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Bridge\PhpUnit\ClassExistsMock;
+use Symfony\Component\Console\Helper\Dumper;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\VarDumper\Dumper\CliDumper;
+
+class DumperNativeFallbackTest extends TestCase
+{
+ public static function setUpBeforeClass()
+ {
+ ClassExistsMock::register(Dumper::class);
+ ClassExistsMock::withMockedClasses([
+ CliDumper::class => false,
+ ]);
+ }
+
+ public static function tearDownAfterClass()
+ {
+ ClassExistsMock::withMockedClasses([]);
+ }
+
+ /**
+ * @dataProvider provideVariables
+ */
+ public function testInvoke($variable, $primitiveString)
+ {
+ $dumper = new Dumper($this->getMockBuilder(OutputInterface::class)->getMock());
+
+ $this->assertSame($primitiveString, $dumper($variable));
+ }
+
+ public function provideVariables()
+ {
+ return [
+ [null, 'null'],
+ [true, 'true'],
+ [false, 'false'],
+ [1, '1'],
+ [-1.5, '-1.5'],
+ ['string', '"string"'],
+ [[1, '2'], "Array\n(\n [0] => 1\n [1] => 2\n)"],
+ [new \stdClass(), "stdClass Object\n(\n)"],
+ ];
+ }
+}
diff --git a/vendor/symfony/console/Tests/Helper/DumperTest.php b/vendor/symfony/console/Tests/Helper/DumperTest.php
new file mode 100644
index 0000000000..00c480a6a9
--- /dev/null
+++ b/vendor/symfony/console/Tests/Helper/DumperTest.php
@@ -0,0 +1,58 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Console\Tests\Helper;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Console\Helper\Dumper;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\VarDumper\Test\VarDumperTestTrait;
+
+class DumperTest extends TestCase
+{
+ use VarDumperTestTrait;
+
+ public static function setUpBeforeClass()
+ {
+ putenv('DUMP_LIGHT_ARRAY=1');
+ putenv('DUMP_COMMA_SEPARATOR=1');
+ }
+
+ public static function tearDownAfterClass()
+ {
+ putenv('DUMP_LIGHT_ARRAY');
+ putenv('DUMP_COMMA_SEPARATOR');
+ }
+
+ /**
+ * @dataProvider provideVariables
+ */
+ public function testInvoke($variable)
+ {
+ $dumper = new Dumper($this->getMockBuilder(OutputInterface::class)->getMock());
+
+ $this->assertDumpMatchesFormat($dumper($variable), $variable);
+ }
+
+ public function provideVariables()
+ {
+ return [
+ [null],
+ [true],
+ [false],
+ [1],
+ [-1.5],
+ ['string'],
+ [[1, '2']],
+ [new \stdClass()],
+ ];
+ }
+}
diff --git a/vendor/symfony/console/Tests/Helper/HelperSetTest.php b/vendor/symfony/console/Tests/Helper/HelperSetTest.php
index 826bc51dcd..ffb12b3421 100644
--- a/vendor/symfony/console/Tests/Helper/HelperSetTest.php
+++ b/vendor/symfony/console/Tests/Helper/HelperSetTest.php
@@ -114,7 +114,7 @@ private function getGenericMockHelper($name, HelperSet $helperset = null)
$mock_helper = $this->getMockBuilder('\Symfony\Component\Console\Helper\HelperInterface')->getMock();
$mock_helper->expects($this->any())
->method('getName')
- ->will($this->returnValue($name));
+ ->willReturn($name);
if ($helperset) {
$mock_helper->expects($this->any())
diff --git a/vendor/symfony/console/Tests/Helper/ProgressBarTest.php b/vendor/symfony/console/Tests/Helper/ProgressBarTest.php
index b0c834cc41..8d37d3af04 100644
--- a/vendor/symfony/console/Tests/Helper/ProgressBarTest.php
+++ b/vendor/symfony/console/Tests/Helper/ProgressBarTest.php
@@ -880,6 +880,41 @@ public function provideFormat()
];
}
+ public function testIterate(): void
+ {
+ $bar = new ProgressBar($output = $this->getOutputStream());
+
+ $this->assertEquals([1, 2], \iterator_to_array($bar->iterate([1, 2])));
+
+ rewind($output->getStream());
+ $this->assertEquals(
+ ' 0/2 [>---------------------------] 0%'.
+ $this->generateOutput(' 1/2 [==============>-------------] 50%').
+ $this->generateOutput(' 2/2 [============================] 100%').
+ $this->generateOutput(' 2/2 [============================] 100%'),
+ stream_get_contents($output->getStream())
+ );
+ }
+
+ public function testIterateUncountable(): void
+ {
+ $bar = new ProgressBar($output = $this->getOutputStream());
+
+ $this->assertEquals([1, 2], \iterator_to_array($bar->iterate((function () {
+ yield 1;
+ yield 2;
+ })())));
+
+ rewind($output->getStream());
+ $this->assertEquals(
+ ' 0 [>---------------------------]'.
+ $this->generateOutput(' 1 [->--------------------------]').
+ $this->generateOutput(' 2 [-->-------------------------]').
+ $this->generateOutput(' 2 [============================]'),
+ stream_get_contents($output->getStream())
+ );
+ }
+
protected function getOutputStream($decorated = true, $verbosity = StreamOutput::VERBOSITY_NORMAL)
{
return new StreamOutput(fopen('php://memory', 'r+', false), $verbosity, $decorated);
diff --git a/vendor/symfony/console/Tests/Helper/QuestionHelperTest.php b/vendor/symfony/console/Tests/Helper/QuestionHelperTest.php
index 69d5470b8c..eca929fd30 100644
--- a/vendor/symfony/console/Tests/Helper/QuestionHelperTest.php
+++ b/vendor/symfony/console/Tests/Helper/QuestionHelperTest.php
@@ -198,6 +198,49 @@ public function testAskWithAutocomplete()
$this->assertEquals('FooBundle', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
}
+ public function testAskWithAutocompleteCallback()
+ {
+ if (!$this->hasSttyAvailable()) {
+ $this->markTestSkipped('`stty` is required to test autocomplete functionality');
+ }
+
+ // PoCrP
+ $inputStream = $this->getInputStream("Pa\177\177o\tCr\tP\033[A\033[A\n");
+
+ $dialog = new QuestionHelper();
+ $helperSet = new HelperSet([new FormatterHelper()]);
+ $dialog->setHelperSet($helperSet);
+
+ $question = new Question('What\'s for dinner?');
+
+ // A simple test callback - return an array containing the words the
+ // user has already completed, suffixed with all known words.
+ //
+ // Eg: If the user inputs "Potato C", the return will be:
+ //
+ // ["Potato Carrot ", "Potato Creme ", "Potato Curry ", ...]
+ //
+ // No effort is made to avoid irrelevant suggestions, as this is handled
+ // by the autocomplete function.
+ $callback = function ($input) {
+ $knownWords = ['Carrot', 'Creme', 'Curry', 'Parsnip', 'Pie', 'Potato', 'Tart'];
+ $inputWords = explode(' ', $input);
+ array_pop($inputWords);
+ $suggestionBase = $inputWords ? implode(' ', $inputWords).' ' : '';
+
+ return array_map(
+ function ($word) use ($suggestionBase) {
+ return $suggestionBase.$word.' ';
+ },
+ $knownWords
+ );
+ };
+
+ $question->setAutocompleterCallback($callback);
+
+ $this->assertSame('Potato Creme Pie', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
+ }
+
public function testAskWithAutocompleteWithNonSequentialKeys()
{
if (!$this->hasSttyAvailable()) {
@@ -667,6 +710,37 @@ public function testTraversableAutocomplete()
$this->assertEquals('FooBundle', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
}
+ public function testTraversableMultiselectAutocomplete()
+ {
+ //
+ // F
+ // A<3x UP ARROW>,F
+ // F00o,A,SecurityBundle
+ // Acme,As<29x BACKSPACE>S
+ // Ac,As<3x BACKSPACE>d
+ $inputStream = $this->getInputStream("\nF\t\nA\033[A\033[A\033[A\t,F\t\nF00\177\177o\t,A\033[B\t, SecurityBundle\nSecurityBundle\nAcme\t, As\t\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177S\t\nAc\t,As\t\177\177\177d\t\n");
+
+ $dialog = new QuestionHelper();
+ $helperSet = new HelperSet([new FormatterHelper()]);
+ $dialog->setHelperSet($helperSet);
+
+ $question = new ChoiceQuestion(
+ 'Please select a bundle (defaults to AcmeDemoBundle and AsseticBundle)',
+ ['AcmeDemoBundle', 'AsseticBundle', 'SecurityBundle', 'FooBundle'],
+ '0,1'
+ );
+
+ // This tests that autocomplete works for all multiselect choices entered by the user
+ $question->setMultiselect(true);
+
+ $this->assertEquals(['AcmeDemoBundle', 'AsseticBundle'], $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
+ $this->assertEquals(['FooBundle'], $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
+ $this->assertEquals(['AsseticBundle', 'FooBundle'], $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
+ $this->assertEquals(['FooBundle', 'AsseticBundle', 'SecurityBundle'], $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
+ $this->assertEquals(['SecurityBundle'], $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
+ $this->assertEquals(['AcmeDemoBundle', 'AsseticBundle'], $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
+ }
+
protected function getInputStream($input)
{
$stream = fopen('php://memory', 'r+', false);
@@ -686,7 +760,7 @@ protected function createInputInterfaceMock($interactive = true)
$mock = $this->getMockBuilder('Symfony\Component\Console\Input\InputInterface')->getMock();
$mock->expects($this->any())
->method('isInteractive')
- ->will($this->returnValue($interactive));
+ ->willReturn($interactive);
return $mock;
}
diff --git a/vendor/symfony/console/Tests/Helper/SymfonyQuestionHelperTest.php b/vendor/symfony/console/Tests/Helper/SymfonyQuestionHelperTest.php
index cf7a78c34e..6f621db954 100644
--- a/vendor/symfony/console/Tests/Helper/SymfonyQuestionHelperTest.php
+++ b/vendor/symfony/console/Tests/Helper/SymfonyQuestionHelperTest.php
@@ -154,7 +154,7 @@ protected function createInputInterfaceMock($interactive = true)
$mock = $this->getMockBuilder('Symfony\Component\Console\Input\InputInterface')->getMock();
$mock->expects($this->any())
->method('isInteractive')
- ->will($this->returnValue($interactive));
+ ->willReturn($interactive);
return $mock;
}
diff --git a/vendor/symfony/console/Tests/Logger/ConsoleLoggerTest.php b/vendor/symfony/console/Tests/Logger/ConsoleLoggerTest.php
index efeec4234e..c99eb839b7 100644
--- a/vendor/symfony/console/Tests/Logger/ConsoleLoggerTest.php
+++ b/vendor/symfony/console/Tests/Logger/ConsoleLoggerTest.php
@@ -166,7 +166,7 @@ public function testObjectCastToString()
} else {
$dummy = $this->getMock('Symfony\Component\Console\Tests\Logger\DummyTest', ['__toString']);
}
- $dummy->method('__toString')->will($this->returnValue('DUMMY'));
+ $dummy->method('__toString')->willReturn('DUMMY');
$this->getLogger()->warning($dummy);
diff --git a/vendor/symfony/console/Tests/Question/QuestionTest.php b/vendor/symfony/console/Tests/Question/QuestionTest.php
new file mode 100644
index 0000000000..13c8e362e1
--- /dev/null
+++ b/vendor/symfony/console/Tests/Question/QuestionTest.php
@@ -0,0 +1,304 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Console\Tests\Question;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Console\Exception\InvalidArgumentException;
+use Symfony\Component\Console\Question\Question;
+
+class QuestionTest extends TestCase
+{
+ private $question;
+
+ protected function setUp()
+ {
+ parent::setUp();
+ $this->question = new Question('Test question');
+ }
+
+ public function providerTrueFalse()
+ {
+ return [[true], [false]];
+ }
+
+ public function testGetQuestion()
+ {
+ self::assertSame('Test question', $this->question->getQuestion());
+ }
+
+ public function testGetDefault()
+ {
+ $question = new Question('Test question', 'Default value');
+ self::assertSame('Default value', $question->getDefault());
+ }
+
+ public function testGetDefaultDefault()
+ {
+ self::assertNull($this->question->getDefault());
+ }
+
+ /**
+ * @dataProvider providerTrueFalse
+ */
+ public function testIsSetHidden(bool $hidden)
+ {
+ $this->question->setHidden($hidden);
+ self::assertSame($hidden, $this->question->isHidden());
+ }
+
+ public function testIsHiddenDefault()
+ {
+ self::assertFalse($this->question->isHidden());
+ }
+
+ public function testSetHiddenWithAutocompleterCallback()
+ {
+ $this->question->setAutocompleterCallback(
+ function (string $input): array { return []; }
+ );
+
+ $this->expectException(\LogicException::class);
+ $this->expectExceptionMessage(
+ 'A hidden question cannot use the autocompleter.'
+ );
+
+ $this->question->setHidden(true);
+ }
+
+ public function testSetHiddenWithNoAutocompleterCallback()
+ {
+ $this->question->setAutocompleterCallback(
+ function (string $input): array { return []; }
+ );
+ $this->question->setAutocompleterCallback(null);
+
+ $exception = null;
+ try {
+ $this->question->setHidden(true);
+ } catch (\Exception $exception) {
+ // Do nothing
+ }
+
+ $this->assertNull($exception);
+ }
+
+ /**
+ * @dataProvider providerTrueFalse
+ */
+ public function testIsSetHiddenFallback(bool $hidden)
+ {
+ $this->question->setHiddenFallback($hidden);
+ self::assertSame($hidden, $this->question->isHiddenFallback());
+ }
+
+ public function testIsHiddenFallbackDefault()
+ {
+ self::assertTrue($this->question->isHiddenFallback());
+ }
+
+ public function providerGetSetAutocompleterValues()
+ {
+ return [
+ 'array' => [
+ ['a', 'b', 'c', 'd'],
+ ['a', 'b', 'c', 'd'],
+ ],
+ 'associative array' => [
+ ['a' => 'c', 'b' => 'd'],
+ ['a', 'b', 'c', 'd'],
+ ],
+ 'iterator' => [
+ new \ArrayIterator(['a', 'b', 'c', 'd']),
+ ['a', 'b', 'c', 'd'],
+ ],
+ 'null' => [null, null],
+ ];
+ }
+
+ /**
+ * @dataProvider providerGetSetAutocompleterValues
+ */
+ public function testGetSetAutocompleterValues($values, $expectValues)
+ {
+ $this->question->setAutocompleterValues($values);
+ self::assertSame(
+ $expectValues,
+ $this->question->getAutocompleterValues()
+ );
+ }
+
+ public function providerSetAutocompleterValuesInvalid()
+ {
+ return [
+ ['Potato'],
+ [new \stdclass()],
+ [false],
+ ];
+ }
+
+ /**
+ * @dataProvider providerSetAutocompleterValuesInvalid
+ */
+ public function testSetAutocompleterValuesInvalid($values)
+ {
+ self::expectException(InvalidArgumentException::class);
+ self::expectExceptionMessage(
+ 'Autocompleter values can be either an array, "null" or a "Traversable" object.'
+ );
+
+ $this->question->setAutocompleterValues($values);
+ }
+
+ public function testSetAutocompleterValuesWithTraversable()
+ {
+ $question1 = new Question('Test question 1');
+ $iterator1 = $this->getMockForAbstractClass(\IteratorAggregate::class);
+ $iterator1
+ ->expects($this->once())
+ ->method('getIterator')
+ ->willReturn(new \ArrayIterator(['Potato']));
+ $question1->setAutocompleterValues($iterator1);
+
+ $question2 = new Question('Test question 2');
+ $iterator2 = $this->getMockForAbstractClass(\IteratorAggregate::class);
+ $iterator2
+ ->expects($this->once())
+ ->method('getIterator')
+ ->willReturn(new \ArrayIterator(['Carrot']));
+ $question2->setAutocompleterValues($iterator2);
+
+ // Call multiple times to verify that Traversable result is cached, and
+ // that there is no crosstalk between cached copies.
+ self::assertSame(['Potato'], $question1->getAutocompleterValues());
+ self::assertSame(['Carrot'], $question2->getAutocompleterValues());
+ self::assertSame(['Potato'], $question1->getAutocompleterValues());
+ self::assertSame(['Carrot'], $question2->getAutocompleterValues());
+ }
+
+ public function testGetAutocompleterValuesDefault()
+ {
+ self::assertNull($this->question->getAutocompleterValues());
+ }
+
+ public function testGetSetAutocompleterCallback()
+ {
+ $callback = function (string $input): array { return []; };
+
+ $this->question->setAutocompleterCallback($callback);
+ self::assertSame($callback, $this->question->getAutocompleterCallback());
+ }
+
+ public function testGetAutocompleterCallbackDefault()
+ {
+ self::assertNull($this->question->getAutocompleterCallback());
+ }
+
+ public function testSetAutocompleterCallbackWhenHidden()
+ {
+ $this->question->setHidden(true);
+
+ $this->expectException(\LogicException::class);
+ $this->expectExceptionMessage(
+ 'A hidden question cannot use the autocompleter.'
+ );
+
+ $this->question->setAutocompleterCallback(
+ function (string $input): array { return []; }
+ );
+ }
+
+ public function testSetAutocompleterCallbackWhenNotHidden()
+ {
+ $this->question->setHidden(true);
+ $this->question->setHidden(false);
+
+ $exception = null;
+ try {
+ $this->question->setAutocompleterCallback(
+ function (string $input): array { return []; }
+ );
+ } catch (\Exception $exception) {
+ // Do nothing
+ }
+
+ $this->assertNull($exception);
+ }
+
+ public function providerGetSetValidator()
+ {
+ return [
+ [function ($input) { return $input; }],
+ [null],
+ ];
+ }
+
+ /**
+ * @dataProvider providerGetSetValidator
+ */
+ public function testGetSetValidator($callback)
+ {
+ $this->question->setValidator($callback);
+ self::assertSame($callback, $this->question->getValidator());
+ }
+
+ public function testGetValidatorDefault()
+ {
+ self::assertNull($this->question->getValidator());
+ }
+
+ public function providerGetSetMaxAttempts()
+ {
+ return [[1], [5], [null]];
+ }
+
+ /**
+ * @dataProvider providerGetSetMaxAttempts
+ */
+ public function testGetSetMaxAttempts($attempts)
+ {
+ $this->question->setMaxAttempts($attempts);
+ self::assertSame($attempts, $this->question->getMaxAttempts());
+ }
+
+ public function providerSetMaxAttemptsInvalid()
+ {
+ return [['Potato'], [0], [-1]];
+ }
+
+ /**
+ * @dataProvider providerSetMaxAttemptsInvalid
+ */
+ public function testSetMaxAttemptsInvalid($attempts)
+ {
+ self::expectException(\InvalidArgumentException::class);
+ self::expectExceptionMessage('Maximum number of attempts must be a positive value.');
+
+ $this->question->setMaxAttempts($attempts);
+ }
+
+ public function testGetMaxAttemptsDefault()
+ {
+ self::assertNull($this->question->getMaxAttempts());
+ }
+
+ public function testGetSetNormalizer()
+ {
+ $normalizer = function ($input) { return $input; };
+ $this->question->setNormalizer($normalizer);
+ self::assertSame($normalizer, $this->question->getNormalizer());
+ }
+
+ public function testGetNormalizerDefault()
+ {
+ self::assertNull($this->question->getNormalizer());
+ }
+}
diff --git a/vendor/symfony/console/composer.json b/vendor/symfony/console/composer.json
index 33922220b9..5613467fd2 100644
--- a/vendor/symfony/console/composer.json
+++ b/vendor/symfony/console/composer.json
@@ -17,15 +17,17 @@
],
"require": {
"php": "^7.1.3",
- "symfony/contracts": "^1.0",
- "symfony/polyfill-mbstring": "~1.0"
+ "symfony/polyfill-mbstring": "~1.0",
+ "symfony/polyfill-php73": "^1.8",
+ "symfony/service-contracts": "^1.1"
},
"require-dev": {
"symfony/config": "~3.4|~4.0",
- "symfony/event-dispatcher": "~3.4|~4.0",
+ "symfony/event-dispatcher": "^4.3",
"symfony/dependency-injection": "~3.4|~4.0",
"symfony/lock": "~3.4|~4.0",
"symfony/process": "~3.4|~4.0",
+ "symfony/var-dumper": "^4.3",
"psr/log": "~1.0"
},
"provide": {
@@ -39,6 +41,7 @@
},
"conflict": {
"symfony/dependency-injection": "<3.4",
+ "symfony/event-dispatcher": "<4.3",
"symfony/process": "<3.3"
},
"autoload": {
@@ -50,7 +53,7 @@
"minimum-stability": "dev",
"extra": {
"branch-alias": {
- "dev-master": "4.2-dev"
+ "dev-master": "4.3-dev"
}
}
}
diff --git a/vendor/symfony/filesystem/CHANGELOG.md b/vendor/symfony/filesystem/CHANGELOG.md
index 9f1f817e75..f6453c16e3 100644
--- a/vendor/symfony/filesystem/CHANGELOG.md
+++ b/vendor/symfony/filesystem/CHANGELOG.md
@@ -1,6 +1,12 @@
CHANGELOG
=========
+4.3.0
+-----
+
+ * support for passing arrays to `Filesystem::dumpFile()` is deprecated and will be removed in 5.0
+ * support for passing arrays to `Filesystem::appendToFile()` is deprecated and will be removed in 5.0
+
4.0.0
-----
diff --git a/vendor/symfony/filesystem/Filesystem.php b/vendor/symfony/filesystem/Filesystem.php
index 082084b4d9..fa88fd0d05 100644
--- a/vendor/symfony/filesystem/Filesystem.php
+++ b/vendor/symfony/filesystem/Filesystem.php
@@ -541,6 +541,10 @@ public function mirror($originDir, $targetDir, \Traversable $iterator = null, $o
$originDir = rtrim($originDir, '/\\');
$originDirLen = \strlen($originDir);
+ if (!$this->exists($originDir)) {
+ throw new IOException(sprintf('The origin directory specified "%s" was not found.', $originDir), 0, null, $originDir);
+ }
+
// Iterate in destination folder to remove obsolete entries
if ($this->exists($targetDir) && isset($options['delete']) && $options['delete']) {
$deleteIterator = $iterator;
@@ -564,35 +568,24 @@ public function mirror($originDir, $targetDir, \Traversable $iterator = null, $o
$iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($originDir, $flags), \RecursiveIteratorIterator::SELF_FIRST);
}
- if ($this->exists($originDir)) {
- $this->mkdir($targetDir);
- }
+ $this->mkdir($targetDir);
+ $targetDirInfo = new \SplFileInfo($targetDir);
foreach ($iterator as $file) {
- if (false === strpos($file->getPath(), $originDir)) {
- throw new IOException(sprintf('Unable to mirror "%s" directory. If the origin directory is relative, try using "realpath" before calling the mirror method.', $originDir), 0, null, $originDir);
+ if ($file->getPathname() === $targetDir || $file->getRealPath() === $targetDir || 0 === strpos($file->getRealPath(), $targetDirInfo->getRealPath())) {
+ continue;
}
$target = $targetDir.substr($file->getPathname(), $originDirLen);
- if ($copyOnWindows) {
- if (is_file($file)) {
- $this->copy($file, $target, isset($options['override']) ? $options['override'] : false);
- } elseif (is_dir($file)) {
- $this->mkdir($target);
- } else {
- throw new IOException(sprintf('Unable to guess "%s" file type.', $file), 0, null, $file);
- }
+ if (!$copyOnWindows && is_link($file)) {
+ $this->symlink($file->getLinkTarget(), $target);
+ } elseif (is_dir($file)) {
+ $this->mkdir($target);
+ } elseif (is_file($file)) {
+ $this->copy($file, $target, isset($options['override']) ? $options['override'] : false);
} else {
- if (is_link($file)) {
- $this->symlink($file->getLinkTarget(), $target);
- } elseif (is_dir($file)) {
- $this->mkdir($target);
- } elseif (is_file($file)) {
- $this->copy($file, $target, isset($options['override']) ? $options['override'] : false);
- } else {
- throw new IOException(sprintf('Unable to guess "%s" file type.', $file), 0, null, $file);
- }
+ throw new IOException(sprintf('Unable to guess "%s" file type.', $file), 0, null, $file);
}
}
}
@@ -670,13 +663,17 @@ public function tempnam($dir, $prefix)
/**
* Atomically dumps content into a file.
*
- * @param string $filename The file to be written to
- * @param string $content The data to write into the file
+ * @param string $filename The file to be written to
+ * @param string|resource $content The data to write into the file
*
* @throws IOException if the file cannot be written to
*/
public function dumpFile($filename, $content)
{
+ if (\is_array($content)) {
+ @trigger_error(sprintf('Calling "%s()" with an array in the $content argument is deprecated since Symfony 4.3.', __METHOD__), E_USER_DEPRECATED);
+ }
+
$dir = \dirname($filename);
if (!is_dir($dir)) {
@@ -703,13 +700,17 @@ public function dumpFile($filename, $content)
/**
* Appends content to an existing file.
*
- * @param string $filename The file to which to append content
- * @param string $content The content to append
+ * @param string $filename The file to which to append content
+ * @param string|resource $content The content to append
*
* @throws IOException If the file is not writable
*/
public function appendToFile($filename, $content)
{
+ if (\is_array($content)) {
+ @trigger_error(sprintf('Calling "%s()" with an array in the $content argument is deprecated since Symfony 4.3.', __METHOD__), E_USER_DEPRECATED);
+ }
+
$dir = \dirname($filename);
if (!is_dir($dir)) {
@@ -750,7 +751,6 @@ private static function box($func)
return $result;
} catch (\Throwable $e) {
- } catch (\Exception $e) {
}
\restore_error_handler();
diff --git a/vendor/symfony/filesystem/Tests/FilesystemTest.php b/vendor/symfony/filesystem/Tests/FilesystemTest.php
index 8ad94a1e28..a1a3ce0e6e 100644
--- a/vendor/symfony/filesystem/Tests/FilesystemTest.php
+++ b/vendor/symfony/filesystem/Tests/FilesystemTest.php
@@ -1332,44 +1332,34 @@ public function testMirrorContentsWithSameNameAsSourceOrTargetWithDeleteOption()
$this->assertFileNotExists($targetPath.'target');
}
- public function testMirrorWithCustomIterator()
+ public function testMirrorAvoidCopyingTargetDirectoryIfInSourceDirectory()
{
$sourcePath = $this->workspace.\DIRECTORY_SEPARATOR.'source'.\DIRECTORY_SEPARATOR;
- mkdir($sourcePath);
-
- $file = $sourcePath.\DIRECTORY_SEPARATOR.'file';
- file_put_contents($file, 'FILE');
-
- $targetPath = $this->workspace.\DIRECTORY_SEPARATOR.'target'.\DIRECTORY_SEPARATOR;
-
- $splFile = new \SplFileInfo($file);
- $iterator = new \ArrayObject([$splFile]);
+ $directory = $sourcePath.'directory'.\DIRECTORY_SEPARATOR;
+ $file1 = $directory.'file1';
+ $file2 = $sourcePath.'file2';
- $this->filesystem->mirror($sourcePath, $targetPath, $iterator);
+ mkdir($sourcePath);
+ mkdir($directory);
+ file_put_contents($file1, 'FILE1');
+ file_put_contents($file2, 'FILE2');
- $this->assertTrue(is_dir($targetPath));
- $this->assertFileEquals($file, $targetPath.\DIRECTORY_SEPARATOR.'file');
- }
+ $targetPath = $sourcePath.'target'.\DIRECTORY_SEPARATOR;
- /**
- * @expectedException \Symfony\Component\Filesystem\Exception\IOException
- * @expectedExceptionMessageRegExp /Unable to mirror "(.*)" directory/
- */
- public function testMirrorWithCustomIteratorWithRelativePath()
- {
- $sourcePath = $this->workspace.\DIRECTORY_SEPARATOR.'source'.\DIRECTORY_SEPARATOR.'..'.\DIRECTORY_SEPARATOR.'source'.\DIRECTORY_SEPARATOR;
- $realSourcePath = $this->workspace.\DIRECTORY_SEPARATOR.'source'.\DIRECTORY_SEPARATOR;
- mkdir($realSourcePath);
+ if ('\\' !== \DIRECTORY_SEPARATOR) {
+ $this->filesystem->symlink($targetPath, $sourcePath.'target_simlink');
+ }
- $file = $realSourcePath.'file';
- file_put_contents($file, 'FILE');
+ $this->filesystem->mirror($sourcePath, $targetPath, null, ['delete' => true]);
- $targetPath = $this->workspace.\DIRECTORY_SEPARATOR.'target'.\DIRECTORY_SEPARATOR.'..'.\DIRECTORY_SEPARATOR.'target'.\DIRECTORY_SEPARATOR;
+ $this->assertTrue($this->filesystem->exists($targetPath));
+ $this->assertTrue($this->filesystem->exists($targetPath.'directory'));
- $splFile = new \SplFileInfo($file);
- $iterator = new \ArrayObject([$splFile]);
+ $this->assertFileEquals($file1, $targetPath.'directory'.\DIRECTORY_SEPARATOR.'file1');
+ $this->assertFileEquals($file2, $targetPath.'file2');
- $this->filesystem->mirror($sourcePath, $targetPath, $iterator);
+ $this->assertFalse($this->filesystem->exists($targetPath.'target_simlink'));
+ $this->assertFalse($this->filesystem->exists($targetPath.'target'));
}
/**
@@ -1518,6 +1508,10 @@ public function testDumpFile()
}
}
+ /**
+ * @group legacy
+ * @expectedDeprecation Calling "Symfony\Component\Filesystem\Filesystem::dumpFile()" with an array in the $content argument is deprecated since Symfony 4.3.
+ */
public function testDumpFileWithArray()
{
$filename = $this->workspace.\DIRECTORY_SEPARATOR.'foo'.\DIRECTORY_SEPARATOR.'baz.txt';
@@ -1600,6 +1594,60 @@ public function testAppendToFile()
}
}
+ /**
+ * @group legacy
+ * @expectedDeprecation Calling "Symfony\Component\Filesystem\Filesystem::appendToFile()" with an array in the $content argument is deprecated since Symfony 4.3.
+ */
+ public function testAppendToFileWithArray()
+ {
+ $filename = $this->workspace.\DIRECTORY_SEPARATOR.'foo'.\DIRECTORY_SEPARATOR.'bar.txt';
+
+ // skip mode check on Windows
+ if ('\\' !== \DIRECTORY_SEPARATOR) {
+ $oldMask = umask(0002);
+ }
+
+ $this->filesystem->dumpFile($filename, 'foo');
+
+ $this->filesystem->appendToFile($filename, ['bar']);
+
+ $this->assertFileExists($filename);
+ $this->assertStringEqualsFile($filename, 'foobar');
+
+ // skip mode check on Windows
+ if ('\\' !== \DIRECTORY_SEPARATOR) {
+ $this->assertFilePermissions(664, $filename);
+ umask($oldMask);
+ }
+ }
+
+ public function testAppendToFileWithResource()
+ {
+ $filename = $this->workspace.\DIRECTORY_SEPARATOR.'foo'.\DIRECTORY_SEPARATOR.'bar.txt';
+
+ // skip mode check on Windows
+ if ('\\' !== \DIRECTORY_SEPARATOR) {
+ $oldMask = umask(0002);
+ }
+
+ $this->filesystem->dumpFile($filename, 'foo');
+
+ $resource = fopen('php://memory', 'rw');
+ fwrite($resource, 'bar');
+ fseek($resource, 0);
+
+ $this->filesystem->appendToFile($filename, $resource);
+
+ $this->assertFileExists($filename);
+ $this->assertStringEqualsFile($filename, 'foobar');
+
+ // skip mode check on Windows
+ if ('\\' !== \DIRECTORY_SEPARATOR) {
+ $this->assertFilePermissions(664, $filename);
+ umask($oldMask);
+ }
+ }
+
public function testAppendToFileWithScheme()
{
$scheme = 'file://';
diff --git a/vendor/symfony/filesystem/composer.json b/vendor/symfony/filesystem/composer.json
index ee8a319a7d..d13397b424 100644
--- a/vendor/symfony/filesystem/composer.json
+++ b/vendor/symfony/filesystem/composer.json
@@ -28,7 +28,7 @@
"minimum-stability": "dev",
"extra": {
"branch-alias": {
- "dev-master": "4.2-dev"
+ "dev-master": "4.3-dev"
}
}
}
diff --git a/vendor/symfony/polyfill-php73/LICENSE b/vendor/symfony/polyfill-php73/LICENSE
new file mode 100644
index 0000000000..3f853aaf35
--- /dev/null
+++ b/vendor/symfony/polyfill-php73/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2018-2019 Fabien Potencier
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/vendor/symfony/polyfill-php73/Php73.php b/vendor/symfony/polyfill-php73/Php73.php
new file mode 100644
index 0000000000..aa3708f64f
--- /dev/null
+++ b/vendor/symfony/polyfill-php73/Php73.php
@@ -0,0 +1,34 @@
+
+ * @author Ion Bazan
+ *
+ * @internal
+ */
+final class Php73
+{
+ public static $startAt = 1533462603;
+
+ /**
+ * @param bool $asNum
+ *
+ * @return array|float|int
+ */
+ public static function hrtime($asNum = false)
+ {
+ $ns = \microtime(false);
+ $s = \substr($ns, 11) - self::$startAt;
+ $ns = 1E9 * (float) $ns;
+
+ if ($asNum) {
+ $ns += $s * 1E9;
+
+ return \PHP_INT_SIZE === 4 ? $ns : (int) $ns;
+ }
+
+ return array($s, (int) $ns);
+ }
+}
diff --git a/vendor/symfony/polyfill-php73/README.md b/vendor/symfony/polyfill-php73/README.md
new file mode 100644
index 0000000000..b3ebbce511
--- /dev/null
+++ b/vendor/symfony/polyfill-php73/README.md
@@ -0,0 +1,18 @@
+Symfony Polyfill / Php73
+========================
+
+This component provides functions added to PHP 7.3 core:
+
+- [`array_key_first`](https://php.net/array_key_first)
+- [`array_key_last`](https://php.net/array_key_last)
+- [`hrtime`](https://php.net/function.hrtime)
+- [`is_countable`](https://php.net/is_countable)
+- [`JsonException`](https://php.net/JsonException)
+
+More information can be found in the
+[main Polyfill README](https://github.com/symfony/polyfill/blob/master/README.md).
+
+License
+=======
+
+This library is released under the [MIT license](LICENSE).
diff --git a/vendor/symfony/polyfill-php73/Resources/stubs/JsonException.php b/vendor/symfony/polyfill-php73/Resources/stubs/JsonException.php
new file mode 100644
index 0000000000..673d100224
--- /dev/null
+++ b/vendor/symfony/polyfill-php73/Resources/stubs/JsonException.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class JsonException extends Exception
+{
+}
diff --git a/vendor/symfony/polyfill-php73/bootstrap.php b/vendor/symfony/polyfill-php73/bootstrap.php
new file mode 100644
index 0000000000..049b8bb8aa
--- /dev/null
+++ b/vendor/symfony/polyfill-php73/bootstrap.php
@@ -0,0 +1,31 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use Symfony\Polyfill\Php73 as p;
+
+if (PHP_VERSION_ID < 70300) {
+ if (!function_exists('is_countable')) {
+ function is_countable($var) { return is_array($var) || $var instanceof Countable || $var instanceof ResourceBundle || $var instanceof SimpleXmlElement; }
+ }
+
+ if (!function_exists('hrtime')) {
+ p\Php73::$startAt = (int) microtime(true);
+ function hrtime($asNum = false) { return p\Php73::hrtime($asNum); }
+ }
+
+ if (!function_exists('array_key_first')) {
+ function array_key_first(array $array) { foreach ($array as $key => $value) { return $key; } }
+ }
+
+ if (!function_exists('array_key_last')) {
+ function array_key_last(array $array) { end($array); return key($array); }
+ }
+}
diff --git a/vendor/symfony/polyfill-php73/composer.json b/vendor/symfony/polyfill-php73/composer.json
new file mode 100644
index 0000000000..e98167ed51
--- /dev/null
+++ b/vendor/symfony/polyfill-php73/composer.json
@@ -0,0 +1,32 @@
+{
+ "name": "symfony/polyfill-php73",
+ "type": "library",
+ "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions",
+ "keywords": ["polyfill", "shim", "compatibility", "portable"],
+ "homepage": "https://symfony.com",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "require": {
+ "php": ">=5.3.3"
+ },
+ "autoload": {
+ "psr-4": { "Symfony\\Polyfill\\Php73\\": "" },
+ "files": [ "bootstrap.php" ],
+ "classmap": [ "Resources/stubs" ]
+ },
+ "minimum-stability": "dev",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.11-dev"
+ }
+ }
+}
diff --git a/vendor/symfony/service-contracts/LICENSE b/vendor/symfony/service-contracts/LICENSE
new file mode 100644
index 0000000000..3f853aaf35
--- /dev/null
+++ b/vendor/symfony/service-contracts/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2018-2019 Fabien Potencier
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/vendor/symfony/service-contracts/README.md b/vendor/symfony/service-contracts/README.md
new file mode 100644
index 0000000000..d033a439b9
--- /dev/null
+++ b/vendor/symfony/service-contracts/README.md
@@ -0,0 +1,9 @@
+Symfony Service Contracts
+=========================
+
+A set of abstractions extracted out of the Symfony components.
+
+Can be used to build on semantics that the Symfony components proved useful - and
+that already have battle tested implementations.
+
+See https://github.com/symfony/contracts/blob/master/README.md for more information.
diff --git a/vendor/symfony/service-contracts/ResetInterface.php b/vendor/symfony/service-contracts/ResetInterface.php
new file mode 100644
index 0000000000..1af1075eee
--- /dev/null
+++ b/vendor/symfony/service-contracts/ResetInterface.php
@@ -0,0 +1,30 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Contracts\Service;
+
+/**
+ * Provides a way to reset an object to its initial state.
+ *
+ * When calling the "reset()" method on an object, it should be put back to its
+ * initial state. This usually means clearing any internal buffers and forwarding
+ * the call to internal dependencies. All properties of the object should be put
+ * back to the same state it had when it was first ready to use.
+ *
+ * This method could be called, for example, to recycle objects that are used as
+ * services, so that they can be used to handle several requests in the same
+ * process loop (note that we advise making your services stateless instead of
+ * implementing this interface when possible.)
+ */
+interface ResetInterface
+{
+ public function reset();
+}
diff --git a/vendor/symfony/service-contracts/ServiceLocatorTrait.php b/vendor/symfony/service-contracts/ServiceLocatorTrait.php
new file mode 100644
index 0000000000..71b1b7460d
--- /dev/null
+++ b/vendor/symfony/service-contracts/ServiceLocatorTrait.php
@@ -0,0 +1,120 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Contracts\Service;
+
+use Psr\Container\ContainerExceptionInterface;
+use Psr\Container\NotFoundExceptionInterface;
+
+/**
+ * A trait to help implement ServiceProviderInterface.
+ *
+ * @author Robin Chalas
+ * @author Nicolas Grekas
+ */
+trait ServiceLocatorTrait
+{
+ private $factories;
+ private $loading = [];
+ private $providedTypes;
+
+ /**
+ * @param callable[] $factories
+ */
+ public function __construct(array $factories)
+ {
+ $this->factories = $factories;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function has($id)
+ {
+ return isset($this->factories[$id]);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get($id)
+ {
+ if (!isset($this->factories[$id])) {
+ throw $this->createNotFoundException($id);
+ }
+
+ if (isset($this->loading[$id])) {
+ $ids = array_values($this->loading);
+ $ids = \array_slice($this->loading, array_search($id, $ids));
+ $ids[] = $id;
+
+ throw $this->createCircularReferenceException($id, $ids);
+ }
+
+ $this->loading[$id] = $id;
+ try {
+ return $this->factories[$id]($this);
+ } finally {
+ unset($this->loading[$id]);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getProvidedServices(): array
+ {
+ if (null === $this->providedTypes) {
+ $this->providedTypes = [];
+
+ foreach ($this->factories as $name => $factory) {
+ if (!\is_callable($factory)) {
+ $this->providedTypes[$name] = '?';
+ } else {
+ $type = (new \ReflectionFunction($factory))->getReturnType();
+
+ $this->providedTypes[$name] = $type ? ($type->allowsNull() ? '?' : '').$type->getName() : '?';
+ }
+ }
+ }
+
+ return $this->providedTypes;
+ }
+
+ private function createNotFoundException(string $id): NotFoundExceptionInterface
+ {
+ if (!$alternatives = array_keys($this->factories)) {
+ $message = 'is empty...';
+ } else {
+ $last = array_pop($alternatives);
+ if ($alternatives) {
+ $message = sprintf('only knows about the "%s" and "%s" services.', implode('", "', $alternatives), $last);
+ } else {
+ $message = sprintf('only knows about the "%s" service.', $last);
+ }
+ }
+
+ if ($this->loading) {
+ $message = sprintf('The service "%s" has a dependency on a non-existent service "%s". This locator %s', end($this->loading), $id, $message);
+ } else {
+ $message = sprintf('Service "%s" not found: the current service locator %s', $id, $message);
+ }
+
+ return new class($message) extends \InvalidArgumentException implements NotFoundExceptionInterface {
+ };
+ }
+
+ private function createCircularReferenceException(string $id, array $path): ContainerExceptionInterface
+ {
+ return new class(sprintf('Circular reference detected for service "%s", path: "%s".', $id, implode(' -> ', $path))) extends \RuntimeException implements ContainerExceptionInterface {
+ };
+ }
+}
diff --git a/vendor/symfony/service-contracts/ServiceProviderInterface.php b/vendor/symfony/service-contracts/ServiceProviderInterface.php
new file mode 100644
index 0000000000..c60ad0bd4b
--- /dev/null
+++ b/vendor/symfony/service-contracts/ServiceProviderInterface.php
@@ -0,0 +1,36 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Contracts\Service;
+
+use Psr\Container\ContainerInterface;
+
+/**
+ * A ServiceProviderInterface exposes the identifiers and the types of services provided by a container.
+ *
+ * @author Nicolas Grekas
+ * @author Mateusz Sip
+ */
+interface ServiceProviderInterface extends ContainerInterface
+{
+ /**
+ * Returns an associative array of service types keyed by the identifiers provided by the current container.
+ *
+ * Examples:
+ *
+ * * ['logger' => 'Psr\Log\LoggerInterface'] means the object provides a service named "logger" that implements Psr\Log\LoggerInterface
+ * * ['foo' => '?'] means the container provides service name "foo" of unspecified type
+ * * ['bar' => '?Bar\Baz'] means the container provides a service "bar" of type Bar\Baz|null
+ *
+ * @return string[] The provided service types, keyed by service names
+ */
+ public function getProvidedServices(): array;
+}
diff --git a/vendor/symfony/service-contracts/ServiceSubscriberInterface.php b/vendor/symfony/service-contracts/ServiceSubscriberInterface.php
new file mode 100644
index 0000000000..8bb320f5b3
--- /dev/null
+++ b/vendor/symfony/service-contracts/ServiceSubscriberInterface.php
@@ -0,0 +1,53 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Contracts\Service;
+
+/**
+ * A ServiceSubscriber exposes its dependencies via the static {@link getSubscribedServices} method.
+ *
+ * The getSubscribedServices method returns an array of service types required by such instances,
+ * optionally keyed by the service names used internally. Service types that start with an interrogation
+ * mark "?" are optional, while the other ones are mandatory service dependencies.
+ *
+ * The injected service locators SHOULD NOT allow access to any other services not specified by the method.
+ *
+ * It is expected that ServiceSubscriber instances consume PSR-11-based service locators internally.
+ * This interface does not dictate any injection method for these service locators, although constructor
+ * injection is recommended.
+ *
+ * @author Nicolas Grekas
+ */
+interface ServiceSubscriberInterface
+{
+ /**
+ * Returns an array of service types required by such instances, optionally keyed by the service names used internally.
+ *
+ * For mandatory dependencies:
+ *
+ * * ['logger' => 'Psr\Log\LoggerInterface'] means the objects use the "logger" name
+ * internally to fetch a service which must implement Psr\Log\LoggerInterface.
+ * * ['loggers' => 'Psr\Log\LoggerInterface[]'] means the objects use the "loggers" name
+ * internally to fetch an iterable of Psr\Log\LoggerInterface instances.
+ * * ['Psr\Log\LoggerInterface'] is a shortcut for
+ * * ['Psr\Log\LoggerInterface' => 'Psr\Log\LoggerInterface']
+ *
+ * otherwise:
+ *
+ * * ['logger' => '?Psr\Log\LoggerInterface'] denotes an optional dependency
+ * * ['loggers' => '?Psr\Log\LoggerInterface[]'] denotes an optional iterable dependency
+ * * ['?Psr\Log\LoggerInterface'] is a shortcut for
+ * * ['Psr\Log\LoggerInterface' => '?Psr\Log\LoggerInterface']
+ *
+ * @return array The required service types, optionally keyed by service names
+ */
+ public static function getSubscribedServices();
+}
diff --git a/vendor/symfony/service-contracts/ServiceSubscriberTrait.php b/vendor/symfony/service-contracts/ServiceSubscriberTrait.php
new file mode 100644
index 0000000000..ceaef6fa14
--- /dev/null
+++ b/vendor/symfony/service-contracts/ServiceSubscriberTrait.php
@@ -0,0 +1,61 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Contracts\Service;
+
+use Psr\Container\ContainerInterface;
+
+/**
+ * Implementation of ServiceSubscriberInterface that determines subscribed services from
+ * private method return types. Service ids are available as "ClassName::methodName".
+ *
+ * @author Kevin Bond
+ */
+trait ServiceSubscriberTrait
+{
+ /** @var ContainerInterface */
+ private $container;
+
+ public static function getSubscribedServices(): array
+ {
+ static $services;
+
+ if (null !== $services) {
+ return $services;
+ }
+
+ $services = \is_callable(['parent', __FUNCTION__]) ? parent::getSubscribedServices() : [];
+
+ foreach ((new \ReflectionClass(self::class))->getMethods() as $method) {
+ if ($method->isStatic() || $method->isAbstract() || $method->isGenerator() || $method->isInternal() || $method->getNumberOfRequiredParameters()) {
+ continue;
+ }
+
+ if (self::class === $method->getDeclaringClass()->name && ($returnType = $method->getReturnType()) && !$returnType->isBuiltin()) {
+ $services[self::class.'::'.$method->name] = '?'.$returnType->getName();
+ }
+ }
+
+ return $services;
+ }
+
+ /**
+ * @required
+ */
+ public function setContainer(ContainerInterface $container)
+ {
+ $this->container = $container;
+
+ if (\is_callable(['parent', __FUNCTION__])) {
+ return parent::setContainer($container);
+ }
+ }
+}
diff --git a/vendor/symfony/service-contracts/Test/ServiceLocatorTest.php b/vendor/symfony/service-contracts/Test/ServiceLocatorTest.php
new file mode 100644
index 0000000000..69594583f5
--- /dev/null
+++ b/vendor/symfony/service-contracts/Test/ServiceLocatorTest.php
@@ -0,0 +1,94 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Contracts\Service\Test;
+
+use PHPUnit\Framework\TestCase;
+use Psr\Container\ContainerInterface;
+use Symfony\Contracts\Service\ServiceLocatorTrait;
+
+class ServiceLocatorTest extends TestCase
+{
+ public function getServiceLocator(array $factories)
+ {
+ return new class($factories) implements ContainerInterface {
+ use ServiceLocatorTrait;
+ };
+ }
+
+ public function testHas()
+ {
+ $locator = $this->getServiceLocator([
+ 'foo' => function () { return 'bar'; },
+ 'bar' => function () { return 'baz'; },
+ function () { return 'dummy'; },
+ ]);
+
+ $this->assertTrue($locator->has('foo'));
+ $this->assertTrue($locator->has('bar'));
+ $this->assertFalse($locator->has('dummy'));
+ }
+
+ public function testGet()
+ {
+ $locator = $this->getServiceLocator([
+ 'foo' => function () { return 'bar'; },
+ 'bar' => function () { return 'baz'; },
+ ]);
+
+ $this->assertSame('bar', $locator->get('foo'));
+ $this->assertSame('baz', $locator->get('bar'));
+ }
+
+ public function testGetDoesNotMemoize()
+ {
+ $i = 0;
+ $locator = $this->getServiceLocator([
+ 'foo' => function () use (&$i) {
+ ++$i;
+
+ return 'bar';
+ },
+ ]);
+
+ $this->assertSame('bar', $locator->get('foo'));
+ $this->assertSame('bar', $locator->get('foo'));
+ $this->assertSame(2, $i);
+ }
+
+ /**
+ * @expectedException \Psr\Container\NotFoundExceptionInterface
+ * @expectedExceptionMessage The service "foo" has a dependency on a non-existent service "bar". This locator only knows about the "foo" service.
+ */
+ public function testThrowsOnUndefinedInternalService()
+ {
+ $locator = $this->getServiceLocator([
+ 'foo' => function () use (&$locator) { return $locator->get('bar'); },
+ ]);
+
+ $locator->get('foo');
+ }
+
+ /**
+ * @expectedException \Psr\Container\ContainerExceptionInterface
+ * @expectedExceptionMessage Circular reference detected for service "bar", path: "bar -> baz -> bar".
+ */
+ public function testThrowsOnCircularReference()
+ {
+ $locator = $this->getServiceLocator([
+ 'foo' => function () use (&$locator) { return $locator->get('bar'); },
+ 'bar' => function () use (&$locator) { return $locator->get('baz'); },
+ 'baz' => function () use (&$locator) { return $locator->get('bar'); },
+ ]);
+
+ $locator->get('foo');
+ }
+}
diff --git a/vendor/symfony/service-contracts/composer.json b/vendor/symfony/service-contracts/composer.json
new file mode 100644
index 0000000000..54341174ce
--- /dev/null
+++ b/vendor/symfony/service-contracts/composer.json
@@ -0,0 +1,34 @@
+{
+ "name": "symfony/service-contracts",
+ "type": "library",
+ "description": "Generic abstractions related to writing services",
+ "keywords": ["abstractions", "contracts", "decoupling", "interfaces", "interoperability", "standards"],
+ "homepage": "https://symfony.com",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "require": {
+ "php": "^7.1.3"
+ },
+ "suggest": {
+ "psr/container": "",
+ "symfony/service-implementation": ""
+ },
+ "autoload": {
+ "psr-4": { "Symfony\\Contracts\\Service\\": "" }
+ },
+ "minimum-stability": "dev",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.1-dev"
+ }
+ }
+}
diff --git a/vendor/symfony/translation-contracts/LICENSE b/vendor/symfony/translation-contracts/LICENSE
new file mode 100644
index 0000000000..3f853aaf35
--- /dev/null
+++ b/vendor/symfony/translation-contracts/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2018-2019 Fabien Potencier
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/vendor/symfony/translation-contracts/LocaleAwareInterface.php b/vendor/symfony/translation-contracts/LocaleAwareInterface.php
new file mode 100644
index 0000000000..dbd8894fe7
--- /dev/null
+++ b/vendor/symfony/translation-contracts/LocaleAwareInterface.php
@@ -0,0 +1,31 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Contracts\Translation;
+
+interface LocaleAwareInterface
+{
+ /**
+ * Sets the current locale.
+ *
+ * @param string $locale The locale
+ *
+ * @throws \InvalidArgumentException If the locale contains invalid characters
+ */
+ public function setLocale($locale);
+
+ /**
+ * Returns the current locale.
+ *
+ * @return string The locale
+ */
+ public function getLocale();
+}
diff --git a/vendor/symfony/translation-contracts/README.md b/vendor/symfony/translation-contracts/README.md
new file mode 100644
index 0000000000..6c693ce0b3
--- /dev/null
+++ b/vendor/symfony/translation-contracts/README.md
@@ -0,0 +1,9 @@
+Symfony Translation Contracts
+=============================
+
+A set of abstractions extracted out of the Symfony components.
+
+Can be used to build on semantics that the Symfony components proved useful - and
+that already have battle tested implementations.
+
+See https://github.com/symfony/contracts/blob/master/README.md for more information.
diff --git a/vendor/symfony/translation-contracts/Test/TranslatorTest.php b/vendor/symfony/translation-contracts/Test/TranslatorTest.php
new file mode 100644
index 0000000000..48466300b5
--- /dev/null
+++ b/vendor/symfony/translation-contracts/Test/TranslatorTest.php
@@ -0,0 +1,353 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Contracts\Translation\Test;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Contracts\Translation\TranslatorInterface;
+use Symfony\Contracts\Translation\TranslatorTrait;
+
+/**
+ * Test should cover all languages mentioned on http://translate.sourceforge.net/wiki/l10n/pluralforms
+ * and Plural forms mentioned on http://www.gnu.org/software/gettext/manual/gettext.html#Plural-forms.
+ *
+ * See also https://developer.mozilla.org/en/Localization_and_Plurals which mentions 15 rules having a maximum of 6 forms.
+ * The mozilla code is also interesting to check for.
+ *
+ * As mentioned by chx http://drupal.org/node/1273968 we can cover all by testing number from 0 to 199
+ *
+ * The goal to cover all languages is to far fetched so this test case is smaller.
+ *
+ * @author Clemens Tolboom clemens@build2be.nl
+ */
+class TranslatorTest extends TestCase
+{
+ public function getTranslator()
+ {
+ return new class() implements TranslatorInterface {
+ use TranslatorTrait;
+ };
+ }
+
+ /**
+ * @dataProvider getTransTests
+ */
+ public function testTrans($expected, $id, $parameters)
+ {
+ $translator = $this->getTranslator();
+
+ $this->assertEquals($expected, $translator->trans($id, $parameters));
+ }
+
+ /**
+ * @dataProvider getTransChoiceTests
+ */
+ public function testTransChoiceWithExplicitLocale($expected, $id, $number)
+ {
+ $translator = $this->getTranslator();
+ $translator->setLocale('en');
+
+ $this->assertEquals($expected, $translator->trans($id, ['%count%' => $number]));
+ }
+
+ /**
+ * @dataProvider getTransChoiceTests
+ */
+ public function testTransChoiceWithDefaultLocale($expected, $id, $number)
+ {
+ \Locale::setDefault('en');
+
+ $translator = $this->getTranslator();
+
+ $this->assertEquals($expected, $translator->trans($id, ['%count%' => $number]));
+ }
+
+ public function testGetSetLocale()
+ {
+ $translator = $this->getTranslator();
+ $translator->setLocale('en');
+
+ $this->assertEquals('en', $translator->getLocale());
+ }
+
+ /**
+ * @requires extension intl
+ */
+ public function testGetLocaleReturnsDefaultLocaleIfNotSet()
+ {
+ $translator = $this->getTranslator();
+
+ \Locale::setDefault('pt_BR');
+ $this->assertEquals('pt_BR', $translator->getLocale());
+
+ \Locale::setDefault('en');
+ $this->assertEquals('en', $translator->getLocale());
+ }
+
+ public function getTransTests()
+ {
+ return [
+ ['Symfony is great!', 'Symfony is great!', []],
+ ['Symfony is awesome!', 'Symfony is %what%!', ['%what%' => 'awesome']],
+ ];
+ }
+
+ public function getTransChoiceTests()
+ {
+ return [
+ ['There are no apples', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 0],
+ ['There is one apple', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 1],
+ ['There are 10 apples', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 10],
+ ['There are 0 apples', 'There is 1 apple|There are %count% apples', 0],
+ ['There is 1 apple', 'There is 1 apple|There are %count% apples', 1],
+ ['There are 10 apples', 'There is 1 apple|There are %count% apples', 10],
+ // custom validation messages may be coded with a fixed value
+ ['There are 2 apples', 'There are 2 apples', 2],
+ ];
+ }
+
+ /**
+ * @dataProvider getInternal
+ */
+ public function testInterval($expected, $number, $interval)
+ {
+ $translator = $this->getTranslator();
+
+ $this->assertEquals($expected, $translator->trans($interval.' foo|[1,Inf[ bar', ['%count%' => $number]));
+ }
+
+ public function getInternal()
+ {
+ return [
+ ['foo', 3, '{1,2, 3 ,4}'],
+ ['bar', 10, '{1,2, 3 ,4}'],
+ ['bar', 3, '[1,2]'],
+ ['foo', 1, '[1,2]'],
+ ['foo', 2, '[1,2]'],
+ ['bar', 1, ']1,2['],
+ ['bar', 2, ']1,2['],
+ ['foo', log(0), '[-Inf,2['],
+ ['foo', -log(0), '[-2,+Inf]'],
+ ];
+ }
+
+ /**
+ * @dataProvider getChooseTests
+ */
+ public function testChoose($expected, $id, $number)
+ {
+ $translator = $this->getTranslator();
+
+ $this->assertEquals($expected, $translator->trans($id, ['%count%' => $number]));
+ }
+
+ public function testReturnMessageIfExactlyOneStandardRuleIsGiven()
+ {
+ $translator = $this->getTranslator();
+
+ $this->assertEquals('There are two apples', $translator->trans('There are two apples', ['%count%' => 2]));
+ }
+
+ /**
+ * @dataProvider getNonMatchingMessages
+ * @expectedException \InvalidArgumentException
+ */
+ public function testThrowExceptionIfMatchingMessageCannotBeFound($id, $number)
+ {
+ $translator = $this->getTranslator();
+
+ $translator->trans($id, ['%count%' => $number]);
+ }
+
+ public function getNonMatchingMessages()
+ {
+ return [
+ ['{0} There are no apples|{1} There is one apple', 2],
+ ['{1} There is one apple|]1,Inf] There are %count% apples', 0],
+ ['{1} There is one apple|]2,Inf] There are %count% apples', 2],
+ ['{0} There are no apples|There is one apple', 2],
+ ];
+ }
+
+ public function getChooseTests()
+ {
+ return [
+ ['There are no apples', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 0],
+ ['There are no apples', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 0],
+ ['There are no apples', '{0}There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 0],
+
+ ['There is one apple', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 1],
+
+ ['There are 10 apples', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 10],
+ ['There are 10 apples', '{0} There are no apples|{1} There is one apple|]1,Inf]There are %count% apples', 10],
+ ['There are 10 apples', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 10],
+
+ ['There are 0 apples', 'There is one apple|There are %count% apples', 0],
+ ['There is one apple', 'There is one apple|There are %count% apples', 1],
+ ['There are 10 apples', 'There is one apple|There are %count% apples', 10],
+
+ ['There are 0 apples', 'one: There is one apple|more: There are %count% apples', 0],
+ ['There is one apple', 'one: There is one apple|more: There are %count% apples', 1],
+ ['There are 10 apples', 'one: There is one apple|more: There are %count% apples', 10],
+
+ ['There are no apples', '{0} There are no apples|one: There is one apple|more: There are %count% apples', 0],
+ ['There is one apple', '{0} There are no apples|one: There is one apple|more: There are %count% apples', 1],
+ ['There are 10 apples', '{0} There are no apples|one: There is one apple|more: There are %count% apples', 10],
+
+ ['', '{0}|{1} There is one apple|]1,Inf] There are %count% apples', 0],
+ ['', '{0} There are no apples|{1}|]1,Inf] There are %count% apples', 1],
+
+ // Indexed only tests which are Gettext PoFile* compatible strings.
+ ['There are 0 apples', 'There is one apple|There are %count% apples', 0],
+ ['There is one apple', 'There is one apple|There are %count% apples', 1],
+ ['There are 2 apples', 'There is one apple|There are %count% apples', 2],
+
+ // Tests for float numbers
+ ['There is almost one apple', '{0} There are no apples|]0,1[ There is almost one apple|{1} There is one apple|[1,Inf] There is more than one apple', 0.7],
+ ['There is one apple', '{0} There are no apples|]0,1[There are %count% apples|{1} There is one apple|[1,Inf] There is more than one apple', 1],
+ ['There is more than one apple', '{0} There are no apples|]0,1[There are %count% apples|{1} There is one apple|[1,Inf] There is more than one apple', 1.7],
+ ['There are no apples', '{0} There are no apples|]0,1[There are %count% apples|{1} There is one apple|[1,Inf] There is more than one apple', 0],
+ ['There are no apples', '{0} There are no apples|]0,1[There are %count% apples|{1} There is one apple|[1,Inf] There is more than one apple', 0.0],
+ ['There are no apples', '{0.0} There are no apples|]0,1[There are %count% apples|{1} There is one apple|[1,Inf] There is more than one apple', 0],
+
+ // Test texts with new-lines
+ // with double-quotes and \n in id & double-quotes and actual newlines in text
+ ["This is a text with a\n new-line in it. Selector = 0.", '{0}This is a text with a
+ new-line in it. Selector = 0.|{1}This is a text with a
+ new-line in it. Selector = 1.|[1,Inf]This is a text with a
+ new-line in it. Selector > 1.', 0],
+ // with double-quotes and \n in id and single-quotes and actual newlines in text
+ ["This is a text with a\n new-line in it. Selector = 1.", '{0}This is a text with a
+ new-line in it. Selector = 0.|{1}This is a text with a
+ new-line in it. Selector = 1.|[1,Inf]This is a text with a
+ new-line in it. Selector > 1.', 1],
+ ["This is a text with a\n new-line in it. Selector > 1.", '{0}This is a text with a
+ new-line in it. Selector = 0.|{1}This is a text with a
+ new-line in it. Selector = 1.|[1,Inf]This is a text with a
+ new-line in it. Selector > 1.', 5],
+ // with double-quotes and id split accros lines
+ ['This is a text with a
+ new-line in it. Selector = 1.', '{0}This is a text with a
+ new-line in it. Selector = 0.|{1}This is a text with a
+ new-line in it. Selector = 1.|[1,Inf]This is a text with a
+ new-line in it. Selector > 1.', 1],
+ // with single-quotes and id split accros lines
+ ['This is a text with a
+ new-line in it. Selector > 1.', '{0}This is a text with a
+ new-line in it. Selector = 0.|{1}This is a text with a
+ new-line in it. Selector = 1.|[1,Inf]This is a text with a
+ new-line in it. Selector > 1.', 5],
+ // with single-quotes and \n in text
+ ['This is a text with a\nnew-line in it. Selector = 0.', '{0}This is a text with a\nnew-line in it. Selector = 0.|{1}This is a text with a\nnew-line in it. Selector = 1.|[1,Inf]This is a text with a\nnew-line in it. Selector > 1.', 0],
+ // with double-quotes and id split accros lines
+ ["This is a text with a\nnew-line in it. Selector = 1.", "{0}This is a text with a\nnew-line in it. Selector = 0.|{1}This is a text with a\nnew-line in it. Selector = 1.|[1,Inf]This is a text with a\nnew-line in it. Selector > 1.", 1],
+ // esacape pipe
+ ['This is a text with | in it. Selector = 0.', '{0}This is a text with || in it. Selector = 0.|{1}This is a text with || in it. Selector = 1.', 0],
+ // Empty plural set (2 plural forms) from a .PO file
+ ['', '|', 1],
+ // Empty plural set (3 plural forms) from a .PO file
+ ['', '||', 1],
+ ];
+ }
+
+ /**
+ * @dataProvider failingLangcodes
+ */
+ public function testFailedLangcodes($nplural, $langCodes)
+ {
+ $matrix = $this->generateTestData($langCodes);
+ $this->validateMatrix($nplural, $matrix, false);
+ }
+
+ /**
+ * @dataProvider successLangcodes
+ */
+ public function testLangcodes($nplural, $langCodes)
+ {
+ $matrix = $this->generateTestData($langCodes);
+ $this->validateMatrix($nplural, $matrix);
+ }
+
+ /**
+ * This array should contain all currently known langcodes.
+ *
+ * As it is impossible to have this ever complete we should try as hard as possible to have it almost complete.
+ *
+ * @return array
+ */
+ public function successLangcodes()
+ {
+ return [
+ ['1', ['ay', 'bo', 'cgg', 'dz', 'id', 'ja', 'jbo', 'ka', 'kk', 'km', 'ko', 'ky']],
+ ['2', ['nl', 'fr', 'en', 'de', 'de_GE', 'hy', 'hy_AM']],
+ ['3', ['be', 'bs', 'cs', 'hr']],
+ ['4', ['cy', 'mt', 'sl']],
+ ['6', ['ar']],
+ ];
+ }
+
+ /**
+ * This array should be at least empty within the near future.
+ *
+ * This both depends on a complete list trying to add above as understanding
+ * the plural rules of the current failing languages.
+ *
+ * @return array with nplural together with langcodes
+ */
+ public function failingLangcodes()
+ {
+ return [
+ ['1', ['fa']],
+ ['2', ['jbo']],
+ ['3', ['cbs']],
+ ['4', ['gd', 'kw']],
+ ['5', ['ga']],
+ ];
+ }
+
+ /**
+ * We validate only on the plural coverage. Thus the real rules is not tested.
+ *
+ * @param string $nplural Plural expected
+ * @param array $matrix Containing langcodes and their plural index values
+ * @param bool $expectSuccess
+ */
+ protected function validateMatrix($nplural, $matrix, $expectSuccess = true)
+ {
+ foreach ($matrix as $langCode => $data) {
+ $indexes = array_flip($data);
+ if ($expectSuccess) {
+ $this->assertEquals($nplural, \count($indexes), "Langcode '$langCode' has '$nplural' plural forms.");
+ } else {
+ $this->assertNotEquals((int) $nplural, \count($indexes), "Langcode '$langCode' has '$nplural' plural forms.");
+ }
+ }
+ }
+
+ protected function generateTestData($langCodes)
+ {
+ $translator = new class() {
+ use TranslatorTrait {
+ getPluralizationRule as public;
+ }
+ };
+
+ $matrix = [];
+ foreach ($langCodes as $langCode) {
+ for ($count = 0; $count < 200; ++$count) {
+ $plural = $translator->getPluralizationRule($count, $langCode);
+ $matrix[$langCode][$count] = $plural;
+ }
+ }
+
+ return $matrix;
+ }
+}
diff --git a/vendor/symfony/translation-contracts/TranslatorInterface.php b/vendor/symfony/translation-contracts/TranslatorInterface.php
new file mode 100644
index 0000000000..d867637370
--- /dev/null
+++ b/vendor/symfony/translation-contracts/TranslatorInterface.php
@@ -0,0 +1,65 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Contracts\Translation;
+
+/**
+ * @author Fabien Potencier
+ */
+interface TranslatorInterface
+{
+ /**
+ * Translates the given message.
+ *
+ * When a number is provided as a parameter named "%count%", the message is parsed for plural
+ * forms and a translation is chosen according to this number using the following rules:
+ *
+ * Given a message with different plural translations separated by a
+ * pipe (|), this method returns the correct portion of the message based
+ * on the given number, locale and the pluralization rules in the message
+ * itself.
+ *
+ * The message supports two different types of pluralization rules:
+ *
+ * interval: {0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples
+ * indexed: There is one apple|There are %count% apples
+ *
+ * The indexed solution can also contain labels (e.g. one: There is one apple).
+ * This is purely for making the translations more clear - it does not
+ * affect the functionality.
+ *
+ * The two methods can also be mixed:
+ * {0} There are no apples|one: There is one apple|more: There are %count% apples
+ *
+ * An interval can represent a finite set of numbers:
+ * {1,2,3,4}
+ *
+ * An interval can represent numbers between two numbers:
+ * [1, +Inf]
+ * ]-1,2[
+ *
+ * The left delimiter can be [ (inclusive) or ] (exclusive).
+ * The right delimiter can be [ (exclusive) or ] (inclusive).
+ * Beside numbers, you can use -Inf and +Inf for the infinite.
+ *
+ * @see https://en.wikipedia.org/wiki/ISO_31-11
+ *
+ * @param string $id The message id (may also be an object that can be cast to string)
+ * @param array $parameters An array of parameters for the message
+ * @param string|null $domain The domain for the message or null to use the default
+ * @param string|null $locale The locale or null to use the default
+ *
+ * @return string The translated string
+ *
+ * @throws \InvalidArgumentException If the locale contains invalid characters
+ */
+ public function trans($id, array $parameters = [], $domain = null, $locale = null);
+}
diff --git a/vendor/symfony/translation-contracts/TranslatorTrait.php b/vendor/symfony/translation-contracts/TranslatorTrait.php
new file mode 100644
index 0000000000..c1021923c8
--- /dev/null
+++ b/vendor/symfony/translation-contracts/TranslatorTrait.php
@@ -0,0 +1,255 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Contracts\Translation;
+
+use Symfony\Component\Translation\Exception\InvalidArgumentException;
+
+/**
+ * A trait to help implement TranslatorInterface and LocaleAwareInterface.
+ *
+ * @author Fabien Potencier
+ */
+trait TranslatorTrait
+{
+ private $locale;
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setLocale($locale)
+ {
+ $this->locale = (string) $locale;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getLocale()
+ {
+ return $this->locale ?: \Locale::getDefault();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function trans($id, array $parameters = [], $domain = null, $locale = null)
+ {
+ $id = (string) $id;
+
+ if (!isset($parameters['%count%']) || !is_numeric($parameters['%count%'])) {
+ return strtr($id, $parameters);
+ }
+
+ $number = (float) $parameters['%count%'];
+ $locale = (string) $locale ?: $this->getLocale();
+
+ $parts = [];
+ if (preg_match('/^\|++$/', $id)) {
+ $parts = explode('|', $id);
+ } elseif (preg_match_all('/(?:\|\||[^\|])++/', $id, $matches)) {
+ $parts = $matches[0];
+ }
+
+ $intervalRegexp = <<<'EOF'
+/^(?P
+ ({\s*
+ (\-?\d+(\.\d+)?[\s*,\s*\-?\d+(\.\d+)?]*)
+ \s*})
+
+ |
+
+ (?P[\[\]])
+ \s*
+ (?P-Inf|\-?\d+(\.\d+)?)
+ \s*,\s*
+ (?P\+?Inf|\-?\d+(\.\d+)?)
+ \s*
+ (?P[\[\]])
+)\s*(?P.*?)$/xs
+EOF;
+
+ $standardRules = [];
+ foreach ($parts as $part) {
+ $part = trim(str_replace('||', '|', $part));
+
+ // try to match an explicit rule, then fallback to the standard ones
+ if (preg_match($intervalRegexp, $part, $matches)) {
+ if ($matches[2]) {
+ foreach (explode(',', $matches[3]) as $n) {
+ if ($number == $n) {
+ return strtr($matches['message'], $parameters);
+ }
+ }
+ } else {
+ $leftNumber = '-Inf' === $matches['left'] ? -INF : (float) $matches['left'];
+ $rightNumber = \is_numeric($matches['right']) ? (float) $matches['right'] : INF;
+
+ if (('[' === $matches['left_delimiter'] ? $number >= $leftNumber : $number > $leftNumber)
+ && (']' === $matches['right_delimiter'] ? $number <= $rightNumber : $number < $rightNumber)
+ ) {
+ return strtr($matches['message'], $parameters);
+ }
+ }
+ } elseif (preg_match('/^\w+\:\s*(.*?)$/', $part, $matches)) {
+ $standardRules[] = $matches[1];
+ } else {
+ $standardRules[] = $part;
+ }
+ }
+
+ $position = $this->getPluralizationRule($number, $locale);
+
+ if (!isset($standardRules[$position])) {
+ // when there's exactly one rule given, and that rule is a standard
+ // rule, use this rule
+ if (1 === \count($parts) && isset($standardRules[0])) {
+ return strtr($standardRules[0], $parameters);
+ }
+
+ $message = sprintf('Unable to choose a translation for "%s" with locale "%s" for value "%d". Double check that this translation has the correct plural options (e.g. "There is one apple|There are %%count%% apples").', $id, $locale, $number);
+
+ if (\class_exists(InvalidArgumentException::class)) {
+ throw new InvalidArgumentException($message);
+ }
+
+ throw new \InvalidArgumentException($message);
+ }
+
+ return strtr($standardRules[$position], $parameters);
+ }
+
+ /**
+ * Returns the plural position to use for the given locale and number.
+ *
+ * The plural rules are derived from code of the Zend Framework (2010-09-25),
+ * which is subject to the new BSD license (http://framework.zend.com/license/new-bsd).
+ * Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ */
+ private function getPluralizationRule(int $number, string $locale): int
+ {
+ switch ('pt_BR' !== $locale && \strlen($locale) > 3 ? substr($locale, 0, strrpos($locale, '_')) : $locale) {
+ case 'af':
+ case 'bn':
+ case 'bg':
+ case 'ca':
+ case 'da':
+ case 'de':
+ case 'el':
+ case 'en':
+ case 'eo':
+ case 'es':
+ case 'et':
+ case 'eu':
+ case 'fa':
+ case 'fi':
+ case 'fo':
+ case 'fur':
+ case 'fy':
+ case 'gl':
+ case 'gu':
+ case 'ha':
+ case 'he':
+ case 'hu':
+ case 'is':
+ case 'it':
+ case 'ku':
+ case 'lb':
+ case 'ml':
+ case 'mn':
+ case 'mr':
+ case 'nah':
+ case 'nb':
+ case 'ne':
+ case 'nl':
+ case 'nn':
+ case 'no':
+ case 'oc':
+ case 'om':
+ case 'or':
+ case 'pa':
+ case 'pap':
+ case 'ps':
+ case 'pt':
+ case 'so':
+ case 'sq':
+ case 'sv':
+ case 'sw':
+ case 'ta':
+ case 'te':
+ case 'tk':
+ case 'ur':
+ case 'zu':
+ return (1 == $number) ? 0 : 1;
+
+ case 'am':
+ case 'bh':
+ case 'fil':
+ case 'fr':
+ case 'gun':
+ case 'hi':
+ case 'hy':
+ case 'ln':
+ case 'mg':
+ case 'nso':
+ case 'pt_BR':
+ case 'ti':
+ case 'wa':
+ return ((0 == $number) || (1 == $number)) ? 0 : 1;
+
+ case 'be':
+ case 'bs':
+ case 'hr':
+ case 'ru':
+ case 'sh':
+ case 'sr':
+ case 'uk':
+ return ((1 == $number % 10) && (11 != $number % 100)) ? 0 : ((($number % 10 >= 2) && ($number % 10 <= 4) && (($number % 100 < 10) || ($number % 100 >= 20))) ? 1 : 2);
+
+ case 'cs':
+ case 'sk':
+ return (1 == $number) ? 0 : ((($number >= 2) && ($number <= 4)) ? 1 : 2);
+
+ case 'ga':
+ return (1 == $number) ? 0 : ((2 == $number) ? 1 : 2);
+
+ case 'lt':
+ return ((1 == $number % 10) && (11 != $number % 100)) ? 0 : ((($number % 10 >= 2) && (($number % 100 < 10) || ($number % 100 >= 20))) ? 1 : 2);
+
+ case 'sl':
+ return (1 == $number % 100) ? 0 : ((2 == $number % 100) ? 1 : (((3 == $number % 100) || (4 == $number % 100)) ? 2 : 3));
+
+ case 'mk':
+ return (1 == $number % 10) ? 0 : 1;
+
+ case 'mt':
+ return (1 == $number) ? 0 : (((0 == $number) || (($number % 100 > 1) && ($number % 100 < 11))) ? 1 : ((($number % 100 > 10) && ($number % 100 < 20)) ? 2 : 3));
+
+ case 'lv':
+ return (0 == $number) ? 0 : (((1 == $number % 10) && (11 != $number % 100)) ? 1 : 2);
+
+ case 'pl':
+ return (1 == $number) ? 0 : ((($number % 10 >= 2) && ($number % 10 <= 4) && (($number % 100 < 12) || ($number % 100 > 14))) ? 1 : 2);
+
+ case 'cy':
+ return (1 == $number) ? 0 : ((2 == $number) ? 1 : (((8 == $number) || (11 == $number)) ? 2 : 3));
+
+ case 'ro':
+ return (1 == $number) ? 0 : (((0 == $number) || (($number % 100 > 0) && ($number % 100 < 20))) ? 1 : 2);
+
+ case 'ar':
+ return (0 == $number) ? 0 : ((1 == $number) ? 1 : ((2 == $number) ? 2 : ((($number % 100 >= 3) && ($number % 100 <= 10)) ? 3 : ((($number % 100 >= 11) && ($number % 100 <= 99)) ? 4 : 5))));
+
+ default:
+ return 0;
+ }
+ }
+}
diff --git a/vendor/symfony/translation-contracts/composer.json b/vendor/symfony/translation-contracts/composer.json
new file mode 100644
index 0000000000..09749d35f5
--- /dev/null
+++ b/vendor/symfony/translation-contracts/composer.json
@@ -0,0 +1,33 @@
+{
+ "name": "symfony/translation-contracts",
+ "type": "library",
+ "description": "Generic abstractions related to translation",
+ "keywords": ["abstractions", "contracts", "decoupling", "interfaces", "interoperability", "standards"],
+ "homepage": "https://symfony.com",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "require": {
+ "php": "^7.1.3"
+ },
+ "suggest": {
+ "symfony/translation-implementation": ""
+ },
+ "autoload": {
+ "psr-4": { "Symfony\\Contracts\\Translation\\": "" }
+ },
+ "minimum-stability": "dev",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.1-dev"
+ }
+ }
+}
diff --git a/vendor/symfony/translation/CHANGELOG.md b/vendor/symfony/translation/CHANGELOG.md
index 87eb2fa4a3..c80716838b 100644
--- a/vendor/symfony/translation/CHANGELOG.md
+++ b/vendor/symfony/translation/CHANGELOG.md
@@ -1,6 +1,12 @@
CHANGELOG
=========
+4.3.0
+-----
+
+ * Improved Xliff 1.2 loader to load the original file's metadata
+ * Added `TranslatorPathsPass`
+
4.2.0
-----
diff --git a/vendor/symfony/translation/Command/XliffLintCommand.php b/vendor/symfony/translation/Command/XliffLintCommand.php
index 9bea4d9499..3c2cc9efde 100644
--- a/vendor/symfony/translation/Command/XliffLintCommand.php
+++ b/vendor/symfony/translation/Command/XliffLintCommand.php
@@ -124,7 +124,9 @@ private function validate($content, $file = null)
$normalizedLocale = preg_quote(str_replace('-', '_', $targetLanguage), '/');
// strict file names require translation files to be named '____.locale.xlf'
// otherwise, both '____.locale.xlf' and 'locale.____.xlf' are allowed
- $expectedFilenamePattern = $this->requireStrictFileNames ? sprintf('/^.*\.%s\.xlf/', $normalizedLocale) : sprintf('/^(.*\.%s\.xlf|%s\..*\.xlf)/', $normalizedLocale, $normalizedLocale);
+ // also, the regexp matching must be case-insensitive, as defined for 'target-language' values
+ // http://docs.oasis-open.org/xliff/v1.2/os/xliff-core.html#target-language
+ $expectedFilenamePattern = $this->requireStrictFileNames ? sprintf('/^.*\.(?i:%s)\.xlf/', $normalizedLocale) : sprintf('/^(.*\.(?i:%s)\.xlf|(?i:%s)\..*\.xlf)/', $normalizedLocale, $normalizedLocale);
if (0 === preg_match($expectedFilenamePattern, basename($file))) {
$errors[] = [
diff --git a/vendor/symfony/translation/DataCollector/TranslationDataCollector.php b/vendor/symfony/translation/DataCollector/TranslationDataCollector.php
index d99b493ac6..35dfc0e344 100644
--- a/vendor/symfony/translation/DataCollector/TranslationDataCollector.php
+++ b/vendor/symfony/translation/DataCollector/TranslationDataCollector.php
@@ -36,12 +36,9 @@ public function lateCollect()
{
$messages = $this->sanitizeCollectedMessages($this->translator->getCollectedMessages());
- $this->data = $this->computeCount($messages);
+ $this->data += $this->computeCount($messages);
$this->data['messages'] = $messages;
- $this->data['locale'] = $this->translator->getLocale();
- $this->data['fallback_locales'] = $this->translator->getFallbackLocales();
-
$this->data = $this->cloneVar($this->data);
}
@@ -50,6 +47,8 @@ public function lateCollect()
*/
public function collect(Request $request, Response $response, \Exception $exception = null)
{
+ $this->data['locale'] = $this->translator->getLocale();
+ $this->data['fallback_locales'] = $this->translator->getFallbackLocales();
}
/**
diff --git a/vendor/symfony/translation/DataCollectorTranslator.php b/vendor/symfony/translation/DataCollectorTranslator.php
index 68200a7f45..0284b77e9b 100644
--- a/vendor/symfony/translation/DataCollectorTranslator.php
+++ b/vendor/symfony/translation/DataCollectorTranslator.php
@@ -68,10 +68,10 @@ public function transChoice($id, $number, array $parameters = [], $domain = null
{
if ($this->translator instanceof TranslatorInterface) {
$trans = $this->translator->trans($id, ['%count%' => $number] + $parameters, $domain, $locale);
+ } else {
+ $trans = $this->translator->transChoice($id, $number, $parameters, $domain, $locale);
}
- $trans = $this->translator->transChoice($id, $number, $parameters, $domain, $locale);
-
$this->collectMessage($locale, $domain, $id, $trans, ['%count%' => $number] + $parameters);
return $trans;
diff --git a/vendor/symfony/translation/DependencyInjection/TranslatorPass.php b/vendor/symfony/translation/DependencyInjection/TranslatorPass.php
index fc1c08fc32..ed4a840d86 100644
--- a/vendor/symfony/translation/DependencyInjection/TranslatorPass.php
+++ b/vendor/symfony/translation/DependencyInjection/TranslatorPass.php
@@ -68,12 +68,22 @@ public function process(ContainerBuilder $container)
return;
}
+ $paths = array_keys($container->getDefinition('twig.template_iterator')->getArgument(2));
if ($container->hasDefinition($this->debugCommandServiceId)) {
- $container->getDefinition($this->debugCommandServiceId)->replaceArgument(4, $container->getParameter('twig.default_path'));
- }
+ $definition = $container->getDefinition($this->debugCommandServiceId);
+ $definition->replaceArgument(4, $container->getParameter('twig.default_path'));
+ if (\count($definition->getArguments()) > 6) {
+ $definition->replaceArgument(6, $paths);
+ }
+ }
if ($container->hasDefinition($this->updateCommandServiceId)) {
- $container->getDefinition($this->updateCommandServiceId)->replaceArgument(5, $container->getParameter('twig.default_path'));
+ $definition = $container->getDefinition($this->updateCommandServiceId);
+ $definition->replaceArgument(5, $container->getParameter('twig.default_path'));
+
+ if (\count($definition->getArguments()) > 7) {
+ $definition->replaceArgument(7, $paths);
+ }
}
}
}
diff --git a/vendor/symfony/translation/DependencyInjection/TranslatorPathsPass.php b/vendor/symfony/translation/DependencyInjection/TranslatorPathsPass.php
new file mode 100644
index 0000000000..d9fc71911f
--- /dev/null
+++ b/vendor/symfony/translation/DependencyInjection/TranslatorPathsPass.php
@@ -0,0 +1,144 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\DependencyInjection;
+
+use Symfony\Component\DependencyInjection\Compiler\AbstractRecursivePass;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Definition;
+use Symfony\Component\DependencyInjection\Reference;
+use Symfony\Component\DependencyInjection\ServiceLocator;
+
+/**
+ * @author Yonel Ceruto
+ */
+class TranslatorPathsPass extends AbstractRecursivePass
+{
+ private $translatorServiceId;
+ private $debugCommandServiceId;
+ private $updateCommandServiceId;
+ private $resolverServiceId;
+ private $level = 0;
+ private $paths = [];
+ private $definitions = [];
+ private $controllers = [];
+
+ public function __construct(string $translatorServiceId = 'translator', string $debugCommandServiceId = 'console.command.translation_debug', string $updateCommandServiceId = 'console.command.translation_update', string $resolverServiceId = 'argument_resolver.service')
+ {
+ $this->translatorServiceId = $translatorServiceId;
+ $this->debugCommandServiceId = $debugCommandServiceId;
+ $this->updateCommandServiceId = $updateCommandServiceId;
+ $this->resolverServiceId = $resolverServiceId;
+ }
+
+ public function process(ContainerBuilder $container)
+ {
+ if (!$container->hasDefinition($this->translatorServiceId)) {
+ return;
+ }
+
+ foreach ($this->findControllerArguments($container) as $controller => $argument) {
+ $id = \substr($controller, 0, \strpos($controller, ':') ?: \strlen($controller));
+ if ($container->hasDefinition($id)) {
+ list($locatorRef) = $argument->getValues();
+ $this->controllers[(string) $locatorRef][$container->getDefinition($id)->getClass()] = true;
+ }
+ }
+
+ try {
+ parent::process($container);
+
+ $paths = [];
+ foreach ($this->paths as $class => $_) {
+ if (($r = $container->getReflectionClass($class)) && !$r->isInterface()) {
+ $paths[] = $r->getFileName();
+ }
+ }
+ if ($paths) {
+ if ($container->hasDefinition($this->debugCommandServiceId)) {
+ $definition = $container->getDefinition($this->debugCommandServiceId);
+ $definition->replaceArgument(6, array_merge($definition->getArgument(6), $paths));
+ }
+ if ($container->hasDefinition($this->updateCommandServiceId)) {
+ $definition = $container->getDefinition($this->updateCommandServiceId);
+ $definition->replaceArgument(7, array_merge($definition->getArgument(7), $paths));
+ }
+ }
+ } finally {
+ $this->level = 0;
+ $this->paths = [];
+ $this->definitions = [];
+ }
+ }
+
+ protected function processValue($value, $isRoot = false)
+ {
+ if ($value instanceof Reference) {
+ if ((string) $value === $this->translatorServiceId) {
+ for ($i = $this->level - 1; $i >= 0; --$i) {
+ $class = $this->definitions[$i]->getClass();
+
+ if (ServiceLocator::class === $class) {
+ if (!isset($this->controllers[$this->currentId])) {
+ continue;
+ }
+ foreach ($this->controllers[$this->currentId] as $class => $_) {
+ $this->paths[$class] = true;
+ }
+ } else {
+ $this->paths[$class] = true;
+ }
+
+ break;
+ }
+ }
+
+ return $value;
+ }
+
+ if ($value instanceof Definition) {
+ $this->definitions[$this->level++] = $value;
+ $value = parent::processValue($value, $isRoot);
+ unset($this->definitions[--$this->level]);
+
+ return $value;
+ }
+
+ return parent::processValue($value, $isRoot);
+ }
+
+ private function findControllerArguments(ContainerBuilder $container): array
+ {
+ if ($container->hasDefinition($this->resolverServiceId)) {
+ $argument = $container->getDefinition($this->resolverServiceId)->getArgument(0);
+ if ($argument instanceof Reference) {
+ $argument = $container->getDefinition($argument);
+ }
+
+ return $argument->getArgument(0);
+ }
+
+ if ($container->hasDefinition('debug.'.$this->resolverServiceId)) {
+ $argument = $container->getDefinition('debug.'.$this->resolverServiceId)->getArgument(0);
+ if ($argument instanceof Reference) {
+ $argument = $container->getDefinition($argument);
+ }
+ $argument = $argument->getArgument(0);
+ if ($argument instanceof Reference) {
+ $argument = $container->getDefinition($argument);
+ }
+
+ return $argument->getArgument(0);
+ }
+
+ return [];
+ }
+}
diff --git a/vendor/symfony/translation/Dumper/PoFileDumper.php b/vendor/symfony/translation/Dumper/PoFileDumper.php
index 0f7e6fa834..5f60086285 100644
--- a/vendor/symfony/translation/Dumper/PoFileDumper.php
+++ b/vendor/symfony/translation/Dumper/PoFileDumper.php
@@ -39,6 +39,18 @@ public function formatCatalogue(MessageCatalogue $messages, $domain, array $opti
} else {
$newLine = true;
}
+ $metadata = $messages->getMetadata($source, $domain);
+
+ if (isset($metadata['comments'])) {
+ $output .= $this->formatComments($metadata['comments']);
+ }
+ if (isset($metadata['flags'])) {
+ $output .= $this->formatComments(implode(',', (array) $metadata['flags']), ',');
+ }
+ if (isset($metadata['sources'])) {
+ $output .= $this->formatComments(implode(' ', (array) $metadata['sources']), ':');
+ }
+
$output .= sprintf('msgid "%s"'."\n", $this->escape($source));
$output .= sprintf('msgstr "%s"'."\n", $this->escape($target));
}
@@ -58,4 +70,15 @@ private function escape($str)
{
return addcslashes($str, "\0..\37\42\134");
}
+
+ private function formatComments($comments, string $prefix = ''): ?string
+ {
+ $output = null;
+
+ foreach ((array) $comments as $comment) {
+ $output .= sprintf('#%s %s'."\n", $prefix, $comment);
+ }
+
+ return $output;
+ }
}
diff --git a/vendor/symfony/translation/Dumper/QtFileDumper.php b/vendor/symfony/translation/Dumper/QtFileDumper.php
index ec93f92e4a..79a64b2430 100644
--- a/vendor/symfony/translation/Dumper/QtFileDumper.php
+++ b/vendor/symfony/translation/Dumper/QtFileDumper.php
@@ -33,6 +33,17 @@ public function formatCatalogue(MessageCatalogue $messages, $domain, array $opti
foreach ($messages->all($domain) as $source => $target) {
$message = $context->appendChild($dom->createElement('message'));
+ $metadata = $messages->getMetadata($source, $domain);
+ if (isset($metadata['sources'])) {
+ foreach ((array) $metadata['sources'] as $location) {
+ $loc = explode(':', $location, 2);
+ $location = $message->appendChild($dom->createElement('location'));
+ $location->setAttribute('filename', $loc[0]);
+ if (isset($loc[1])) {
+ $location->setAttribute('line', $loc[1]);
+ }
+ }
+ }
$message->appendChild($dom->createElement('source', $source));
$message->appendChild($dom->createElement('translation', $target));
}
diff --git a/vendor/symfony/translation/Extractor/PhpExtractor.php b/vendor/symfony/translation/Extractor/PhpExtractor.php
index 55ebfa1623..84fd7400f8 100644
--- a/vendor/symfony/translation/Extractor/PhpExtractor.php
+++ b/vendor/symfony/translation/Extractor/PhpExtractor.php
@@ -81,9 +81,8 @@ public function extract($resource, MessageCatalogue $catalog)
{
$files = $this->extractFiles($resource);
foreach ($files as $file) {
- $this->parseTokens(token_get_all(file_get_contents($file)), $catalog);
+ $this->parseTokens(token_get_all(file_get_contents($file)), $catalog, $file);
- // PHP 7 memory manager will not release after token_get_all(), see https://bugs.php.net/70098
gc_mem_caches();
}
}
@@ -198,9 +197,15 @@ private function getValue(\Iterator $tokenIterator)
*
* @param array $tokens
* @param MessageCatalogue $catalog
+ * @param string $filename
*/
- protected function parseTokens($tokens, MessageCatalogue $catalog)
+ protected function parseTokens($tokens, MessageCatalogue $catalog/*, string $filename*/)
{
+ if (\func_num_args() < 3 && __CLASS__ !== \get_class($this) && __CLASS__ !== (new \ReflectionMethod($this, __FUNCTION__))->getDeclaringClass()->getName() && !$this instanceof \PHPUnit\Framework\MockObject\MockObject && !$this instanceof \Prophecy\Prophecy\ProphecySubjectInterface) {
+ @trigger_error(sprintf('The "%s()" method will have a new "string $filename" argument in version 5.0, not defining it is deprecated since Symfony 4.3.', __METHOD__), E_USER_DEPRECATED);
+ }
+ $filename = 2 < \func_num_args() ? \func_get_arg(2) : '';
+
$tokenIterator = new \ArrayIterator($tokens);
for ($key = 0; $key < $tokenIterator->count(); ++$key) {
@@ -237,6 +242,10 @@ protected function parseTokens($tokens, MessageCatalogue $catalog)
if ($message) {
$catalog->set($message, $this->prefix.$message, $domain);
+ $metadata = $catalog->getMetadata($message, $domain) ?? [];
+ $normalizedFilename = preg_replace('{[\\\\/]+}', '/', $filename);
+ $metadata['sources'][] = $normalizedFilename.':'.$tokens[$key][2];
+ $catalog->setMetadata($message, $metadata, $domain);
break;
}
}
diff --git a/vendor/symfony/translation/Loader/XliffFileLoader.php b/vendor/symfony/translation/Loader/XliffFileLoader.php
index ed97cae290..6e01a7119b 100644
--- a/vendor/symfony/translation/Loader/XliffFileLoader.php
+++ b/vendor/symfony/translation/Loader/XliffFileLoader.php
@@ -82,38 +82,51 @@ private function extractXliff1(\DOMDocument $dom, MessageCatalogue $catalogue, s
$xml = simplexml_import_dom($dom);
$encoding = strtoupper($dom->encoding);
- $xml->registerXPathNamespace('xliff', 'urn:oasis:names:tc:xliff:document:1.2');
- foreach ($xml->xpath('//xliff:trans-unit') as $translation) {
- $attributes = $translation->attributes();
+ $namespace = 'urn:oasis:names:tc:xliff:document:1.2';
+ $xml->registerXPathNamespace('xliff', $namespace);
- if (!(isset($attributes['resname']) || isset($translation->source))) {
- continue;
- }
+ foreach ($xml->xpath('//xliff:file') as $file) {
+ $fileAttributes = $file->attributes();
- $source = isset($attributes['resname']) && $attributes['resname'] ? $attributes['resname'] : $translation->source;
- // If the xlf file has another encoding specified, try to convert it because
- // simple_xml will always return utf-8 encoded values
- $target = $this->utf8ToCharset((string) (isset($translation->target) ? $translation->target : $translation->source), $encoding);
+ $file->registerXPathNamespace('xliff', $namespace);
- $catalogue->set((string) $source, $target, $domain);
+ foreach ($file->xpath('.//xliff:trans-unit') as $translation) {
+ $attributes = $translation->attributes();
- $metadata = [];
- if ($notes = $this->parseNotesMetadata($translation->note, $encoding)) {
- $metadata['notes'] = $notes;
- }
+ if (!(isset($attributes['resname']) || isset($translation->source))) {
+ continue;
+ }
- if (isset($translation->target) && $translation->target->attributes()) {
- $metadata['target-attributes'] = [];
- foreach ($translation->target->attributes() as $key => $value) {
- $metadata['target-attributes'][$key] = (string) $value;
+ $source = isset($attributes['resname']) && $attributes['resname'] ? $attributes['resname'] : $translation->source;
+ // If the xlf file has another encoding specified, try to convert it because
+ // simple_xml will always return utf-8 encoded values
+ $target = $this->utf8ToCharset((string) ($translation->target ?? $translation->source), $encoding);
+
+ $catalogue->set((string) $source, $target, $domain);
+
+ $metadata = [
+ 'source' => (string) $translation->source,
+ 'file' => [
+ 'original' => (string) $fileAttributes['original'],
+ ],
+ ];
+ if ($notes = $this->parseNotesMetadata($translation->note, $encoding)) {
+ $metadata['notes'] = $notes;
}
- }
- if (isset($attributes['id'])) {
- $metadata['id'] = (string) $attributes['id'];
- }
+ if (isset($translation->target) && $translation->target->attributes()) {
+ $metadata['target-attributes'] = [];
+ foreach ($translation->target->attributes() as $key => $value) {
+ $metadata['target-attributes'][$key] = (string) $value;
+ }
+ }
- $catalogue->setMetadata((string) $source, $metadata, $domain);
+ if (isset($attributes['id'])) {
+ $metadata['id'] = (string) $attributes['id'];
+ }
+
+ $catalogue->setMetadata((string) $source, $metadata, $domain);
+ }
}
}
diff --git a/vendor/symfony/translation/LoggingTranslator.php b/vendor/symfony/translation/LoggingTranslator.php
index d6b711d27e..3a84bf1170 100644
--- a/vendor/symfony/translation/LoggingTranslator.php
+++ b/vendor/symfony/translation/LoggingTranslator.php
@@ -82,7 +82,9 @@ public function transChoice($id, $number, array $parameters = [], $domain = null
*/
public function setLocale($locale)
{
+ $prev = $this->translator->getLocale();
$this->translator->setLocale($locale);
+ $this->logger->debug(sprintf('The locale of the translator has changed from "%s" to "%s".', $prev, $locale));
}
/**
diff --git a/vendor/symfony/translation/Tests/Catalogue/AbstractOperationTest.php b/vendor/symfony/translation/Tests/Catalogue/AbstractOperationTest.php
index e39ef39ec5..f82b18fdd7 100644
--- a/vendor/symfony/translation/Tests/Catalogue/AbstractOperationTest.php
+++ b/vendor/symfony/translation/Tests/Catalogue/AbstractOperationTest.php
@@ -41,7 +41,7 @@ public function testGetMergedDomains()
public function testGetMessagesFromUnknownDomain()
{
- $this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('InvalidArgumentException');
+ $this->expectException('InvalidArgumentException');
$this->createOperation(
new MessageCatalogue('en'),
new MessageCatalogue('en')
diff --git a/vendor/symfony/translation/Tests/Command/XliffLintCommandTest.php b/vendor/symfony/translation/Tests/Command/XliffLintCommandTest.php
index 516d98af53..df2e2f0951 100644
--- a/vendor/symfony/translation/Tests/Command/XliffLintCommandTest.php
+++ b/vendor/symfony/translation/Tests/Command/XliffLintCommandTest.php
@@ -94,6 +94,17 @@ public function testLintIncorrectTargetLanguage()
$this->assertContains('There is a mismatch between the language included in the file name ("messages.en.xlf") and the "es" value used in the "target-language" attribute of the file.', trim($tester->getDisplay()));
}
+ public function testLintTargetLanguageIsCaseInsensitive()
+ {
+ $tester = $this->createCommandTester();
+ $filename = $this->createFile('note', 'zh-cn', 'messages.zh_CN.xlf');
+
+ $tester->execute(['filename' => $filename], ['decorated' => false]);
+
+ $this->assertEquals(0, $tester->getStatusCode());
+ $this->assertContains('[OK] All 1 XLIFF files contain valid syntax.', trim($tester->getDisplay()));
+ }
+
/**
* @expectedException \RuntimeException
*/
diff --git a/vendor/symfony/translation/Tests/DataCollector/TranslationDataCollectorTest.php b/vendor/symfony/translation/Tests/DataCollector/TranslationDataCollectorTest.php
index b4d350ef86..bd97a2445c 100644
--- a/vendor/symfony/translation/Tests/DataCollector/TranslationDataCollectorTest.php
+++ b/vendor/symfony/translation/Tests/DataCollector/TranslationDataCollectorTest.php
@@ -27,7 +27,7 @@ protected function setUp()
public function testCollectEmptyMessages()
{
$translator = $this->getTranslator();
- $translator->expects($this->any())->method('getCollectedMessages')->will($this->returnValue([]));
+ $translator->expects($this->any())->method('getCollectedMessages')->willReturn([]);
$dataCollector = new TranslationDataCollector($translator);
$dataCollector->lateCollect();
@@ -125,7 +125,7 @@ public function testCollect()
];
$translator = $this->getTranslator();
- $translator->expects($this->any())->method('getCollectedMessages')->will($this->returnValue($collectedMessages));
+ $translator->expects($this->any())->method('getCollectedMessages')->willReturn($collectedMessages);
$dataCollector = new TranslationDataCollector($translator);
$dataCollector->lateCollect();
diff --git a/vendor/symfony/translation/Tests/DependencyInjection/TranslationPassTest.php b/vendor/symfony/translation/Tests/DependencyInjection/TranslationPassTest.php
index 8b9c03d991..f62fc85ebc 100644
--- a/vendor/symfony/translation/Tests/DependencyInjection/TranslationPassTest.php
+++ b/vendor/symfony/translation/Tests/DependencyInjection/TranslationPassTest.php
@@ -54,4 +54,69 @@ public function testValidCollector()
$expected = ['translation.xliff_loader' => new ServiceClosureArgument(new Reference('translation.xliff_loader'))];
$this->assertEquals($expected, $container->getDefinition((string) $translator->getArgument(0))->getArgument(0));
}
+
+ public function testValidCommandsViewPathsArgument()
+ {
+ $container = new ContainerBuilder();
+ $container->register('translator.default')
+ ->setArguments([null, null, null, null])
+ ;
+ $debugCommand = $container->register('console.command.translation_debug')
+ ->setArguments([null, null, null, null, null, [], []])
+ ;
+ $updateCommand = $container->register('console.command.translation_update')
+ ->setArguments([null, null, null, null, null, null, [], []])
+ ;
+ $container->register('twig.template_iterator')
+ ->setArguments([null, null, ['other/templates' => null, 'tpl' => 'App']])
+ ;
+ $container->setParameter('twig.default_path', 'templates');
+
+ $pass = new TranslatorPass('translator.default');
+ $pass->process($container);
+
+ $expectedViewPaths = ['other/templates', 'tpl'];
+
+ $this->assertSame('templates', $debugCommand->getArgument(4));
+ $this->assertSame('templates', $updateCommand->getArgument(5));
+ $this->assertSame($expectedViewPaths, $debugCommand->getArgument(6));
+ $this->assertSame($expectedViewPaths, $updateCommand->getArgument(7));
+ }
+
+ public function testCommandsViewPathsArgumentsAreIgnoredWithOldServiceDefinitions()
+ {
+ $container = new ContainerBuilder();
+ $container->register('translator.default')
+ ->setArguments([null, null, null, null])
+ ;
+ $debugCommand = $container->register('console.command.translation_debug')
+ ->setArguments([
+ new Reference('translator'),
+ new Reference('translation.reader'),
+ new Reference('translation.extractor'),
+ '%translator.default_path%',
+ null,
+ ])
+ ;
+ $updateCommand = $container->register('console.command.translation_update')
+ ->setArguments([
+ new Reference('translation.writer'),
+ new Reference('translation.reader'),
+ new Reference('translation.extractor'),
+ '%kernel.default_locale%',
+ '%translator.default_path%',
+ null,
+ ])
+ ;
+ $container->register('twig.template_iterator')
+ ->setArguments([null, null, ['other/templates' => null, 'tpl' => 'App']])
+ ;
+ $container->setParameter('twig.default_path', 'templates');
+
+ $pass = new TranslatorPass('translator.default');
+ $pass->process($container);
+
+ $this->assertSame('templates', $debugCommand->getArgument(4));
+ $this->assertSame('templates', $updateCommand->getArgument(5));
+ }
}
diff --git a/vendor/symfony/translation/Tests/DependencyInjection/TranslationPathsPassTest.php b/vendor/symfony/translation/Tests/DependencyInjection/TranslationPathsPassTest.php
new file mode 100644
index 0000000000..42ab398dff
--- /dev/null
+++ b/vendor/symfony/translation/Tests/DependencyInjection/TranslationPathsPassTest.php
@@ -0,0 +1,89 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Tests\DependencyInjection;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Definition;
+use Symfony\Component\DependencyInjection\Reference;
+use Symfony\Component\DependencyInjection\ServiceLocator;
+use Symfony\Component\Translation\DependencyInjection\TranslatorPathsPass;
+use Symfony\Component\Translation\Tests\DependencyInjection\fixtures\ControllerArguments;
+use Symfony\Component\Translation\Tests\DependencyInjection\fixtures\ServiceArguments;
+use Symfony\Component\Translation\Tests\DependencyInjection\fixtures\ServiceMethodCalls;
+use Symfony\Component\Translation\Tests\DependencyInjection\fixtures\ServiceProperties;
+use Symfony\Component\Translation\Tests\DependencyInjection\fixtures\ServiceSubscriber;
+
+class TranslationPathsPassTest extends TestCase
+{
+ public function testProcess()
+ {
+ $container = new ContainerBuilder();
+ $container->register('translator');
+ $debugCommand = $container->register('console.command.translation_debug')
+ ->setArguments([null, null, null, null, null, [], []])
+ ;
+ $updateCommand = $container->register('console.command.translation_update')
+ ->setArguments([null, null, null, null, null, null, [], []])
+ ;
+ $container->register(ControllerArguments::class, ControllerArguments::class)
+ ->setTags(['controller.service_arguments'])
+ ;
+ $container->register(ServiceArguments::class, ServiceArguments::class)
+ ->setArguments([new Reference('translator')])
+ ;
+ $container->register(ServiceProperties::class, ServiceProperties::class)
+ ->setProperties([new Reference('translator')])
+ ;
+ $container->register(ServiceMethodCalls::class, ServiceMethodCalls::class)
+ ->setMethodCalls([['setTranslator', [new Reference('translator')]]])
+ ;
+ $container->register('service_rc')
+ ->setArguments([new Definition(), new Reference(ServiceMethodCalls::class)])
+ ;
+ $serviceLocator1 = $container->register('.service_locator.foo', ServiceLocator::class)
+ ->setArguments([new ServiceClosureArgument(new Reference('translator'))])
+ ;
+ $serviceLocator2 = (new Definition(ServiceLocator::class))
+ ->setArguments([ServiceSubscriber::class, new Reference('service_container')])
+ ->setFactory([$serviceLocator1, 'withContext'])
+ ;
+ $container->register('service_subscriber', ServiceSubscriber::class)
+ ->setArguments([$serviceLocator2])
+ ;
+ $container->register('.service_locator.bar', ServiceLocator::class)
+ ->setArguments([[
+ ControllerArguments::class.'::index' => new ServiceClosureArgument(new Reference('.service_locator.foo')),
+ ControllerArguments::class.'::__invoke' => new ServiceClosureArgument(new Reference('.service_locator.foo')),
+ ControllerArguments::class => new ServiceClosureArgument(new Reference('.service_locator.foo')),
+ ]])
+ ;
+ $container->register('argument_resolver.service')
+ ->setArguments([new Reference('.service_locator.bar')])
+ ;
+
+ $pass = new TranslatorPathsPass('translator', 'console.command.translation_debug', 'console.command.translation_update', 'argument_resolver.service');
+ $pass->process($container);
+
+ $expectedPaths = [
+ $container->getReflectionClass(ServiceArguments::class)->getFileName(),
+ $container->getReflectionClass(ServiceProperties::class)->getFileName(),
+ $container->getReflectionClass(ServiceMethodCalls::class)->getFileName(),
+ $container->getReflectionClass(ControllerArguments::class)->getFileName(),
+ $container->getReflectionClass(ServiceSubscriber::class)->getFileName(),
+ ];
+
+ $this->assertSame($expectedPaths, $debugCommand->getArgument(6));
+ $this->assertSame($expectedPaths, $updateCommand->getArgument(7));
+ }
+}
diff --git a/vendor/symfony/translation/Tests/DependencyInjection/fixtures/ControllerArguments.php b/vendor/symfony/translation/Tests/DependencyInjection/fixtures/ControllerArguments.php
new file mode 100644
index 0000000000..97a53fa76b
--- /dev/null
+++ b/vendor/symfony/translation/Tests/DependencyInjection/fixtures/ControllerArguments.php
@@ -0,0 +1,16 @@
+ TranslatorInterface::class];
+ }
+}
diff --git a/vendor/symfony/translation/Tests/Dumper/PoFileDumperTest.php b/vendor/symfony/translation/Tests/Dumper/PoFileDumperTest.php
index 960ec2df65..46df869f89 100644
--- a/vendor/symfony/translation/Tests/Dumper/PoFileDumperTest.php
+++ b/vendor/symfony/translation/Tests/Dumper/PoFileDumperTest.php
@@ -20,7 +20,26 @@ class PoFileDumperTest extends TestCase
public function testFormatCatalogue()
{
$catalogue = new MessageCatalogue('en');
- $catalogue->add(['foo' => 'bar', 'bar' => 'foo']);
+ $catalogue->add(['foo' => 'bar', 'bar' => 'foo', 'foo_bar' => 'foobar', 'bar_foo' => 'barfoo']);
+ $catalogue->setMetadata('foo_bar', [
+ 'comments' => [
+ 'Comment 1',
+ 'Comment 2',
+ ],
+ 'flags' => [
+ 'fuzzy',
+ 'another',
+ ],
+ 'sources' => [
+ 'src/file_1',
+ 'src/file_2:50',
+ ],
+ ]);
+ $catalogue->setMetadata('bar_foo', [
+ 'comments' => 'Comment',
+ 'flags' => 'fuzzy',
+ 'sources' => 'src/file_1',
+ ]);
$dumper = new PoFileDumper();
diff --git a/vendor/symfony/translation/Tests/Dumper/QtFileDumperTest.php b/vendor/symfony/translation/Tests/Dumper/QtFileDumperTest.php
index edfad6005c..6c4b559278 100644
--- a/vendor/symfony/translation/Tests/Dumper/QtFileDumperTest.php
+++ b/vendor/symfony/translation/Tests/Dumper/QtFileDumperTest.php
@@ -20,7 +20,26 @@ class QtFileDumperTest extends TestCase
public function testFormatCatalogue()
{
$catalogue = new MessageCatalogue('en');
- $catalogue->add(['foo' => 'bar'], 'resources');
+ $catalogue->add(['foo' => 'bar', 'foo_bar' => 'foobar', 'bar_foo' => 'barfoo'], 'resources');
+ $catalogue->setMetadata('foo_bar', [
+ 'comments' => [
+ 'Comment 1',
+ 'Comment 2',
+ ],
+ 'flags' => [
+ 'fuzzy',
+ 'another',
+ ],
+ 'sources' => [
+ 'src/file_1',
+ 'src/file_2:50',
+ ],
+ ], 'resources');
+ $catalogue->setMetadata('bar_foo', [
+ 'comments' => 'Comment',
+ 'flags' => 'fuzzy',
+ 'sources' => 'src/file_1',
+ ], 'resources');
$dumper = new QtFileDumper();
diff --git a/vendor/symfony/translation/Tests/Extractor/PhpExtractorTest.php b/vendor/symfony/translation/Tests/Extractor/PhpExtractorTest.php
index 7cde108080..a6d7c5001c 100644
--- a/vendor/symfony/translation/Tests/Extractor/PhpExtractorTest.php
+++ b/vendor/symfony/translation/Tests/Extractor/PhpExtractorTest.php
@@ -69,6 +69,10 @@ public function testExtraction($resource)
$actualCatalogue = $catalogue->all();
$this->assertEquals($expectedCatalogue, $actualCatalogue);
+
+ $filename = str_replace(\DIRECTORY_SEPARATOR, '/', __DIR__).'/../fixtures/extractor/translation.html.php';
+ $this->assertEquals(['sources' => [$filename.':2']], $catalogue->getMetadata('single-quoted key'));
+ $this->assertEquals(['sources' => [$filename.':43']], $catalogue->getMetadata('other-domain-test-no-params-short-array', 'not_messages'));
}
public function resourcesProvider()
diff --git a/vendor/symfony/translation/Tests/IdentityTranslatorTest.php b/vendor/symfony/translation/Tests/IdentityTranslatorTest.php
index be0a548aa1..cf618d95a2 100644
--- a/vendor/symfony/translation/Tests/IdentityTranslatorTest.php
+++ b/vendor/symfony/translation/Tests/IdentityTranslatorTest.php
@@ -12,7 +12,7 @@
namespace Symfony\Component\Translation\Tests;
use Symfony\Component\Translation\IdentityTranslator;
-use Symfony\Contracts\Tests\Translation\TranslatorTest;
+use Symfony\Contracts\Translation\Test\TranslatorTest;
class IdentityTranslatorTest extends TranslatorTest
{
diff --git a/vendor/symfony/translation/Tests/Loader/QtFileLoaderTest.php b/vendor/symfony/translation/Tests/Loader/QtFileLoaderTest.php
index 08f55e9022..f149b8c715 100644
--- a/vendor/symfony/translation/Tests/Loader/QtFileLoaderTest.php
+++ b/vendor/symfony/translation/Tests/Loader/QtFileLoaderTest.php
@@ -23,7 +23,11 @@ public function testLoad()
$resource = __DIR__.'/../fixtures/resources.ts';
$catalogue = $loader->load($resource, 'en', 'resources');
- $this->assertEquals(['foo' => 'bar'], $catalogue->all('resources'));
+ $this->assertEquals([
+ 'foo' => 'bar',
+ 'foo_bar' => 'foobar',
+ 'bar_foo' => 'barfoo',
+ ], $catalogue->all('resources'));
$this->assertEquals('en', $catalogue->getLocale());
$this->assertEquals([new FileResource($resource)], $catalogue->getResources());
}
diff --git a/vendor/symfony/translation/Tests/Loader/XliffFileLoaderTest.php b/vendor/symfony/translation/Tests/Loader/XliffFileLoaderTest.php
index 7cb9f54fde..1ca8336d52 100644
--- a/vendor/symfony/translation/Tests/Loader/XliffFileLoaderTest.php
+++ b/vendor/symfony/translation/Tests/Loader/XliffFileLoaderTest.php
@@ -84,7 +84,17 @@ public function testEncoding()
$this->assertEquals(utf8_decode('föö'), $catalogue->get('bar', 'domain1'));
$this->assertEquals(utf8_decode('bär'), $catalogue->get('foo', 'domain1'));
- $this->assertEquals(['notes' => [['content' => utf8_decode('bäz')]], 'id' => '1'], $catalogue->getMetadata('foo', 'domain1'));
+ $this->assertEquals(
+ [
+ 'source' => 'foo',
+ 'notes' => [['content' => utf8_decode('bäz')]],
+ 'id' => '1',
+ 'file' => [
+ 'original' => 'file.ext',
+ ],
+ ],
+ $catalogue->getMetadata('foo', 'domain1')
+ );
}
public function testTargetAttributesAreStoredCorrectly()
@@ -164,11 +174,44 @@ public function testLoadNotes()
$loader = new XliffFileLoader();
$catalogue = $loader->load(__DIR__.'/../fixtures/withnote.xlf', 'en', 'domain1');
- $this->assertEquals(['notes' => [['priority' => 1, 'content' => 'foo']], 'id' => '1'], $catalogue->getMetadata('foo', 'domain1'));
+ $this->assertEquals(
+ [
+ 'source' => 'foo',
+ 'notes' => [['priority' => 1, 'content' => 'foo']],
+ 'id' => '1',
+ 'file' => [
+ 'original' => 'file.ext',
+ ],
+ ],
+ $catalogue->getMetadata('foo', 'domain1')
+ );
// message without target
- $this->assertEquals(['notes' => [['content' => 'bar', 'from' => 'foo']], 'id' => '2'], $catalogue->getMetadata('extra', 'domain1'));
+ $this->assertEquals(
+ [
+ 'source' => 'extrasource',
+ 'notes' => [['content' => 'bar', 'from' => 'foo']],
+ 'id' => '2',
+ 'file' => [
+ 'original' => 'file.ext',
+ ],
+ ],
+ $catalogue->getMetadata('extra', 'domain1')
+ );
// message with empty target
- $this->assertEquals(['notes' => [['content' => 'baz'], ['priority' => 2, 'from' => 'bar', 'content' => 'qux']], 'id' => '123'], $catalogue->getMetadata('key', 'domain1'));
+ $this->assertEquals(
+ [
+ 'source' => 'key',
+ 'notes' => [
+ ['content' => 'baz'],
+ ['priority' => 2, 'from' => 'bar', 'content' => 'qux'],
+ ],
+ 'id' => '123',
+ 'file' => [
+ 'original' => 'file.ext',
+ ],
+ ],
+ $catalogue->getMetadata('key', 'domain1')
+ );
}
public function testLoadVersion2()
@@ -257,4 +300,32 @@ public function testLoadVersion2WithMultiSegmentUnit()
$this->assertSame('processed', $metadata['notes'][0]['category']);
$this->assertSame('true', $metadata['notes'][0]['content']);
}
+
+ public function testLoadWithMultipleFileNodes()
+ {
+ $loader = new XliffFileLoader();
+ $catalogue = $loader->load(__DIR__.'/../fixtures/resources-multi-files.xlf', 'en', 'domain1');
+
+ $this->assertEquals(
+ [
+ 'source' => 'foo',
+ 'id' => '1',
+ 'file' => [
+ 'original' => 'file.ext',
+ ],
+ ],
+ $catalogue->getMetadata('foo', 'domain1')
+ );
+ $this->assertEquals(
+ [
+ 'source' => 'test',
+ 'notes' => [['content' => 'note']],
+ 'id' => '4',
+ 'file' => [
+ 'original' => 'otherfile.ext',
+ ],
+ ],
+ $catalogue->getMetadata('test', 'domain1')
+ );
+ }
}
diff --git a/vendor/symfony/translation/Tests/MessageCatalogueTest.php b/vendor/symfony/translation/Tests/MessageCatalogueTest.php
index 6fe9368f5c..cf0dd1a24c 100644
--- a/vendor/symfony/translation/Tests/MessageCatalogueTest.php
+++ b/vendor/symfony/translation/Tests/MessageCatalogueTest.php
@@ -103,10 +103,10 @@ public function testReplace()
public function testAddCatalogue()
{
$r = $this->getMockBuilder('Symfony\Component\Config\Resource\ResourceInterface')->getMock();
- $r->expects($this->any())->method('__toString')->will($this->returnValue('r'));
+ $r->expects($this->any())->method('__toString')->willReturn('r');
$r1 = $this->getMockBuilder('Symfony\Component\Config\Resource\ResourceInterface')->getMock();
- $r1->expects($this->any())->method('__toString')->will($this->returnValue('r1'));
+ $r1->expects($this->any())->method('__toString')->willReturn('r1');
$catalogue = new MessageCatalogue('en', ['domain1' => ['foo' => 'foo']]);
$catalogue->addResource($r);
@@ -127,13 +127,13 @@ public function testAddCatalogue()
public function testAddFallbackCatalogue()
{
$r = $this->getMockBuilder('Symfony\Component\Config\Resource\ResourceInterface')->getMock();
- $r->expects($this->any())->method('__toString')->will($this->returnValue('r'));
+ $r->expects($this->any())->method('__toString')->willReturn('r');
$r1 = $this->getMockBuilder('Symfony\Component\Config\Resource\ResourceInterface')->getMock();
- $r1->expects($this->any())->method('__toString')->will($this->returnValue('r1'));
+ $r1->expects($this->any())->method('__toString')->willReturn('r1');
$r2 = $this->getMockBuilder('Symfony\Component\Config\Resource\ResourceInterface')->getMock();
- $r2->expects($this->any())->method('__toString')->will($this->returnValue('r2'));
+ $r2->expects($this->any())->method('__toString')->willReturn('r2');
$catalogue = new MessageCatalogue('fr_FR', ['domain1' => ['foo' => 'foo'], 'domain2' => ['bar' => 'bar']]);
$catalogue->addResource($r);
@@ -192,11 +192,11 @@ public function testGetAddResource()
{
$catalogue = new MessageCatalogue('en');
$r = $this->getMockBuilder('Symfony\Component\Config\Resource\ResourceInterface')->getMock();
- $r->expects($this->any())->method('__toString')->will($this->returnValue('r'));
+ $r->expects($this->any())->method('__toString')->willReturn('r');
$catalogue->addResource($r);
$catalogue->addResource($r);
$r1 = $this->getMockBuilder('Symfony\Component\Config\Resource\ResourceInterface')->getMock();
- $r1->expects($this->any())->method('__toString')->will($this->returnValue('r1'));
+ $r1->expects($this->any())->method('__toString')->willReturn('r1');
$catalogue->addResource($r1);
$this->assertEquals([$r, $r1], $catalogue->getResources());
diff --git a/vendor/symfony/translation/Tests/TranslatorCacheTest.php b/vendor/symfony/translation/Tests/TranslatorCacheTest.php
index ec909aaa38..5d437ff76c 100644
--- a/vendor/symfony/translation/Tests/TranslatorCacheTest.php
+++ b/vendor/symfony/translation/Tests/TranslatorCacheTest.php
@@ -104,7 +104,7 @@ public function testCatalogueIsReloadedWhenResourcesAreNoLongerFresh()
$loader
->expects($this->exactly(2))
->method('load')
- ->will($this->returnValue($catalogue))
+ ->willReturn($catalogue)
;
// 1st pass
@@ -249,11 +249,11 @@ public function testRefreshCacheWhenResourcesAreNoLongerFresh()
{
$resource = $this->getMockBuilder('Symfony\Component\Config\Resource\SelfCheckingResourceInterface')->getMock();
$loader = $this->getMockBuilder('Symfony\Component\Translation\Loader\LoaderInterface')->getMock();
- $resource->method('isFresh')->will($this->returnValue(false));
+ $resource->method('isFresh')->willReturn(false);
$loader
->expects($this->exactly(2))
->method('load')
- ->will($this->returnValue($this->getCatalogue('fr', [], [$resource])));
+ ->willReturn($this->getCatalogue('fr', [], [$resource]));
// prime the cache
$translator = new Translator('fr', null, $this->tmpDir, true);
diff --git a/vendor/symfony/translation/Tests/fixtures/resources-multi-files.xlf b/vendor/symfony/translation/Tests/fixtures/resources-multi-files.xlf
new file mode 100644
index 0000000000..5f451508bc
--- /dev/null
+++ b/vendor/symfony/translation/Tests/fixtures/resources-multi-files.xlf
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+ bar
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ with
+ note
+
+
+
+
diff --git a/vendor/symfony/translation/Tests/fixtures/resources.po b/vendor/symfony/translation/Tests/fixtures/resources.po
index a20e619828..68e0f2d7e0 100644
--- a/vendor/symfony/translation/Tests/fixtures/resources.po
+++ b/vendor/symfony/translation/Tests/fixtures/resources.po
@@ -9,3 +9,16 @@ msgstr "bar"
msgid "bar"
msgstr "foo"
+
+# Comment 1
+# Comment 2
+#, fuzzy,another
+#: src/file_1 src/file_2:50
+msgid "foo_bar"
+msgstr "foobar"
+
+# Comment
+#, fuzzy
+#: src/file_1
+msgid "bar_foo"
+msgstr "barfoo"
diff --git a/vendor/symfony/translation/Tests/fixtures/resources.ts b/vendor/symfony/translation/Tests/fixtures/resources.ts
index 40e18522c8..29e6a6fadf 100644
--- a/vendor/symfony/translation/Tests/fixtures/resources.ts
+++ b/vendor/symfony/translation/Tests/fixtures/resources.ts
@@ -6,5 +6,16 @@
bar
+
+
+
+
+ foobar
+
+
+
+
+ barfoo
+
diff --git a/vendor/symfony/translation/Tests/fixtures/withnote.xlf b/vendor/symfony/translation/Tests/fixtures/withnote.xlf
index c045e21e23..f98cf7f97b 100644
--- a/vendor/symfony/translation/Tests/fixtures/withnote.xlf
+++ b/vendor/symfony/translation/Tests/fixtures/withnote.xlf
@@ -7,8 +7,8 @@
barfoo
-
-
+
+
bar
diff --git a/vendor/symfony/translation/Translator.php b/vendor/symfony/translation/Translator.php
index 8a2b2dd9d0..f5ce39ef0d 100644
--- a/vendor/symfony/translation/Translator.php
+++ b/vendor/symfony/translation/Translator.php
@@ -395,7 +395,10 @@ private function getCatalogueCachePath($locale)
return $this->cacheDir.'/catalogue.'.$locale.'.'.strtr(substr(base64_encode(hash('sha256', serialize($this->fallbackLocales), true)), 0, 7), '/', '_').'.php';
}
- private function doLoadCatalogue($locale): void
+ /**
+ * @internal
+ */
+ protected function doLoadCatalogue($locale): void
{
$this->catalogues[$locale] = new MessageCatalogue($locale);
diff --git a/vendor/symfony/translation/composer.json b/vendor/symfony/translation/composer.json
index 809625b5ad..fd25a4cf04 100644
--- a/vendor/symfony/translation/composer.json
+++ b/vendor/symfony/translation/composer.json
@@ -17,8 +17,8 @@
],
"require": {
"php": "^7.1.3",
- "symfony/contracts": "^1.0.2",
- "symfony/polyfill-mbstring": "~1.0"
+ "symfony/polyfill-mbstring": "~1.0",
+ "symfony/translation-contracts": "^1.1.2"
},
"require-dev": {
"symfony/config": "~3.4|~4.0",
@@ -26,6 +26,7 @@
"symfony/dependency-injection": "~3.4|~4.0",
"symfony/http-kernel": "~3.4|~4.0",
"symfony/intl": "~3.4|~4.0",
+ "symfony/service-contracts": "^1.1.2",
"symfony/var-dumper": "~3.4|~4.0",
"symfony/yaml": "~3.4|~4.0",
"symfony/finder": "~2.8|~3.0|~4.0",
@@ -37,7 +38,7 @@
"symfony/yaml": "<3.4"
},
"provide": {
- "symfony/translation-contracts-implementation": "1.0"
+ "symfony/translation-implementation": "1.0"
},
"suggest": {
"symfony/config": "",
@@ -53,7 +54,7 @@
"minimum-stability": "dev",
"extra": {
"branch-alias": {
- "dev-master": "4.2-dev"
+ "dev-master": "4.3-dev"
}
}
}
diff --git a/vendor/symfony/validator/Resources/translations/validators.be.xlf b/vendor/symfony/validator/Resources/translations/validators.be.xlf
new file mode 100644
index 0000000000..ab3845ee20
--- /dev/null
+++ b/vendor/symfony/validator/Resources/translations/validators.be.xlf
@@ -0,0 +1,367 @@
+
+
+
+
+
+
+ Значэнне павінна быць Не.
+
+
+
+ Значэнне павінна быць Так.
+
+
+
+ Тып значэння павінен быць {{ type }}.
+
+
+
+ Значэнне павінна быць пустым.
+
+
+
+ Абранае вамі значэнне не сапраўднае.
+
+
+
+ Вы павінны выбраць хаця б {{ limit }} варыянт.|Вы павінны выбраць хаця б {{ limit }} варыянтаў.
+
+
+
+ Вы павінны выбраць не больш за {{ limit }} варыянт.|Вы павінны выбраць не больш за {{ limit }} варыянтаў.
+
+
+
+ Адзін або некалькі пазначаных значэнняў з'яўляецца несапраўдным.
+
+
+
+ Гэта поле не чакаецца.
+
+
+
+ Гэта поле адсутнічае.
+
+
+
+ Гэта значэнне не з'яўляецца карэктнай датай.
+
+
+
+ Гэта значэнне не з'яўляецца карэктнай датай i часом.
+
+
+
+ Гэта значэнне не з'яўляецца карэктным адрасам электроннай пошты.
+
+
+
+ Файл не знойдзен.
+
+
+
+ Файл не чытаецца.
+
+
+
+ Файл занадта вялікі ({{ size }} {{ suffix }}). Максімальна дазволены памер {{ limit }} {{ suffix }}.
+
+
+
+ MIME-тып файлу некарэкты ({{ type }}). Дазволеныя MIME-тыпы файлу {{ types }}.
+
+
+
+ Значэнне павінна быць {{ limit }} або менш.
+
+
+
+ Значэнне занадта доўгае. Яно павінна мець {{ limit }} сімвал або менш.|Значэнне занадта доўгае. Яно павінна мець {{ limit }} сімвалаў або менш.
+
+
+
+ Значэнне павінна быць {{ limit }} або больш.
+
+
+
+ Значэнне занадта кароткае. Яно павінна мець прынамсі {{ limit }} сімвал.|Значэнне занадта кароткае. Яно павінна мець прынамсі {{ limit }} сімвалаў.
+
+
+
+ Значэнне не павінна быць пустым.
+
+
+
+ Значэнне не павінна быць null.
+
+
+
+ Значэнне павінна быць null.
+
+
+
+ Значэнне з'яўляецца не сапраўдным.
+
+
+
+ Значэнне не з'яўляецца сапраўдным часам.
+
+
+
+ Значэнне не з'яўляецца сапраўдным URL-адрасам.
+
+
+
+ Абодва значэнні павінны быць аднолькавымі.
+
+
+
+ Файл занадта вялікі. Максімальна дазволены памер {{ limit }} {{ suffix }}.
+
+
+
+ Файл занадта вялікі.
+
+
+
+ Немагчыма запампаваць файл.
+
+
+
+ Значэнне павінна быць лікам.
+
+
+
+ Гэты файл не з'яўляецца сапраўднай выявай.
+
+
+
+ Значэнне не з'яўляецца сапраўдным IP-адрасам.
+
+
+
+ Значэнне не з'яўляецца сапраўдным мовай.
+
+
+
+ Значэнне не з'яўляецца сапраўднай лакаллю.
+
+
+
+ Значэнне не з'яўляецца сапраўднай краінай.
+
+
+
+ Гэта значэнне ўжо выкарыстоўваецца.
+
+
+
+ Немагчыма вызначыць памер выявы.
+
+
+
+ Гэта выява занадта вялікая ({{ width }}px). Дазваляецца максімальная шырыня {{ max_width }}px.
+
+
+
+ Гэта выява занадта маленькая ({{ width }}px). Дазваляецца мінімальная шырыня {{ min_width }}px.
+
+
+
+ Гэты выява занадта вялікая ({{ width }}px). Дазваляецца максімальная вышыня {{ max_width }}px.
+
+
+
+ Гэта выява занадта маленькая ({{ width }}px). Дазваляецца мінімальная вышыня {{ min_width }}px.
+
+
+
+ Значэнне павінна быць цяперашнім паролем карыстальніка.
+
+
+
+ Значэнне павінна мець {{ limit }} сімвал.|Значэнне павінна мець {{ limit }} сімвалаў.
+
+
+
+ Файл быў запампаваны толькі часткова.
+
+
+
+ Файл не быў запампаваны.
+
+
+
+ У php.ini не была налажана часовая папка, або часовая папка не існуе.
+
+
+
+ Немагчыма запісаць часовы файл на дыск.
+
+
+
+ Пашырэнне PHP выклікала памылку загрузкі.
+
+
+
+ Калекцыя павінна змяшчаць прынамсі {{ limit }} элемент.|Калекцыя павінна змяшчаць прынамсі {{ limit }} элементаў.
+
+
+
+ Калекцыя павінна змяшчаць {{ limit }} або менш элемент.|Калекцыя павінна змяшчаць {{ limit }} або менш элементаў.
+
+
+
+ Калекцыя павінна змяшчаць роўна {{ limit }} элемент.|Калекцыя павінна змяшчаць роўна {{ limit }} элементаў.
+
+
+
+ Несапраўдны нумар карты.
+
+
+
+ Тып карты не падтрымліваецца або несапраўдны нумар карты.
+
+
+
+ Несапраўдны міжнародны нумар банкаўскага рахунку (IBAN).
+
+
+
+ Гэта значэнне не з'яўляецца сапраўдным ISBN-10.
+
+
+
+ Гэта значэнне не з'яўляецца сапраўдным ISBN-13.
+
+
+
+ Гэта значэнне не з'яўляецца сапраўдным ISBN-10 або ISBN-13.
+
+
+
+ Гэта значэнне не з'яўляецца сапраўдным ISSN.
+
+
+
+ Гэта значэнне не з'яўляецца сапраўднай валютай.
+
+
+
+ Значэнне павінна раўняцца {{ compared_value }}.
+
+
+
+ Значэнне павінна быць больш чым {{ compared_value }}.
+
+
+
+ Значэнне павінна быць больш чым або раўняцца {{ compared_value }}.
+
+
+
+ Значэнне павінна быць ідэнтычным {{ compared_value_type }} {{ compared_value }}.
+
+
+
+ Значэнне павінна быць менш чым {{ compared_value }}.
+
+
+
+ Значэнне павінна быць менш чым або раўняцца {{ compared_value }}.
+
+
+
+ Значэнне не павінна раўняцца {{ compared_value }}.
+
+
+
+ Значэнне не павінна быць ідэнтычным {{ compared_value_type }} {{ compared_value }}.
+
+
+
+ Суадносіны бакоў выявы з'яўляецца занадта вялікім ({{ ratio }}). Дазваляецца максімальныя суадносіны {{max_ratio}} .
+
+
+
+ Суадносіны бакоў выявы з'яўляецца занадта маленькімі ({{ ratio }}). Дазваляецца мінімальныя суадносіны {{ min_ratio }}.
+
+
+
+ Выява квадратная ({{width}}x{{height}}px). Квадратныя выявы не дазволены.
+
+
+
+ Выява ў альбомнай арыентацыі ({{ width }}x{{ height }}px). Выявы ў альбомнай арыентацыі не дазволены.
+
+
+
+ Выява ў партрэтнай арыентацыі ({{ width }}x{{ height }}px). Выявы ў партрэтнай арыентацыі не дазволены.
+
+
+
+ Пусты файл не дазволены.
+
+
+
+ Не магчыма знайсці імя хоста.
+
+
+
+ Гэта значэнне не супадае з чаканай {{ charset }} кадыроўкай.
+
+
+
+ Несапраўдны банкаўскі ідэнтыфікацыйны код (BIC).
+
+
+
+ Памылка
+
+
+
+ Гэта несапраўдны UUID.
+
+
+
+ Значэнне павінна быць кратным {{ compared_value }}.
+
+
+
+ Банкаўскі ідэнтыфікацыйны код (BIC) не звязан з IBAN {{ iban }}.
+
+
+
+ Гэта значэнне павінна быць у фармаце JSON.
+
+
+
+ Калекцыя павінна змяшчаць толькі ўнікальныя элементы.
+
+
+
+ Значэнне павінна быць дадатным.
+
+
+
+ Значэнне павінна быць дадатным ці нуль.
+
+
+
+ Значэнне павінна быць адмоўным.
+
+
+
+ Значэнне павінна быць адмоўным ці нуль.
+
+
+
+ Значэнне не з'яўляецца сапраўдным гадзінным поясам.
+
+
+
+ Гэты пароль быў выкрадзены ў выніку ўзлому дадзеных, таму яго нельга выкарыстоўваць. Калі ласка, выкарыстоўвайце іншы пароль.
+
+
+
+
diff --git a/vendor/symfony/yaml/CHANGELOG.md b/vendor/symfony/yaml/CHANGELOG.md
index cfd81fad27..1bc5561ba3 100644
--- a/vendor/symfony/yaml/CHANGELOG.md
+++ b/vendor/symfony/yaml/CHANGELOG.md
@@ -1,6 +1,11 @@
CHANGELOG
=========
+4.3.0
+-----
+
+ * Using a mapping inside a multi-line string is deprecated and will throw a `ParseException` in 5.0.
+
4.2.0
-----
diff --git a/vendor/symfony/yaml/Parser.php b/vendor/symfony/yaml/Parser.php
index cfec2de670..4519edb840 100644
--- a/vendor/symfony/yaml/Parser.php
+++ b/vendor/symfony/yaml/Parser.php
@@ -390,6 +390,11 @@ private function doParse(string $value, int $flags)
if (0 === $this->offset && !$deprecatedUsage && isset($line[0]) && ' ' === $line[0]) {
throw new ParseException('Unable to parse.', $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename);
}
+
+ if (false !== strpos($line, ': ')) {
+ @trigger_error('Support for mapping keys in multi-line blocks is deprecated since Symfony 4.3 and will throw a ParseException in 5.0.', E_USER_DEPRECATED);
+ }
+
if ('' === trim($line)) {
$value .= "\n";
} elseif (!$previousLineWasNewline && !$previousLineWasTerminatedWithBackslash) {
diff --git a/vendor/symfony/yaml/Tests/ParserTest.php b/vendor/symfony/yaml/Tests/ParserTest.php
index 7e465595ca..6fc02e1d1f 100644
--- a/vendor/symfony/yaml/Tests/ParserTest.php
+++ b/vendor/symfony/yaml/Tests/ParserTest.php
@@ -525,6 +525,20 @@ public function testObjectsSupportDisabledWithExceptions()
$this->parser->parse($yaml, Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE);
}
+ /**
+ * @group legacy
+ * @expectedDeprecation Support for mapping keys in multi-line blocks is deprecated since Symfony 4.3 and will throw a ParseException in 5.0.
+ */
+ public function testMappingKeyInMultiLineStringTriggersDeprecationNotice()
+ {
+ $yaml = <<<'EOF'
+data:
+ dbal:wrong
+ default_connection: monolith
+EOF;
+ $this->parser->parse($yaml);
+ }
+
public function testCanParseContentWithTrailingSpaces()
{
$yaml = "items: \n foo: bar";
diff --git a/vendor/symfony/yaml/composer.json b/vendor/symfony/yaml/composer.json
index c8b7123f23..2338728efe 100644
--- a/vendor/symfony/yaml/composer.json
+++ b/vendor/symfony/yaml/composer.json
@@ -37,7 +37,7 @@
"minimum-stability": "dev",
"extra": {
"branch-alias": {
- "dev-master": "4.2-dev"
+ "dev-master": "4.3-dev"
}
}
}
diff --git a/vendor/twig/twig/CHANGELOG b/vendor/twig/twig/CHANGELOG
index 3deb94544c..0468803bad 100644
--- a/vendor/twig/twig/CHANGELOG
+++ b/vendor/twig/twig/CHANGELOG
@@ -1,3 +1,27 @@
+* 2.11.2 (2019-06-05)
+
+ * fixed macro auto-import
+
+* 2.11.1 (2019-06-04)
+
+ * added support for "Twig\Markup" instances in the "in" test (again)
+ * allowed string operators as variables names in assignments
+ * fixed support for macros defined in parent templates
+
+* 2.11.0 (2019-05-31)
+
+ * added the possibility to register classes/interfaces as being safe for the escaper ("EscaperExtension::addSafeClass()")
+ * deprecated CoreExtension::setEscaper() and CoreExtension::getEscapers() in favor of the same methods on EscaperExtension
+ * macros are now auto-imported in the template they are defined (under the ``_self`` variable)
+ * added support for macros on "is defined" tests
+ * fixed macros "import" when using the same name in the parent and child templates
+ * fixed recursive macros
+ * macros imported "globally" in a template are now available in macros without re-importing them
+ * fixed the "filter" filter when the argument is \Traversable but does not implement \Iterator (\SimpleXmlElement for instance)
+ * fixed a PHP fatal error when calling a macro imported in a block in a nested block
+ * fixed a PHP fatal error when calling a macro imported in the template in another macro
+ * fixed wrong error message on "import" and "from"
+
* 2.10.0 (2019-05-14)
* deprecated "if" conditions on "for" tags
@@ -225,7 +249,23 @@
* improved the performance of the filesystem loader
* removed features that were deprecated in 1.x
-* 1.41.0 (2019-XX-XX)
+* 1.42.2 (2019-XX-XX)
+
+ * n/a
+
+* 1.42.1 (2019-06-04)
+
+ * added support for "Twig\Markup" instances in the "in" test (again)
+ * allowed string operators as variables names in assignments
+
+* 1.42.0 (2019-05-31)
+
+ * fixed the "filter" filter when the argument is \Traversable but does not implement \Iterator (\SimpleXmlElement for instance)
+ * fixed a PHP fatal error when calling a macro imported in a block in a nested block
+ * fixed a PHP fatal error when calling a macro imported in the template in another macro
+ * fixed wrong error message on "import" and "from"
+
+* 1.41.0 (2019-05-14)
* fixed support for PHP 7.4
* added "filter", "map", and "reduce" filters (and support for arrow functions)
diff --git a/vendor/twig/twig/composer.json b/vendor/twig/twig/composer.json
index c78e1db9d2..b0c5836f80 100644
--- a/vendor/twig/twig/composer.json
+++ b/vendor/twig/twig/composer.json
@@ -29,7 +29,7 @@
"symfony/polyfill-ctype": "^1.8"
},
"require-dev": {
- "symfony/phpunit-bridge": "^3.4.19|^4.1.8",
+ "symfony/phpunit-bridge": "^3.4.19|^4.1.8|^5.0",
"symfony/debug": "^2.7",
"psr/container": "^1.0"
},
@@ -48,7 +48,7 @@
},
"extra": {
"branch-alias": {
- "dev-master": "2.10-dev"
+ "dev-master": "2.11-dev"
}
}
}
diff --git a/vendor/twig/twig/doc/advanced.rst b/vendor/twig/twig/doc/advanced.rst
index 9bce7a6574..4d22fcce61 100644
--- a/vendor/twig/twig/doc/advanced.rst
+++ b/vendor/twig/twig/doc/advanced.rst
@@ -863,49 +863,6 @@ It is now possible to move the runtime logic to a new
}
}
-Overloading
------------
-
-To overload an already defined filter, test, operator, global variable, or
-function, re-define it in an extension and register it **as late as
-possible** (order matters)::
-
- class MyCoreExtension extends \Twig\Extension\AbstractExtension
- {
- public function getFilters()
- {
- return [
- new \Twig\TwigFilter('date', [$this, 'dateFilter']),
- ];
- }
-
- public function dateFilter($timestamp, $format = 'F j, Y H:i')
- {
- // do something different from the built-in date filter
- }
- }
-
- $twig = new \Twig\Environment($loader);
- $twig->addExtension(new MyCoreExtension());
-
-Here, we have overloaded the built-in ``date`` filter with a custom one.
-
-If you do the same on the ``\Twig\Environment`` itself, beware that it takes
-precedence over any other registered extensions::
-
- $twig = new \Twig\Environment($loader);
- $twig->addFilter(new \Twig\TwigFilter('date', function ($timestamp, $format = 'F j, Y H:i') {
- // do something different from the built-in date filter
- }));
- // the date filter will come from the above registration, not
- // from the registered extension below
- $twig->addExtension(new MyCoreExtension());
-
-.. caution::
-
- Note that overloading the built-in Twig elements is not recommended as it
- might be confusing.
-
Testing an Extension
--------------------
diff --git a/vendor/twig/twig/doc/api.rst b/vendor/twig/twig/doc/api.rst
index 0f0abcee95..5c0294c20b 100644
--- a/vendor/twig/twig/doc/api.rst
+++ b/vendor/twig/twig/doc/api.rst
@@ -415,6 +415,24 @@ The escaping rules are implemented as follows:
{% set text = "Twig " %}
{{ foo ? text|escape : " Twig" }} {# the result of the expression won't be escaped #}
+* Objects with a ``__toString`` method are converted to strings and
+ escaped. You can mark some classes and/or interfaces as being safe for some
+ strategies via ``EscaperExtension::addSafeClass()``:
+
+ .. code-block:: twig
+
+ // mark object of class Foo as safe for the HTML strategy
+ $escaper->addSafeClass('Foo', ['html']);
+
+ // mark object of interface Foo as safe for the HTML strategy
+ $escaper->addSafeClass('FooInterface', ['html']);
+
+ // mark object of class Foo as safe for the HTML and JS strategies
+ $escaper->addSafeClass('Foo', ['html', 'js']);
+
+ // mark object of class Foo as safe for all strategies
+ $escaper->addSafeClass('Foo', ['all']);
+
* Escaping is applied before printing, after any other filter is applied:
.. code-block:: twig
diff --git a/vendor/twig/twig/doc/deprecated.rst b/vendor/twig/twig/doc/deprecated.rst
index bc19efdcc7..d7a6145ed4 100644
--- a/vendor/twig/twig/doc/deprecated.rst
+++ b/vendor/twig/twig/doc/deprecated.rst
@@ -93,6 +93,13 @@ Interfaces
* As of Twig 2.7, the ``Twig\Extension\InitRuntimeInterface`` interface is
deprecated and will be removed in Twig 3.0.
+Extensions
+----------
+
+* As of Twig 2.11, the ``Twig\Extension\CoreExtension::setEscaper()`` and
+ ``Twig\Extension\CoreExtension::getEscapers()`` are deprecated. Use the same
+ methods on ``Twig\Extension\EscaperExtension`` instead.
+
Miscellaneous
-------------
diff --git a/vendor/twig/twig/doc/filters/filter.rst b/vendor/twig/twig/doc/filters/filter.rst
index a6c1c132db..310e63a73e 100644
--- a/vendor/twig/twig/doc/filters/filter.rst
+++ b/vendor/twig/twig/doc/filters/filter.rst
@@ -2,7 +2,7 @@
=========
.. versionadded:: 1.41
- The ``filter`` filter was added in Twig 1.41.
+ The ``filter`` filter was added in Twig 1.41 and 2.10.
The ``filter`` filter filters elements of a sequence or a mapping using an arrow
function. The arrow function receives the value of the sequence or mapping:
@@ -11,11 +11,22 @@ function. The arrow function receives the value of the sequence or mapping:
{% set sizes = [34, 36, 38, 40, 42] %}
+ {{ sizes|filter(v => v > 38)|join(', ') }}
+ {# output 40, 42 #}
+
+Combined with the ``for`` tag, it allows to filter the items to iterate over:
+
+.. code-block:: twig
+
{% for v in sizes|filter(v => v > 38) -%}
{{ v }}
{% endfor %}
{# output 40 42 #}
+It also works with mappings:
+
+.. code-block:: twig
+
{% set sizes = {
xs: 34,
s: 36,
diff --git a/vendor/twig/twig/doc/filters/map.rst b/vendor/twig/twig/doc/filters/map.rst
index b4849a6297..777b05a7c3 100644
--- a/vendor/twig/twig/doc/filters/map.rst
+++ b/vendor/twig/twig/doc/filters/map.rst
@@ -2,7 +2,7 @@
=======
.. versionadded:: 1.41
- The ``map`` filter was added in Twig 1.41.
+ The ``map`` filter was added in Twig 1.41 and 2.10.
The ``map`` filter applies an arrow function to the elements of a sequence or a
mapping. The arrow function receives the value of the sequence or mapping:
diff --git a/vendor/twig/twig/doc/filters/reduce.rst b/vendor/twig/twig/doc/filters/reduce.rst
index 10a0d5acb5..7d04d6c7b0 100644
--- a/vendor/twig/twig/doc/filters/reduce.rst
+++ b/vendor/twig/twig/doc/filters/reduce.rst
@@ -2,7 +2,7 @@
=========
.. versionadded:: 1.41
- The ``reduce`` filter was added in Twig 1.41.
+ The ``reduce`` filter was added in Twig 1.41 and 2.10.
The ``reduce`` filter iteratively reduces a sequence or a mapping to a single
value using an arrow function, so as to reduce it to a single value. The arrow
diff --git a/vendor/twig/twig/doc/tags/for.rst b/vendor/twig/twig/doc/tags/for.rst
index 77b1dc82d6..8e9b4e5824 100644
--- a/vendor/twig/twig/doc/tags/for.rst
+++ b/vendor/twig/twig/doc/tags/for.rst
@@ -84,6 +84,13 @@ Variable Description
Adding a condition
------------------
+.. tip::
+
+ As of Twig 2.10, use the :doc:`filter <../filters/filter>` filter instead,
+ or an ``if`` condition inside the ``for`` body (if your condition depends on
+ a variable updated inside the loop and you are not using the ``loop``
+ variable).
+
Unlike in PHP, it's not possible to ``break`` or ``continue`` in a loop. You
can however filter the sequence during iteration which allows you to skip
items. The following example skips all the users which are not active:
diff --git a/vendor/twig/twig/doc/tags/from.rst b/vendor/twig/twig/doc/tags/from.rst
index 39334fdde2..96c439aa74 100644
--- a/vendor/twig/twig/doc/tags/from.rst
+++ b/vendor/twig/twig/doc/tags/from.rst
@@ -3,6 +3,4 @@
The ``from`` tag imports :doc:`macro<../tags/macro>` names into the current
namespace. The tag is documented in detail in the documentation for the
-:doc:`import<../tags/import>` tag.
-
-.. seealso:: :doc:`macro<../tags/macro>`, :doc:`import<../tags/import>`
+:doc:`macro<../tags/macro>` tag.
diff --git a/vendor/twig/twig/doc/tags/import.rst b/vendor/twig/twig/doc/tags/import.rst
index 255a586928..f217479fb3 100644
--- a/vendor/twig/twig/doc/tags/import.rst
+++ b/vendor/twig/twig/doc/tags/import.rst
@@ -1,65 +1,6 @@
``import``
==========
-Twig supports putting often used code into :doc:`macros<../tags/macro>`. These
-macros are defined in regular templates.
-
-Imagine having a generic helper template that define how to render forms via
-macros (called ``forms.html``):
-
-.. code-block:: twig
-
- {% macro input(name, value, type, size) %}
-
- {% endmacro %}
-
- {% macro textarea(name, value, rows, cols) %}
-
- {% endmacro %}
-
-There are two ways to import macros. You can import the complete template
-containing the macros into a local variable or only import specific macros from
-the template.
-
-The easiest and most flexible is importing the whole module into a local
-variable:
-
-.. code-block:: twig
-
- {% import 'forms.html' as forms %}
-
-
-
Username
-
{{ forms.input('username') }}
-
Password
-
{{ forms.input('password', null, 'password') }}
-
-
{{ forms.textarea('comment') }}
-
-Alternatively you can import names from the template into the current
-namespace:
-
-.. code-block:: twig
-
- {% from 'forms.html' import input as input_field, textarea %}
-
-
-
Username
-
{{ input_field('username') }}
-
Password
-
{{ input_field('password', '', 'password') }}
-
-
{{ textarea('comment') }}
-
-.. note::
-
- Importing macros using ``import`` or ``from`` is **local** to the current
- file. The imported macros are not available in included templates or child
- templates; you need to explicitely re-import macros in each file.
-
-.. tip::
-
- To import macros from the current file, use the special ``_self`` variable
- for the source.
-
-.. seealso:: :doc:`macro<../tags/macro>`, :doc:`from<../tags/from>`
+The ``import`` tag imports :doc:`macro<../tags/macro>` names in a local
+variable. The tag is documented in detail in the documentation for the
+:doc:`macro<../tags/macro>` tag.
diff --git a/vendor/twig/twig/doc/tags/macro.rst b/vendor/twig/twig/doc/tags/macro.rst
index c173f9f7f9..33808f5a8f 100644
--- a/vendor/twig/twig/doc/tags/macro.rst
+++ b/vendor/twig/twig/doc/tags/macro.rst
@@ -2,10 +2,12 @@
=========
Macros are comparable with functions in regular programming languages. They
-are useful to put often used HTML idioms into reusable elements to not repeat
-yourself.
+are useful to reuse template fragments to not repeat yourself.
-Here is a small example of a macro that renders a form element:
+Macros are defined in regular templates.
+
+Imagine having a generic helper template that define how to render HTML forms
+via macros (called ``forms.html``):
.. code-block:: twig
@@ -13,8 +15,12 @@ Here is a small example of a macro that renders a form element:
{% endmacro %}
-Each argument can have a default value (here ``text`` is the default value for
-``type`` if not provided in the call).
+ {% macro textarea(name, value, rows = 10, cols = 40) %}
+
+ {% endmacro %}
+
+Each macro argument can have a default value (here ``text`` is the default value
+for ``type`` if not provided in the call).
Macros differ from native PHP functions in a few ways:
@@ -31,53 +37,135 @@ variables.
You can pass the whole context as an argument by using the special
``_context`` variable.
-Import
-------
+Importing Macros
+----------------
-Macros can be defined in any template, and need to be "imported" before being
-used (see the documentation for the :doc:`import<../tags/import>` tag for more
-information):
+There are two ways to import macros. You can import the complete template
+containing the macros into a local variable (via the ``import`` tag) or only
+import specific macros from the template (via the ``from`` tag).
+
+To import all macros from a template into a local variable, use the ``import``
+tag:
.. code-block:: twig
{% import "forms.html" as forms %}
-The above ``import`` call imports the "forms.html" file (which can contain only
-macros, or a template and some macros), and import the functions as items of
+The above ``import`` call imports the ``forms.html`` file (which can contain
+only macros, or a template and some macros), and import the macros as items of
the ``forms`` local variable.
-The macro can then be called at will in the current template:
+The macros can then be called at will in the *current* template:
.. code-block:: twig
{{ forms.input('username') }}
{{ forms.input('password', null, 'password') }}
-If macros are defined and used in the same template, you can use the
-special ``_self`` variable to import them:
+Alternatively you can import names from the template into the current namespace
+via the ``from`` tag:
.. code-block:: twig
- {% import _self as forms %}
+ {% from 'forms.html' import input as input_field, textarea %}
-
{{ forms.input('username') }}
+
{{ input_field('password', '', 'password') }}
+
{{ textarea('comment') }}
-When you want to use a macro in another macro from the same file, you need to
-import it locally:
+.. tip::
-.. code-block:: twig
+ When macro usages and definitions are in the same template, you don't need to
+ import the macros as they are automatically available under the special
+ ``_self`` variable:
- {% macro input(name, value, type, size) %}
-
- {% endmacro %}
+ .. code-block:: twig
+
+
{{ _self.input('password', '', 'password') }}
+
+ {% macro input(name, value, type = "text", size = 20) %}
+
+ {% endmacro %}
+
+ Auto-import is only available as of Twig 2.11. For older versions, import
+ macros using the special ``_self`` variable for the template name:
+
+ .. code-block:: twig
- {% macro wrapped_input(name, value, type, size) %}
{% import _self as forms %}
-
- {{ forms.input(name, value, type, size) }}
-
- {% endmacro %}
+
{{ forms.input('username') }}
+
+.. note::
+
+ Before Twig 2.11, when you want to use a macro in another macro from the
+ same file, you need to import it locally:
+
+ .. code-block:: twig
+
+ {% macro input(name, value, type, size) %}
+
+ {% endmacro %}
+
+ {% macro wrapped_input(name, value, type, size) %}
+ {% import _self as forms %}
+
+
+ {{ forms.input(name, value, type, size) }}
+
+ {% endmacro %}
+
+Macros Scoping
+--------------
+
+.. versionadded:: 2.11
+
+ The scoping rules described in this paragraph are implemented as of Twig
+ 2.11.
+
+The scoping rules are the same whether you imported macros via ``import`` or
+``from``.
+
+Imported macros are always **local** to the current template. It means that
+macros are available in all blocks and other macros defined in the current
+template, but they are not available in included templates or child templates;
+you need to explicitely re-import macros in each template.
+
+When calling ``import`` or ``from`` from a ``block`` tag, the imported macros
+are only defined in the current block and they override macros defined at the
+template level with the same names.
+
+When calling ``import`` or ``from`` from a ``macro`` tag, the imported macros
+are only defined in the current macro and they override macros defined at the
+template level with the same names.
+
+.. note::
+
+ Before Twig 2.11, it was possible to use macros imported in a block in a
+ "sub-block". When upgrading to 2.11, you need to either move the import in
+ the global scope or reimport the macros explicitly in the "sub-blocks".
+
+Checking if a Macro is defined
+------------------------------
+
+.. versionadded:: 2.11
+
+ Support for the ``defined`` test on macros was added in Twig 2.11.
+
+You can check if a macro is defined via the ``defined`` test:
+
+.. code-block:: twig
+
+ {% import "macros.twig" as macros %}
+
+ {% from "macros.twig" import hello %}
+
+ {% if macros.hello is defined -%}
+ OK
+ {% endif %}
+
+ {% if hello is defined -%}
+ OK
+ {% endif %}
Named Macro End-Tags
--------------------
@@ -92,5 +180,3 @@ readability:
{% endmacro input %}
Of course, the name after the ``endmacro`` word must match the macro name.
-
-.. seealso:: :doc:`from<../tags/from>`, :doc:`import<../tags/import>`
diff --git a/vendor/twig/twig/doc/templates.rst b/vendor/twig/twig/doc/templates.rst
index f8f57c9e4f..3aa3ea96f9 100644
--- a/vendor/twig/twig/doc/templates.rst
+++ b/vendor/twig/twig/doc/templates.rst
@@ -494,52 +494,9 @@ For bigger sections it makes sense to mark a block
Macros
------
-Macros are comparable with functions in regular programming languages. They
-are useful to reuse often used HTML fragments to not repeat yourself.
-
-A macro is defined via the :doc:`macro` tag. Here is a small example
-(subsequently called ``forms.html``) of a macro that renders a form element:
-
-.. code-block:: twig
-
- {% macro input(name, value, type, size) %}
-
- {% endmacro %}
-
-Macros can be defined in any template, and need to be "imported" via the
-:doc:`import` tag before being used:
-
-.. code-block:: twig
-
- {% import "forms.html" as forms %}
-
-
{{ forms.input('username') }}
-
-Alternatively, you can import individual macro names from a template into the
-current namespace via the :doc:`from` tag and optionally alias them:
-
-.. code-block:: twig
-
- {% from 'forms.html' import input as input_field %}
-
-
-
Username
-
{{ input_field('username') }}
-
Password
-
{{ input_field('password', '', 'password') }}
-
-
-A default value can also be defined for macro arguments when not provided in a
-macro call:
-
-.. code-block:: twig
-
- {% macro input(name, value = "", type = "text", size = 20) %}
-
- {% endmacro %}
-
-If extra positional arguments are passed to a macro call, they end up in the
-special ``varargs`` variable as a list of values.
+Macros are comparable with functions in regular programming languages. They are
+useful to reuse HTML fragments to not repeat yourself. They are described in the
+:doc:`macro` tag documentation.
.. _twig-expressions:
diff --git a/vendor/twig/twig/src/Environment.php b/vendor/twig/twig/src/Environment.php
index 89828b7b0a..926ce1a05e 100644
--- a/vendor/twig/twig/src/Environment.php
+++ b/vendor/twig/twig/src/Environment.php
@@ -38,11 +38,11 @@
*/
class Environment
{
- const VERSION = '2.10.0';
- const VERSION_ID = 21000;
+ const VERSION = '2.11.2';
+ const VERSION_ID = 21102;
const MAJOR_VERSION = 2;
- const MINOR_VERSION = 10;
- const RELEASE_VERSION = 0;
+ const MINOR_VERSION = 11;
+ const RELEASE_VERSION = 2;
const EXTRA_VERSION = '';
private $charset;
diff --git a/vendor/twig/twig/src/ExpressionParser.php b/vendor/twig/twig/src/ExpressionParser.php
index 4c346406af..2c5df63f14 100644
--- a/vendor/twig/twig/src/ExpressionParser.php
+++ b/vendor/twig/twig/src/ExpressionParser.php
@@ -635,7 +635,13 @@ public function parseAssignmentExpression()
$stream = $this->parser->getStream();
$targets = [];
while (true) {
- $token = $stream->expect(/* Token::NAME_TYPE */ 5, null, 'Only variables can be assigned to');
+ $token = $this->parser->getCurrentToken();
+ if ($stream->test(/* Token::OPERATOR_TYPE */ 8) && preg_match(Lexer::REGEX_NAME, $token->getValue())) {
+ // in this context, string operators are variable names
+ $this->parser->getStream()->next();
+ } else {
+ $stream->expect(/* Token::NAME_TYPE */ 5, null, 'Only variables can be assigned to');
+ }
$value = $token->getValue();
if (\in_array(strtolower($value), ['true', 'false', 'none', 'null'])) {
throw new SyntaxError(sprintf('You cannot assign a value to "%s".', $value), $token->getLine(), $stream->getSourceContext());
@@ -679,6 +685,11 @@ private function parseTestExpression(Node $node): TestExpression
$arguments = $this->parseArguments(true);
}
+ if ('defined' === $name && $node instanceof NameExpression && null !== $alias = $this->parser->getImportedSymbol('function', $node->getAttribute('name'))) {
+ $node = new MethodCallExpression($alias['node'], $alias['name'], new ArrayExpression([], $node->getTemplateLine()), $node->getTemplateLine());
+ $node->setAttribute('safe', true);
+ }
+
return new $class($node, $name, $arguments, $this->parser->getCurrentToken()->getLine());
}
diff --git a/vendor/twig/twig/src/Extension/CoreExtension.php b/vendor/twig/twig/src/Extension/CoreExtension.php
index f0aa6265b2..eda0487276 100644
--- a/vendor/twig/twig/src/Extension/CoreExtension.php
+++ b/vendor/twig/twig/src/Extension/CoreExtension.php
@@ -48,6 +48,7 @@
use Twig\Node\Expression\Unary\NegUnary;
use Twig\Node\Expression\Unary\NotUnary;
use Twig\Node\Expression\Unary\PosUnary;
+use Twig\NodeVisitor\MacroAutoImportNodeVisitor;
use Twig\TokenParser\ApplyTokenParser;
use Twig\TokenParser\BlockTokenParser;
use Twig\TokenParser\DeprecatedTokenParser;
@@ -82,9 +83,13 @@ final class CoreExtension extends AbstractExtension
*
* @param string $strategy The strategy name that should be used as a strategy in the escape call
* @param callable $callable A valid PHP callable
+ *
+ * @deprecated since Twig 2.11, to be removed in 3.0; use the same method on EscaperExtension instead
*/
public function setEscaper($strategy, callable $callable)
{
+ @trigger_error(sprintf('The "%s" method is deprecated since Twig 2.11; use "%s::setEscaper" instead.', __METHOD__, EscaperExtension::class), E_USER_DEPRECATED);
+
$this->escapers[$strategy] = $callable;
}
@@ -92,9 +97,15 @@ public function setEscaper($strategy, callable $callable)
* Gets all defined escapers.
*
* @return callable[] An array of escapers
+ *
+ * @deprecated since Twig 2.11, to be removed in 3.0; use the same method on EscaperExtension instead
*/
- public function getEscapers()
+ public function getEscapers(/* $triggerDeprecation = true */)
{
+ if (0 === \func_num_args() || func_get_arg(0)) {
+ @trigger_error(sprintf('The "%s" method is deprecated since Twig 2.11; use "%s::getEscapers" instead.', __METHOD__, EscaperExtension::class), E_USER_DEPRECATED);
+ }
+
return $this->escapers;
}
@@ -243,10 +254,6 @@ public function getFilters()
// iteration and runtime
new TwigFilter('default', '_twig_default_filter', ['node_class' => DefaultFilter::class]),
new TwigFilter('keys', 'twig_get_array_keys_filter'),
-
- // escaping
- new TwigFilter('escape', 'twig_escape_filter', ['needs_environment' => true, 'is_safe_callback' => 'twig_escape_filter_is_safe']),
- new TwigFilter('e', 'twig_escape_filter', ['needs_environment' => true, 'is_safe_callback' => 'twig_escape_filter_is_safe']),
];
}
@@ -281,6 +288,11 @@ public function getTests()
];
}
+ public function getNodeVisitors()
+ {
+ return [new MacroAutoImportNodeVisitor()];
+ }
+
public function getOperators()
{
return [
@@ -333,8 +345,6 @@ class_alias('Twig\Extension\CoreExtension', 'Twig_Extension_Core');
use Twig\Extension\CoreExtension;
use Twig\Extension\SandboxExtension;
use Twig\Markup;
- use Twig\Node\Expression\ConstantExpression;
- use Twig\Node\Node;
use Twig\Source;
use Twig\Template;
@@ -915,6 +925,9 @@ function twig_in_filter($value, $compare)
if ($value instanceof Markup) {
$value = (string) $value;
}
+ if ($compare instanceof Markup) {
+ $compare = (string) $compare;
+ }
if (\is_array($compare)) {
return \in_array($value, $compare, \is_object($value) || \is_resource($value));
@@ -976,250 +989,6 @@ function twig_spaceless($content)
return trim(preg_replace('/>\s+', '><', $content));
}
-/**
- * Escapes a string.
- *
- * @param mixed $string The value to be escaped
- * @param string $strategy The escaping strategy
- * @param string $charset The charset
- * @param bool $autoescape Whether the function is called by the auto-escaping feature (true) or by the developer (false)
- *
- * @return string
- */
-function twig_escape_filter(Environment $env, $string, $strategy = 'html', $charset = null, $autoescape = false)
-{
- if ($autoescape && $string instanceof Markup) {
- return $string;
- }
-
- if (!\is_string($string)) {
- if (\is_object($string) && method_exists($string, '__toString')) {
- $string = (string) $string;
- } elseif (\in_array($strategy, ['html', 'js', 'css', 'html_attr', 'url'])) {
- return $string;
- }
- }
-
- if ('' === $string) {
- return '';
- }
-
- if (null === $charset) {
- $charset = $env->getCharset();
- }
-
- switch ($strategy) {
- case 'html':
- // see https://secure.php.net/htmlspecialchars
-
- // Using a static variable to avoid initializing the array
- // each time the function is called. Moving the declaration on the
- // top of the function slow downs other escaping strategies.
- static $htmlspecialcharsCharsets = [
- 'ISO-8859-1' => true, 'ISO8859-1' => true,
- 'ISO-8859-15' => true, 'ISO8859-15' => true,
- 'utf-8' => true, 'UTF-8' => true,
- 'CP866' => true, 'IBM866' => true, '866' => true,
- 'CP1251' => true, 'WINDOWS-1251' => true, 'WIN-1251' => true,
- '1251' => true,
- 'CP1252' => true, 'WINDOWS-1252' => true, '1252' => true,
- 'KOI8-R' => true, 'KOI8-RU' => true, 'KOI8R' => true,
- 'BIG5' => true, '950' => true,
- 'GB2312' => true, '936' => true,
- 'BIG5-HKSCS' => true,
- 'SHIFT_JIS' => true, 'SJIS' => true, '932' => true,
- 'EUC-JP' => true, 'EUCJP' => true,
- 'ISO8859-5' => true, 'ISO-8859-5' => true, 'MACROMAN' => true,
- ];
-
- if (isset($htmlspecialcharsCharsets[$charset])) {
- return htmlspecialchars($string, ENT_QUOTES | ENT_SUBSTITUTE, $charset);
- }
-
- if (isset($htmlspecialcharsCharsets[strtoupper($charset)])) {
- // cache the lowercase variant for future iterations
- $htmlspecialcharsCharsets[$charset] = true;
-
- return htmlspecialchars($string, ENT_QUOTES | ENT_SUBSTITUTE, $charset);
- }
-
- $string = iconv($charset, 'UTF-8', $string);
- $string = htmlspecialchars($string, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
-
- return iconv('UTF-8', $charset, $string);
-
- case 'js':
- // escape all non-alphanumeric characters
- // into their \x or \uHHHH representations
- if ('UTF-8' !== $charset) {
- $string = iconv($charset, 'UTF-8', $string);
- }
-
- if (!preg_match('//u', $string)) {
- throw new RuntimeError('The string to escape is not a valid UTF-8 string.');
- }
-
- $string = preg_replace_callback('#[^a-zA-Z0-9,\._]#Su', function ($matches) {
- $char = $matches[0];
-
- /*
- * A few characters have short escape sequences in JSON and JavaScript.
- * Escape sequences supported only by JavaScript, not JSON, are ommitted.
- * \" is also supported but omitted, because the resulting string is not HTML safe.
- */
- static $shortMap = [
- '\\' => '\\\\',
- '/' => '\\/',
- "\x08" => '\b',
- "\x0C" => '\f',
- "\x0A" => '\n',
- "\x0D" => '\r',
- "\x09" => '\t',
- ];
-
- if (isset($shortMap[$char])) {
- return $shortMap[$char];
- }
-
- // \uHHHH
- $char = twig_convert_encoding($char, 'UTF-16BE', 'UTF-8');
- $char = strtoupper(bin2hex($char));
-
- if (4 >= \strlen($char)) {
- return sprintf('\u%04s', $char);
- }
-
- return sprintf('\u%04s\u%04s', substr($char, 0, -4), substr($char, -4));
- }, $string);
-
- if ('UTF-8' !== $charset) {
- $string = iconv('UTF-8', $charset, $string);
- }
-
- return $string;
-
- case 'css':
- if ('UTF-8' !== $charset) {
- $string = iconv($charset, 'UTF-8', $string);
- }
-
- if (!preg_match('//u', $string)) {
- throw new RuntimeError('The string to escape is not a valid UTF-8 string.');
- }
-
- $string = preg_replace_callback('#[^a-zA-Z0-9]#Su', function ($matches) {
- $char = $matches[0];
-
- return sprintf('\\%X ', 1 === \strlen($char) ? \ord($char) : mb_ord($char, 'UTF-8'));
- }, $string);
-
- if ('UTF-8' !== $charset) {
- $string = iconv('UTF-8', $charset, $string);
- }
-
- return $string;
-
- case 'html_attr':
- if ('UTF-8' !== $charset) {
- $string = iconv($charset, 'UTF-8', $string);
- }
-
- if (!preg_match('//u', $string)) {
- throw new RuntimeError('The string to escape is not a valid UTF-8 string.');
- }
-
- $string = preg_replace_callback('#[^a-zA-Z0-9,\.\-_]#Su', function ($matches) {
- /**
- * This function is adapted from code coming from Zend Framework.
- *
- * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (https://www.zend.com)
- * @license https://framework.zend.com/license/new-bsd New BSD License
- */
- $chr = $matches[0];
- $ord = \ord($chr);
-
- /*
- * The following replaces characters undefined in HTML with the
- * hex entity for the Unicode replacement character.
- */
- if (($ord <= 0x1f && "\t" != $chr && "\n" != $chr && "\r" != $chr) || ($ord >= 0x7f && $ord <= 0x9f)) {
- return '�';
- }
-
- /*
- * Check if the current character to escape has a name entity we should
- * replace it with while grabbing the hex value of the character.
- */
- if (1 === \strlen($chr)) {
- /*
- * While HTML supports far more named entities, the lowest common denominator
- * has become HTML5's XML Serialisation which is restricted to the those named
- * entities that XML supports. Using HTML entities would result in this error:
- * XML Parsing Error: undefined entity
- */
- static $entityMap = [
- 34 => '"', /* quotation mark */
- 38 => '&', /* ampersand */
- 60 => '<', /* less-than sign */
- 62 => '>', /* greater-than sign */
- ];
-
- if (isset($entityMap[$ord])) {
- return $entityMap[$ord];
- }
-
- return sprintf('%02X;', $ord);
- }
-
- /*
- * Per OWASP recommendations, we'll use hex entities for any other
- * characters where a named entity does not exist.
- */
- return sprintf('%04X;', mb_ord($chr, 'UTF-8'));
- }, $string);
-
- if ('UTF-8' !== $charset) {
- $string = iconv('UTF-8', $charset, $string);
- }
-
- return $string;
-
- case 'url':
- return rawurlencode($string);
-
- default:
- static $escapers;
-
- if (null === $escapers) {
- $escapers = $env->getExtension(CoreExtension::class)->getEscapers();
- }
-
- if (isset($escapers[$strategy])) {
- return $escapers[$strategy]($env, $string, $charset);
- }
-
- $validStrategies = implode(', ', array_merge(['html', 'js', 'url', 'css', 'html_attr'], array_keys($escapers)));
-
- throw new RuntimeError(sprintf('Invalid escaping strategy "%s" (valid ones: %s).', $strategy, $validStrategies));
- }
-}
-
-/**
- * @internal
- */
-function twig_escape_filter_is_safe(Node $filterArgs)
-{
- foreach ($filterArgs as $arg) {
- if ($arg instanceof ConstantExpression) {
- return [$arg->getAttribute('value')];
- }
-
- return [];
- }
-
- return ['html'];
-}
-
function twig_convert_encoding($string, $to, $from)
{
return iconv($from, $to, $string);
@@ -1311,6 +1080,25 @@ function twig_capitalize_string_filter(Environment $env, $string)
return mb_strtoupper(mb_substr($string, 0, 1, $charset), $charset).mb_strtolower(mb_substr($string, 1, null, $charset), $charset);
}
+/**
+ * @internal
+ */
+function twig_call_macro(Template $template, string $method, array $args, int $lineno, array $context, Source $source)
+{
+ if (!method_exists($template, $method)) {
+ $parent = $template;
+ while ($parent = $parent->getParent($context)) {
+ if (method_exists($parent, $method)) {
+ return $parent->$method(...$args);
+ }
+ }
+
+ throw new RuntimeError(sprintf('Macro "%s" is not defined in template "%s".', substr($method, \strlen('macro_')), $template->getTemplateName()), $lineno, $source);
+ }
+
+ return $template->$method(...$args);
+}
+
/**
* @internal
*/
@@ -1734,19 +1522,11 @@ function twig_array_column($array, $name): array
function twig_array_filter($array, $arrow)
{
- if (\is_array($array)) {
- if (\PHP_VERSION_ID >= 50600) {
- return array_filter($array, $arrow, \ARRAY_FILTER_USE_BOTH);
+ foreach ($array as $k => $v) {
+ if ($arrow($v, $k)) {
+ yield $k => $v;
}
-
- return array_filter($array, $arrow);
}
-
- while ($array instanceof \IteratorAggregate) {
- $array = $array->getIterator();
- }
-
- return new \CallbackFilterIterator($array, $arrow);
}
function twig_array_map($array, $arrow)
diff --git a/vendor/twig/twig/src/Extension/EscaperExtension.php b/vendor/twig/twig/src/Extension/EscaperExtension.php
index b024ab11b3..b8dabccf11 100644
--- a/vendor/twig/twig/src/Extension/EscaperExtension.php
+++ b/vendor/twig/twig/src/Extension/EscaperExtension.php
@@ -18,6 +18,13 @@
final class EscaperExtension extends AbstractExtension
{
private $defaultStrategy;
+ private $escapers = [];
+
+ /** @internal */
+ public $safeClasses = [];
+
+ /** @internal */
+ public $safeLookup = [];
/**
* @param string|false|callable $defaultStrategy An escaping strategy
@@ -42,6 +49,8 @@ public function getNodeVisitors()
public function getFilters()
{
return [
+ new TwigFilter('escape', 'twig_escape_filter', ['needs_environment' => true, 'is_safe_callback' => 'twig_escape_filter_is_safe']),
+ new TwigFilter('e', 'twig_escape_filter', ['needs_environment' => true, 'is_safe_callback' => 'twig_escape_filter_is_safe']),
new TwigFilter('raw', 'twig_raw_filter', ['is_safe' => ['all']]),
];
}
@@ -80,12 +89,63 @@ public function getDefaultStrategy($name)
return $this->defaultStrategy;
}
+
+ /**
+ * Defines a new escaper to be used via the escape filter.
+ *
+ * @param string $strategy The strategy name that should be used as a strategy in the escape call
+ * @param callable $callable A valid PHP callable
+ */
+ public function setEscaper($strategy, callable $callable)
+ {
+ $this->escapers[$strategy] = $callable;
+ }
+
+ /**
+ * Gets all defined escapers.
+ *
+ * @return callable[] An array of escapers
+ */
+ public function getEscapers()
+ {
+ return $this->escapers;
+ }
+
+ public function setSafeClasses(array $safeClasses = [])
+ {
+ $this->safeClasses = [];
+ $this->safeLookup = [];
+ foreach ($safeClasses as $class => $strategies) {
+ $this->addSafeClass($class, $strategies);
+ }
+ }
+
+ public function addSafeClass(string $class, array $strategies)
+ {
+ $class = ltrim($class, '\\');
+ if (!isset($this->safeClasses[$class])) {
+ $this->safeClasses[$class] = [];
+ }
+ $this->safeClasses[$class] = array_merge($this->safeClasses[$class], $strategies);
+
+ foreach ($strategies as $strategy) {
+ $this->safeLookup[$strategy][$class] = true;
+ }
+ }
}
class_alias('Twig\Extension\EscaperExtension', 'Twig_Extension_Escaper');
}
namespace {
+use Twig\Environment;
+use Twig\Error\RuntimeError;
+use Twig\Extension\CoreExtension;
+use Twig\Extension\EscaperExtension;
+use Twig\Markup;
+use Twig\Node\Expression\ConstantExpression;
+use Twig\Node\Node;
+
/**
* Marks a variable as being safe.
*
@@ -97,4 +157,271 @@ function twig_raw_filter($string)
{
return $string;
}
+
+/**
+ * Escapes a string.
+ *
+ * @param mixed $string The value to be escaped
+ * @param string $strategy The escaping strategy
+ * @param string $charset The charset
+ * @param bool $autoescape Whether the function is called by the auto-escaping feature (true) or by the developer (false)
+ *
+ * @return string
+ */
+function twig_escape_filter(Environment $env, $string, $strategy = 'html', $charset = null, $autoescape = false)
+{
+ if ($autoescape && $string instanceof Markup) {
+ return $string;
+ }
+
+ if (!\is_string($string)) {
+ if (\is_object($string) && method_exists($string, '__toString')) {
+ if ($autoescape) {
+ $c = \get_class($string);
+ $ext = $env->getExtension(EscaperExtension::class);
+ if (!isset($ext->safeClasses[$c])) {
+ $ext->safeClasses[$c] = [];
+ foreach (class_parents($string) + class_implements($string) as $class) {
+ if (isset($ext->safeClasses[$class])) {
+ $ext->safeClasses[$c] = array_unique(array_merge($ext->safeClasses[$c], $ext->safeClasses[$class]));
+ foreach ($ext->safeClasses[$class] as $s) {
+ $ext->safeLookup[$s][$c] = true;
+ }
+ }
+ }
+ }
+ if (isset($ext->safeLookup[$strategy][$c]) || isset($ext->safeLookup['all'][$c])) {
+ return (string) $string;
+ }
+ }
+
+ $string = (string) $string;
+ } elseif (\in_array($strategy, ['html', 'js', 'css', 'html_attr', 'url'])) {
+ return $string;
+ }
+ }
+
+ if ('' === $string) {
+ return '';
+ }
+
+ if (null === $charset) {
+ $charset = $env->getCharset();
+ }
+
+ switch ($strategy) {
+ case 'html':
+ // see https://secure.php.net/htmlspecialchars
+
+ // Using a static variable to avoid initializing the array
+ // each time the function is called. Moving the declaration on the
+ // top of the function slow downs other escaping strategies.
+ static $htmlspecialcharsCharsets = [
+ 'ISO-8859-1' => true, 'ISO8859-1' => true,
+ 'ISO-8859-15' => true, 'ISO8859-15' => true,
+ 'utf-8' => true, 'UTF-8' => true,
+ 'CP866' => true, 'IBM866' => true, '866' => true,
+ 'CP1251' => true, 'WINDOWS-1251' => true, 'WIN-1251' => true,
+ '1251' => true,
+ 'CP1252' => true, 'WINDOWS-1252' => true, '1252' => true,
+ 'KOI8-R' => true, 'KOI8-RU' => true, 'KOI8R' => true,
+ 'BIG5' => true, '950' => true,
+ 'GB2312' => true, '936' => true,
+ 'BIG5-HKSCS' => true,
+ 'SHIFT_JIS' => true, 'SJIS' => true, '932' => true,
+ 'EUC-JP' => true, 'EUCJP' => true,
+ 'ISO8859-5' => true, 'ISO-8859-5' => true, 'MACROMAN' => true,
+ ];
+
+ if (isset($htmlspecialcharsCharsets[$charset])) {
+ return htmlspecialchars($string, ENT_QUOTES | ENT_SUBSTITUTE, $charset);
+ }
+
+ if (isset($htmlspecialcharsCharsets[strtoupper($charset)])) {
+ // cache the lowercase variant for future iterations
+ $htmlspecialcharsCharsets[$charset] = true;
+
+ return htmlspecialchars($string, ENT_QUOTES | ENT_SUBSTITUTE, $charset);
+ }
+
+ $string = iconv($charset, 'UTF-8', $string);
+ $string = htmlspecialchars($string, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
+
+ return iconv('UTF-8', $charset, $string);
+
+ case 'js':
+ // escape all non-alphanumeric characters
+ // into their \x or \uHHHH representations
+ if ('UTF-8' !== $charset) {
+ $string = iconv($charset, 'UTF-8', $string);
+ }
+
+ if (!preg_match('//u', $string)) {
+ throw new RuntimeError('The string to escape is not a valid UTF-8 string.');
+ }
+
+ $string = preg_replace_callback('#[^a-zA-Z0-9,\._]#Su', function ($matches) {
+ $char = $matches[0];
+
+ /*
+ * A few characters have short escape sequences in JSON and JavaScript.
+ * Escape sequences supported only by JavaScript, not JSON, are ommitted.
+ * \" is also supported but omitted, because the resulting string is not HTML safe.
+ */
+ static $shortMap = [
+ '\\' => '\\\\',
+ '/' => '\\/',
+ "\x08" => '\b',
+ "\x0C" => '\f',
+ "\x0A" => '\n',
+ "\x0D" => '\r',
+ "\x09" => '\t',
+ ];
+
+ if (isset($shortMap[$char])) {
+ return $shortMap[$char];
+ }
+
+ // \uHHHH
+ $char = twig_convert_encoding($char, 'UTF-16BE', 'UTF-8');
+ $char = strtoupper(bin2hex($char));
+
+ if (4 >= \strlen($char)) {
+ return sprintf('\u%04s', $char);
+ }
+
+ return sprintf('\u%04s\u%04s', substr($char, 0, -4), substr($char, -4));
+ }, $string);
+
+ if ('UTF-8' !== $charset) {
+ $string = iconv('UTF-8', $charset, $string);
+ }
+
+ return $string;
+
+ case 'css':
+ if ('UTF-8' !== $charset) {
+ $string = iconv($charset, 'UTF-8', $string);
+ }
+
+ if (!preg_match('//u', $string)) {
+ throw new RuntimeError('The string to escape is not a valid UTF-8 string.');
+ }
+
+ $string = preg_replace_callback('#[^a-zA-Z0-9]#Su', function ($matches) {
+ $char = $matches[0];
+
+ return sprintf('\\%X ', 1 === \strlen($char) ? \ord($char) : mb_ord($char, 'UTF-8'));
+ }, $string);
+
+ if ('UTF-8' !== $charset) {
+ $string = iconv('UTF-8', $charset, $string);
+ }
+
+ return $string;
+
+ case 'html_attr':
+ if ('UTF-8' !== $charset) {
+ $string = iconv($charset, 'UTF-8', $string);
+ }
+
+ if (!preg_match('//u', $string)) {
+ throw new RuntimeError('The string to escape is not a valid UTF-8 string.');
+ }
+
+ $string = preg_replace_callback('#[^a-zA-Z0-9,\.\-_]#Su', function ($matches) {
+ /**
+ * This function is adapted from code coming from Zend Framework.
+ *
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (https://www.zend.com)
+ * @license https://framework.zend.com/license/new-bsd New BSD License
+ */
+ $chr = $matches[0];
+ $ord = \ord($chr);
+
+ /*
+ * The following replaces characters undefined in HTML with the
+ * hex entity for the Unicode replacement character.
+ */
+ if (($ord <= 0x1f && "\t" != $chr && "\n" != $chr && "\r" != $chr) || ($ord >= 0x7f && $ord <= 0x9f)) {
+ return '�';
+ }
+
+ /*
+ * Check if the current character to escape has a name entity we should
+ * replace it with while grabbing the hex value of the character.
+ */
+ if (1 === \strlen($chr)) {
+ /*
+ * While HTML supports far more named entities, the lowest common denominator
+ * has become HTML5's XML Serialisation which is restricted to the those named
+ * entities that XML supports. Using HTML entities would result in this error:
+ * XML Parsing Error: undefined entity
+ */
+ static $entityMap = [
+ 34 => '"', /* quotation mark */
+ 38 => '&', /* ampersand */
+ 60 => '<', /* less-than sign */
+ 62 => '>', /* greater-than sign */
+ ];
+
+ if (isset($entityMap[$ord])) {
+ return $entityMap[$ord];
+ }
+
+ return sprintf('%02X;', $ord);
+ }
+
+ /*
+ * Per OWASP recommendations, we'll use hex entities for any other
+ * characters where a named entity does not exist.
+ */
+ return sprintf('%04X;', mb_ord($chr, 'UTF-8'));
+ }, $string);
+
+ if ('UTF-8' !== $charset) {
+ $string = iconv('UTF-8', $charset, $string);
+ }
+
+ return $string;
+
+ case 'url':
+ return rawurlencode($string);
+
+ default:
+ static $escapers;
+
+ if (null === $escapers) {
+ // merge the ones set on CoreExtension for BC (to be removed in 3.0)
+ $escapers = array_merge(
+ $env->getExtension(CoreExtension::class)->getEscapers(false),
+ $env->getExtension(EscaperExtension::class)->getEscapers()
+ );
+ }
+
+ if (isset($escapers[$strategy])) {
+ return $escapers[$strategy]($env, $string, $charset);
+ }
+
+ $validStrategies = implode(', ', array_merge(['html', 'js', 'url', 'css', 'html_attr'], array_keys($escapers)));
+
+ throw new RuntimeError(sprintf('Invalid escaping strategy "%s" (valid ones: %s).', $strategy, $validStrategies));
+ }
+}
+
+/**
+ * @internal
+ */
+function twig_escape_filter_is_safe(Node $filterArgs)
+{
+ foreach ($filterArgs as $arg) {
+ if ($arg instanceof ConstantExpression) {
+ return [$arg->getAttribute('value')];
+ }
+
+ return [];
+ }
+
+ return ['html'];
+}
}
diff --git a/vendor/twig/twig/src/Node/BlockNode.php b/vendor/twig/twig/src/Node/BlockNode.php
index cafceb95b3..4da6e6ff77 100644
--- a/vendor/twig/twig/src/Node/BlockNode.php
+++ b/vendor/twig/twig/src/Node/BlockNode.php
@@ -32,6 +32,7 @@ public function compile(Compiler $compiler)
->addDebugInfo($this)
->write(sprintf("public function block_%s(\$context, array \$blocks = [])\n", $this->getAttribute('name')), "{\n")
->indent()
+ ->write("\$macros = \$this->macros;\n")
;
$compiler
diff --git a/vendor/twig/twig/src/Node/Expression/MethodCallExpression.php b/vendor/twig/twig/src/Node/Expression/MethodCallExpression.php
index 7450c51802..d5287f85f3 100644
--- a/vendor/twig/twig/src/Node/Expression/MethodCallExpression.php
+++ b/vendor/twig/twig/src/Node/Expression/MethodCallExpression.php
@@ -17,7 +17,7 @@ class MethodCallExpression extends AbstractExpression
{
public function __construct(AbstractExpression $node, string $method, ArrayExpression $arguments, int $lineno)
{
- parent::__construct(['node' => $node, 'arguments' => $arguments], ['method' => $method, 'safe' => false], $lineno);
+ parent::__construct(['node' => $node, 'arguments' => $arguments], ['method' => $method, 'safe' => false, 'is_defined_test' => false], $lineno);
if ($node instanceof NameExpression) {
$node->setAttribute('always_defined', true);
@@ -26,11 +26,24 @@ public function __construct(AbstractExpression $node, string $method, ArrayExpre
public function compile(Compiler $compiler)
{
+ if ($this->getAttribute('is_defined_test')) {
+ $compiler
+ ->raw('method_exists($macros[')
+ ->repr($this->getNode('node')->getAttribute('name'))
+ ->raw('], ')
+ ->repr($this->getAttribute('method'))
+ ->raw(')')
+ ;
+
+ return;
+ }
+
$compiler
- ->subcompile($this->getNode('node'))
- ->raw('->')
- ->raw($this->getAttribute('method'))
- ->raw('(')
+ ->raw('twig_call_macro($macros[')
+ ->repr($this->getNode('node')->getAttribute('name'))
+ ->raw('], ')
+ ->repr($this->getAttribute('method'))
+ ->raw(', [')
;
$first = true;
foreach ($this->getNode('arguments')->getKeyValuePairs() as $pair) {
@@ -41,7 +54,10 @@ public function compile(Compiler $compiler)
$compiler->subcompile($pair['value']);
}
- $compiler->raw(')');
+ $compiler
+ ->raw('], ')
+ ->repr($this->getTemplateLine())
+ ->raw(', $context, $this->getSourceContext())');
}
}
diff --git a/vendor/twig/twig/src/Node/Expression/Test/DefinedTest.php b/vendor/twig/twig/src/Node/Expression/Test/DefinedTest.php
index e87b835777..7a898406cc 100644
--- a/vendor/twig/twig/src/Node/Expression/Test/DefinedTest.php
+++ b/vendor/twig/twig/src/Node/Expression/Test/DefinedTest.php
@@ -18,6 +18,7 @@
use Twig\Node\Expression\ConstantExpression;
use Twig\Node\Expression\FunctionExpression;
use Twig\Node\Expression\GetAttrExpression;
+use Twig\Node\Expression\MethodCallExpression;
use Twig\Node\Expression\NameExpression;
use Twig\Node\Expression\TestExpression;
use Twig\Node\Node;
@@ -47,6 +48,8 @@ public function __construct(Node $node, string $name, Node $arguments = null, in
$node->setAttribute('is_defined_test', true);
} elseif ($node instanceof ConstantExpression || $node instanceof ArrayExpression) {
$node = new ConstantExpression(true, $node->getTemplateLine());
+ } elseif ($node instanceof MethodCallExpression) {
+ $node->setAttribute('is_defined_test', true);
} else {
throw new SyntaxError('The "defined" test only works with simple variables.', $lineno);
}
diff --git a/vendor/twig/twig/src/Node/ImportNode.php b/vendor/twig/twig/src/Node/ImportNode.php
index e66954bf99..b661f4313a 100644
--- a/vendor/twig/twig/src/Node/ImportNode.php
+++ b/vendor/twig/twig/src/Node/ImportNode.php
@@ -22,20 +22,28 @@
*/
class ImportNode extends Node
{
- public function __construct(AbstractExpression $expr, AbstractExpression $var, int $lineno, string $tag = null)
+ public function __construct(AbstractExpression $expr, AbstractExpression $var, int $lineno, string $tag = null, bool $global = true)
{
- parent::__construct(['expr' => $expr, 'var' => $var], [], $lineno, $tag);
+ parent::__construct(['expr' => $expr, 'var' => $var], ['global' => $global], $lineno, $tag);
}
public function compile(Compiler $compiler)
{
$compiler
->addDebugInfo($this)
- ->write('')
- ->subcompile($this->getNode('var'))
- ->raw(' = ')
+ ->write('$macros[')
+ ->repr($this->getNode('var')->getAttribute('name'))
+ ->raw('] = ')
;
+ if ($this->getAttribute('global')) {
+ $compiler
+ ->raw('$this->macros[')
+ ->repr($this->getNode('var')->getAttribute('name'))
+ ->raw('] = ')
+ ;
+ }
+
if ($this->getNode('expr') instanceof NameExpression && '_self' === $this->getNode('expr')->getAttribute('name')) {
$compiler->raw('$this');
} else {
diff --git a/vendor/twig/twig/src/Node/MacroNode.php b/vendor/twig/twig/src/Node/MacroNode.php
index 8e927a51de..6bb190937d 100644
--- a/vendor/twig/twig/src/Node/MacroNode.php
+++ b/vendor/twig/twig/src/Node/MacroNode.php
@@ -63,9 +63,7 @@ public function compile(Compiler $compiler)
->raw(")\n")
->write("{\n")
->indent()
- ;
-
- $compiler
+ ->write("\$macros = \$this->macros;\n")
->write("\$context = \$this->env->mergeGlobals([\n")
->indent()
;
diff --git a/vendor/twig/twig/src/Node/ModuleNode.php b/vendor/twig/twig/src/Node/ModuleNode.php
index 745ec3a397..b23a342e86 100644
--- a/vendor/twig/twig/src/Node/ModuleNode.php
+++ b/vendor/twig/twig/src/Node/ModuleNode.php
@@ -166,7 +166,8 @@ protected function compileClassHeader(Compiler $compiler)
->raw(sprintf(" extends %s\n", $compiler->getEnvironment()->getBaseTemplateClass(false)))
->write("{\n")
->indent()
- ->write("private \$source;\n\n")
+ ->write("private \$source;\n")
+ ->write("private \$macros = [];\n\n")
;
}
@@ -310,6 +311,7 @@ protected function compileDisplay(Compiler $compiler)
$compiler
->write("protected function doDisplay(array \$context, array \$blocks = [])\n", "{\n")
->indent()
+ ->write("\$macros = \$this->macros;\n")
->subcompile($this->getNode('display_start'))
->subcompile($this->getNode('body'))
;
diff --git a/vendor/twig/twig/src/NodeVisitor/MacroAutoImportNodeVisitor.php b/vendor/twig/twig/src/NodeVisitor/MacroAutoImportNodeVisitor.php
new file mode 100644
index 0000000000..684e73af4a
--- /dev/null
+++ b/vendor/twig/twig/src/NodeVisitor/MacroAutoImportNodeVisitor.php
@@ -0,0 +1,76 @@
+
+ */
+final class MacroAutoImportNodeVisitor implements NodeVisitorInterface
+{
+ private $inAModule = false;
+ private $hasMacroCalls = false;
+
+ public function enterNode(Node $node, Environment $env)
+ {
+ if ($node instanceof ModuleNode) {
+ $this->inAModule = true;
+ $this->hasMacroCalls = false;
+ }
+
+ return $node;
+ }
+
+ public function leaveNode(Node $node, Environment $env)
+ {
+ if ($node instanceof ModuleNode) {
+ $this->inAModule = false;
+ if ($this->hasMacroCalls) {
+ $body = [new ImportNode(new NameExpression('_self', 0), new AssignNameExpression('_self', 0), 0, 'import', true)];
+ foreach ($node->getNode('body') as $n) {
+ $body[] = $n;
+ }
+ $node->setNode('body', new Node($body));
+ }
+ } elseif ($this->inAModule) {
+ if (
+ $node instanceof GetAttrExpression &&
+ $node->getNode('node') instanceof NameExpression &&
+ '_self' === $node->getNode('node')->getAttribute('name') &&
+ $node->getNode('attribute') instanceof ConstantExpression
+ ) {
+ $this->hasMacroCalls = true;
+
+ $name = $node->getNode('attribute')->getAttribute('value');
+ $node = new MethodCallExpression($node->getNode('node'), 'macro_'.$name, $node->getNode('arguments'), $node->getTemplateLine());
+ $node->setAttribute('safe', true);
+ }
+ }
+
+ return $node;
+ }
+
+ public function getPriority()
+ {
+ // we must be ran before auto-escaping
+ return -10;
+ }
+}
diff --git a/vendor/twig/twig/src/Parser.php b/vendor/twig/twig/src/Parser.php
index e93df8d0be..8a937e329d 100644
--- a/vendor/twig/twig/src/Parser.php
+++ b/vendor/twig/twig/src/Parser.php
@@ -207,7 +207,7 @@ public function getBlockStack()
public function peekBlockStack()
{
- return $this->blockStack[\count($this->blockStack) - 1];
+ return isset($this->blockStack[\count($this->blockStack) - 1]) ? $this->blockStack[\count($this->blockStack) - 1] : null;
}
public function popBlockStack()
@@ -279,11 +279,8 @@ public function addImportedSymbol($type, $alias, $name = null, AbstractExpressio
public function getImportedSymbol($type, $alias)
{
- foreach ($this->importedSymbols as $functions) {
- if (isset($functions[$type][$alias])) {
- return $functions[$type][$alias];
- }
- }
+ // if the symbol does not exist in the current scope (0), try in the main/global scope (last index)
+ return $this->importedSymbols[0][$type][$alias] ?? ($this->importedSymbols[\count($this->importedSymbols) - 1][$type][$alias] ?? null);
}
public function isMainScope()
diff --git a/vendor/twig/twig/src/TokenParser/ExtendsTokenParser.php b/vendor/twig/twig/src/TokenParser/ExtendsTokenParser.php
index a2e6b54b07..a44980fbc7 100644
--- a/vendor/twig/twig/src/TokenParser/ExtendsTokenParser.php
+++ b/vendor/twig/twig/src/TokenParser/ExtendsTokenParser.php
@@ -27,8 +27,10 @@ public function parse(Token $token)
{
$stream = $this->parser->getStream();
- if (!$this->parser->isMainScope()) {
- throw new SyntaxError('Cannot extend from a block.', $token->getLine(), $stream->getSourceContext());
+ if ($this->parser->peekBlockStack()) {
+ throw new SyntaxError('Cannot use "extend" in a block.', $token->getLine(), $stream->getSourceContext());
+ } elseif (!$this->parser->isMainScope()) {
+ throw new SyntaxError('Cannot use "extend" in a macro.', $token->getLine(), $stream->getSourceContext());
}
if (null !== $this->parser->getParent()) {
diff --git a/vendor/twig/twig/src/TokenParser/FilterTokenParser.php b/vendor/twig/twig/src/TokenParser/FilterTokenParser.php
index ad2ac33f1a..e57fc90aa7 100644
--- a/vendor/twig/twig/src/TokenParser/FilterTokenParser.php
+++ b/vendor/twig/twig/src/TokenParser/FilterTokenParser.php
@@ -30,21 +30,24 @@ final class FilterTokenParser extends AbstractTokenParser
{
public function parse(Token $token)
{
- @trigger_error('The "filter" tag is deprecated since Twig 2.9, use the "apply" tag instead.', E_USER_DEPRECATED);
+ $stream = $this->parser->getStream();
+ $lineno = $token->getLine();
+
+ @trigger_error(sprintf('The "filter" tag in "%s" at line %d is deprecated since Twig 2.9, use the "apply" tag instead.', $stream->getSourceContext()->getName(), $lineno), E_USER_DEPRECATED);
$name = $this->parser->getVarName();
- $ref = new BlockReferenceExpression(new ConstantExpression($name, $token->getLine()), null, $token->getLine(), $this->getTag());
+ $ref = new BlockReferenceExpression(new ConstantExpression($name, $lineno), null, $lineno, $this->getTag());
$filter = $this->parser->getExpressionParser()->parseFilterExpressionRaw($ref, $this->getTag());
- $this->parser->getStream()->expect(/* Token::BLOCK_END_TYPE */ 3);
+ $stream->expect(/* Token::BLOCK_END_TYPE */ 3);
$body = $this->parser->subparse([$this, 'decideBlockEnd'], true);
- $this->parser->getStream()->expect(/* Token::BLOCK_END_TYPE */ 3);
+ $stream->expect(/* Token::BLOCK_END_TYPE */ 3);
- $block = new BlockNode($name, $body, $token->getLine());
+ $block = new BlockNode($name, $body, $lineno);
$this->parser->setBlock($name, $block);
- return new PrintNode($filter, $token->getLine(), $this->getTag());
+ return new PrintNode($filter, $lineno, $this->getTag());
}
public function decideBlockEnd(Token $token)
diff --git a/vendor/twig/twig/src/TokenParser/ForTokenParser.php b/vendor/twig/twig/src/TokenParser/ForTokenParser.php
index 1d0c84d7ef..34430f00b0 100644
--- a/vendor/twig/twig/src/TokenParser/ForTokenParser.php
+++ b/vendor/twig/twig/src/TokenParser/ForTokenParser.php
@@ -43,7 +43,7 @@ public function parse(Token $token)
$ifexpr = null;
if ($stream->nextIf(/* Token::NAME_TYPE */ 5, 'if')) {
- @trigger_error(sprintf('Using an "if" condition on "for" tag is deprecated since Twig 2.10.0, use a "filter" filter or an "if" condition inside the "for" body instead (if your condition depends on a variable updated inside the loop).', __CLASS__), E_USER_DEPRECATED);
+ @trigger_error(sprintf('Using an "if" condition on "for" tag in "%s" at line %d is deprecated since Twig 2.10.0, use a "filter" filter or an "if" condition inside the "for" body instead (if your condition depends on a variable updated inside the loop).', $stream->getSourceContext()->getName(), $lineno), E_USER_DEPRECATED);
$ifexpr = $this->parser->getExpressionParser()->parseExpression();
}
diff --git a/vendor/twig/twig/src/TokenParser/FromTokenParser.php b/vendor/twig/twig/src/TokenParser/FromTokenParser.php
index de03586f68..dd49f2fd33 100644
--- a/vendor/twig/twig/src/TokenParser/FromTokenParser.php
+++ b/vendor/twig/twig/src/TokenParser/FromTokenParser.php
@@ -26,7 +26,7 @@ public function parse(Token $token)
{
$macro = $this->parser->getExpressionParser()->parseExpression();
$stream = $this->parser->getStream();
- $stream->expect('import');
+ $stream->expect(/* Token::NAME_TYPE */ 5, 'import');
$targets = [];
do {
@@ -46,10 +46,11 @@ public function parse(Token $token)
$stream->expect(/* Token::BLOCK_END_TYPE */ 3);
- $node = new ImportNode($macro, new AssignNameExpression($this->parser->getVarName(), $token->getLine()), $token->getLine(), $this->getTag());
+ $var = new AssignNameExpression($this->parser->getVarName(), $token->getLine());
+ $node = new ImportNode($macro, $var, $token->getLine(), $this->getTag(), $this->parser->isMainScope());
foreach ($targets as $name => $alias) {
- $this->parser->addImportedSymbol('function', $alias, 'macro_'.$name, $node->getNode('var'));
+ $this->parser->addImportedSymbol('function', $alias, 'macro_'.$name, $var);
}
return $node;
diff --git a/vendor/twig/twig/src/TokenParser/ImportTokenParser.php b/vendor/twig/twig/src/TokenParser/ImportTokenParser.php
index 4b8c3db89a..b5674c1998 100644
--- a/vendor/twig/twig/src/TokenParser/ImportTokenParser.php
+++ b/vendor/twig/twig/src/TokenParser/ImportTokenParser.php
@@ -25,13 +25,13 @@ final class ImportTokenParser extends AbstractTokenParser
public function parse(Token $token)
{
$macro = $this->parser->getExpressionParser()->parseExpression();
- $this->parser->getStream()->expect('as');
+ $this->parser->getStream()->expect(/* Token::NAME_TYPE */ 5, 'as');
$var = new AssignNameExpression($this->parser->getStream()->expect(/* Token::NAME_TYPE */ 5)->getValue(), $token->getLine());
$this->parser->getStream()->expect(/* Token::BLOCK_END_TYPE */ 3);
$this->parser->addImportedSymbol('template', $var->getAttribute('name'));
- return new ImportNode($macro, $var, $token->getLine(), $this->getTag());
+ return new ImportNode($macro, $var, $token->getLine(), $this->getTag(), $this->parser->isMainScope());
}
public function getTag()
diff --git a/vendor/twig/twig/src/TokenParser/SpacelessTokenParser.php b/vendor/twig/twig/src/TokenParser/SpacelessTokenParser.php
index c4fd36e865..601e476fc8 100644
--- a/vendor/twig/twig/src/TokenParser/SpacelessTokenParser.php
+++ b/vendor/twig/twig/src/TokenParser/SpacelessTokenParser.php
@@ -30,13 +30,14 @@ final class SpacelessTokenParser extends AbstractTokenParser
{
public function parse(Token $token)
{
- @trigger_error('The spaceless tag is deprecated since Twig 2.7, use the spaceless filter instead.', E_USER_DEPRECATED);
-
+ $stream = $this->parser->getStream();
$lineno = $token->getLine();
- $this->parser->getStream()->expect(/* Token::BLOCK_END_TYPE */ 3);
+ @trigger_error(sprintf('The spaceless tag in "%s" at line %d is deprecated since Twig 2.7, use the spaceless filter instead.', $stream->getSourceContext()->getName(), $lineno), E_USER_DEPRECATED);
+
+ $stream->expect(/* Token::BLOCK_END_TYPE */ 3);
$body = $this->parser->subparse([$this, 'decideSpacelessEnd'], true);
- $this->parser->getStream()->expect(/* Token::BLOCK_END_TYPE */ 3);
+ $stream->expect(/* Token::BLOCK_END_TYPE */ 3);
return new SpacelessNode($body, $lineno, $this->getTag());
}
diff --git a/vendor/twig/twig/src/TokenStream.php b/vendor/twig/twig/src/TokenStream.php
index a1ecd5669c..3fb9e86e3d 100644
--- a/vendor/twig/twig/src/TokenStream.php
+++ b/vendor/twig/twig/src/TokenStream.php
@@ -73,9 +73,10 @@ public function expect($type, $value = null, string $message = null): Token
$token = $this->tokens[$this->current];
if (!$token->test($type, $value)) {
$line = $token->getLine();
- throw new SyntaxError(sprintf('%sUnexpected token "%s" of value "%s" ("%s" expected%s).',
+ throw new SyntaxError(sprintf('%sUnexpected token "%s"%s ("%s" expected%s).',
$message ? $message.'. ' : '',
- Token::typeToEnglish($token->getType()), $token->getValue(),
+ Token::typeToEnglish($token->getType()),
+ $token->getValue() ? sprintf(' of value "%s"', $token->getValue()) : '',
Token::typeToEnglish($type), $value ? sprintf(' with value "%s"', $value) : ''),
$line,
$this->source
diff --git a/vendor/twig/twig/test/Twig/Tests/EnvironmentTest.php b/vendor/twig/twig/test/Twig/Tests/EnvironmentTest.php
index 27fdc99ccf..a164a0cde6 100644
--- a/vendor/twig/twig/test/Twig/Tests/EnvironmentTest.php
+++ b/vendor/twig/twig/test/Twig/Tests/EnvironmentTest.php
@@ -55,7 +55,7 @@ public function escapingStrategyCallback($name)
public function testGlobals()
{
$loader = $this->getMockBuilder(LoaderInterface::class)->getMock();
- $loader->expects($this->any())->method('getSourceContext')->will($this->returnValue(new Source('', '')));
+ $loader->expects($this->any())->method('getSourceContext')->willReturn(new Source('', ''));
// globals can be added after calling getGlobals
$twig = new Environment($loader);
@@ -189,10 +189,10 @@ public function testAutoReloadCacheMiss()
// skipped.
$cache->expects($this->once())
->method('generateKey')
- ->will($this->returnValue('key'));
+ ->willReturn('key');
$cache->expects($this->once())
->method('getTimestamp')
- ->will($this->returnValue(0));
+ ->willReturn(0);
$loader->expects($this->never())
->method('isFresh');
$cache->expects($this->once())
@@ -218,13 +218,13 @@ public function testAutoReloadCacheHit()
// the loader returns true for isFresh().
$cache->expects($this->once())
->method('generateKey')
- ->will($this->returnValue('key'));
+ ->willReturn('key');
$cache->expects($this->once())
->method('getTimestamp')
- ->will($this->returnValue($now));
+ ->willReturn($now);
$loader->expects($this->once())
->method('isFresh')
- ->will($this->returnValue(true));
+ ->willReturn(true);
$cache->expects($this->atLeastOnce())
->method('load');
@@ -244,13 +244,13 @@ public function testAutoReloadOutdatedCacheHit()
$cache->expects($this->once())
->method('generateKey')
- ->will($this->returnValue('key'));
+ ->willReturn('key');
$cache->expects($this->once())
->method('getTimestamp')
- ->will($this->returnValue($now));
+ ->willReturn($now);
$loader->expects($this->once())
->method('isFresh')
- ->will($this->returnValue(false));
+ ->willReturn(false);
$cache->expects($this->once())
->method('write');
$cache->expects($this->once())
@@ -315,7 +315,7 @@ public function testInitRuntimeWithAnExtensionUsingInitRuntimeNoDeprecation()
{
$loader = $this->getMockBuilder(LoaderInterface::class)->getMock();
$twig = new Environment($loader);
- $loader->expects($this->once())->method('getSourceContext')->will($this->returnValue(new Source('', '')));
+ $loader->expects($this->once())->method('getSourceContext')->willReturn(new Source('', ''));
$twig->addExtension(new Twig_Tests_EnvironmentTest_ExtensionWithoutDeprecationInitRuntime());
$twig->load('');
@@ -339,7 +339,7 @@ public function testOverrideExtension()
public function testAddRuntimeLoader()
{
$runtimeLoader = $this->getMockBuilder(RuntimeLoaderInterface::class)->getMock();
- $runtimeLoader->expects($this->any())->method('load')->will($this->returnValue(new Twig_Tests_EnvironmentTest_Runtime()));
+ $runtimeLoader->expects($this->any())->method('load')->willReturn(new Twig_Tests_EnvironmentTest_Runtime());
$loader = new ArrayLoader([
'func_array' => '{{ from_runtime_array("foo") }}',
@@ -379,11 +379,11 @@ protected function getMockLoader($templateName, $templateContent)
$loader->expects($this->any())
->method('getSourceContext')
->with($templateName)
- ->will($this->returnValue(new Source($templateContent, $templateName)));
+ ->willReturn(new Source($templateContent, $templateName));
$loader->expects($this->any())
->method('getCacheKey')
->with($templateName)
- ->will($this->returnValue($templateName));
+ ->willReturn($templateName);
return $loader;
}
diff --git a/vendor/twig/twig/test/Twig/Tests/Extension/CoreTest.php b/vendor/twig/twig/test/Twig/Tests/Extension/CoreTest.php
index 06e55ef3ee..5dc0685241 100644
--- a/vendor/twig/twig/test/Twig/Tests/Extension/CoreTest.php
+++ b/vendor/twig/twig/test/Twig/Tests/Extension/CoreTest.php
@@ -10,7 +10,6 @@
*/
use Twig\Environment;
-use Twig\Extension\CoreExtension;
use Twig\Loader\LoaderInterface;
class Twig_Tests_Extension_CoreTest extends \PHPUnit\Framework\TestCase
@@ -126,34 +125,6 @@ public function testReverseFilterOnNonUTF8String()
$this->assertEquals($output, 'éÄ');
}
- /**
- * @dataProvider provideCustomEscaperCases
- */
- public function testCustomEscaper($expected, $string, $strategy)
- {
- $twig = new Environment($this->getMockBuilder(LoaderInterface::class)->getMock());
- $twig->getExtension(CoreExtension::class)->setEscaper('foo', 'foo_escaper_for_test');
-
- $this->assertSame($expected, twig_escape_filter($twig, $string, $strategy));
- }
-
- public function provideCustomEscaperCases()
- {
- return [
- ['fooUTF-8', 'foo', 'foo'],
- ['UTF-8', null, 'foo'],
- ['42UTF-8', 42, 'foo'],
- ];
- }
-
- /**
- * @expectedException \Twig\Error\RuntimeError
- */
- public function testUnknownCustomEscaper()
- {
- twig_escape_filter(new Environment($this->getMockBuilder(LoaderInterface::class)->getMock()), 'foo', 'bar');
- }
-
/**
* @dataProvider provideTwigFirstCases
*/
@@ -280,11 +251,6 @@ public function provideSliceFilterCases()
}
}
-function foo_escaper_for_test(Environment $env, $string, $charset)
-{
- return $string.$charset;
-}
-
final class CoreTestIteratorAggregate implements \IteratorAggregate
{
private $iterator;
diff --git a/vendor/twig/twig/test/Twig/Tests/Extension/EscaperTest.php b/vendor/twig/twig/test/Twig/Tests/Extension/EscaperTest.php
new file mode 100644
index 0000000000..98f5a07842
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Extension/EscaperTest.php
@@ -0,0 +1,403 @@
+ ''',
+ '"' => '"',
+ '<' => '<',
+ '>' => '>',
+ '&' => '&',
+ ];
+
+ protected $htmlAttrSpecialChars = [
+ '\'' => ''',
+ /* Characters beyond ASCII value 255 to unicode escape */
+ 'Ā' => 'Ā',
+ '😀' => '😀',
+ /* Immune chars excluded */
+ ',' => ',',
+ '.' => '.',
+ '-' => '-',
+ '_' => '_',
+ /* Basic alnums excluded */
+ 'a' => 'a',
+ 'A' => 'A',
+ 'z' => 'z',
+ 'Z' => 'Z',
+ '0' => '0',
+ '9' => '9',
+ /* Basic control characters and null */
+ "\r" => '
',
+ "\n" => '
',
+ "\t" => ' ',
+ "\0" => '�', // should use Unicode replacement char
+ /* Encode chars as named entities where possible */
+ '<' => '<',
+ '>' => '>',
+ '&' => '&',
+ '"' => '"',
+ /* Encode spaces for quoteless attribute protection */
+ ' ' => ' ',
+ ];
+
+ protected $jsSpecialChars = [
+ /* HTML special chars - escape without exception to hex */
+ '<' => '\\u003C',
+ '>' => '\\u003E',
+ '\'' => '\\u0027',
+ '"' => '\\u0022',
+ '&' => '\\u0026',
+ '/' => '\\/',
+ /* Characters beyond ASCII value 255 to unicode escape */
+ 'Ā' => '\\u0100',
+ '😀' => '\\uD83D\\uDE00',
+ /* Immune chars excluded */
+ ',' => ',',
+ '.' => '.',
+ '_' => '_',
+ /* Basic alnums excluded */
+ 'a' => 'a',
+ 'A' => 'A',
+ 'z' => 'z',
+ 'Z' => 'Z',
+ '0' => '0',
+ '9' => '9',
+ /* Basic control characters and null */
+ "\r" => '\r',
+ "\n" => '\n',
+ "\x08" => '\b',
+ "\t" => '\t',
+ "\x0C" => '\f',
+ "\0" => '\\u0000',
+ /* Encode spaces for quoteless attribute protection */
+ ' ' => '\\u0020',
+ ];
+
+ protected $urlSpecialChars = [
+ /* HTML special chars - escape without exception to percent encoding */
+ '<' => '%3C',
+ '>' => '%3E',
+ '\'' => '%27',
+ '"' => '%22',
+ '&' => '%26',
+ /* Characters beyond ASCII value 255 to hex sequence */
+ 'Ā' => '%C4%80',
+ /* Punctuation and unreserved check */
+ ',' => '%2C',
+ '.' => '.',
+ '_' => '_',
+ '-' => '-',
+ ':' => '%3A',
+ ';' => '%3B',
+ '!' => '%21',
+ /* Basic alnums excluded */
+ 'a' => 'a',
+ 'A' => 'A',
+ 'z' => 'z',
+ 'Z' => 'Z',
+ '0' => '0',
+ '9' => '9',
+ /* Basic control characters and null */
+ "\r" => '%0D',
+ "\n" => '%0A',
+ "\t" => '%09',
+ "\0" => '%00',
+ /* PHP quirks from the past */
+ ' ' => '%20',
+ '~' => '~',
+ '+' => '%2B',
+ ];
+
+ protected $cssSpecialChars = [
+ /* HTML special chars - escape without exception to hex */
+ '<' => '\\3C ',
+ '>' => '\\3E ',
+ '\'' => '\\27 ',
+ '"' => '\\22 ',
+ '&' => '\\26 ',
+ /* Characters beyond ASCII value 255 to unicode escape */
+ 'Ā' => '\\100 ',
+ /* Immune chars excluded */
+ ',' => '\\2C ',
+ '.' => '\\2E ',
+ '_' => '\\5F ',
+ /* Basic alnums excluded */
+ 'a' => 'a',
+ 'A' => 'A',
+ 'z' => 'z',
+ 'Z' => 'Z',
+ '0' => '0',
+ '9' => '9',
+ /* Basic control characters and null */
+ "\r" => '\\D ',
+ "\n" => '\\A ',
+ "\t" => '\\9 ',
+ "\0" => '\\0 ',
+ /* Encode spaces for quoteless attribute protection */
+ ' ' => '\\20 ',
+ ];
+
+ public function testHtmlEscapingConvertsSpecialChars()
+ {
+ $twig = new \Twig\Environment($this->getMockBuilder(\Twig\Loader\LoaderInterface::class)->getMock());
+ foreach ($this->htmlSpecialChars as $key => $value) {
+ $this->assertEquals($value, twig_escape_filter($twig, $key, 'html'), 'Failed to escape: '.$key);
+ }
+ }
+
+ public function testHtmlAttributeEscapingConvertsSpecialChars()
+ {
+ $twig = new \Twig\Environment($this->getMockBuilder(\Twig\Loader\LoaderInterface::class)->getMock());
+ foreach ($this->htmlAttrSpecialChars as $key => $value) {
+ $this->assertEquals($value, twig_escape_filter($twig, $key, 'html_attr'), 'Failed to escape: '.$key);
+ }
+ }
+
+ public function testJavascriptEscapingConvertsSpecialChars()
+ {
+ $twig = new \Twig\Environment($this->getMockBuilder(\Twig\Loader\LoaderInterface::class)->getMock());
+ foreach ($this->jsSpecialChars as $key => $value) {
+ $this->assertEquals($value, twig_escape_filter($twig, $key, 'js'), 'Failed to escape: '.$key);
+ }
+ }
+
+ public function testJavascriptEscapingReturnsStringIfZeroLength()
+ {
+ $twig = new \Twig\Environment($this->getMockBuilder(\Twig\Loader\LoaderInterface::class)->getMock());
+ $this->assertEquals('', twig_escape_filter($twig, '', 'js'));
+ }
+
+ public function testJavascriptEscapingReturnsStringIfContainsOnlyDigits()
+ {
+ $twig = new \Twig\Environment($this->getMockBuilder(\Twig\Loader\LoaderInterface::class)->getMock());
+ $this->assertEquals('123', twig_escape_filter($twig, '123', 'js'));
+ }
+
+ public function testCssEscapingConvertsSpecialChars()
+ {
+ $twig = new \Twig\Environment($this->getMockBuilder(\Twig\Loader\LoaderInterface::class)->getMock());
+ foreach ($this->cssSpecialChars as $key => $value) {
+ $this->assertEquals($value, twig_escape_filter($twig, $key, 'css'), 'Failed to escape: '.$key);
+ }
+ }
+
+ public function testCssEscapingReturnsStringIfZeroLength()
+ {
+ $twig = new \Twig\Environment($this->getMockBuilder(\Twig\Loader\LoaderInterface::class)->getMock());
+ $this->assertEquals('', twig_escape_filter($twig, '', 'css'));
+ }
+
+ public function testCssEscapingReturnsStringIfContainsOnlyDigits()
+ {
+ $twig = new \Twig\Environment($this->getMockBuilder(\Twig\Loader\LoaderInterface::class)->getMock());
+ $this->assertEquals('123', twig_escape_filter($twig, '123', 'css'));
+ }
+
+ public function testUrlEscapingConvertsSpecialChars()
+ {
+ $twig = new \Twig\Environment($this->getMockBuilder(\Twig\Loader\LoaderInterface::class)->getMock());
+ foreach ($this->urlSpecialChars as $key => $value) {
+ $this->assertEquals($value, twig_escape_filter($twig, $key, 'url'), 'Failed to escape: '.$key);
+ }
+ }
+
+ /**
+ * Range tests to confirm escaped range of characters is within OWASP recommendation.
+ */
+
+ /**
+ * Only testing the first few 2 ranges on this prot. function as that's all these
+ * other range tests require.
+ */
+ public function testUnicodeCodepointConversionToUtf8()
+ {
+ $expected = ' ~ޙ';
+ $codepoints = [0x20, 0x7e, 0x799];
+ $result = '';
+ foreach ($codepoints as $value) {
+ $result .= $this->codepointToUtf8($value);
+ }
+ $this->assertEquals($expected, $result);
+ }
+
+ /**
+ * Convert a Unicode Codepoint to a literal UTF-8 character.
+ *
+ * @param int $codepoint Unicode codepoint in hex notation
+ *
+ * @return string UTF-8 literal string
+ */
+ protected function codepointToUtf8($codepoint)
+ {
+ if ($codepoint < 0x80) {
+ return \chr($codepoint);
+ }
+ if ($codepoint < 0x800) {
+ return \chr($codepoint >> 6 & 0x3f | 0xc0)
+ .\chr($codepoint & 0x3f | 0x80);
+ }
+ if ($codepoint < 0x10000) {
+ return \chr($codepoint >> 12 & 0x0f | 0xe0)
+ .\chr($codepoint >> 6 & 0x3f | 0x80)
+ .\chr($codepoint & 0x3f | 0x80);
+ }
+ if ($codepoint < 0x110000) {
+ return \chr($codepoint >> 18 & 0x07 | 0xf0)
+ .\chr($codepoint >> 12 & 0x3f | 0x80)
+ .\chr($codepoint >> 6 & 0x3f | 0x80)
+ .\chr($codepoint & 0x3f | 0x80);
+ }
+ throw new \Exception('Codepoint requested outside of Unicode range.');
+ }
+
+ public function testJavascriptEscapingEscapesOwaspRecommendedRanges()
+ {
+ $twig = new \Twig\Environment($this->getMockBuilder(\Twig\Loader\LoaderInterface::class)->getMock());
+ $immune = [',', '.', '_']; // Exceptions to escaping ranges
+ for ($chr = 0; $chr < 0xFF; ++$chr) {
+ if ($chr >= 0x30 && $chr <= 0x39
+ || $chr >= 0x41 && $chr <= 0x5A
+ || $chr >= 0x61 && $chr <= 0x7A) {
+ $literal = $this->codepointToUtf8($chr);
+ $this->assertEquals($literal, twig_escape_filter($twig, $literal, 'js'));
+ } else {
+ $literal = $this->codepointToUtf8($chr);
+ if (\in_array($literal, $immune)) {
+ $this->assertEquals($literal, twig_escape_filter($twig, $literal, 'js'));
+ } else {
+ $this->assertNotEquals(
+ $literal,
+ twig_escape_filter($twig, $literal, 'js'),
+ "$literal should be escaped!");
+ }
+ }
+ }
+ }
+
+ public function testHtmlAttributeEscapingEscapesOwaspRecommendedRanges()
+ {
+ $twig = new \Twig\Environment($this->getMockBuilder(\Twig\Loader\LoaderInterface::class)->getMock());
+ $immune = [',', '.', '-', '_']; // Exceptions to escaping ranges
+ for ($chr = 0; $chr < 0xFF; ++$chr) {
+ if ($chr >= 0x30 && $chr <= 0x39
+ || $chr >= 0x41 && $chr <= 0x5A
+ || $chr >= 0x61 && $chr <= 0x7A) {
+ $literal = $this->codepointToUtf8($chr);
+ $this->assertEquals($literal, twig_escape_filter($twig, $literal, 'html_attr'));
+ } else {
+ $literal = $this->codepointToUtf8($chr);
+ if (\in_array($literal, $immune)) {
+ $this->assertEquals($literal, twig_escape_filter($twig, $literal, 'html_attr'));
+ } else {
+ $this->assertNotEquals(
+ $literal,
+ twig_escape_filter($twig, $literal, 'html_attr'),
+ "$literal should be escaped!");
+ }
+ }
+ }
+ }
+
+ public function testCssEscapingEscapesOwaspRecommendedRanges()
+ {
+ $twig = new \Twig\Environment($this->getMockBuilder(\Twig\Loader\LoaderInterface::class)->getMock());
+ // CSS has no exceptions to escaping ranges
+ for ($chr = 0; $chr < 0xFF; ++$chr) {
+ if ($chr >= 0x30 && $chr <= 0x39
+ || $chr >= 0x41 && $chr <= 0x5A
+ || $chr >= 0x61 && $chr <= 0x7A) {
+ $literal = $this->codepointToUtf8($chr);
+ $this->assertEquals($literal, twig_escape_filter($twig, $literal, 'css'));
+ } else {
+ $literal = $this->codepointToUtf8($chr);
+ $this->assertNotEquals(
+ $literal,
+ twig_escape_filter($twig, $literal, 'css'),
+ "$literal should be escaped!");
+ }
+ }
+ }
+
+ /**
+ * @dataProvider provideCustomEscaperCases
+ */
+ public function testCustomEscaper($expected, $string, $strategy)
+ {
+ $twig = new Environment($this->getMockBuilder(LoaderInterface::class)->getMock());
+ $twig->getExtension(EscaperExtension::class)->setEscaper('foo', 'foo_escaper_for_test');
+
+ $this->assertSame($expected, twig_escape_filter($twig, $string, $strategy));
+ }
+
+ public function provideCustomEscaperCases()
+ {
+ return [
+ ['fooUTF-8', 'foo', 'foo'],
+ ['UTF-8', null, 'foo'],
+ ['42UTF-8', 42, 'foo'],
+ ];
+ }
+
+ /**
+ * @expectedException \Twig\Error\RuntimeError
+ */
+ public function testUnknownCustomEscaper()
+ {
+ twig_escape_filter(new Environment($this->getMockBuilder(LoaderInterface::class)->getMock()), 'foo', 'bar');
+ }
+
+ /**
+ * @dataProvider provideObjectsForEscaping
+ */
+ public function testObjectEscaping(string $escapedHtml, string $escapedJs, array $safeClasses)
+ {
+ $obj = new Twig_Tests_Extension_TestClass();
+ $twig = new Environment($this->getMockBuilder('\Twig\Loader\LoaderInterface')->getMock());
+ $twig->getExtension('\Twig\Extension\EscaperExtension')->setSafeClasses($safeClasses);
+ $this->assertSame($escapedHtml, twig_escape_filter($twig, $obj, 'html', null, true));
+ $this->assertSame($escapedJs, twig_escape_filter($twig, $obj, 'js', null, true));
+ }
+
+ public function provideObjectsForEscaping()
+ {
+ return [
+ ['<br />', ' ', ['\Twig_Tests_Extension_TestClass' => ['js']]],
+ [' ', '\u003Cbr\u0020\/\u003E', ['\Twig_Tests_Extension_TestClass' => ['html']]],
+ ['<br />', ' ', ['\Twig_Tests_Extension_SafeHtmlInterface' => ['js']]],
+ [' ', ' ', ['\Twig_Tests_Extension_SafeHtmlInterface' => ['all']]],
+ ];
+ }
+}
+
+function foo_escaper_for_test(Environment $twig, $string, $charset)
+{
+ return $string.$charset;
+}
+
+interface Twig_Tests_Extension_SafeHtmlInterface
+{
+}
+class Twig_Tests_Extension_TestClass implements Twig_Tests_Extension_SafeHtmlInterface
+{
+ public function __toString()
+ {
+ return ' ';
+ }
+}
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/string_operator_as_var_assignment.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/string_operator_as_var_assignment.test
new file mode 100644
index 0000000000..478d4eb5e5
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/string_operator_as_var_assignment.test
@@ -0,0 +1,18 @@
+--TEST--
+Twig supports the string operators as variable names in assignments
+--TEMPLATE--
+{% for matches in [1, 2] %}
+ {{- matches }}
+{% endfor %}
+
+{% set matches = [1, 2] %}
+
+OK
+--DATA--
+return []
+--EXPECT--
+1
+2
+
+
+OK
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/filter.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/filter.test
index af1b8cc116..3d3fdcc68b 100644
--- a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/filter.test
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/filter.test
@@ -26,10 +26,20 @@
{% for k, v in ita|filter(v => v > offset) -%}
{{ k }} = {{ v }}
{% endfor %}
+
+{% for k, v in xml|filter(x => true) %}
+{{ k }}/{{ v }}
+{% endfor %}
+
+{# we can iterate more than once #}
+{% for k, v in xml|filter(x => true) %}
+{{ k }}/{{ v }}
+{% endfor %}
--DATA--
return [
'it' => new \ArrayIterator(['a' => 1, 'b' => 2, 'c' => 5, 'd' => 8]),
'ita' => new IteratorAggregateStub(['a' => 1, 'b' => 2, 'c' => 5, 'd' => 8]),
+ 'xml' => new \SimpleXMLElement('foobarbaz'),
]
--EXPECT--
1 = 5
@@ -50,3 +60,11 @@ d = 8
c = 5
d = 8
+
+elem/foo
+elem/bar
+elem/baz
+
+elem/foo
+elem/bar
+elem/baz
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/macros/unknown_macro.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/macros/unknown_macro.test
new file mode 100644
index 0000000000..10ea6c62a7
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/macros/unknown_macro.test
@@ -0,0 +1,10 @@
+--TEST--
+macro
+--TEMPLATE--
+{% import _self as macros %}
+
+{{ macros.unknown() }}
+--DATA--
+return []
+--EXCEPTION--
+Twig\Error\RuntimeError: Macro "unknown" is not defined in template "index.twig" in "index.twig" at line 4.
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/block/capturing_block.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/block/capturing_block.test
index 1456fe3f64..4182b8c361 100644
--- a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/block/capturing_block.test
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/block/capturing_block.test
@@ -1,7 +1,7 @@
--TEST--
capturing "block" tag
--DEPRECATION--
-The spaceless tag is deprecated since Twig 2.7, use the spaceless filter instead.
+The spaceless tag in "index.twig" at line 4 is deprecated since Twig 2.7, use the spaceless filter instead.
--TEMPLATE--
{% set foo %}{% block foo %}FOO{% endblock %}{% endset %}
{{ foo }}
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/basic.legacy.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/basic.legacy.test
index 1efedb94ad..28f12c1abc 100644
--- a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/basic.legacy.test
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/basic.legacy.test
@@ -1,7 +1,7 @@
--TEST--
"filter" tag applies a filter on its children
--DEPRECATION--
-The "filter" tag is deprecated since Twig 2.9, use the "apply" tag instead.
+The "filter" tag in "index.twig" at line 2 is deprecated since Twig 2.9, use the "apply" tag instead.
--TEMPLATE--
{% filter upper %}
Some text with a {{ var }}
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/json_encode.legacy.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/json_encode.legacy.test
index 7ec6be3bd7..1da38498f8 100644
--- a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/json_encode.legacy.test
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/json_encode.legacy.test
@@ -1,7 +1,7 @@
--TEST--
"filter" tag applies a filter on its children
--DEPRECATION--
-The "filter" tag is deprecated since Twig 2.9, use the "apply" tag instead.
+The "filter" tag in "index.twig" at line 2 is deprecated since Twig 2.9, use the "apply" tag instead.
--TEMPLATE--
{% filter json_encode|raw %}test{% endfilter %}
--DATA--
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/multiple.legacy.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/multiple.legacy.test
index 0e8d251e14..f7aa54fb38 100644
--- a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/multiple.legacy.test
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/multiple.legacy.test
@@ -1,7 +1,7 @@
--TEST--
"filter" tags accept multiple chained filters
--DEPRECATION--
-The "filter" tag is deprecated since Twig 2.9, use the "apply" tag instead.
+The "filter" tag in "index.twig" at line 2 is deprecated since Twig 2.9, use the "apply" tag instead.
--TEMPLATE--
{% filter lower|title %}
{{ var }}
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/nested.legacy.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/nested.legacy.test
index 1d894fea6b..a8c69066ba 100644
--- a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/nested.legacy.test
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/nested.legacy.test
@@ -1,8 +1,8 @@
--TEST--
"filter" tags can be nested at will
--DEPRECATION--
-The "filter" tag is deprecated since Twig 2.9, use the "apply" tag instead.
-The "filter" tag is deprecated since Twig 2.9, use the "apply" tag instead.
+The "filter" tag in "index.twig" at line 2 is deprecated since Twig 2.9, use the "apply" tag instead.
+The "filter" tag in "index.twig" at line 4 is deprecated since Twig 2.9, use the "apply" tag instead.
--TEMPLATE--
{% filter lower|title %}
{{ var }}
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/scope.legacy.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/scope.legacy.test
index 6a7720e847..6ee1ac835b 100644
--- a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/scope.legacy.test
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/scope.legacy.test
@@ -1,7 +1,7 @@
--TEST--
"scope" tag creates a new scope
--DEPRECATION--
-The "filter" tag is deprecated since Twig 2.9, use the "apply" tag instead.
+The "filter" tag in "index.twig" at line 2 is deprecated since Twig 2.9, use the "apply" tag instead.
--TEMPLATE--
{% filter spaceless %}
{% set item = 'foo' %}
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/with_for_tag.legacy.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/with_for_tag.legacy.test
index 76985646be..5f3ab7bc84 100644
--- a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/with_for_tag.legacy.test
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/with_for_tag.legacy.test
@@ -1,7 +1,7 @@
--TEST--
"filter" tag applies the filter on "for" tags
--DEPRECATION--
-The "filter" tag is deprecated since Twig 2.9, use the "apply" tag instead.
+The "filter" tag in "index.twig" at line 2 is deprecated since Twig 2.9, use the "apply" tag instead.
--TEMPLATE--
{% filter upper %}
{% for item in items %}
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/with_if_tag.legacy.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/with_if_tag.legacy.test
index 74a4bb5773..034caaa538 100644
--- a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/with_if_tag.legacy.test
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/with_if_tag.legacy.test
@@ -1,7 +1,7 @@
--TEST--
"filter" tag applies the filter on "if" tags
--DEPRECATION--
-The "filter" tag is deprecated since Twig 2.9, use the "apply" tag instead.
+The "filter" tag in "index.twig" at line 2 is deprecated since Twig 2.9, use the "apply" tag instead.
--TEMPLATE--
{% filter upper %}
{% if items %}
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/condition.legacy.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/condition.legacy.test
index 690207e429..a35047c062 100644
--- a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/condition.legacy.test
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/condition.legacy.test
@@ -1,7 +1,7 @@
--TEST--
"for" tag takes a condition
--DEPRECATION--
-Using an "if" condition on "for" tag is deprecated since Twig 2.10.0, use a "filter" filter or an "if" condition inside the "for" body instead (if your condition depends on a variable updated inside the loop).
+Using an "if" condition on "for" tag in "index.twig" at line 2 is deprecated since Twig 2.10.0, use a "filter" filter or an "if" condition inside the "for" body instead (if your condition depends on a variable updated inside the loop).
--TEMPLATE--
{% for i in 1..5 if i is odd -%}
{{ loop.index }}.{{ i }}{{ foo.bar }}
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/extends_in_block.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/extends_in_block.test
new file mode 100644
index 0000000000..a372ea1c81
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/extends_in_block.test
@@ -0,0 +1,10 @@
+--TEST--
+"extends" tag in a block
+--TEMPLATE--
+{% block foo %}
+ {% extends "foo.twig" %}
+{% endblock %}
+--DATA--
+return []
+--EXCEPTION--
+Twig\Error\SyntaxError: Cannot use "extend" in a block in "index.twig" at line 3.
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/extends_in_macro.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/extends_in_macro.test
new file mode 100644
index 0000000000..dc87b2a8c2
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/extends_in_macro.test
@@ -0,0 +1,10 @@
+--TEST--
+"extends" tag in a macro
+--TEMPLATE--
+{% macro foo() %}
+ {% extends "foo.twig" %}
+{% endmacro %}
+--DATA--
+return []
+--EXCEPTION--
+Twig\Error\SyntaxError: Cannot use "extend" in a macro in "index.twig" at line 3.
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/auto_import.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/auto_import.test
new file mode 100644
index 0000000000..14e0afe00a
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/auto_import.test
@@ -0,0 +1,16 @@
+--TEST--
+"macro" tag
+--TEMPLATE--
+{{ _self.hello('Fabien') }}
+
+{% macro hello(name) -%}
+ Hello {{ _self.up(name) }}
+{% endmacro %}
+
+{% macro up(name) -%}
+ {{ name|upper }}
+{% endmacro %}
+--DATA--
+return []
+--EXPECT--
+Hello FABIEN
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/auto_import_blocks.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/auto_import_blocks.test
new file mode 100644
index 0000000000..59b739377b
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/auto_import_blocks.test
@@ -0,0 +1,18 @@
+--TEST--
+"macro" tag
+--TEMPLATE--
+{% block content %}
+ {{ _self.hello('Fabien') }}
+{% endblock %}
+
+{% macro hello(name) -%}
+ Hello {{ _self.up(name) }}
+{% endmacro %}
+
+{% macro up(name) -%}
+ {{ name|upper }}
+{% endmacro %}
+--DATA--
+return []
+--EXPECT--
+Hello FABIEN
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/from_embed_with_global_macro.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/from_embed_with_global_macro.test
new file mode 100644
index 0000000000..f06c31c938
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/from_embed_with_global_macro.test
@@ -0,0 +1,21 @@
+--TEST--
+"macro" tag
+--TEMPLATE--
+{% from _self import input %}
+
+{% embed 'embed' %}
+ {% block foo %}
+ {{ input("username") }}
+ {% endblock %}
+{% endembed %}
+
+{% macro input(name) -%}
+
+{% endmacro %}
+--TEMPLATE(embed)--
+ {% block foo %}
+ {% endblock %}
+--DATA--
+return []
+--EXCEPTION--
+Twig\Error\SyntaxError: Unknown "input" function in "index.twig" at line 6.
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/from_in_block_is_local.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/from_in_block_is_local.test
new file mode 100644
index 0000000000..0c89ce62a8
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/from_in_block_is_local.test
@@ -0,0 +1,18 @@
+--TEST--
+"macro" tag
+--TEMPLATE--
+{% block foo %}
+ {%- from _self import input as linput %}
+{% endblock %}
+
+{% block bar %}
+ {{- linput('username') }}
+{% endblock %}
+
+{% macro input(name) -%}
+
+{% endmacro %}
+--DATA--
+return []
+--EXCEPTION--
+Twig\Error\SyntaxError: Unknown "linput" function in "index.twig" at line 7.
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/from_local_override.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/from_local_override.test
new file mode 100644
index 0000000000..27bfbaee1f
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/from_local_override.test
@@ -0,0 +1,28 @@
+--TEST--
+"macro" tag
+--TEMPLATE--
+{%- from _self import input %}
+
+{% block foo %}
+ {%- from "macros" import input %}
+ {{- input('username') }}
+{% endblock %}
+
+{% block bar %}
+ {{- input('username') }}
+{% endblock %}
+
+{% macro input(name) -%}
+
+{% endmacro %}
+--TEMPLATE(macros)--
+{% macro input(name) %}
+
+{% endmacro %}
+--DATA--
+return []
+--EXPECT--
+
+
+
+
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/from_macro_in_a_macro.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/from_macro_in_a_macro.test
new file mode 100644
index 0000000000..168c9b3a90
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/from_macro_in_a_macro.test
@@ -0,0 +1,18 @@
+--TEST--
+"from" tag with syntax error
+--TEMPLATE--
+{% from _self import another, foo %}
+
+{{ foo() }}
+
+{% macro foo() %}
+ {{ another() }}
+{% endmacro %}
+
+{% macro another() %}
+ OK
+{% endmacro %}
+--DATA--
+return []
+--EXPECT--
+OK
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/from_macros_in_parent.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/from_macros_in_parent.test
new file mode 100644
index 0000000000..57cff7ab17
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/from_macros_in_parent.test
@@ -0,0 +1,16 @@
+--TEST--
+"macro" tag
+--TEMPLATE--
+{% from "macros" import hello %}
+
+{{ hello() }}
+--TEMPLATE(macros)--
+{% extends "parent" %}
+--TEMPLATE(parent)--
+{% macro hello() %}
+ Test
+{% endmacro %}
+--DATA--
+return []
+--EXPECT--
+Test
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/from_nested_blocks.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/from_nested_blocks.test
new file mode 100644
index 0000000000..8ede5db509
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/from_nested_blocks.test
@@ -0,0 +1,18 @@
+--TEST--
+"macro" tag
+--TEMPLATE--
+{% block foo %}
+ {%- from _self import input as linput %}
+
+ {% block bar %}
+ {{- linput('username') }}
+ {% endblock %}
+{% endblock %}
+
+{% macro input(name) -%}
+
+{% endmacro %}
+--DATA--
+return []
+--EXCEPTION--
+Twig\Error\SyntaxError: Unknown "linput" function in "index.twig" at line 6.
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/from_nested_blocks_with_global_macro.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/from_nested_blocks_with_global_macro.test
new file mode 100644
index 0000000000..384b02d8fe
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/from_nested_blocks_with_global_macro.test
@@ -0,0 +1,18 @@
+--TEST--
+"macro" tag
+--TEMPLATE--
+{%- from _self import input %}
+
+{% block foo %}
+ {% block bar %}
+ {{- input('username') }}
+ {% endblock %}
+{% endblock %}
+
+{% macro input(name) -%}
+
+{% endmacro %}
+--DATA--
+return []
+--EXPECT--
+
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/from_recursive.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/from_recursive.test
new file mode 100644
index 0000000000..09a29839d0
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/from_recursive.test
@@ -0,0 +1,27 @@
+--TEST--
+"import" tag
+--TEMPLATE--
+{% from _self import recursive_macro %}
+
+{{ recursive_macro(10) }}
+
+{% macro recursive_macro(n) %}
+ {% if n > 0 %}
+ {{- recursive_macro(n - 1) -}}
+ {% endif %}
+ {{- n }}
+{% endmacro %}
+--DATA--
+return []
+--EXPECT--
+0
+1
+2
+3
+4
+5
+6
+7
+8
+9
+10
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/from_self_parent.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/from_self_parent.test
new file mode 100644
index 0000000000..2a19871192
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/from_self_parent.test
@@ -0,0 +1,20 @@
+--TEST--
+"macro" tag
+--TEMPLATE--
+{% extends "parent" %}
+
+{% block test %}
+ {{ _self.hello() }}
+{% endblock test %}
+--TEMPLATE(parent)--
+{% block test %}
+Hello
+{% endblock test %}
+
+{% macro hello() %}
+ Test
+{% endmacro %}
+--DATA--
+return []
+--EXPECT--
+Test
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/from_syntax_error.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/from_syntax_error.test
new file mode 100644
index 0000000000..6223cfe947
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/from_syntax_error.test
@@ -0,0 +1,8 @@
+--TEST--
+"from" tag with syntax error
+--TEMPLATE--
+{% from 'forms.twig' %}
+--DATA--
+return []
+--EXCEPTION--
+Twig\Error\SyntaxError: Unexpected token "end of statement block" ("name" expected with value "import") in "index.twig" at line 2.
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/import_and_blocks.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/import_and_blocks.test
new file mode 100644
index 0000000000..721f5506a5
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/import_and_blocks.test
@@ -0,0 +1,36 @@
+--TEST--
+"macro" tag
+--TEMPLATE--
+{% import _self as macros %}
+{% from _self import input %}
+
+{% block foo %}
+ {{- macros.input('username') }}
+ {{- input('username') }}
+
+ {%- import _self as lmacros %}
+ {%- from _self import input as linput %}
+
+ {{- lmacros.input('username') }}
+ {{- linput('username') }}
+{% endblock %}
+
+{% block bar %}
+ {{- macros.input('username') }}
+ {{- input('username') }}
+{% endblock %}
+
+{% macro input(name) -%}
+
+{% endmacro %}
+--DATA--
+return []
+--EXPECT--
+
+
+
+
+
+
+
+
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/import_embed_with_global_macro.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/import_embed_with_global_macro.test
new file mode 100644
index 0000000000..3609881f0d
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/import_embed_with_global_macro.test
@@ -0,0 +1,21 @@
+--TEST--
+"macro" tag
+--TEMPLATE--
+{% import _self as macros %}
+
+{% embed 'embed' %}
+ {% block foo %}
+ {{ macros.input("username") }}
+ {% endblock %}
+{% endembed %}
+
+{% macro input(name) -%}
+
+{% endmacro %}
+--TEMPLATE(embed)--
+ {% block foo %}
+ {% endblock %}
+--DATA--
+return []
+--EXCEPTION--
+Twig\Error\RuntimeError: Variable "macros" does not exist in "index.twig" at line 6.
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/import_in_block_is_local.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/import_in_block_is_local.test
new file mode 100644
index 0000000000..9443e12221
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/import_in_block_is_local.test
@@ -0,0 +1,18 @@
+--TEST--
+"macro" tag
+--TEMPLATE--
+{% block foo %}
+ {%- import _self as lmacros %}
+{% endblock %}
+
+{% block bar %}
+ {{- lmacros.input('username') }}
+{% endblock %}
+
+{% macro input(name) -%}
+
+{% endmacro %}
+--DATA--
+return []
+--EXCEPTION--
+Twig\Error\RuntimeError: Variable "lmacros" does not exist in "index.twig" at line 7.
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/import_local_override.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/import_local_override.test
new file mode 100644
index 0000000000..7cf0552f83
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/import_local_override.test
@@ -0,0 +1,28 @@
+--TEST--
+"macro" tag
+--TEMPLATE--
+{%- import _self as macros %}
+
+{% block foo %}
+ {%- import "macros" as macros %}
+ {{- macros.input('username') }}
+{% endblock %}
+
+{% block bar %}
+ {{- macros.input('username') }}
+{% endblock %}
+
+{% macro input(name) -%}
+
+{% endmacro %}
+--TEMPLATE(macros)--
+{% macro input(name) %}
+
+{% endmacro %}
+--DATA--
+return []
+--EXPECT--
+
+
+
+
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/import_macro_in_a_macro.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/import_macro_in_a_macro.test
new file mode 100644
index 0000000000..1851f09749
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/import_macro_in_a_macro.test
@@ -0,0 +1,18 @@
+--TEST--
+"import" tag with syntax error
+--TEMPLATE--
+{% import _self as foo %}
+
+{{ foo.foo() }}
+
+{% macro foo() %}
+ {{ foo.another() }}
+{% endmacro %}
+
+{% macro another() %}
+ OK
+{% endmacro %}
+--DATA--
+return []
+--EXPECT--
+OK
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/import_macros_in_parent.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/import_macros_in_parent.test
new file mode 100644
index 0000000000..d8d5d1639a
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/import_macros_in_parent.test
@@ -0,0 +1,16 @@
+--TEST--
+"macro" tag
+--TEMPLATE--
+{% import "macros" as m %}
+
+{{ m.hello() }}
+--TEMPLATE(macros)--
+{% extends "parent" %}
+--TEMPLATE(parent)--
+{% macro hello() %}
+ Test
+{% endmacro %}
+--DATA--
+return []
+--EXPECT--
+Test
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/import_nested_blocks.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/import_nested_blocks.test
new file mode 100644
index 0000000000..821f64bf75
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/import_nested_blocks.test
@@ -0,0 +1,18 @@
+--TEST--
+"macro" tag
+--TEMPLATE--
+{% block foo %}
+ {%- import _self as lmacros %}
+
+ {% block bar %}
+ {{- lmacros.input('username') }}
+ {% endblock %}
+{% endblock %}
+
+{% macro input(name) -%}
+
+{% endmacro %}
+--DATA--
+return []
+--EXCEPTION--
+Twig\Error\RuntimeError: Variable "lmacros" does not exist in "index.twig" at line 6.
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/import_nested_blocks_with_global_macro.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/import_nested_blocks_with_global_macro.test
new file mode 100644
index 0000000000..697d665f84
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/import_nested_blocks_with_global_macro.test
@@ -0,0 +1,18 @@
+--TEST--
+"macro" tag
+--TEMPLATE--
+{%- import _self as macros %}
+
+{% block foo %}
+ {% block bar %}
+ {{- macros.input('username') }}
+ {% endblock %}
+{% endblock %}
+
+{% macro input(name) -%}
+
+{% endmacro %}
+--DATA--
+return []
+--EXPECT--
+
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/import_same_parent_and_child.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/import_same_parent_and_child.test
new file mode 100644
index 0000000000..8d9b3caa49
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/import_same_parent_and_child.test
@@ -0,0 +1,30 @@
+--TEST--
+"import" tag
+--TEMPLATE--
+{% extends "parent" %}
+
+{% macro anotherThing() -%}
+ Do it too
+{% endmacro %}
+
+{% import _self as macros %}
+{% block content %}
+ {{ parent() }}
+ {{ macros.anotherThing() }}
+{% endblock %}
+--TEMPLATE(parent)--
+{% macro thing() %}
+ Do it
+{% endmacro %}
+
+{% import _self as macros %}
+{% block content %}
+ {{ macros.thing() }}
+{% endblock %}
+--DATA--
+return []
+--EXPECT--
+Do it
+
+
+ Do it too
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/import_self_parent.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/import_self_parent.test
new file mode 100644
index 0000000000..24a8cdb50b
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/import_self_parent.test
@@ -0,0 +1,23 @@
+--TEST--
+"macro" tag
+--TEMPLATE--
+{% extends "parent" %}
+{% import _self as me %}
+
+{% block test %}
+ {{ me.hello() }}
+{% endblock test %}
+--TEMPLATE(parent)--
+{% import _self as me %}
+
+{% block test %}
+Hello
+{% endblock test %}
+
+{% macro hello() %}
+ Test
+{% endmacro %}
+--DATA--
+return []
+--EXPECT--
+Test
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/import_syntax_error.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/import_syntax_error.test
new file mode 100644
index 0000000000..b9817f0eed
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/import_syntax_error.test
@@ -0,0 +1,10 @@
+--TEST--
+"import" tag with reserved name
+--TEMPLATE--
+{% import 'forms.twig' %}
+
+{{ macros.parent() }}
+--DATA--
+return []
+--EXCEPTION--
+Twig\Error\SyntaxError: Unexpected token "end of statement block" ("name" expected with value "as") in "index.twig" at line 2.
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/spaceless/root_level_in_child.legacy.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/spaceless/root_level_in_child.legacy.test
index 8b74ef35bf..9ed406e6fb 100644
--- a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/spaceless/root_level_in_child.legacy.test
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/spaceless/root_level_in_child.legacy.test
@@ -1,7 +1,7 @@
--TEST--
"spaceless" tag in the root level of a child template
--DEPRECATION--
-The spaceless tag is deprecated since Twig 2.7, use the spaceless filter instead.
+The spaceless tag in "index.twig" at line 3 is deprecated since Twig 2.7, use the spaceless filter instead.
Using the spaceless tag at the root level of a child template in "index.twig" at line 3 is deprecated since Twig 2.5.0 and will become a syntax error in 3.0.
Nesting a block definition under a non-capturing node in "index.twig" at line 4 is deprecated since Twig 2.5.0 and will become a syntax error in 3.0.
--TEMPLATE--
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/spaceless/simple.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/spaceless/simple.test
index 1159fffb82..8dbe660e5d 100644
--- a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/spaceless/simple.test
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/spaceless/simple.test
@@ -1,7 +1,7 @@
--TEST--
"spaceless" tag removes whites between HTML tags
--DEPRECATION--
-The spaceless tag is deprecated since Twig 2.7, use the spaceless filter instead.
+The spaceless tag in "index.twig" at line 2 is deprecated since Twig 2.7, use the spaceless filter instead.
--TEMPLATE--
{% spaceless %}
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/defined_for_macros.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/defined_for_macros.test
new file mode 100644
index 0000000000..1aa45fc826
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/defined_for_macros.test
@@ -0,0 +1,41 @@
+--TEST--
+"defined" support for macros
+--TEMPLATE--
+{% import _self as macros %}
+{% from _self import hello, bar %}
+
+{% if macros.hello is defined -%}
+ OK
+{% endif %}
+
+{% if macros.foo is not defined -%}
+ OK
+{% endif %}
+
+{% if hello is defined -%}
+ OK
+{% endif %}
+
+{% if bar is not defined -%}
+ OK
+{% endif %}
+
+{% if foo is not defined -%}
+ OK
+{% endif %}
+
+{% macro hello(name) %}
+ Hello {{ name }}
+{% endmacro %}
+--DATA--
+return []
+--EXPECT--
+OK
+
+OK
+
+OK
+
+OK
+
+OK
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/in.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/in.test
index b34fd65df6..f84465ee32 100644
--- a/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/in.test
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/in.test
@@ -56,6 +56,7 @@ Twig supports the in operator
{{ '5.5' in 125.5 ? 'KO' : 'OK' }}
{{ safe in ['foo', 'bar'] ? 'OK' : 'KO' }}
+{{ 'fo' in safe ? 'OK' : 'KO' }}
--DATA--
return ['bar' => 'bar', 'foo' => ['bar' => 'bar'], 'dir_object' => new \SplFileInfo(__DIR__), 'object' => new \stdClass(), 'resource' => opendir(__DIR__), 'safe' => new \Twig\Markup('foo', 'UTF-8')]
--EXPECT--
@@ -114,3 +115,4 @@ OK
OK
OK
+OK
diff --git a/vendor/twig/twig/test/Twig/Tests/Loader/ChainTest.php b/vendor/twig/twig/test/Twig/Tests/Loader/ChainTest.php
index 9830d3ef40..a71cc96db3 100644
--- a/vendor/twig/twig/test/Twig/Tests/Loader/ChainTest.php
+++ b/vendor/twig/twig/test/Twig/Tests/Loader/ChainTest.php
@@ -79,11 +79,11 @@ public function testAddLoader()
public function testExists()
{
$loader1 = $this->getMockBuilder(LoaderInterface::class)->getMock();
- $loader1->expects($this->once())->method('exists')->will($this->returnValue(false));
+ $loader1->expects($this->once())->method('exists')->willReturn(false);
$loader1->expects($this->never())->method('getSourceContext');
$loader2 = $this->getMockBuilder(LoaderInterface::class)->getMock();
- $loader2->expects($this->once())->method('exists')->will($this->returnValue(true));
+ $loader2->expects($this->once())->method('exists')->willReturn(true);
$loader2->expects($this->never())->method('getSourceContext');
$loader = new ChainLoader();
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/BlockTest.php b/vendor/twig/twig/test/Twig/Tests/Node/BlockTest.php
index 8b4a1eca4a..c91a4d0f75 100644
--- a/vendor/twig/twig/test/Twig/Tests/Node/BlockTest.php
+++ b/vendor/twig/twig/test/Twig/Tests/Node/BlockTest.php
@@ -34,6 +34,7 @@ public function getTests()
// line 1
public function block_foo(\$context, array \$blocks = [])
{
+ \$macros = \$this->macros;
echo "foo";
}
EOF
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/ImportTest.php b/vendor/twig/twig/test/Twig/Tests/Node/ImportTest.php
index 2118bff59b..f811bfb4cd 100644
--- a/vendor/twig/twig/test/Twig/Tests/Node/ImportTest.php
+++ b/vendor/twig/twig/test/Twig/Tests/Node/ImportTest.php
@@ -36,7 +36,7 @@ public function getTests()
$tests[] = [$node, <<loadTemplate("foo.twig", null, 1)->unwrap();
+\$macros["macro"] = \$this->macros["macro"] = \$this->loadTemplate("foo.twig", null, 1)->unwrap();
EOF
];
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/MacroTest.php b/vendor/twig/twig/test/Twig/Tests/Node/MacroTest.php
index 1dda543cdd..afa68adf43 100644
--- a/vendor/twig/twig/test/Twig/Tests/Node/MacroTest.php
+++ b/vendor/twig/twig/test/Twig/Tests/Node/MacroTest.php
@@ -43,6 +43,7 @@ public function getTests()
// line 1
public function macro_foo(\$__foo__ = null, \$__bar__ = "Foo", ...\$__varargs__)
{
+ \$macros = \$this->macros;
\$context = \$this->env->mergeGlobals([
"foo" => \$__foo__,
"bar" => \$__bar__,
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/ModuleTest.php b/vendor/twig/twig/test/Twig/Tests/Node/ModuleTest.php
index 1e2d2b640c..812b1ef0b4 100644
--- a/vendor/twig/twig/test/Twig/Tests/Node/ModuleTest.php
+++ b/vendor/twig/twig/test/Twig/Tests/Node/ModuleTest.php
@@ -74,6 +74,7 @@ public function getTests()
class __TwigTemplate_%x extends \Twig\Template
{
private \$source;
+ private \$macros = [];
public function __construct(Environment \$env)
{
@@ -89,6 +90,7 @@ public function __construct(Environment \$env)
protected function doDisplay(array \$context, array \$blocks = [])
{
+ \$macros = \$this->macros;
// line 1
echo "foo";
}
@@ -100,7 +102,7 @@ public function getTemplateName()
public function getDebugInfo()
{
- return array ( 35 => 1,);
+ return array ( 37 => 1,);
}
public function getSourceContext()
@@ -136,6 +138,7 @@ public function getSourceContext()
class __TwigTemplate_%x extends \Twig\Template
{
private \$source;
+ private \$macros = [];
public function __construct(Environment \$env)
{
@@ -155,8 +158,9 @@ protected function doGetParent(array \$context)
protected function doDisplay(array \$context, array \$blocks = [])
{
+ \$macros = \$this->macros;
// line 2
- \$context["macro"] = \$this->loadTemplate("foo.twig", "foo.twig", 2)->unwrap();
+ \$macros["macro"] = \$this->macros["macro"] = \$this->loadTemplate("foo.twig", "foo.twig", 2)->unwrap();
// line 1
\$this->parent = \$this->loadTemplate("layout.twig", "foo.twig", 1);
\$this->parent->display(\$context, array_merge(\$this->blocks, \$blocks));
@@ -174,7 +178,7 @@ public function isTraitable()
public function getDebugInfo()
{
- return array ( 41 => 1, 39 => 2, 33 => 1,);
+ return array ( 43 => 1, 41 => 2, 34 => 1,);
}
public function getSourceContext()
@@ -215,6 +219,7 @@ public function getSourceContext()
class __TwigTemplate_%x extends \Twig\Template
{
private \$source;
+ private \$macros = [];
public function __construct(Environment \$env)
{
@@ -234,6 +239,7 @@ protected function doGetParent(array \$context)
protected function doDisplay(array \$context, array \$blocks = [])
{
+ \$macros = \$this->macros;
// line 4
\$context["foo"] = "foo";
// line 2
@@ -252,7 +258,7 @@ public function isTraitable()
public function getDebugInfo()
{
- return array ( 41 => 2, 39 => 4, 33 => 2,);
+ return array ( 43 => 2, 41 => 4, 34 => 2,);
}
public function getSourceContext()