tests/TemplateTest.php
1698c883
 <?php
 
2119e60c
 namespace Twig\Tests;
 
1698c883
 /*
  * This file is part of Twig.
  *
  * (c) Fabien Potencier
  *
  * For the full copyright and license information, please view the LICENSE
  * file that was distributed with this source code.
  */
90d579e4
 
34bdab4d
 use PHPUnit\Framework\TestCase;
90d579e4
 use Twig\Environment;
 use Twig\Error\RuntimeError;
 use Twig\Extension\SandboxExtension;
 use Twig\Loader\ArrayLoader;
 use Twig\Loader\LoaderInterface;
 use Twig\Sandbox\SecurityError;
 use Twig\Sandbox\SecurityPolicy;
b3498de4
 use Twig\Source;
90d579e4
 use Twig\Template;
 
34bdab4d
 class TemplateTest extends TestCase
1698c883
 {
30be0775
     public function testDisplayBlocksAcceptTemplateOnlyAsBlocks()
     {
5ebcecf2
         $this->expectException(\LogicException::class);
b776e41f
 
656c295e
         $twig = new Environment($this->createMock(LoaderInterface::class));
0947777b
         $template = new TemplateForTest($twig);
b8daa6c4
         $template->displayBlock('foo', [], ['foo' => [new \stdClass(), 'foo']]);
30be0775
     }
 
     /**
d4a8c8b2
      * @dataProvider getAttributeExceptions
4e28408c
      */
014ab1f8
     public function testGetAttributeExceptions($template, $message)
4e28408c
     {
5c55243d
         $templates = ['index' => $template];
90d579e4
         $env = new Environment(new ArrayLoader($templates), ['strict_variables' => true]);
e2103c87
         $template = $env->load('index');
d4a8c8b2
 
5c55243d
         $context = [
b0c41d42
             'string' => 'foo',
             'null' => null,
5c55243d
             'empty_array' => [],
             'array' => ['foo' => 'foo'],
2119e60c
             'array_access' => new TemplateArrayAccessObject(),
             'magic_exception' => new TemplateMagicPropertyObjectWithException(),
b8daa6c4
             'object' => new \stdClass(),
5c55243d
         ];
d4a8c8b2
 
         try {
             $template->render($context);
e5291e1b
             $this->fail('Accessing an invalid attribute should throw an exception.');
90d579e4
         } catch (RuntimeError $e) {
014ab1f8
             $this->assertSame(sprintf($message, 'index'), $e->getMessage());
d4a8c8b2
         }
     }
 
     public function getAttributeExceptions()
     {
5c55243d
         return [
             ['{{ string["a"] }}', 'Impossible to access a key ("a") on a string variable ("foo") in "%s" at line 1.'],
             ['{{ null["a"] }}', 'Impossible to access a key ("a") on a null variable in "%s" at line 1.'],
             ['{{ empty_array["a"] }}', 'Key "a" does not exist as the array is empty in "%s" at line 1.'],
             ['{{ array["a"] }}', 'Key "a" for array with keys "foo" does not exist in "%s" at line 1.'],
2119e60c
             ['{{ array_access["a"] }}', 'Key "a" in object with ArrayAccess of class "Twig\Tests\TemplateArrayAccessObject" does not exist in "%s" at line 1.'],
5c55243d
             ['{{ string.a }}', 'Impossible to access an attribute ("a") on a string variable ("foo") in "%s" at line 1.'],
             ['{{ string.a() }}', 'Impossible to invoke a method ("a") on a string variable ("foo") in "%s" at line 1.'],
             ['{{ null.a }}', 'Impossible to access an attribute ("a") on a null variable in "%s" at line 1.'],
             ['{{ null.a() }}', 'Impossible to invoke a method ("a") on a null variable in "%s" at line 1.'],
             ['{{ array.a() }}', 'Impossible to invoke a method ("a") on an array in "%s" at line 1.'],
             ['{{ empty_array.a }}', 'Key "a" does not exist as the array is empty in "%s" at line 1.'],
             ['{{ array.a }}', 'Key "a" for array with keys "foo" does not exist in "%s" at line 1.'],
             ['{{ attribute(array, -10) }}', 'Key "-10" for array with keys "foo" does not exist in "%s" at line 1.'],
ccf27274
             ['{{ array_access.a }}', 'Neither the property "a" nor one of the methods "a()", "geta()"/"isa()"/"hasa()" or "__call()" exist and have public access in class "Twig\Tests\TemplateArrayAccessObject" in "%s" at line 1.'],
             ['{% from _self import foo %}{% macro foo(obj) %}{{ obj.missing_method() }}{% endmacro %}{{ foo(array_access) }}', 'Neither the property "missing_method" nor one of the methods "missing_method()", "getmissing_method()"/"ismissing_method()"/"hasmissing_method()" or "__call()" exist and have public access in class "Twig\Tests\TemplateArrayAccessObject" in "%s" at line 1.'],
5c55243d
             ['{{ magic_exception.test }}', 'An exception has been thrown during the rendering of a template ("Hey! Don\'t try to isset me!") in "%s" at line 1.'],
             ['{{ object["a"] }}', 'Impossible to access a key "a" on an object of class "stdClass" that does not implement ArrayAccess interface in "%s" at line 1.'],
         ];
4e28408c
     }
 
     /**
b92552f3
      * @dataProvider getGetAttributeWithSandbox
      */
014ab1f8
     public function testGetAttributeWithSandbox($object, $item, $allowed)
b92552f3
     {
656c295e
         $twig = new Environment($this->createMock(LoaderInterface::class));
90d579e4
         $policy = new SecurityPolicy([], [], [/*method*/], [/*prop*/], []);
         $twig->addExtension(new SandboxExtension($policy, !$allowed));
2119e60c
         $template = new TemplateForTest($twig);
b92552f3
 
         try {
2ed21eea
             twig_get_attribute($twig, $template->getSourceContext(), $object, $item, [], 'any', false, false, true);
b92552f3
 
             if (!$allowed) {
                 $this->fail();
7259e52f
             } else {
                 $this->addToAssertionCount(1);
b92552f3
             }
90d579e4
         } catch (SecurityError $e) {
b92552f3
             if ($allowed) {
                 $this->fail();
7259e52f
             } else {
                 $this->addToAssertionCount(1);
b92552f3
             }
 
b776e41f
             $this->assertStringContainsString('is not allowed', $e->getMessage());
b92552f3
         }
     }
 
     public function getGetAttributeWithSandbox()
     {
5c55243d
         return [
2119e60c
             [new TemplatePropertyObject(), 'defined', false],
             [new TemplatePropertyObject(), 'defined', true],
             [new TemplateMethodObject(), 'defined', false],
             [new TemplateMethodObject(), 'defined', true],
5c55243d
         ];
b92552f3
     }
 
b09faf83
     public function testRenderBlockWithUndefinedBlock()
     {
5ebcecf2
         $this->expectException(RuntimeError::class);
         $this->expectExceptionMessage('Block "unknown" on template "index.twig" does not exist in "index.twig".');
 
656c295e
         $twig = new Environment($this->createMock(LoaderInterface::class));
2119e60c
         $template = new TemplateForTest($twig, 'index.twig');
b09faf83
         try {
2ed21eea
             $template->renderBlock('unknown', []);
b09faf83
         } catch (\Exception $e) {
             ob_end_clean();
 
             throw $e;
         }
     }
 
     public function testDisplayBlockWithUndefinedBlock()
     {
5ebcecf2
         $this->expectException(RuntimeError::class);
         $this->expectExceptionMessage('Block "unknown" on template "index.twig" does not exist in "index.twig".');
 
656c295e
         $twig = new Environment($this->createMock(LoaderInterface::class));
2119e60c
         $template = new TemplateForTest($twig, 'index.twig');
2ed21eea
         $template->displayBlock('unknown', []);
b09faf83
     }
 
4587f676
     public function testDisplayBlockWithUndefinedParentBlock()
     {
5ebcecf2
         $this->expectException(RuntimeError::class);
         $this->expectExceptionMessage('Block "foo" should not call parent() in "index.twig" as the block does not exist in the parent template "parent.twig"');
 
656c295e
         $twig = new Environment($this->createMock(LoaderInterface::class));
ccf27274
         $template = new TemplateForTest($twig, 'parent.twig');
         $template->displayBlock('foo', [], ['foo' => [new TemplateForTest($twig, 'index.twig'), 'block_foo']], false);
4587f676
     }
 
014ab1f8
     public function testGetAttributeOnArrayWithConfusableKey()
26bac14c
     {
656c295e
         $twig = new Environment($this->createMock(LoaderInterface::class));
ccf27274
         $template = new TemplateForTest($twig);
26bac14c
 
5c55243d
         $array = ['Zero', 'One', -1 => 'MinusOne', '' => 'EmptyString', '1.5' => 'FloatButString', '01' => 'IntegerButStringWithLeadingZeros'];
26bac14c
 
         $this->assertSame('Zero', $array[false]);
         $this->assertSame('One', $array[true]);
         $this->assertSame('One', $array[1.5]);
         $this->assertSame('One', $array['1']);
         $this->assertSame('MinusOne', $array[-1.5]);
         $this->assertSame('FloatButString', $array['1.5']);
         $this->assertSame('IntegerButStringWithLeadingZeros', $array['01']);
         $this->assertSame('EmptyString', $array[null]);
 
64056843
         $this->assertSame('Zero', twig_get_attribute($twig, $template->getSourceContext(), $array, false), 'false is treated as 0 when accessing an array (equals PHP behavior)');
         $this->assertSame('One', twig_get_attribute($twig, $template->getSourceContext(), $array, true), 'true is treated as 1 when accessing an array (equals PHP behavior)');
         $this->assertSame('One', twig_get_attribute($twig, $template->getSourceContext(), $array, 1.5), 'float is casted to int when accessing an array (equals PHP behavior)');
         $this->assertSame('One', twig_get_attribute($twig, $template->getSourceContext(), $array, '1'), '"1" is treated as integer 1 when accessing an array (equals PHP behavior)');
         $this->assertSame('MinusOne', twig_get_attribute($twig, $template->getSourceContext(), $array, -1.5), 'negative float is casted to int when accessing an array (equals PHP behavior)');
         $this->assertSame('FloatButString', twig_get_attribute($twig, $template->getSourceContext(), $array, '1.5'), '"1.5" is treated as-is when accessing an array (equals PHP behavior)');
         $this->assertSame('IntegerButStringWithLeadingZeros', twig_get_attribute($twig, $template->getSourceContext(), $array, '01'), '"01" is treated as-is when accessing an array (equals PHP behavior)');
         $this->assertSame('EmptyString', twig_get_attribute($twig, $template->getSourceContext(), $array, null), 'null is treated as "" when accessing an array (equals PHP behavior)');
26bac14c
     }
 
     /**
f742efd6
      * @dataProvider getGetAttributeTests
      */
014ab1f8
     public function testGetAttribute($defined, $value, $object, $item, $arguments, $type)
024b9367
     {
656c295e
         $twig = new Environment($this->createMock(LoaderInterface::class));
ccf27274
         $template = new TemplateForTest($twig);
024b9367
 
64056843
         $this->assertEquals($value, twig_get_attribute($twig, $template->getSourceContext(), $object, $item, $arguments, $type));
024b9367
     }
 
     /**
f742efd6
      * @dataProvider getGetAttributeTests
024b9367
      */
014ab1f8
     public function testGetAttributeStrict($defined, $value, $object, $item, $arguments, $type, $exceptionMessage = null)
024b9367
     {
656c295e
         $twig = new Environment($this->createMock(LoaderInterface::class), ['strict_variables' => true]);
ccf27274
         $template = new TemplateForTest($twig);
f742efd6
 
         if ($defined) {
64056843
             $this->assertEquals($value, twig_get_attribute($twig, $template->getSourceContext(), $object, $item, $arguments, $type));
f742efd6
         } else {
c829dafd
             $this->expectException(RuntimeError::class);
b776e41f
             if (null !== $exceptionMessage) {
                 $this->expectExceptionMessage($exceptionMessage);
9e6f2853
             }
4d40c66d
             $this->assertEquals($value, twig_get_attribute($twig, $template->getSourceContext(), $object, $item, $arguments, $type));
f742efd6
         }
024b9367
     }
 
1698c883
     /**
      * @dataProvider getGetAttributeTests
      */
014ab1f8
     public function testGetAttributeDefined($defined, $value, $object, $item, $arguments, $type)
1698c883
     {
656c295e
         $twig = new Environment($this->createMock(LoaderInterface::class));
ccf27274
         $template = new TemplateForTest($twig);
1698c883
 
64056843
         $this->assertEquals($defined, twig_get_attribute($twig, $template->getSourceContext(), $object, $item, $arguments, $type, true));
f742efd6
     }
 
     /**
      * @dataProvider getGetAttributeTests
      */
014ab1f8
     public function testGetAttributeDefinedStrict($defined, $value, $object, $item, $arguments, $type)
f742efd6
     {
656c295e
         $twig = new Environment($this->createMock(LoaderInterface::class), ['strict_variables' => true]);
ccf27274
         $template = new TemplateForTest($twig);
f742efd6
 
64056843
         $this->assertEquals($defined, twig_get_attribute($twig, $template->getSourceContext(), $object, $item, $arguments, $type, true));
1698c883
     }
 
014ab1f8
     public function testGetAttributeCallExceptions()
9ab290bd
     {
656c295e
         $twig = new Environment($this->createMock(LoaderInterface::class));
ccf27274
         $template = new TemplateForTest($twig);
9ab290bd
 
2119e60c
         $object = new TemplateMagicMethodExceptionObject();
9ab290bd
 
64056843
         $this->assertNull(twig_get_attribute($twig, $template->getSourceContext(), $object, 'foo'));
9ab290bd
     }
 
1698c883
     public function getGetAttributeTests()
     {
5c55243d
         $array = [
f742efd6
             'defined' => 'defined',
b0c41d42
             'zero' => 0,
             'null' => null,
             '1' => 1,
             'bar' => true,
d3c3c605
             'foo' => true,
87783139
             'baz' => 'baz',
64056843
             'baf' => 'baf',
b0c41d42
             '09' => '09',
             '+4' => '+4',
5c55243d
         ];
f742efd6
 
2119e60c
         $objectArray = new TemplateArrayAccessObject();
b8daa6c4
         $arrayObject = new \ArrayObject($array);
b0c41d42
         $stdObject = (object) $array;
2119e60c
         $magicPropertyObject = new TemplateMagicPropertyObject();
         $propertyObject = new TemplatePropertyObject();
         $propertyObject1 = new TemplatePropertyObjectAndIterator();
         $propertyObject2 = new TemplatePropertyObjectAndArrayAccess();
         $propertyObject3 = new TemplatePropertyObjectDefinedWithUndefinedValue();
         $methodObject = new TemplateMethodObject();
         $magicMethodObject = new TemplateMagicMethodObject();
b0c41d42
 
90d579e4
         $anyType = Template::ANY_CALL;
         $methodType = Template::METHOD_CALL;
         $arrayType = Template::ARRAY_CALL;
f742efd6
 
5c55243d
         $basicTests = [
f742efd6
             // array(defined, value, property to fetch)
5c55243d
             [true,  'defined', 'defined'],
             [false, null,      'undefined'],
             [false, null,      'protected'],
             [true,  0,         'zero'],
             [true,  1,         1],
             [true,  1,         1.0],
             [true,  null,      'null'],
             [true,  true,      'bar'],
2ed21eea
             [true,  true,      'foo'],
5c55243d
             [true,  'baz',     'baz'],
2ed21eea
             [true,  'baf',     'baf'],
5c55243d
             [true,  '09',      '09'],
             [true,  '+4',      '+4'],
         ];
         $testObjects = [
f742efd6
             // array(object, type of fetch)
5c55243d
             [$array,               $arrayType],
             [$objectArray,         $arrayType],
80e65b4c
             [$arrayObject,         $anyType],
5c55243d
             [$stdObject,           $anyType],
             [$magicPropertyObject, $anyType],
             [$methodObject,        $methodType],
             [$methodObject,        $anyType],
             [$propertyObject,      $anyType],
             [$propertyObject1,     $anyType],
             [$propertyObject2,     $anyType],
         ];
 
         $tests = [];
f742efd6
         foreach ($testObjects as $testObject) {
             foreach ($basicTests as $test) {
aa6b8358
                 // properties cannot be numbers
2119e60c
                 if (($testObject[0] instanceof \stdClass || $testObject[0] instanceof TemplatePropertyObject) && is_numeric($test[2])) {
89df3c20
                     continue;
c4d5b3ff
                 }
 
                 if ('+4' === $test[2] && $methodObject === $testObject[0]) {
aa6b8358
                     continue;
                 }
 
5c55243d
                 $tests[] = [$test[0], $test[1], $testObject[0], $test[2], [], $testObject[1]];
f742efd6
             }
         }
 
6e800d82
         // additional properties tests
5c55243d
         $tests = array_merge($tests, [
             [true, null, $propertyObject3, 'foo', [], $anyType],
         ]);
6e800d82
 
f742efd6
         // additional method tests
5c55243d
         $tests = array_merge($tests, [
             [true, 'defined', $methodObject, 'defined',    [], $methodType],
             [true, 'defined', $methodObject, 'DEFINED',    [], $methodType],
             [true, 'defined', $methodObject, 'getDefined', [], $methodType],
             [true, 'defined', $methodObject, 'GETDEFINED', [], $methodType],
             [true, 'static',  $methodObject, 'static',     [], $methodType],
             [true, 'static',  $methodObject, 'getStatic',  [], $methodType],
 
             [true, '__call_undefined', $magicMethodObject, 'undefined', [], $methodType],
             [true, '__call_UNDEFINED', $magicMethodObject, 'UNDEFINED', [], $methodType],
         ]);
1698c883
 
         // add the same tests for the any type
         foreach ($tests as $test) {
f742efd6
             if ($anyType !== $test[5]) {
                 $test[5] = $anyType;
1698c883
                 $tests[] = $test;
             }
         }
 
2119e60c
         $methodAndPropObject = new TemplateMethodAndPropObject();
7e566b84
 
         // additional method tests
5c55243d
         $tests = array_merge($tests, [
             [true, 'a', $methodAndPropObject, 'a', [], $anyType],
             [true, 'a', $methodAndPropObject, 'a', [], $methodType],
             [false, null, $methodAndPropObject, 'a', [], $arrayType],
 
             [true, 'b_prop', $methodAndPropObject, 'b', [], $anyType],
             [true, 'b', $methodAndPropObject, 'B', [], $anyType],
             [true, 'b', $methodAndPropObject, 'b', [], $methodType],
             [true, 'b', $methodAndPropObject, 'B', [], $methodType],
             [false, null, $methodAndPropObject, 'b', [], $arrayType],
 
             [false, null, $methodAndPropObject, 'c', [], $anyType],
             [false, null, $methodAndPropObject, 'c', [], $methodType],
             [false, null, $methodAndPropObject, 'c', [], $arrayType],
         ]);
7e566b84
 
2119e60c
         $arrayAccess = new TemplateArrayAccess();
6df989ba
         $tests = array_merge($tests, [
             [true, ['foo' => 'bar'], $arrayAccess, 'vars', [], $anyType],
         ]);
 
9e6f2853
         // tests when input is not an array or object
5c55243d
         $tests = array_merge($tests, [
             [false, null, 42, 'a', [], $anyType, 'Impossible to access an attribute ("a") on a integer variable ("42") in "index.twig".'],
             [false, null, 'string', 'a', [], $anyType, 'Impossible to access an attribute ("a") on a string variable ("string") in "index.twig".'],
             [false, null, [], 'a', [], $anyType, 'Key "a" does not exist as the array is empty in "index.twig".'],
         ]);
9e6f2853
 
1698c883
         return $tests;
     }
413be416
 
     public function testGetIsMethods()
     {
656c295e
         $twig = new Environment($this->createMock(LoaderInterface::class));
b776e41f
 
2119e60c
         $getIsObject = new TemplateGetIsMethods();
ccf27274
         $template = new TemplateForTest($twig, 'index.twig');
413be416
         // first time should not create a cache for "get"
bda3380e
         $this->assertNull(twig_get_attribute($twig, $template->getSourceContext(), $getIsObject, 'get'));
413be416
         // 0 should be in the method cache now, so this should fail
bda3380e
         $this->assertNull(twig_get_attribute($twig, $template->getSourceContext(), $getIsObject, 0));
413be416
     }
1698c883
 }
 
2119e60c
 class TemplateForTest extends Template
2a3365a3
 {
0595c402
     private $name;
7e566b84
 
90d579e4
     public function __construct(Environment $env, $name = 'index.twig')
7e566b84
     {
         parent::__construct($env);
0595c402
         $this->name = $name;
7e566b84
     }
 
df13370b
     public function getZero()
     {
         return 0;
     }
 
014f459b
     public function getEmpty()
     {
         return '';
     }
 
     public function getString()
     {
         return 'some_string';
     }
 
     public function getTrue()
     {
         return true;
     }
 
fd057310
     public function getTemplateName()
     {
0595c402
         return $this->name;
fd057310
     }
 
49ee8d6e
     public function getDebugInfo()
     {
5c55243d
         return [];
49ee8d6e
     }
 
b3498de4
     public function getSourceContext()
     {
         return new Source('', $this->getTemplateName());
     }
 
c201137f
     protected function doGetParent(array $context)
     {
0595c402
         return false;
c201137f
     }
 
5c55243d
     protected function doDisplay(array $context, array $blocks = [])
2a3365a3
     {
     }
 
5c55243d
     public function block_name($context, array $blocks = [])
75d474a2
     {
     }
2a3365a3
 }
 
2119e60c
 class TemplateArrayAccessObject implements \ArrayAccess
1698c883
 {
82e3ecc6
     protected $protected = 'protected';
 
5c55243d
     public $attributes = [
f742efd6
         'defined' => 'defined',
b0c41d42
         'zero' => 0,
         'null' => null,
         '1' => 1,
         'bar' => true,
d3c3c605
         'foo' => true,
87783139
         'baz' => 'baz',
64056843
         'baf' => 'baf',
b0c41d42
         '09' => '09',
         '+4' => '+4',
5c55243d
     ];
74dc1596
 
f742efd6
     public function offsetExists($name)
74dc1596
     {
1e446652
         return \array_key_exists($name, $this->attributes);
74dc1596
     }
1698c883
 
f742efd6
     public function offsetGet($name)
1698c883
     {
1e446652
         return \array_key_exists($name, $this->attributes) ? $this->attributes[$name] : null;
1698c883
     }
 
f742efd6
     public function offsetSet($name, $value)
1698c883
     {
     }
 
f742efd6
     public function offsetUnset($name)
1698c883
     {
     }
 }
 
2119e60c
 class TemplateMagicPropertyObject
1698c883
 {
82e3ecc6
     public $defined = 'defined';
 
5c55243d
     public $attributes = [
b0c41d42
         'zero' => 0,
         'null' => null,
         '1' => 1,
         'bar' => true,
d3c3c605
         'foo' => true,
87783139
         'baz' => 'baz',
64056843
         'baf' => 'baf',
b0c41d42
         '09' => '09',
         '+4' => '+4',
5c55243d
     ];
1698c883
 
82e3ecc6
     protected $protected = 'protected';
 
f742efd6
     public function __isset($name)
1698c883
     {
1e446652
         return \array_key_exists($name, $this->attributes);
1698c883
     }
 
f742efd6
     public function __get($name)
1698c883
     {
1e446652
         return \array_key_exists($name, $this->attributes) ? $this->attributes[$name] : null;
1698c883
     }
f742efd6
 }
1698c883
 
2119e60c
 class TemplateMagicPropertyObjectWithException
cefa33ad
 {
     public function __isset($key)
     {
b8daa6c4
         throw new \Exception('Hey! Don\'t try to isset me!');
cefa33ad
     }
 }
 
2119e60c
 class TemplatePropertyObject
82e3ecc6
 {
     public $defined = 'defined';
b0c41d42
     public $zero = 0;
     public $null = null;
     public $bar = true;
d3c3c605
     public $foo = true;
87783139
     public $baz = 'baz';
64056843
     public $baf = 'baf';
82e3ecc6
 
     protected $protected = 'protected';
 }
 
2119e60c
 class TemplatePropertyObjectAndIterator extends TemplatePropertyObject implements \IteratorAggregate
82e3ecc6
 {
     public function getIterator()
     {
b8daa6c4
         return new \ArrayIterator(['foo', 'bar']);
82e3ecc6
     }
 }
 
2119e60c
 class TemplatePropertyObjectAndArrayAccess extends TemplatePropertyObject implements \ArrayAccess
d6fc86a8
 {
5c55243d
     private $data = [
7259e52f
         'defined' => 'defined',
         'zero' => 0,
         'null' => null,
         'bar' => true,
86b16884
         'foo' => true,
7259e52f
         'baz' => 'baz',
86b16884
         'baf' => 'baf',
5c55243d
     ];
d6fc86a8
 
     public function offsetExists($offset)
     {
1e446652
         return \array_key_exists($offset, $this->data);
d6fc86a8
     }
 
     public function offsetGet($offset)
     {
         return $this->offsetExists($offset) ? $this->data[$offset] : 'n/a';
     }
 
     public function offsetSet($offset, $value)
     {
     }
 
     public function offsetUnset($offset)
     {
     }
 }
 
2119e60c
 class TemplatePropertyObjectDefinedWithUndefinedValue
6e800d82
 {
     public $foo;
 
     public function __construct()
     {
         $this->foo = @$notExist;
     }
 }
 
2119e60c
 class TemplateMethodObject
f742efd6
 {
     public function getDefined()
1698c883
     {
f742efd6
         return 'defined';
1698c883
     }
 
aa6b8358
     public function get1()
     {
         return 1;
     }
 
c4d5b3ff
     public function get09()
     {
         return '09';
     }
 
f742efd6
     public function getZero()
1698c883
     {
f742efd6
         return 0;
1698c883
     }
 
f742efd6
     public function getNull()
1698c883
     {
     }
 
35f611f2
     public function isBar()
     {
         return true;
     }
 
d3c3c605
     public function hasFoo()
     {
         return true;
     }
 
64056843
     public function hasBaz()
     {
         return 'should never be returned (has)';
     }
 
87783139
     public function isBaz()
     {
64056843
         return 'should never be returned (is)';
87783139
     }
 
     public function getBaz()
     {
2c153bd1
         return 'Baz';
     }
 
     public function baz()
     {
87783139
         return 'baz';
     }
 
64056843
     public function hasBaf()
     {
         return 'should never be returned (has)';
     }
 
     public function isBaf()
     {
         return 'baf';
     }
 
82e3ecc6
     protected function getProtected()
     {
         return 'protected';
     }
 
a009821d
     public static function getStatic()
1698c883
     {
f742efd6
         return 'static';
1698c883
     }
f742efd6
 }
1698c883
 
2119e60c
 class TemplateGetIsMethods
413be416
 {
     public function get()
     {
     }
 
     public function is()
     {
     }
 }
 
2119e60c
 class TemplateMethodAndPropObject
7e566b84
 {
     private $a = 'a_prop';
52af84c1
 
a009821d
     public function getA()
     {
7e566b84
         return 'a';
     }
 
     public $b = 'b_prop';
52af84c1
 
a009821d
     public function getB()
     {
7e566b84
         return 'b';
     }
 
     private $c = 'c_prop';
52af84c1
 
a009821d
     private function getC()
     {
7e566b84
         return 'c';
     }
 }
 
2119e60c
 class TemplateArrayAccess implements \ArrayAccess
6df989ba
 {
     public $vars = [
         'foo' => 'bar',
     ];
     private $children = [];
 
     public function offsetExists($offset)
     {
1e446652
         return \array_key_exists($offset, $this->children);
6df989ba
     }
 
     public function offsetGet($offset)
     {
         return $this->children[$offset];
     }
 
     public function offsetSet($offset, $value)
     {
         $this->children[$offset] = $value;
     }
 
     public function offsetUnset($offset)
     {
         unset($this->children[$offset]);
     }
 }
 
2119e60c
 class TemplateMagicMethodObject
f742efd6
 {
a009821d
     public function __call($method, $arguments)
     {
1698c883
         return '__call_'.$method;
     }
 }
d4a8c8b2
 
2119e60c
 class TemplateMagicMethodExceptionObject
9ab290bd
 {
     public function __call($method, $arguments)
     {
b8daa6c4
         throw new \BadMethodCallException(sprintf('Unknown method "%s".', $method));
9ab290bd
     }
 }