Twig_Template::getAttribute()   F
last analyzed

Complexity

Conditions 44
Paths 980

Size

Total Lines 129
Code Lines 73

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 129
rs 2
nc 980
cc 44
eloc 73
nop 6

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/*
4
 * This file is part of Twig.
5
 *
6
 * (c) 2009 Fabien Potencier
7
 * (c) 2009 Armin Ronacher
8
 *
9
 * For the full copyright and license information, please view the LICENSE
10
 * file that was distributed with this source code.
11
 */
12
13
/**
14
 * Default base class for compiled templates.
15
 *
16
 * @author Fabien Potencier <[email protected]>
17
 */
18
abstract class Twig_Template implements Twig_TemplateInterface
0 ignored issues
show
Deprecated Code introduced by
The interface Twig_TemplateInterface has been deprecated with message: since 1.12 (to be removed in 3.0)

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
19
{
20
    protected static $cache = array();
21
22
    protected $parent;
23
    protected $parents = array();
24
    protected $env;
25
    protected $blocks;
26
    protected $traits;
27
28
    /**
29
     * Constructor.
30
     *
31
     * @param Twig_Environment $env A Twig_Environment instance
32
     */
33
    public function __construct(Twig_Environment $env)
34
    {
35
        $this->env = $env;
36
        $this->blocks = array();
37
        $this->traits = array();
38
    }
39
40
    /**
41
     * Returns the template name.
42
     *
43
     * @return string The template name
44
     */
45
    abstract public function getTemplateName();
46
47
    /**
48
     * {@inheritdoc}
49
     */
50
    public function getEnvironment()
51
    {
52
        return $this->env;
53
    }
54
55
    /**
56
     * Returns the parent template.
57
     *
58
     * This method is for internal use only and should never be called
59
     * directly.
60
     *
61
     * @return Twig_TemplateInterface|false The parent template or false if there is no parent
62
     */
63
    public function getParent(array $context)
64
    {
65
        if (null !== $this->parent) {
66
            return $this->parent;
67
        }
68
69
        try {
70
            $parent = $this->doGetParent($context);
71
72
            if (false === $parent) {
73
                return false;
74
            }
75
76
            if ($parent instanceof Twig_Template) {
77
                return $this->parents[$parent->getTemplateName()] = $parent;
78
            }
79
80
            if (!isset($this->parents[$parent])) {
81
                $this->parents[$parent] = $this->env->loadTemplate($parent);
0 ignored issues
show
Documentation introduced by
$parent is of type boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
82
            }
83
        } catch (Twig_Error_Loader $e) {
84
            $e->setTemplateFile(null);
85
            $e->guess();
86
87
            throw $e;
88
        }
89
90
        return $this->parents[$parent];
91
    }
92
93
    protected function doGetParent(array $context)
0 ignored issues
show
Unused Code introduced by
The parameter $context is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
94
    {
95
        return false;
96
    }
97
98
    public function isTraitable()
99
    {
100
        return true;
101
    }
102
103
    /**
104
     * Displays a parent block.
105
     *
106
     * This method is for internal use only and should never be called
107
     * directly.
108
     *
109
     * @param string $name    The block name to display from the parent
110
     * @param array  $context The context
111
     * @param array  $blocks  The current set of blocks
112
     */
113
    public function displayParentBlock($name, array $context, array $blocks = array())
114
    {
115
        $name = (string) $name;
116
117
        if (isset($this->traits[$name])) {
118
            $this->traits[$name][0]->displayBlock($name, $context, $blocks, false);
119
        } elseif (false !== $parent = $this->getParent($context)) {
120
            $parent->displayBlock($name, $context, $blocks, false);
0 ignored issues
show
Bug introduced by
The method displayBlock() does not exist on Twig_TemplateInterface. Did you maybe mean display()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
121
        } else {
122
            throw new Twig_Error_Runtime(sprintf('The template has no parent and no traits defining the "%s" block', $name), -1, $this->getTemplateName());
123
        }
124
    }
125
126
    /**
127
     * Displays a block.
128
     *
129
     * This method is for internal use only and should never be called
130
     * directly.
131
     *
132
     * @param string $name      The block name to display
133
     * @param array  $context   The context
134
     * @param array  $blocks    The current set of blocks
135
     * @param bool   $useBlocks Whether to use the current set of blocks
136
     */
137
    public function displayBlock($name, array $context, array $blocks = array(), $useBlocks = true)
138
    {
139
        $name = (string) $name;
140
141
        if ($useBlocks && isset($blocks[$name])) {
142
            $template = $blocks[$name][0];
143
            $block = $blocks[$name][1];
144
        } elseif (isset($this->blocks[$name])) {
145
            $template = $this->blocks[$name][0];
146
            $block = $this->blocks[$name][1];
147
        } else {
148
            $template = null;
149
            $block = null;
150
        }
151
152
        if (null !== $template) {
153
            try {
154
                $template->$block($context, $blocks);
155
            } catch (Twig_Error $e) {
156
                throw $e;
157
            } catch (Exception $e) {
158
                throw new Twig_Error_Runtime(sprintf('An exception has been thrown during the rendering of a template ("%s").', $e->getMessage()), -1, $template->getTemplateName(), $e);
159
            }
160
        } elseif (false !== $parent = $this->getParent($context)) {
161
            $parent->displayBlock($name, $context, array_merge($this->blocks, $blocks), false);
0 ignored issues
show
Bug introduced by
The method displayBlock() does not exist on Twig_TemplateInterface. Did you maybe mean display()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
162
        }
163
    }
164
165
    /**
166
     * Renders a parent block.
167
     *
168
     * This method is for internal use only and should never be called
169
     * directly.
170
     *
171
     * @param string $name    The block name to render from the parent
172
     * @param array  $context The context
173
     * @param array  $blocks  The current set of blocks
174
     *
175
     * @return string The rendered block
176
     */
177
    public function renderParentBlock($name, array $context, array $blocks = array())
178
    {
179
        ob_start();
180
        $this->displayParentBlock($name, $context, $blocks);
181
182
        return ob_get_clean();
183
    }
184
185
    /**
186
     * Renders a block.
187
     *
188
     * This method is for internal use only and should never be called
189
     * directly.
190
     *
191
     * @param string $name      The block name to render
192
     * @param array  $context   The context
193
     * @param array  $blocks    The current set of blocks
194
     * @param bool   $useBlocks Whether to use the current set of blocks
195
     *
196
     * @return string The rendered block
197
     */
198
    public function renderBlock($name, array $context, array $blocks = array(), $useBlocks = true)
199
    {
200
        ob_start();
201
        $this->displayBlock($name, $context, $blocks, $useBlocks);
202
203
        return ob_get_clean();
204
    }
205
206
    /**
207
     * Returns whether a block exists or not.
208
     *
209
     * This method is for internal use only and should never be called
210
     * directly.
211
     *
212
     * This method does only return blocks defined in the current template
213
     * or defined in "used" traits.
214
     *
215
     * It does not return blocks from parent templates as the parent
216
     * template name can be dynamic, which is only known based on the
217
     * current context.
218
     *
219
     * @param string $name The block name
220
     *
221
     * @return bool true if the block exists, false otherwise
222
     */
223
    public function hasBlock($name)
224
    {
225
        return isset($this->blocks[(string) $name]);
226
    }
227
228
    /**
229
     * Returns all block names.
230
     *
231
     * This method is for internal use only and should never be called
232
     * directly.
233
     *
234
     * @return array An array of block names
235
     *
236
     * @see hasBlock
237
     */
238
    public function getBlockNames()
239
    {
240
        return array_keys($this->blocks);
241
    }
242
243
    /**
244
     * Returns all blocks.
245
     *
246
     * This method is for internal use only and should never be called
247
     * directly.
248
     *
249
     * @return array An array of blocks
250
     *
251
     * @see hasBlock
252
     */
253
    public function getBlocks()
254
    {
255
        return $this->blocks;
256
    }
257
258
    /**
259
     * {@inheritdoc}
260
     */
261
    public function display(array $context, array $blocks = array())
262
    {
263
        $this->displayWithErrorHandling($this->env->mergeGlobals($context), array_merge($this->blocks, $blocks));
264
    }
265
266
    /**
267
     * {@inheritdoc}
268
     */
269
    public function render(array $context)
270
    {
271
        $level = ob_get_level();
272
        ob_start();
273
        try {
274
            $this->display($context);
275
        } catch (Exception $e) {
276
            while (ob_get_level() > $level) {
277
                ob_end_clean();
278
            }
279
280
            throw $e;
281
        }
282
283
        return ob_get_clean();
284
    }
285
286
    protected function displayWithErrorHandling(array $context, array $blocks = array())
287
    {
288
        try {
289
            $this->doDisplay($context, $blocks);
290
        } catch (Twig_Error $e) {
291
            if (!$e->getTemplateFile()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $e->getTemplateFile() of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
292
                $e->setTemplateFile($this->getTemplateName());
293
            }
294
295
            // this is mostly useful for Twig_Error_Loader exceptions
296
            // see Twig_Error_Loader
297
            if (false === $e->getTemplateLine()) {
298
                $e->setTemplateLine(-1);
299
                $e->guess();
300
            }
301
302
            throw $e;
303
        } catch (Exception $e) {
304
            throw new Twig_Error_Runtime(sprintf('An exception has been thrown during the rendering of a template ("%s").', $e->getMessage()), -1, $this->getTemplateName(), $e);
305
        }
306
    }
307
308
    /**
309
     * Auto-generated method to display the template with the given context.
310
     *
311
     * @param array $context An array of parameters to pass to the template
312
     * @param array $blocks  An array of blocks to pass to the template
313
     */
314
    abstract protected function doDisplay(array $context, array $blocks = array());
0 ignored issues
show
Documentation introduced by
For interfaces and abstract methods it is generally a good practice to add a @return annotation even if it is just @return void or @return null, so that implementors know what to do in the overridden method.

For interface and abstract methods, it is impossible to infer the return type from the immediate code. In these cases, it is generally advisible to explicitly annotate these methods with a @return doc comment to communicate to implementors of these methods what they are expected to return.

Loading history...
315
316
    /**
317
     * Returns a variable from the context.
318
     *
319
     * This method is for internal use only and should never be called
320
     * directly.
321
     *
322
     * This method should not be overridden in a sub-class as this is an
323
     * implementation detail that has been introduced to optimize variable
324
     * access for versions of PHP before 5.4. This is not a way to override
325
     * the way to get a variable value.
326
     *
327
     * @param array  $context           The context
328
     * @param string $item              The variable to return from the context
329
     * @param bool   $ignoreStrictCheck Whether to ignore the strict variable check or not
330
     *
331
     * @return The content of the context variable
332
     *
333
     * @throws Twig_Error_Runtime if the variable does not exist and Twig is running in strict mode
334
     */
335
    final protected function getContext($context, $item, $ignoreStrictCheck = false)
336
    {
337
        if (!array_key_exists($item, $context)) {
338
            if ($ignoreStrictCheck || !$this->env->isStrictVariables()) {
339
                return;
340
            }
341
342
            throw new Twig_Error_Runtime(sprintf('Variable "%s" does not exist', $item), -1, $this->getTemplateName());
343
        }
344
345
        return $context[$item];
346
    }
347
348
    /**
349
     * Returns the attribute value for a given array/object.
350
     *
351
     * @param mixed  $object            The object or array from where to get the item
352
     * @param mixed  $item              The item to get from the array or object
353
     * @param array  $arguments         An array of arguments to pass if the item is an object method
354
     * @param string $type              The type of attribute (@see Twig_Template constants)
355
     * @param bool   $isDefinedTest     Whether this is only a defined check
356
     * @param bool   $ignoreStrictCheck Whether to ignore the strict attribute check or not
357
     *
358
     * @return mixed The attribute value, or a Boolean when $isDefinedTest is true, or null when the attribute is not set and $ignoreStrictCheck is true
359
     *
360
     * @throws Twig_Error_Runtime if the attribute does not exist and Twig is running in strict mode and $isDefinedTest is false
361
     */
362
    protected function getAttribute($object, $item, array $arguments = array(), $type = Twig_Template::ANY_CALL, $isDefinedTest = false, $ignoreStrictCheck = false)
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
363
    {
364
        // array
365
        if (Twig_Template::METHOD_CALL !== $type) {
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
366
            $arrayItem = is_bool($item) || is_float($item) ? (int) $item : $item;
367
368
            if ((is_array($object) && array_key_exists($arrayItem, $object))
369
                || ($object instanceof ArrayAccess && isset($object[$arrayItem]))
370
            ) {
371
                if ($isDefinedTest) {
372
                    return true;
373
                }
374
375
                return $object[$arrayItem];
376
            }
377
378
            if (Twig_Template::ARRAY_CALL === $type || !is_object($object)) {
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
379
                if ($isDefinedTest) {
380
                    return false;
381
                }
382
383
                if ($ignoreStrictCheck || !$this->env->isStrictVariables()) {
384
                    return;
385
                }
386
387
                if ($object instanceof ArrayAccess) {
388
                    $message = sprintf('Key "%s" in object with ArrayAccess of class "%s" does not exist', $arrayItem, get_class($object));
389
                } elseif (is_object($object)) {
390
                    $message = sprintf('Impossible to access a key "%s" on an object of class "%s" that does not implement ArrayAccess interface', $item, get_class($object));
391
                } elseif (is_array($object)) {
392
                    if (empty($object)) {
393
                        $message = sprintf('Key "%s" does not exist as the array is empty', $arrayItem);
394
                    } else {
395
                        $message = sprintf('Key "%s" for array with keys "%s" does not exist', $arrayItem, implode(', ', array_keys($object)));
396
                    }
397
                } elseif (Twig_Template::ARRAY_CALL === $type) {
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
398
                    $message = sprintf('Impossible to access a key ("%s") on a %s variable ("%s")', $item, gettype($object), $object);
399
                } else {
400
                    $message = sprintf('Impossible to access an attribute ("%s") on a %s variable ("%s")', $item, gettype($object), $object);
401
                }
402
403
                throw new Twig_Error_Runtime($message, -1, $this->getTemplateName());
404
            }
405
        }
406
407
        if (!is_object($object)) {
408
            if ($isDefinedTest) {
409
                return false;
410
            }
411
412
            if ($ignoreStrictCheck || !$this->env->isStrictVariables()) {
413
                return;
414
            }
415
416
            throw new Twig_Error_Runtime(sprintf('Impossible to invoke a method ("%s") on a %s variable ("%s")', $item, gettype($object), $object), -1, $this->getTemplateName());
417
        }
418
419
        // object property
420
        if (Twig_Template::METHOD_CALL !== $type) {
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
421
            if (isset($object->$item) || array_key_exists((string) $item, $object)) {
422
                if ($isDefinedTest) {
423
                    return true;
424
                }
425
426
                if ($this->env->hasExtension('sandbox')) {
427
                    $this->env->getExtension('sandbox')->checkPropertyAllowed($object, $item);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Twig_ExtensionInterface as the method checkPropertyAllowed() does only exist in the following implementations of said interface: Twig_Extension_Sandbox, Twig_Extension_Sandbox.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
428
                }
429
430
                return $object->$item;
431
            }
432
        }
433
434
        $class = get_class($object);
435
436
        // object method
437
        if (!isset(self::$cache[$class]['methods'])) {
438
            self::$cache[$class]['methods'] = array_change_key_case(array_flip(get_class_methods($object)));
439
        }
440
441
        $call = false;
442
        $lcItem = strtolower($item);
443
        if (isset(self::$cache[$class]['methods'][$lcItem])) {
444
            $method = (string) $item;
445
        } elseif (isset(self::$cache[$class]['methods']['get'.$lcItem])) {
446
            $method = 'get'.$item;
447
        } elseif (isset(self::$cache[$class]['methods']['is'.$lcItem])) {
448
            $method = 'is'.$item;
449
        } elseif (isset(self::$cache[$class]['methods']['__call'])) {
450
            $method = (string) $item;
451
            $call = true;
452
        } else {
453
            if ($isDefinedTest) {
454
                return false;
455
            }
456
457
            if ($ignoreStrictCheck || !$this->env->isStrictVariables()) {
458
                return;
459
            }
460
461
            throw new Twig_Error_Runtime(sprintf('Method "%s" for object "%s" does not exist', $item, get_class($object)), -1, $this->getTemplateName());
462
        }
463
464
        if ($isDefinedTest) {
465
            return true;
466
        }
467
468
        if ($this->env->hasExtension('sandbox')) {
469
            $this->env->getExtension('sandbox')->checkMethodAllowed($object, $method);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Twig_ExtensionInterface as the method checkMethodAllowed() does only exist in the following implementations of said interface: Twig_Extension_Sandbox, Twig_Extension_Sandbox.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
470
        }
471
472
        // Some objects throw exceptions when they have __call, and the method we try
473
        // to call is not supported. If ignoreStrictCheck is true, we should return null.
474
        try {
475
            $ret = call_user_func_array(array($object, $method), $arguments);
476
        } catch (BadMethodCallException $e) {
477
            if ($call && ($ignoreStrictCheck || !$this->env->isStrictVariables())) {
478
                return;
479
            }
480
            throw $e;
481
        }
482
483
        // useful when calling a template method from a template
484
        // this is not supported but unfortunately heavily used in the Symfony profiler
485
        if ($object instanceof Twig_TemplateInterface) {
486
            return $ret === '' ? '' : new Twig_Markup($ret, $this->env->getCharset());
487
        }
488
489
        return $ret;
490
    }
491
}
492