Skip to content

Commit

Permalink
made using Twig_SourceContextLoaderInterface required
Browse files Browse the repository at this point in the history
  • Loading branch information
fabpot committed Oct 22, 2016
1 parent 8c33708 commit 21ecba8
Show file tree
Hide file tree
Showing 17 changed files with 164 additions and 41 deletions.
1 change: 1 addition & 0 deletions CHANGELOG
@@ -1,5 +1,6 @@
* 1.27.0 (2016-XX-XX)

* deprecated Twig_LoaderInterface::getSource() (implement Twig_SourceContextLoaderInterface instead)
* fixed the filesystem loader with relative paths
* deprecated Twig_Node::getLine() in favor of Twig_Node::getTemplateLine()
* deprecated Twig_Template::getSource() in favor of Twig_Template::getSourceContext()
Expand Down
7 changes: 7 additions & 0 deletions doc/api.rst
Expand Up @@ -271,6 +271,8 @@ All loaders implement the ``Twig_LoaderInterface``::
* @param string $name string The name of the template to load
*
* @return string The template source code
*
* @deprecated since 1.27 (to be removed in 2.0), implement Twig_SourceContextLoaderInterface
*/
function getSource($name);

Expand All @@ -295,6 +297,11 @@ All loaders implement the ``Twig_LoaderInterface``::
The ``isFresh()`` method must return ``true`` if the current cached template
is still fresh, given the last modification time, or ``false`` otherwise.

.. note::

As of Twig 1.27, you should also implement
``Twig_SourceContextLoaderInterface`` to avoid deprecation notices.

.. tip::

As of Twig 1.11.0, you can also implement ``Twig_ExistsLoaderInterface``
Expand Down
5 changes: 5 additions & 0 deletions doc/deprecated.rst
Expand Up @@ -137,6 +137,7 @@ Interfaces
* ``Twig_NodeInterface`` (use ``Twig_Node`` instead)
* ``Twig_ParserInterface`` (use ``Twig_Parser`` instead)
* ``Twig_ExistsLoaderInterface`` (merged with ``Twig_LoaderInterface``)
* ``Twig_SourceContextLoaderInterface`` (merged with ``Twig_LoaderInterface``)
* ``Twig_TemplateInterface`` (use ``Twig_Template`` instead, and use
those constants Twig_Template::ANY_CALL, Twig_Template::ARRAY_CALL,
Twig_Template::METHOD_CALL)
Expand All @@ -153,6 +154,10 @@ Loaders
* As of Twig 1.x, ``Twig_Loader_String`` is deprecated and will be removed in
2.0. You can render a string via ``Twig_Environment::createTemplate()``.

* As of Twig 1.27, ``Twig_LoaderInterface::getSource()`` is deprecated.
Implement ``Twig_SourceContextLoaderInterface`` instead and use
``getSourceContext()``.

Node Visitors
-------------

Expand Down
12 changes: 11 additions & 1 deletion doc/recipes.rst
Expand Up @@ -417,7 +417,7 @@ We have created a simple ``templates`` table that hosts two templates:

Now, let's define a loader able to use this database::

class DatabaseTwigLoader implements Twig_LoaderInterface, Twig_ExistsLoaderInterface
class DatabaseTwigLoader implements Twig_LoaderInterface, Twig_ExistsLoaderInterface, Twig_SourceContextLoaderInterface
{
protected $dbh;

Expand All @@ -435,6 +435,16 @@ Now, let's define a loader able to use this database::
return $source;
}

// Twig_SourceContextLoaderInterface as of Twig 1.27
public function getSourceContext($name)
{
if (false === $source = $this->getValue('source', $name)) {
throw new Twig_Error_Loader(sprintf('Template "%s" does not exist.', $name));
}

return new Twig_Source($source, $name);
}

// Twig_ExistsLoaderInterface as of Twig 1.11
public function exists($name)
{
Expand Down
27 changes: 20 additions & 7 deletions lib/Twig/Environment.php
Expand Up @@ -403,13 +403,7 @@ public function loadTemplate($name, $index = null)
}

if (!class_exists($cls, false)) {
$loader = $this->getLoader();
if ($loader instanceof Twig_SourceContextLoaderInterface) {
$source = $loader->getSourceContext($name);
} else {
$source = new Twig_Source($loader->getSource($name), $name);
}
$content = $this->compileSource($source);
$content = $this->compileSource($this->getSourceContext($name));

if ($this->bcWriteCacheFile) {
$this->writeCacheFile($key, $content);
Expand Down Expand Up @@ -735,6 +729,10 @@ public function compileSource($source, $name = null)
*/
public function setLoader(Twig_LoaderInterface $loader)
{
if (!$loader instanceof Twig_SourceContextLoaderInterface && 0 !== strpos(get_class($loader), 'Mock_Twig_LoaderInterface')) {
@trigger_error(sprintf('Twig loader "%s" should implement Twig_SourceContextLoaderInterface since version 1.27.', get_class($loader)), E_USER_DEPRECATED);
}

$this->loader = $loader;
}

Expand All @@ -752,6 +750,21 @@ public function getLoader()
return $this->loader;
}

/**
* Gets the source context for the given template name.
*
* @return Twig_Source
*/
public function getSourceContext($name)
{
$loader = $this->getLoader();
if (!$loader instanceof Twig_SourceContextLoaderInterface) {
return new Twig_Source($loader->getSource($name), $name);
}

return $loader->getSourceContext($name);
}

/**
* Sets the default template charset.
*
Expand Down
2 changes: 1 addition & 1 deletion lib/Twig/Extension/Core.php
Expand Up @@ -1490,7 +1490,7 @@ function twig_include(Twig_Environment $env, $context, $template, $variables = a
function twig_source(Twig_Environment $env, $name, $ignoreMissing = false)
{
try {
return $env->getLoader()->getSource($name);
return $env->getSourceContext($name)->getCode();
} catch (Twig_Error_Loader $e) {
if (!$ignoreMissing) {
throw $e;
Expand Down
17 changes: 16 additions & 1 deletion lib/Twig/Loader/Array.php
Expand Up @@ -21,7 +21,7 @@
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class Twig_Loader_Array implements Twig_LoaderInterface, Twig_ExistsLoaderInterface
class Twig_Loader_Array implements Twig_LoaderInterface, Twig_ExistsLoaderInterface, Twig_SourceContextLoaderInterface
{
protected $templates = array();

Expand Down Expand Up @@ -51,6 +51,8 @@ public function setTemplate($name, $template)
*/
public function getSource($name)
{
@trigger_error(sprintf('Calling "getSource" on "%s" is deprecated since 1.27. Use getSourceContext() instead.', get_class($this)), E_USER_DEPRECATED);

$name = (string) $name;
if (!isset($this->templates[$name])) {
throw new Twig_Error_Loader(sprintf('Template "%s" is not defined.', $name));
Expand All @@ -59,6 +61,19 @@ public function getSource($name)
return $this->templates[$name];
}

/**
* {@inheritdoc}
*/
public function getSourceContext($name)
{
$name = (string) $name;
if (!isset($this->templates[$name])) {
throw new Twig_Error_Loader(sprintf('Template "%s" is not defined.', $name));
}

return new Twig_Source($this->templates[$name], $name);
}

/**
* {@inheritdoc}
*/
Expand Down
8 changes: 7 additions & 1 deletion lib/Twig/Loader/Chain.php
Expand Up @@ -47,6 +47,8 @@ public function addLoader(Twig_LoaderInterface $loader)
*/
public function getSource($name)
{
@trigger_error(sprintf('Calling "getSource" on "%s" is deprecated since 1.27. Use getSourceContext() instead.', get_class($this)), E_USER_DEPRECATED);

$exceptions = array();
foreach ($this->loaders as $loader) {
if ($loader instanceof Twig_ExistsLoaderInterface && !$loader->exists($name)) {
Expand Down Expand Up @@ -109,7 +111,11 @@ public function exists($name)
}

try {
$loader->getSource($name);
if ($loader instanceof Twig_SourceContextLoaderInterface) {
$loader->getSourceContext($name);
} else {
$loader->getSource($name);
}

return $this->hasSourceCache[$name] = true;
} catch (Twig_Error_Loader $e) {
Expand Down
2 changes: 2 additions & 0 deletions lib/Twig/Loader/Filesystem.php
Expand Up @@ -138,6 +138,8 @@ public function prependPath($path, $namespace = self::MAIN_NAMESPACE)
*/
public function getSource($name)
{
@trigger_error(sprintf('Calling "getSource" on "%s" is deprecated since 1.27. Use getSourceContext() instead.', get_class($this)), E_USER_DEPRECATED);

return file_get_contents($this->findTemplate($name));
}

Expand Down
12 changes: 11 additions & 1 deletion lib/Twig/Loader/String.php
Expand Up @@ -27,16 +27,26 @@
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class Twig_Loader_String implements Twig_LoaderInterface, Twig_ExistsLoaderInterface
class Twig_Loader_String implements Twig_LoaderInterface, Twig_ExistsLoaderInterface, Twig_SourceContextLoaderInterface
{
/**
* {@inheritdoc}
*/
public function getSource($name)
{
@trigger_error(sprintf('Calling "getSource" on "%s" is deprecated since 1.27. Use getSourceContext() instead.', get_class($this)), E_USER_DEPRECATED);

return $name;
}

/**
* {@inheritdoc}
*/
public function getSourceContext($name)
{
return new Twig_Source($name, $name);
}

/**
* {@inheritdoc}
*/
Expand Down
2 changes: 2 additions & 0 deletions lib/Twig/LoaderInterface.php
Expand Up @@ -24,6 +24,8 @@ interface Twig_LoaderInterface
* @return string The template source code
*
* @throws Twig_Error_Loader When $name is not found
*
* @deprecated since 1.27 (to be removed in 2.0), implement Twig_SourceContextLoaderInterface
*/
public function getSource($name);

Expand Down
7 changes: 7 additions & 0 deletions lib/Twig/SourceContextLoaderInterface.php
Expand Up @@ -9,6 +9,13 @@
* file that was distributed with this source code.
*/

/**
* Adds a getSourceContext() method for loaders.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @deprecated since 1.27 (to be removed in 3.0)
*/
interface Twig_SourceContextLoaderInterface
{
/**
Expand Down
3 changes: 1 addition & 2 deletions lib/Twig/Test/IntegrationTestCase.php
Expand Up @@ -212,8 +212,7 @@ protected function doIntegrationTest($file, $message, $condition, $templates, $e

foreach (array_keys($templates) as $name) {
echo "Template: $name\n";
$source = $loader->getSource($name);
echo $twig->compile($twig->parse($twig->tokenize($source, $name)));
echo $twig->compile($twig->parse($twig->tokenize($twig->getSourceContext($name), $name)));
}
}
$this->assertEquals($expected, $output, $message.' (in '.$file.')');
Expand Down
36 changes: 24 additions & 12 deletions test/Twig/Tests/EnvironmentTest.php
Expand Up @@ -71,17 +71,22 @@ public function escapingStrategyCallback($name)

public function testGlobals()
{
// to be removed in 2.0
$loader = $this->getMockBuilder('Twig_EnvironmentTestLoaderInterface')->getMock();
//$loader = $this->getMockBuilder(array('Twig_LoaderInterface', 'Twig_SourceContextLoaderInterface'))->getMock();
$loader->expects($this->any())->method('getSourceContext')->will($this->returnValue(new Twig_Source('', '')));

// globals can be added after calling getGlobals

$twig = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock());
$twig = new Twig_Environment($loader);
$twig->addGlobal('foo', 'foo');
$twig->getGlobals();
$twig->addGlobal('foo', 'bar');
$globals = $twig->getGlobals();
$this->assertEquals('bar', $globals['foo']);

// globals can be modified after a template has been loaded
$twig = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock());
$twig = new Twig_Environment($loader);
$twig->addGlobal('foo', 'foo');
$twig->getGlobals();
$twig->loadTemplate('index');
Expand All @@ -90,7 +95,7 @@ public function testGlobals()
$this->assertEquals('bar', $globals['foo']);

// globals can be modified after extensions init
$twig = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock());
$twig = new Twig_Environment($loader);
$twig->addGlobal('foo', 'foo');
$twig->getGlobals();
$twig->getFunctions();
Expand All @@ -99,7 +104,8 @@ public function testGlobals()
$this->assertEquals('bar', $globals['foo']);

// globals can be modified after extensions and a template has been loaded
$twig = new Twig_Environment($loader = new Twig_Loader_Array(array('index' => '{{foo}}')));
$arrayLoader = new Twig_Loader_Array(array('index' => '{{foo}}'));
$twig = new Twig_Environment($arrayLoader);
$twig->addGlobal('foo', 'foo');
$twig->getGlobals();
$twig->getFunctions();
Expand All @@ -108,15 +114,15 @@ public function testGlobals()
$globals = $twig->getGlobals();
$this->assertEquals('bar', $globals['foo']);

$twig = new Twig_Environment($loader);
$twig = new Twig_Environment($arrayLoader);
$twig->getGlobals();
$twig->addGlobal('foo', 'bar');
$template = $twig->loadTemplate('index');
$this->assertEquals('bar', $template->render(array()));

/* to be uncomment in Twig 2.0
// globals cannot be added after a template has been loaded
$twig = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock());
$twig = new Twig_Environment($loader);
$twig->addGlobal('foo', 'foo');
$twig->getGlobals();
$twig->loadTemplate('index');
Expand All @@ -128,7 +134,7 @@ public function testGlobals()
}
// globals cannot be added after extensions init
$twig = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock());
$twig = new Twig_Environment($loader);
$twig->addGlobal('foo', 'foo');
$twig->getGlobals();
$twig->getFunctions();
Expand All @@ -140,7 +146,7 @@ public function testGlobals()
}
// globals cannot be added after extensions and a template has been loaded
$twig = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock());
$twig = new Twig_Environment($loader);
$twig->addGlobal('foo', 'foo');
$twig->getGlobals();
$twig->getFunctions();
Expand All @@ -153,7 +159,7 @@ public function testGlobals()
}
// test adding globals after a template has been loaded without call to getGlobals
$twig = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock());
$twig = new Twig_Environment($loader);
$twig->loadTemplate('index');
try {
$twig->addGlobal('bar', 'bar');
Expand Down Expand Up @@ -445,11 +451,13 @@ public function testAddRuntimeLoader()

protected function getMockLoader($templateName, $templateContent)
{
$loader = $this->getMockBuilder('Twig_LoaderInterface')->getMock();
// to be removed in 2.0
$loader = $this->getMockBuilder('Twig_EnvironmentTestLoaderInterface')->getMock();
//$loader = $this->getMockBuilder(array('Twig_LoaderInterface', 'Twig_SourceContextLoaderInterface'))->getMock();
$loader->expects($this->any())
->method('getSource')
->method('getSourceContext')
->with($templateName)
->will($this->returnValue($templateContent));
->will($this->returnValue(new Twig_Source($templateContent, $templateName)));
$loader->expects($this->any())
->method('getCacheKey')
->with($templateName)
Expand Down Expand Up @@ -597,3 +605,7 @@ public function fromRuntime($name = 'bar')
return $name;
}
}

interface Twig_EnvironmentTestLoaderInterface extends Twig_LoaderInterface, Twig_SourceContextLoaderInterface
{
}

0 comments on commit 21ecba8

Please sign in to comment.