This commit is contained in:
Xes
2025-08-14 22:41:49 +02:00
parent 2de81ccc46
commit 8ce45119b6
39774 changed files with 4309466 additions and 0 deletions

3
vendor/symfony/templating/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
vendor/
composer.lock
phpunit.xml

14
vendor/symfony/templating/CHANGELOG.md vendored Normal file
View File

@@ -0,0 +1,14 @@
CHANGELOG
=========
2.5.0
-----
* added ability to generate versioned URLs
* added ability to generate absolute URLs
2.1.0
-----
* added StreamingEngineInterface
* added ENT_SUBSTITUTE for the HTML escaper

View File

@@ -0,0 +1,103 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Templating;
/**
* DelegatingEngine selects an engine for a given template.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class DelegatingEngine implements EngineInterface, StreamingEngineInterface
{
/**
* @var EngineInterface[]
*/
protected $engines = [];
/**
* @param EngineInterface[] $engines An array of EngineInterface instances to add
*/
public function __construct(array $engines = [])
{
foreach ($engines as $engine) {
$this->addEngine($engine);
}
}
/**
* {@inheritdoc}
*/
public function render($name, array $parameters = [])
{
return $this->getEngine($name)->render($name, $parameters);
}
/**
* {@inheritdoc}
*/
public function stream($name, array $parameters = [])
{
$engine = $this->getEngine($name);
if (!$engine instanceof StreamingEngineInterface) {
throw new \LogicException(sprintf('Template "%s" cannot be streamed as the engine supporting it does not implement StreamingEngineInterface.', $name));
}
$engine->stream($name, $parameters);
}
/**
* {@inheritdoc}
*/
public function exists($name)
{
return $this->getEngine($name)->exists($name);
}
public function addEngine(EngineInterface $engine)
{
$this->engines[] = $engine;
}
/**
* {@inheritdoc}
*/
public function supports($name)
{
try {
$this->getEngine($name);
} catch (\RuntimeException $e) {
return false;
}
return true;
}
/**
* Get an engine able to render the given template.
*
* @param string|TemplateReferenceInterface $name A template name or a TemplateReferenceInterface instance
*
* @return EngineInterface The engine
*
* @throws \RuntimeException if no engine able to work with the template is found
*/
public function getEngine($name)
{
foreach ($this->engines as $engine) {
if ($engine->supports($name)) {
return $engine;
}
}
throw new \RuntimeException(sprintf('No engine is able to work with the template "%s".', $name));
}
}

View File

@@ -0,0 +1,64 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Templating;
/**
* EngineInterface is the interface each engine must implement.
*
* All methods rely on a template name. A template name is a
* "logical" name for the template, and as such it does not refer to
* a path on the filesystem (in fact, the template can be stored
* anywhere, like in a database).
*
* The methods should accept any name. If the name is not an instance of
* TemplateReferenceInterface, a TemplateNameParserInterface should be used to
* convert the name to a TemplateReferenceInterface instance.
*
* Each template loader uses the logical template name to look for
* the template.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
interface EngineInterface
{
/**
* Renders a template.
*
* @param string|TemplateReferenceInterface $name A template name or a TemplateReferenceInterface instance
* @param array $parameters An array of parameters to pass to the template
*
* @return string The evaluated template as a string
*
* @throws \RuntimeException if the template cannot be rendered
*/
public function render($name, array $parameters = []);
/**
* Returns true if the template exists.
*
* @param string|TemplateReferenceInterface $name A template name or a TemplateReferenceInterface instance
*
* @return bool true if the template exists, false otherwise
*
* @throws \RuntimeException if the engine cannot handle the template name
*/
public function exists($name);
/**
* Returns true if this class is able to render the given template.
*
* @param string|TemplateReferenceInterface $name A template name or a TemplateReferenceInterface instance
*
* @return bool true if this class supports the given template, false otherwise
*/
public function supports($name);
}

View File

@@ -0,0 +1,45 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Templating\Helper;
/**
* Helper is the base class for all helper classes.
*
* Most of the time, a Helper is an adapter around an existing
* class that exposes a read-only interface for templates.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
abstract class Helper implements HelperInterface
{
protected $charset = 'UTF-8';
/**
* Sets the default charset.
*
* @param string $charset The charset
*/
public function setCharset($charset)
{
$this->charset = $charset;
}
/**
* Gets the default charset.
*
* @return string The default charset
*/
public function getCharset()
{
return $this->charset;
}
}

View File

@@ -0,0 +1,41 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Templating\Helper;
/**
* HelperInterface is the interface all helpers must implement.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
interface HelperInterface
{
/**
* Returns the canonical name of this helper.
*
* @return string The canonical name
*/
public function getName();
/**
* Sets the default charset.
*
* @param string $charset The charset
*/
public function setCharset($charset);
/**
* Gets the default charset.
*
* @return string The default charset
*/
public function getCharset();
}

View File

@@ -0,0 +1,133 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Templating\Helper;
/**
* SlotsHelper manages template slots.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class SlotsHelper extends Helper
{
protected $slots = [];
protected $openSlots = [];
/**
* Starts a new slot.
*
* This method starts an output buffer that will be
* closed when the stop() method is called.
*
* @param string $name The slot name
*
* @throws \InvalidArgumentException if a slot with the same name is already started
*/
public function start($name)
{
if (\in_array($name, $this->openSlots)) {
throw new \InvalidArgumentException(sprintf('A slot named "%s" is already started.', $name));
}
$this->openSlots[] = $name;
$this->slots[$name] = '';
ob_start();
ob_implicit_flush(0);
}
/**
* Stops a slot.
*
* @throws \LogicException if no slot has been started
*/
public function stop()
{
if (!$this->openSlots) {
throw new \LogicException('No slot started.');
}
$name = array_pop($this->openSlots);
$this->slots[$name] = ob_get_clean();
}
/**
* Returns true if the slot exists.
*
* @param string $name The slot name
*
* @return bool
*/
public function has($name)
{
return isset($this->slots[$name]);
}
/**
* Gets the slot value.
*
* @param string $name The slot name
* @param bool|string $default The default slot content
*
* @return string The slot content
*/
public function get($name, $default = false)
{
return isset($this->slots[$name]) ? $this->slots[$name] : $default;
}
/**
* Sets a slot value.
*
* @param string $name The slot name
* @param string $content The slot content
*/
public function set($name, $content)
{
$this->slots[$name] = $content;
}
/**
* Outputs a slot.
*
* @param string $name The slot name
* @param bool|string $default The default slot content
*
* @return bool true if the slot is defined or if a default content has been provided, false otherwise
*/
public function output($name, $default = false)
{
if (!isset($this->slots[$name])) {
if (false !== $default) {
echo $default;
return true;
}
return false;
}
echo $this->slots[$name];
return true;
}
/**
* Returns the canonical name of this helper.
*
* @return string The canonical name
*/
public function getName()
{
return 'slots';
}
}

19
vendor/symfony/templating/LICENSE vendored Normal file
View File

@@ -0,0 +1,19 @@
Copyright (c) 2004-2020 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.

View File

@@ -0,0 +1,93 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Templating\Loader;
use Symfony\Component\Templating\Storage\FileStorage;
use Symfony\Component\Templating\Storage\Storage;
use Symfony\Component\Templating\TemplateReferenceInterface;
/**
* CacheLoader is a loader that caches other loaders responses
* on the filesystem.
*
* This cache only caches on disk to allow PHP accelerators to cache the opcodes.
* All other mechanism would imply the use of `eval()`.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class CacheLoader extends Loader
{
protected $loader;
protected $dir;
/**
* @param LoaderInterface $loader A Loader instance
* @param string $dir The directory where to store the cache files
*/
public function __construct(LoaderInterface $loader, $dir)
{
$this->loader = $loader;
$this->dir = $dir;
}
/**
* Loads a template.
*
* @return Storage|bool false if the template cannot be loaded, a Storage instance otherwise
*/
public function load(TemplateReferenceInterface $template)
{
$key = hash('sha256', $template->getLogicalName());
$dir = $this->dir.\DIRECTORY_SEPARATOR.substr($key, 0, 2);
$file = substr($key, 2).'.tpl';
$path = $dir.\DIRECTORY_SEPARATOR.$file;
if (is_file($path)) {
if (null !== $this->logger) {
$this->logger->debug('Fetching template from cache.', ['name' => $template->get('name')]);
}
return new FileStorage($path);
}
if (false === $storage = $this->loader->load($template)) {
return false;
}
$content = $storage->getContent();
if (!is_dir($dir) && !@mkdir($dir, 0777, true) && !is_dir($dir)) {
throw new \RuntimeException(sprintf('Cache Loader was not able to create directory "%s".', $dir));
}
file_put_contents($path, $content);
if (null !== $this->logger) {
$this->logger->debug('Storing template in cache.', ['name' => $template->get('name')]);
}
return new FileStorage($path);
}
/**
* Returns true if the template is still fresh.
*
* @param TemplateReferenceInterface $template A template
* @param int $time The last modification time of the cached template (timestamp)
*
* @return bool
*/
public function isFresh(TemplateReferenceInterface $template, $time)
{
return $this->loader->isFresh($template, $time);
}
}

View File

@@ -0,0 +1,76 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Templating\Loader;
use Symfony\Component\Templating\Storage\Storage;
use Symfony\Component\Templating\TemplateReferenceInterface;
/**
* ChainLoader is a loader that calls other loaders to load templates.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class ChainLoader extends Loader
{
protected $loaders = [];
/**
* @param LoaderInterface[] $loaders An array of loader instances
*/
public function __construct(array $loaders = [])
{
foreach ($loaders as $loader) {
$this->addLoader($loader);
}
}
/**
* Adds a loader instance.
*/
public function addLoader(LoaderInterface $loader)
{
$this->loaders[] = $loader;
}
/**
* Loads a template.
*
* @return Storage|bool false if the template cannot be loaded, a Storage instance otherwise
*/
public function load(TemplateReferenceInterface $template)
{
foreach ($this->loaders as $loader) {
if (false !== $storage = $loader->load($template)) {
return $storage;
}
}
return false;
}
/**
* Returns true if the template is still fresh.
*
* @param TemplateReferenceInterface $template A template
* @param int $time The last modification time of the cached template (timestamp)
*
* @return bool
*/
public function isFresh(TemplateReferenceInterface $template, $time)
{
foreach ($this->loaders as $loader) {
return $loader->isFresh($template, $time);
}
return false;
}
}

View File

@@ -0,0 +1,116 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Templating\Loader;
use Symfony\Component\Templating\Storage\FileStorage;
use Symfony\Component\Templating\Storage\Storage;
use Symfony\Component\Templating\TemplateReferenceInterface;
/**
* FilesystemLoader is a loader that read templates from the filesystem.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class FilesystemLoader extends Loader
{
protected $templatePathPatterns;
/**
* @param string|string[] $templatePathPatterns An array of path patterns to look for templates
*/
public function __construct($templatePathPatterns)
{
$this->templatePathPatterns = (array) $templatePathPatterns;
}
/**
* Loads a template.
*
* @return Storage|bool false if the template cannot be loaded, a Storage instance otherwise
*/
public function load(TemplateReferenceInterface $template)
{
$file = $template->get('name');
if (self::isAbsolutePath($file) && is_file($file)) {
return new FileStorage($file);
}
$replacements = [];
foreach ($template->all() as $key => $value) {
$replacements['%'.$key.'%'] = $value;
}
$fileFailures = [];
foreach ($this->templatePathPatterns as $templatePathPattern) {
if (is_file($file = strtr($templatePathPattern, $replacements)) && is_readable($file)) {
if (null !== $this->logger) {
$this->logger->debug('Loaded template file.', ['file' => $file]);
}
return new FileStorage($file);
}
if (null !== $this->logger) {
$fileFailures[] = $file;
}
}
// only log failures if no template could be loaded at all
foreach ($fileFailures as $file) {
if (null !== $this->logger) {
$this->logger->debug('Failed loading template file.', ['file' => $file]);
}
}
return false;
}
/**
* Returns true if the template is still fresh.
*
* @param TemplateReferenceInterface $template A template
* @param int $time The last modification time of the cached template (timestamp)
*
* @return bool true if the template is still fresh, false otherwise
*/
public function isFresh(TemplateReferenceInterface $template, $time)
{
if (false === $storage = $this->load($template)) {
return false;
}
return filemtime((string) $storage) < $time;
}
/**
* Returns true if the file is an existing absolute path.
*
* @param string $file A path
*
* @return bool true if the path exists and is absolute, false otherwise
*/
protected static function isAbsolutePath($file)
{
if ('/' == $file[0] || '\\' == $file[0]
|| (\strlen($file) > 3 && ctype_alpha($file[0])
&& ':' == $file[1]
&& ('\\' == $file[2] || '/' == $file[2])
)
|| null !== parse_url($file, \PHP_URL_SCHEME)
) {
return true;
}
return false;
}
}

View File

@@ -0,0 +1,35 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Templating\Loader;
use Psr\Log\LoggerInterface;
/**
* Loader is the base class for all template loader classes.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
abstract class Loader implements LoaderInterface
{
/**
* @var LoggerInterface|null
*/
protected $logger;
/**
* Sets the debug logger to use for this loader.
*/
public function setLogger(LoggerInterface $logger)
{
$this->logger = $logger;
}
}

View File

@@ -0,0 +1,40 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Templating\Loader;
use Symfony\Component\Templating\Storage\Storage;
use Symfony\Component\Templating\TemplateReferenceInterface;
/**
* LoaderInterface is the interface all loaders must implement.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
interface LoaderInterface
{
/**
* Loads a template.
*
* @return Storage|bool false if the template cannot be loaded, a Storage instance otherwise
*/
public function load(TemplateReferenceInterface $template);
/**
* Returns true if the template is still fresh.
*
* @param TemplateReferenceInterface $template A template
* @param int $time The last modification time of the cached template (timestamp)
*
* @return bool
*/
public function isFresh(TemplateReferenceInterface $template, $time);
}

516
vendor/symfony/templating/PhpEngine.php vendored Normal file
View File

@@ -0,0 +1,516 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Templating;
use Symfony\Component\Templating\Helper\HelperInterface;
use Symfony\Component\Templating\Loader\LoaderInterface;
use Symfony\Component\Templating\Storage\FileStorage;
use Symfony\Component\Templating\Storage\Storage;
use Symfony\Component\Templating\Storage\StringStorage;
/**
* PhpEngine is an engine able to render PHP templates.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class PhpEngine implements EngineInterface, \ArrayAccess
{
protected $loader;
protected $current;
/**
* @var HelperInterface[]
*/
protected $helpers = [];
protected $parents = [];
protected $stack = [];
protected $charset = 'UTF-8';
protected $cache = [];
protected $escapers = [];
protected static $escaperCache = [];
protected $globals = [];
protected $parser;
private $evalTemplate;
private $evalParameters;
/**
* @param TemplateNameParserInterface $parser A TemplateNameParserInterface instance
* @param LoaderInterface $loader A loader instance
* @param HelperInterface[] $helpers An array of helper instances
*/
public function __construct(TemplateNameParserInterface $parser, LoaderInterface $loader, array $helpers = [])
{
$this->parser = $parser;
$this->loader = $loader;
$this->addHelpers($helpers);
$this->initializeEscapers();
foreach ($this->escapers as $context => $escaper) {
$this->setEscaper($context, $escaper);
}
}
/**
* {@inheritdoc}
*
* @throws \InvalidArgumentException if the template does not exist
*/
public function render($name, array $parameters = [])
{
$storage = $this->load($name);
$key = hash('sha256', serialize($storage));
$this->current = $key;
$this->parents[$key] = null;
// attach the global variables
$parameters = array_replace($this->getGlobals(), $parameters);
// render
if (false === $content = $this->evaluate($storage, $parameters)) {
throw new \RuntimeException(sprintf('The template "%s" cannot be rendered.', $this->parser->parse($name)));
}
// decorator
if ($this->parents[$key]) {
$slots = $this->get('slots');
$this->stack[] = $slots->get('_content');
$slots->set('_content', $content);
$content = $this->render($this->parents[$key], $parameters);
$slots->set('_content', array_pop($this->stack));
}
return $content;
}
/**
* {@inheritdoc}
*/
public function exists($name)
{
try {
$this->load($name);
} catch (\InvalidArgumentException $e) {
return false;
}
return true;
}
/**
* {@inheritdoc}
*/
public function supports($name)
{
$template = $this->parser->parse($name);
return 'php' === $template->get('engine');
}
/**
* Evaluates a template.
*
* @param Storage $template The template to render
* @param array $parameters An array of parameters to pass to the template
*
* @return string|false The evaluated template, or false if the engine is unable to render the template
*
* @throws \InvalidArgumentException
*/
protected function evaluate(Storage $template, array $parameters = [])
{
$this->evalTemplate = $template;
$this->evalParameters = $parameters;
unset($template, $parameters);
if (isset($this->evalParameters['this'])) {
throw new \InvalidArgumentException('Invalid parameter (this).');
}
if (isset($this->evalParameters['view'])) {
throw new \InvalidArgumentException('Invalid parameter (view).');
}
// the view variable is exposed to the require file below
$view = $this;
if ($this->evalTemplate instanceof FileStorage) {
extract($this->evalParameters, \EXTR_SKIP);
$this->evalParameters = null;
ob_start();
require $this->evalTemplate;
$this->evalTemplate = null;
return ob_get_clean();
} elseif ($this->evalTemplate instanceof StringStorage) {
extract($this->evalParameters, \EXTR_SKIP);
$this->evalParameters = null;
ob_start();
eval('; ?>'.$this->evalTemplate.'<?php ;');
$this->evalTemplate = null;
return ob_get_clean();
}
return false;
}
/**
* Gets a helper value.
*
* @param string $name The helper name
*
* @return HelperInterface The helper value
*
* @throws \InvalidArgumentException if the helper is not defined
*/
public function offsetGet($name)
{
return $this->get($name);
}
/**
* Returns true if the helper is defined.
*
* @param string $name The helper name
*
* @return bool true if the helper is defined, false otherwise
*/
public function offsetExists($name)
{
return isset($this->helpers[$name]);
}
/**
* Sets a helper.
*
* @param HelperInterface $name The helper instance
* @param string $value An alias
*/
public function offsetSet($name, $value)
{
$this->set($name, $value);
}
/**
* Removes a helper.
*
* @param string $name The helper name
*
* @throws \LogicException
*/
public function offsetUnset($name)
{
throw new \LogicException(sprintf('You can\'t unset a helper (%s).', $name));
}
/**
* Adds some helpers.
*
* @param HelperInterface[] $helpers An array of helper
*/
public function addHelpers(array $helpers)
{
foreach ($helpers as $alias => $helper) {
$this->set($helper, \is_int($alias) ? null : $alias);
}
}
/**
* Sets the helpers.
*
* @param HelperInterface[] $helpers An array of helper
*/
public function setHelpers(array $helpers)
{
$this->helpers = [];
$this->addHelpers($helpers);
}
/**
* Sets a helper.
*
* @param HelperInterface $helper The helper instance
* @param string $alias An alias
*/
public function set(HelperInterface $helper, $alias = null)
{
$this->helpers[$helper->getName()] = $helper;
if (null !== $alias) {
$this->helpers[$alias] = $helper;
}
$helper->setCharset($this->charset);
}
/**
* Returns true if the helper if defined.
*
* @param string $name The helper name
*
* @return bool true if the helper is defined, false otherwise
*/
public function has($name)
{
return isset($this->helpers[$name]);
}
/**
* Gets a helper value.
*
* @param string $name The helper name
*
* @return HelperInterface The helper instance
*
* @throws \InvalidArgumentException if the helper is not defined
*/
public function get($name)
{
if (!isset($this->helpers[$name])) {
throw new \InvalidArgumentException(sprintf('The helper "%s" is not defined.', $name));
}
return $this->helpers[$name];
}
/**
* Decorates the current template with another one.
*
* @param string $template The decorator logical name
*/
public function extend($template)
{
$this->parents[$this->current] = $template;
}
/**
* Escapes a string by using the current charset.
*
* @param mixed $value A variable to escape
* @param string $context The context name
*
* @return mixed The escaped value
*/
public function escape($value, $context = 'html')
{
if (is_numeric($value)) {
return $value;
}
// If we deal with a scalar value, we can cache the result to increase
// the performance when the same value is escaped multiple times (e.g. loops)
if (is_scalar($value)) {
if (!isset(self::$escaperCache[$context][$value])) {
self::$escaperCache[$context][$value] = \call_user_func($this->getEscaper($context), $value);
}
return self::$escaperCache[$context][$value];
}
return \call_user_func($this->getEscaper($context), $value);
}
/**
* Sets the charset to use.
*
* @param string $charset The charset
*/
public function setCharset($charset)
{
if ('UTF8' === $charset = strtoupper($charset)) {
$charset = 'UTF-8'; // iconv on Windows requires "UTF-8" instead of "UTF8"
}
$this->charset = $charset;
foreach ($this->helpers as $helper) {
$helper->setCharset($this->charset);
}
}
/**
* Gets the current charset.
*
* @return string The current charset
*/
public function getCharset()
{
return $this->charset;
}
/**
* Adds an escaper for the given context.
*
* @param string $context The escaper context (html, js, ...)
* @param callable $escaper A PHP callable
*/
public function setEscaper($context, callable $escaper)
{
$this->escapers[$context] = $escaper;
self::$escaperCache[$context] = [];
}
/**
* Gets an escaper for a given context.
*
* @param string $context The context name
*
* @return callable A PHP callable
*
* @throws \InvalidArgumentException
*/
public function getEscaper($context)
{
if (!isset($this->escapers[$context])) {
throw new \InvalidArgumentException(sprintf('No registered escaper for context "%s".', $context));
}
return $this->escapers[$context];
}
/**
* @param string $name
* @param mixed $value
*/
public function addGlobal($name, $value)
{
$this->globals[$name] = $value;
}
/**
* Returns the assigned globals.
*
* @return array
*/
public function getGlobals()
{
return $this->globals;
}
/**
* Initializes the built-in escapers.
*
* Each function specifies a way for applying a transformation to a string
* passed to it. The purpose is for the string to be "escaped" so it is
* suitable for the format it is being displayed in.
*
* For example, the string: "It's required that you enter a username & password.\n"
* If this were to be displayed as HTML it would be sensible to turn the
* ampersand into '&amp;' and the apostrophe into '&aps;'. However if it were
* going to be used as a string in JavaScript to be displayed in an alert box
* it would be right to leave the string as-is, but c-escape the apostrophe and
* the new line.
*
* For each function there is a define to avoid problems with strings being
* incorrectly specified.
*/
protected function initializeEscapers()
{
$flags = \ENT_QUOTES | \ENT_SUBSTITUTE;
$this->escapers = [
'html' =>
/**
* Runs the PHP function htmlspecialchars on the value passed.
*
* @param string $value The value to escape
*
* @return string the escaped value
*/
function ($value) use ($flags) {
// Numbers and Boolean values get turned into strings which can cause problems
// with type comparisons (e.g. === or is_int() etc).
return \is_string($value) ? htmlspecialchars($value, $flags, $this->getCharset(), false) : $value;
},
'js' =>
/**
* A function that escape all non-alphanumeric characters
* into their \xHH or \uHHHH representations.
*
* @param string $value The value to escape
*
* @return string the escaped value
*/
function ($value) {
if ('UTF-8' != $this->getCharset()) {
$value = iconv($this->getCharset(), 'UTF-8', $value);
}
$callback = function ($matches) {
$char = $matches[0];
// \xHH
if (!isset($char[1])) {
return '\\x'.substr('00'.bin2hex($char), -2);
}
// \uHHHH
$char = iconv('UTF-8', 'UTF-16BE', $char);
return '\\u'.substr('0000'.bin2hex($char), -4);
};
if (null === $value = preg_replace_callback('#[^\p{L}\p{N} ]#u', $callback, $value)) {
throw new \InvalidArgumentException('The string to escape is not a valid UTF-8 string.');
}
if ('UTF-8' != $this->getCharset()) {
$value = iconv('UTF-8', $this->getCharset(), $value);
}
return $value;
},
];
self::$escaperCache = [];
}
/**
* Gets the loader associated with this engine.
*
* @return LoaderInterface A LoaderInterface instance
*/
public function getLoader()
{
return $this->loader;
}
/**
* Loads the given template.
*
* @param string|TemplateReferenceInterface $name A template name or a TemplateReferenceInterface instance
*
* @return Storage A Storage instance
*
* @throws \InvalidArgumentException if the template cannot be found
*/
protected function load($name)
{
$template = $this->parser->parse($name);
$key = $template->getLogicalName();
if (isset($this->cache[$key])) {
return $this->cache[$key];
}
$storage = $this->loader->load($template);
if (false === $storage) {
throw new \InvalidArgumentException(sprintf('The template "%s" does not exist.', $template));
}
return $this->cache[$key] = $storage;
}
}

19
vendor/symfony/templating/README.md vendored Normal file
View File

@@ -0,0 +1,19 @@
Templating Component
====================
The Templating component provides all the tools needed to build any kind of
template system.
It provides an infrastructure to load template files and optionally monitor them
for changes. It also provides a concrete template engine implementation using
PHP with additional tools for escaping and separating templates into blocks and
layouts.
Resources
---------
* [Documentation](https://symfony.com/doc/current/components/templating.html)
* [Contributing](https://symfony.com/doc/current/contributing/index.html)
* [Report issues](https://github.com/symfony/symfony/issues) and
[send Pull Requests](https://github.com/symfony/symfony/pulls)
in the [main Symfony repository](https://github.com/symfony/symfony)

View File

@@ -0,0 +1,30 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Templating\Storage;
/**
* FileStorage represents a template stored on the filesystem.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class FileStorage extends Storage
{
/**
* Returns the content of the template.
*
* @return string The template content
*/
public function getContent()
{
return file_get_contents($this->template);
}
}

View File

@@ -0,0 +1,47 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Templating\Storage;
/**
* Storage is the base class for all storage classes.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
abstract class Storage
{
protected $template;
/**
* @param string $template The template name
*/
public function __construct($template)
{
$this->template = $template;
}
/**
* Returns the object string representation.
*
* @return string The template name
*/
public function __toString()
{
return (string) $this->template;
}
/**
* Returns the content of the template.
*
* @return string The template content
*/
abstract public function getContent();
}

View File

@@ -0,0 +1,30 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Templating\Storage;
/**
* StringStorage represents a template stored in a string.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class StringStorage extends Storage
{
/**
* Returns the content of the template.
*
* @return string The template content
*/
public function getContent()
{
return $this->template;
}
}

View File

@@ -0,0 +1,33 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Templating;
/**
* StreamingEngineInterface provides a method that knows how to stream a template.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
interface StreamingEngineInterface
{
/**
* Streams a template.
*
* The implementation should output the content directly to the client.
*
* @param string|TemplateReferenceInterface $name A template name or a TemplateReferenceInterface instance
* @param array $parameters An array of parameters to pass to the template
*
* @throws \RuntimeException if the template cannot be rendered
* @throws \LogicException if the template cannot be streamed
*/
public function stream($name, array $parameters = []);
}

View File

@@ -0,0 +1,40 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Templating;
/**
* TemplateNameParser is the default implementation of TemplateNameParserInterface.
*
* This implementation takes everything as the template name
* and the extension for the engine.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class TemplateNameParser implements TemplateNameParserInterface
{
/**
* {@inheritdoc}
*/
public function parse($name)
{
if ($name instanceof TemplateReferenceInterface) {
return $name;
}
$engine = null;
if (false !== $pos = strrpos($name, '.')) {
$engine = substr($name, $pos + 1);
}
return new TemplateReference($name, $engine);
}
}

View File

@@ -0,0 +1,30 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Templating;
/**
* TemplateNameParserInterface converts template names to TemplateReferenceInterface
* instances.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
interface TemplateNameParserInterface
{
/**
* Convert a template name to a TemplateReferenceInterface instance.
*
* @param string|TemplateReferenceInterface $name A template name or a TemplateReferenceInterface instance
*
* @return TemplateReferenceInterface A template
*/
public function parse($name);
}

View File

@@ -0,0 +1,88 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Templating;
/**
* Internal representation of a template.
*
* @author Victor Berchet <victor@suumit.com>
*/
class TemplateReference implements TemplateReferenceInterface
{
protected $parameters;
public function __construct($name = null, $engine = null)
{
$this->parameters = [
'name' => $name,
'engine' => $engine,
];
}
/**
* {@inheritdoc}
*/
public function __toString()
{
return $this->getLogicalName();
}
/**
* {@inheritdoc}
*/
public function set($name, $value)
{
if (\array_key_exists($name, $this->parameters)) {
$this->parameters[$name] = $value;
} else {
throw new \InvalidArgumentException(sprintf('The template does not support the "%s" parameter.', $name));
}
return $this;
}
/**
* {@inheritdoc}
*/
public function get($name)
{
if (\array_key_exists($name, $this->parameters)) {
return $this->parameters[$name];
}
throw new \InvalidArgumentException(sprintf('The template does not support the "%s" parameter.', $name));
}
/**
* {@inheritdoc}
*/
public function all()
{
return $this->parameters;
}
/**
* {@inheritdoc}
*/
public function getPath()
{
return $this->parameters['name'];
}
/**
* {@inheritdoc}
*/
public function getLogicalName()
{
return $this->parameters['name'];
}
}

View File

@@ -0,0 +1,77 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Templating;
/**
* Interface to be implemented by all templates.
*
* @author Victor Berchet <victor@suumit.com>
*/
interface TemplateReferenceInterface
{
/**
* Gets the template parameters.
*
* @return array An array of parameters
*/
public function all();
/**
* Sets a template parameter.
*
* @param string $name The parameter name
* @param string $value The parameter value
*
* @return $this
*
* @throws \InvalidArgumentException if the parameter name is not supported
*/
public function set($name, $value);
/**
* Gets a template parameter.
*
* @param string $name The parameter name
*
* @return string The parameter value
*
* @throws \InvalidArgumentException if the parameter name is not supported
*/
public function get($name);
/**
* Returns the path to the template.
*
* By default, it just returns the template name.
*
* @return string A path to the template or a resource
*/
public function getPath();
/**
* Returns the "logical" template name.
*
* The template name acts as a unique identifier for the template.
*
* @return string The template name
*/
public function getLogicalName();
/**
* Returns the string representation as shortcut for getLogicalName().
*
* Alias of getLogicalName().
*
* @return string The template name
*/
public function __toString();
}

View File

@@ -0,0 +1,169 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Templating\Tests;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Templating\DelegatingEngine;
use Symfony\Component\Templating\EngineInterface;
use Symfony\Component\Templating\StreamingEngineInterface;
class DelegatingEngineTest extends TestCase
{
public function testRenderDelegatesToSupportedEngine()
{
$firstEngine = $this->getEngineMock('template.php', false);
$secondEngine = $this->getEngineMock('template.php', true);
$secondEngine->expects($this->once())
->method('render')
->with('template.php', ['foo' => 'bar'])
->willReturn('<html />');
$delegatingEngine = new DelegatingEngine([$firstEngine, $secondEngine]);
$result = $delegatingEngine->render('template.php', ['foo' => 'bar']);
$this->assertSame('<html />', $result);
}
public function testRenderWithNoSupportedEngine()
{
$this->expectException('RuntimeException');
$this->expectExceptionMessage('No engine is able to work with the template "template.php"');
$firstEngine = $this->getEngineMock('template.php', false);
$secondEngine = $this->getEngineMock('template.php', false);
$delegatingEngine = new DelegatingEngine([$firstEngine, $secondEngine]);
$delegatingEngine->render('template.php', ['foo' => 'bar']);
}
public function testStreamDelegatesToSupportedEngine()
{
$streamingEngine = $this->getStreamingEngineMock('template.php', true);
$streamingEngine->expects($this->once())
->method('stream')
->with('template.php', ['foo' => 'bar'])
->willReturn('<html />');
$delegatingEngine = new DelegatingEngine([$streamingEngine]);
$result = $delegatingEngine->stream('template.php', ['foo' => 'bar']);
$this->assertNull($result);
}
public function testStreamRequiresStreamingEngine()
{
$this->expectException('LogicException');
$this->expectExceptionMessage('Template "template.php" cannot be streamed as the engine supporting it does not implement StreamingEngineInterface');
$delegatingEngine = new DelegatingEngine([new TestEngine()]);
$delegatingEngine->stream('template.php', ['foo' => 'bar']);
}
public function testExists()
{
$engine = $this->getEngineMock('template.php', true);
$engine->expects($this->once())
->method('exists')
->with('template.php')
->willReturn(true);
$delegatingEngine = new DelegatingEngine([$engine]);
$this->assertTrue($delegatingEngine->exists('template.php'));
}
public function testSupports()
{
$engine = $this->getEngineMock('template.php', true);
$delegatingEngine = new DelegatingEngine([$engine]);
$this->assertTrue($delegatingEngine->supports('template.php'));
}
public function testSupportsWithNoSupportedEngine()
{
$engine = $this->getEngineMock('template.php', false);
$delegatingEngine = new DelegatingEngine([$engine]);
$this->assertFalse($delegatingEngine->supports('template.php'));
}
public function testGetExistingEngine()
{
$firstEngine = $this->getEngineMock('template.php', false);
$secondEngine = $this->getEngineMock('template.php', true);
$delegatingEngine = new DelegatingEngine([$firstEngine, $secondEngine]);
$this->assertSame($secondEngine, $delegatingEngine->getEngine('template.php'));
}
public function testGetInvalidEngine()
{
$this->expectException('RuntimeException');
$this->expectExceptionMessage('No engine is able to work with the template "template.php"');
$firstEngine = $this->getEngineMock('template.php', false);
$secondEngine = $this->getEngineMock('template.php', false);
$delegatingEngine = new DelegatingEngine([$firstEngine, $secondEngine]);
$delegatingEngine->getEngine('template.php');
}
private function getEngineMock($template, $supports)
{
$engine = $this->getMockBuilder('Symfony\Component\Templating\EngineInterface')->getMock();
$engine->expects($this->once())
->method('supports')
->with($template)
->willReturn($supports);
return $engine;
}
private function getStreamingEngineMock($template, $supports)
{
$engine = $this->getMockForAbstractClass('Symfony\Component\Templating\Tests\MyStreamingEngine');
$engine->expects($this->once())
->method('supports')
->with($template)
->willReturn($supports);
return $engine;
}
}
interface MyStreamingEngine extends StreamingEngineInterface, EngineInterface
{
}
class TestEngine implements EngineInterface
{
public function render($name, array $parameters = [])
{
}
public function exists($name)
{
}
public function supports($name)
{
return true;
}
public function stream()
{
}
}

View File

@@ -0,0 +1,34 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Templating\Tests\Fixtures;
use Symfony\Component\Templating\Helper\Helper;
class SimpleHelper extends Helper
{
protected $value = '';
public function __construct($value)
{
$this->value = $value;
}
public function __toString()
{
return $this->value;
}
public function getName()
{
return 'foo';
}
}

View File

@@ -0,0 +1 @@
<?php echo $foo ?>

View File

@@ -0,0 +1,33 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Templating\Tests\Helper;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Templating\Helper\Helper;
class HelperTest extends TestCase
{
public function testGetSetCharset()
{
$helper = new ProjectTemplateHelper();
$helper->setCharset('ISO-8859-1');
$this->assertSame('ISO-8859-1', $helper->getCharset(), '->setCharset() sets the charset set related to this helper');
}
}
class ProjectTemplateHelper extends Helper
{
public function getName()
{
return 'foo';
}
}

View File

@@ -0,0 +1,81 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Templating\Tests\Helper;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Templating\Helper\SlotsHelper;
class SlotsHelperTest extends TestCase
{
public function testHasGetSet()
{
$helper = new SlotsHelper();
$helper->set('foo', 'bar');
$this->assertEquals('bar', $helper->get('foo'), '->set() sets a slot value');
$this->assertEquals('bar', $helper->get('bar', 'bar'), '->get() takes a default value to return if the slot does not exist');
$this->assertTrue($helper->has('foo'), '->has() returns true if the slot exists');
$this->assertFalse($helper->has('bar'), '->has() returns false if the slot does not exist');
}
public function testOutput()
{
$helper = new SlotsHelper();
$helper->set('foo', 'bar');
ob_start();
$ret = $helper->output('foo');
$output = ob_get_clean();
$this->assertEquals('bar', $output, '->output() outputs the content of a slot');
$this->assertTrue($ret, '->output() returns true if the slot exists');
ob_start();
$ret = $helper->output('bar', 'bar');
$output = ob_get_clean();
$this->assertEquals('bar', $output, '->output() takes a default value to return if the slot does not exist');
$this->assertTrue($ret, '->output() returns true if the slot does not exist but a default value is provided');
ob_start();
$ret = $helper->output('bar');
$output = ob_get_clean();
$this->assertEquals('', $output, '->output() outputs nothing if the slot does not exist');
$this->assertFalse($ret, '->output() returns false if the slot does not exist');
}
public function testStartStop()
{
$helper = new SlotsHelper();
$helper->start('bar');
echo 'foo';
$helper->stop();
$this->assertEquals('foo', $helper->get('bar'), '->start() starts a slot');
$this->assertTrue($helper->has('bar'), '->starts() starts a slot');
$helper->start('bar');
try {
$helper->start('bar');
$helper->stop();
$this->fail('->start() throws an InvalidArgumentException if a slot with the same name is already started');
} catch (\Exception $e) {
$helper->stop();
$this->assertInstanceOf('\InvalidArgumentException', $e, '->start() throws an InvalidArgumentException if a slot with the same name is already started');
$this->assertEquals('A slot named "bar" is already started.', $e->getMessage(), '->start() throws an InvalidArgumentException if a slot with the same name is already started');
}
try {
$helper->stop();
$this->fail('->stop() throws an LogicException if no slot is started');
} catch (\Exception $e) {
$this->assertInstanceOf('\LogicException', $e, '->stop() throws an LogicException if no slot is started');
$this->assertEquals('No slot started.', $e->getMessage(), '->stop() throws an LogicException if no slot is started');
}
}
}

View File

@@ -0,0 +1,94 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Templating\Tests\Loader;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Templating\Loader\CacheLoader;
use Symfony\Component\Templating\Loader\Loader;
use Symfony\Component\Templating\Storage\StringStorage;
use Symfony\Component\Templating\TemplateReference;
use Symfony\Component\Templating\TemplateReferenceInterface;
class CacheLoaderTest extends TestCase
{
public function testConstructor()
{
$loader = new ProjectTemplateLoader($varLoader = new ProjectTemplateLoaderVar(), sys_get_temp_dir());
$this->assertSame($loader->getLoader(), $varLoader, '__construct() takes a template loader as its first argument');
$this->assertEquals(sys_get_temp_dir(), $loader->getDir(), '__construct() takes a directory where to store the cache as its second argument');
}
public function testLoad()
{
$dir = sys_get_temp_dir().\DIRECTORY_SEPARATOR.mt_rand(111111, 999999);
mkdir($dir, 0777, true);
$loader = new ProjectTemplateLoader($varLoader = new ProjectTemplateLoaderVar(), $dir);
$this->assertFalse($loader->load(new TemplateReference('foo', 'php')), '->load() returns false if the embed loader is not able to load the template');
$logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
$logger
->expects($this->once())
->method('debug')
->with('Storing template in cache.', ['name' => 'index']);
$loader->setLogger($logger);
$loader->load(new TemplateReference('index'));
$logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
$logger
->expects($this->once())
->method('debug')
->with('Fetching template from cache.', ['name' => 'index']);
$loader->setLogger($logger);
$loader->load(new TemplateReference('index'));
}
}
class ProjectTemplateLoader extends CacheLoader
{
public function getDir()
{
return $this->dir;
}
public function getLoader()
{
return $this->loader;
}
}
class ProjectTemplateLoaderVar extends Loader
{
public function getIndexTemplate()
{
return 'Hello World';
}
public function getSpecialTemplate()
{
return 'Hello {{ name }}';
}
public function load(TemplateReferenceInterface $template)
{
if (method_exists($this, $method = 'get'.ucfirst($template->get('name')).'Template')) {
return new StringStorage($this->$method());
}
return false;
}
public function isFresh(TemplateReferenceInterface $template, $time)
{
return false;
}
}

View File

@@ -0,0 +1,63 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Templating\Tests\Loader;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Templating\Loader\ChainLoader;
use Symfony\Component\Templating\Loader\FilesystemLoader;
use Symfony\Component\Templating\TemplateReference;
class ChainLoaderTest extends TestCase
{
protected $loader1;
protected $loader2;
protected function setUp()
{
$fixturesPath = realpath(__DIR__.'/../Fixtures/');
$this->loader1 = new FilesystemLoader($fixturesPath.'/null/%name%');
$this->loader2 = new FilesystemLoader($fixturesPath.'/templates/%name%');
}
public function testConstructor()
{
$loader = new ProjectTemplateLoader1([$this->loader1, $this->loader2]);
$this->assertEquals([$this->loader1, $this->loader2], $loader->getLoaders(), '__construct() takes an array of template loaders as its second argument');
}
public function testAddLoader()
{
$loader = new ProjectTemplateLoader1([$this->loader1]);
$loader->addLoader($this->loader2);
$this->assertEquals([$this->loader1, $this->loader2], $loader->getLoaders(), '->addLoader() adds a template loader at the end of the loaders');
}
public function testLoad()
{
$loader = new ProjectTemplateLoader1([$this->loader1, $this->loader2]);
$this->assertFalse($loader->load(new TemplateReference('bar', 'php')), '->load() returns false if the template is not found');
$this->assertFalse($loader->load(new TemplateReference('foo', 'php')), '->load() returns false if the template does not exist for the given renderer');
$this->assertInstanceOf(
'Symfony\Component\Templating\Storage\FileStorage',
$loader->load(new TemplateReference('foo.php', 'php')),
'->load() returns a FileStorage if the template exists'
);
}
}
class ProjectTemplateLoader1 extends ChainLoader
{
public function getLoaders()
{
return $this->loaders;
}
}

View File

@@ -0,0 +1,85 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Templating\Tests\Loader;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Templating\Loader\FilesystemLoader;
use Symfony\Component\Templating\TemplateReference;
class FilesystemLoaderTest extends TestCase
{
protected static $fixturesPath;
public static function setUpBeforeClass()
{
self::$fixturesPath = realpath(__DIR__.'/../Fixtures/');
}
public function testConstructor()
{
$pathPattern = self::$fixturesPath.'/templates/%name%.%engine%';
$loader = new ProjectTemplateLoader2($pathPattern);
$this->assertEquals([$pathPattern], $loader->getTemplatePathPatterns(), '__construct() takes a path as its second argument');
$loader = new ProjectTemplateLoader2([$pathPattern]);
$this->assertEquals([$pathPattern], $loader->getTemplatePathPatterns(), '__construct() takes an array of paths as its second argument');
}
public function testIsAbsolutePath()
{
$this->assertTrue(ProjectTemplateLoader2::isAbsolutePath('/foo.xml'), '->isAbsolutePath() returns true if the path is an absolute path');
$this->assertTrue(ProjectTemplateLoader2::isAbsolutePath('c:\\\\foo.xml'), '->isAbsolutePath() returns true if the path is an absolute path');
$this->assertTrue(ProjectTemplateLoader2::isAbsolutePath('c:/foo.xml'), '->isAbsolutePath() returns true if the path is an absolute path');
$this->assertTrue(ProjectTemplateLoader2::isAbsolutePath('\\server\\foo.xml'), '->isAbsolutePath() returns true if the path is an absolute path');
$this->assertTrue(ProjectTemplateLoader2::isAbsolutePath('https://server/foo.xml'), '->isAbsolutePath() returns true if the path is an absolute path');
$this->assertTrue(ProjectTemplateLoader2::isAbsolutePath('phar://server/foo.xml'), '->isAbsolutePath() returns true if the path is an absolute path');
}
public function testLoad()
{
$pathPattern = self::$fixturesPath.'/templates/%name%';
$path = self::$fixturesPath.'/templates';
$loader = new ProjectTemplateLoader2($pathPattern);
$storage = $loader->load(new TemplateReference($path.'/foo.php', 'php'));
$this->assertInstanceOf('Symfony\Component\Templating\Storage\FileStorage', $storage, '->load() returns a FileStorage if you pass an absolute path');
$this->assertEquals($path.'/foo.php', (string) $storage, '->load() returns a FileStorage pointing to the passed absolute path');
$this->assertFalse($loader->load(new TemplateReference('bar', 'php')), '->load() returns false if the template is not found');
$storage = $loader->load(new TemplateReference('foo.php', 'php'));
$this->assertInstanceOf('Symfony\Component\Templating\Storage\FileStorage', $storage, '->load() returns a FileStorage if you pass a relative template that exists');
$this->assertEquals($path.'/foo.php', (string) $storage, '->load() returns a FileStorage pointing to the absolute path of the template');
$logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
$logger->expects($this->exactly(2))->method('debug');
$loader = new ProjectTemplateLoader2($pathPattern);
$loader->setLogger($logger);
$this->assertFalse($loader->load(new TemplateReference('foo.xml', 'php')), '->load() returns false if the template does not exist for the given engine');
$loader = new ProjectTemplateLoader2([self::$fixturesPath.'/null/%name%', $pathPattern]);
$loader->setLogger($logger);
$loader->load(new TemplateReference('foo.php', 'php'));
}
}
class ProjectTemplateLoader2 extends FilesystemLoader
{
public function getTemplatePathPatterns()
{
return $this->templatePathPatterns;
}
public static function isAbsolutePath($path)
{
return parent::isAbsolutePath($path);
}
}

View File

@@ -0,0 +1,44 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Templating\Tests\Loader;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Templating\Loader\Loader;
use Symfony\Component\Templating\TemplateReferenceInterface;
class LoaderTest extends TestCase
{
public function testGetSetLogger()
{
$loader = new ProjectTemplateLoader4();
$logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
$loader->setLogger($logger);
$this->assertSame($logger, $loader->getLogger(), '->setLogger() sets the logger instance');
}
}
class ProjectTemplateLoader4 extends Loader
{
public function load(TemplateReferenceInterface $template)
{
}
public function getLogger()
{
return $this->logger;
}
public function isFresh(TemplateReferenceInterface $template, $time)
{
return false;
}
}

View File

@@ -0,0 +1,226 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Templating\Tests;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Templating\Helper\SlotsHelper;
use Symfony\Component\Templating\Loader\Loader;
use Symfony\Component\Templating\PhpEngine;
use Symfony\Component\Templating\Storage\StringStorage;
use Symfony\Component\Templating\TemplateNameParser;
use Symfony\Component\Templating\TemplateReference;
use Symfony\Component\Templating\TemplateReferenceInterface;
class PhpEngineTest extends TestCase
{
protected $loader;
protected function setUp()
{
$this->loader = new ProjectTemplateLoader();
}
protected function tearDown()
{
$this->loader = null;
}
public function testConstructor()
{
$engine = new ProjectTemplateEngine(new TemplateNameParser(), $this->loader);
$this->assertEquals($this->loader, $engine->getLoader(), '__construct() takes a loader instance as its second first argument');
}
public function testOffsetGet()
{
$engine = new ProjectTemplateEngine(new TemplateNameParser(), $this->loader);
$engine->set($helper = new \Symfony\Component\Templating\Tests\Fixtures\SimpleHelper('bar'), 'foo');
$this->assertEquals($helper, $engine['foo'], '->offsetGet() returns the value of a helper');
try {
$engine['bar'];
$this->fail('->offsetGet() throws an InvalidArgumentException if the helper is not defined');
} catch (\Exception $e) {
$this->assertInstanceOf('\InvalidArgumentException', $e, '->offsetGet() throws an InvalidArgumentException if the helper is not defined');
$this->assertEquals('The helper "bar" is not defined.', $e->getMessage(), '->offsetGet() throws an InvalidArgumentException if the helper is not defined');
}
}
public function testGetSetHas()
{
$engine = new ProjectTemplateEngine(new TemplateNameParser(), $this->loader);
$foo = new \Symfony\Component\Templating\Tests\Fixtures\SimpleHelper('foo');
$engine->set($foo);
$this->assertEquals($foo, $engine->get('foo'), '->set() sets a helper');
$engine[$foo] = 'bar';
$this->assertEquals($foo, $engine->get('bar'), '->set() takes an alias as a second argument');
$this->assertArrayHasKey('bar', $engine);
try {
$engine->get('foobar');
$this->fail('->get() throws an InvalidArgumentException if the helper is not defined');
} catch (\Exception $e) {
$this->assertInstanceOf('\InvalidArgumentException', $e, '->get() throws an InvalidArgumentException if the helper is not defined');
$this->assertEquals('The helper "foobar" is not defined.', $e->getMessage(), '->get() throws an InvalidArgumentException if the helper is not defined');
}
$this->assertArrayHasKey('bar', $engine);
$this->assertTrue($engine->has('foo'), '->has() returns true if the helper exists');
$this->assertFalse($engine->has('foobar'), '->has() returns false if the helper does not exist');
}
public function testUnsetHelper()
{
$engine = new ProjectTemplateEngine(new TemplateNameParser(), $this->loader);
$foo = new \Symfony\Component\Templating\Tests\Fixtures\SimpleHelper('foo');
$engine->set($foo);
$this->expectException('\LogicException');
unset($engine['foo']);
}
public function testExtendRender()
{
$engine = new ProjectTemplateEngine(new TemplateNameParser(), $this->loader, []);
try {
$engine->render('name');
$this->fail('->render() throws an InvalidArgumentException if the template does not exist');
} catch (\Exception $e) {
$this->assertInstanceOf('\InvalidArgumentException', $e, '->render() throws an InvalidArgumentException if the template does not exist');
$this->assertEquals('The template "name" does not exist.', $e->getMessage(), '->render() throws an InvalidArgumentException if the template does not exist');
}
$engine = new ProjectTemplateEngine(new TemplateNameParser(), $this->loader, [new SlotsHelper()]);
$engine->set(new \Symfony\Component\Templating\Tests\Fixtures\SimpleHelper('bar'));
$this->loader->setTemplate('foo.php', '<?php $this->extend("layout.php"); echo $this[\'foo\'].$foo ?>');
$this->loader->setTemplate('layout.php', '-<?php echo $this[\'slots\']->get("_content") ?>-');
$this->assertEquals('-barfoo-', $engine->render('foo.php', ['foo' => 'foo']), '->render() uses the decorator to decorate the template');
$engine = new ProjectTemplateEngine(new TemplateNameParser(), $this->loader, [new SlotsHelper()]);
$engine->set(new \Symfony\Component\Templating\Tests\Fixtures\SimpleHelper('bar'));
$this->loader->setTemplate('bar.php', 'bar');
$this->loader->setTemplate('foo.php', '<?php $this->extend("layout.php"); echo $foo ?>');
$this->loader->setTemplate('layout.php', '<?php echo $this->render("bar.php") ?>-<?php echo $this[\'slots\']->get("_content") ?>-');
$this->assertEquals('bar-foo-', $engine->render('foo.php', ['foo' => 'foo', 'bar' => 'bar']), '->render() supports render() calls in templates');
}
public function testRenderParameter()
{
$engine = new ProjectTemplateEngine(new TemplateNameParser(), $this->loader);
$this->loader->setTemplate('foo.php', '<?php echo $template . $parameters ?>');
$this->assertEquals('foobar', $engine->render('foo.php', ['template' => 'foo', 'parameters' => 'bar']), '->render() extract variables');
}
/**
* @dataProvider forbiddenParameterNames
*/
public function testRenderForbiddenParameter($name)
{
$this->expectException('InvalidArgumentException');
$engine = new ProjectTemplateEngine(new TemplateNameParser(), $this->loader);
$this->loader->setTemplate('foo.php', 'bar');
$engine->render('foo.php', [$name => 'foo']);
}
public function forbiddenParameterNames()
{
return [
['this'],
['view'],
];
}
public function testEscape()
{
$engine = new ProjectTemplateEngine(new TemplateNameParser(), $this->loader);
$this->assertEquals('&lt;br /&gt;', $engine->escape('<br />'), '->escape() escapes strings');
$foo = new \stdClass();
$this->assertEquals($foo, $engine->escape($foo), '->escape() does nothing on non strings');
}
public function testGetSetCharset()
{
$helper = new SlotsHelper();
$engine = new ProjectTemplateEngine(new TemplateNameParser(), $this->loader, [$helper]);
$this->assertEquals('UTF-8', $engine->getCharset(), 'EngineInterface::getCharset() returns UTF-8 by default');
$this->assertEquals('UTF-8', $helper->getCharset(), 'HelperInterface::getCharset() returns UTF-8 by default');
$engine->setCharset('ISO-8859-1');
$this->assertEquals('ISO-8859-1', $engine->getCharset(), 'EngineInterface::setCharset() changes the default charset to use');
$this->assertEquals('ISO-8859-1', $helper->getCharset(), 'EngineInterface::setCharset() changes the default charset of helper');
}
public function testGlobalVariables()
{
$engine = new ProjectTemplateEngine(new TemplateNameParser(), $this->loader);
$engine->addGlobal('global_variable', 'lorem ipsum');
$this->assertEquals([
'global_variable' => 'lorem ipsum',
], $engine->getGlobals());
}
public function testGlobalsGetPassedToTemplate()
{
$engine = new ProjectTemplateEngine(new TemplateNameParser(), $this->loader);
$engine->addGlobal('global', 'global variable');
$this->loader->setTemplate('global.php', '<?php echo $global; ?>');
$this->assertEquals('global variable', $engine->render('global.php'));
$this->assertEquals('overwritten', $engine->render('global.php', ['global' => 'overwritten']));
}
public function testGetLoader()
{
$engine = new ProjectTemplateEngine(new TemplateNameParser(), $this->loader);
$this->assertSame($this->loader, $engine->getLoader());
}
}
class ProjectTemplateEngine extends PhpEngine
{
public function getLoader()
{
return $this->loader;
}
}
class ProjectTemplateLoader extends Loader
{
public $templates = [];
public function setTemplate($name, $content)
{
$template = new TemplateReference($name, 'php');
$this->templates[$template->getLogicalName()] = $content;
}
public function load(TemplateReferenceInterface $template)
{
if (isset($this->templates[$template->getLogicalName()])) {
return new StringStorage($this->templates[$template->getLogicalName()]);
}
return false;
}
public function isFresh(TemplateReferenceInterface $template, $time)
{
return false;
}
}

View File

@@ -0,0 +1,26 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Templating\Tests\Storage;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Templating\Storage\FileStorage;
class FileStorageTest extends TestCase
{
public function testGetContent()
{
$storage = new FileStorage('foo');
$this->assertInstanceOf('Symfony\Component\Templating\Storage\Storage', $storage, 'FileStorage is an instance of Storage');
$storage = new FileStorage(__DIR__.'/../Fixtures/templates/foo.php');
$this->assertEquals('<?php echo $foo ?>'."\n", $storage->getContent(), '->getContent() returns the content of the template');
}
}

View File

@@ -0,0 +1,31 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Templating\Tests\Storage;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Templating\Storage\Storage;
class StorageTest extends TestCase
{
public function testMagicToString()
{
$storage = new TestStorage('foo');
$this->assertEquals('foo', (string) $storage, '__toString() returns the template name');
}
}
class TestStorage extends Storage
{
public function getContent()
{
}
}

View File

@@ -0,0 +1,26 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Templating\Tests\Storage;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Templating\Storage\StringStorage;
class StringStorageTest extends TestCase
{
public function testGetContent()
{
$storage = new StringStorage('foo');
$this->assertInstanceOf('Symfony\Component\Templating\Storage\Storage', $storage, 'StringStorage is an instance of Storage');
$storage = new StringStorage('foo');
$this->assertEquals('foo', $storage->getContent(), '->getContent() returns the content of the template');
}
}

View File

@@ -0,0 +1,51 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Templating\Tests;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Templating\TemplateNameParser;
use Symfony\Component\Templating\TemplateReference;
class TemplateNameParserTest extends TestCase
{
protected $parser;
protected function setUp()
{
$this->parser = new TemplateNameParser();
}
protected function tearDown()
{
$this->parser = null;
}
/**
* @dataProvider getLogicalNameToTemplateProvider
*/
public function testParse($name, $ref)
{
$template = $this->parser->parse($name);
$this->assertEquals($template->getLogicalName(), $ref->getLogicalName());
$this->assertEquals($template->getLogicalName(), $name);
}
public function getLogicalNameToTemplateProvider()
{
return [
['/path/to/section/name.engine', new TemplateReference('/path/to/section/name.engine', 'engine')],
['name.engine', new TemplateReference('name.engine', 'engine')],
['name', new TemplateReference('name')],
];
}
}

35
vendor/symfony/templating/composer.json vendored Normal file
View File

@@ -0,0 +1,35 @@
{
"name": "symfony/templating",
"type": "library",
"description": "Symfony Templating Component",
"keywords": [],
"homepage": "https://symfony.com",
"license": "MIT",
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"require": {
"php": "^5.5.9|>=7.0.8",
"symfony/polyfill-ctype": "~1.8"
},
"require-dev": {
"psr/log": "~1.0"
},
"suggest": {
"psr/log-implementation": "For using debug logging in loaders"
},
"autoload": {
"psr-4": { "Symfony\\Component\\Templating\\": "" },
"exclude-from-classmap": [
"/Tests/"
]
},
"minimum-stability": "dev"
}

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/5.2/phpunit.xsd"
backupGlobals="false"
colors="true"
bootstrap="vendor/autoload.php"
failOnRisky="true"
failOnWarning="true"
>
<php>
<ini name="error_reporting" value="-1" />
</php>
<testsuites>
<testsuite name="Symfony Templating Component Test Suite">
<directory>./Tests/</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory>./</directory>
<exclude>
<directory>./Tests</directory>
<directory>./vendor</directory>
</exclude>
</whitelist>
</filter>
</phpunit>