Passed
Push — fix_coverage_in_scrutinizer ( cd0379...a04ba4 )
by Herberto
13:22
created

SourceCodeExtension::showSourceCode()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 7
ccs 4
cts 4
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 4
nc 1
nop 2
crap 1
1
<?php
2
3
/*
4
 * This file is part of the Symfony package.
5
 *
6
 * (c) Fabien Potencier <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace App\Twig;
13
14
use Twig\Environment;
15
use Twig\Extension\AbstractExtension;
16
use Twig\Template;
17
use Twig\TwigFunction;
18
19
/**
20
 * CAUTION: this is an extremely advanced Twig extension. It's used to get the
21
 * source code of the controller and the template used to render the current
22
 * page. If you are starting with Symfony, don't look at this code and consider
23
 * studying instead the code of the src/App/Twig/AppExtension.php extension.
24
 *
25
 * @author Ryan Weaver <[email protected]>
26
 * @author Javier Eguiluz <[email protected]>
27
 */
28
class SourceCodeExtension extends AbstractExtension
29
{
30
    private $controller;
31
32 12
    public function setController(?callable $controller)
33
    {
34 12
        $this->controller = $controller;
35 12
    }
36
37
    /**
38
     * {@inheritdoc}
39
     */
40 10
    public function getFunctions(): array
41
    {
42
        return [
43 10
            new TwigFunction('show_source_code', [$this, 'showSourceCode'], ['is_safe' => ['html'], 'needs_environment' => true]),
44
        ];
45
    }
46
47 10
    public function showSourceCode(Environment $twig, $template): string
48
    {
49 10
        return $twig->render('debug/source_code.html.twig', [
50 10
            'controller' => $this->getController(),
51 10
            'template' => $this->getTemplateSource($twig->resolveTemplate($template)),
0 ignored issues
show
Compatibility introduced by
$twig->resolveTemplate($template) of type object<Twig_Template> is not a sub-type of object<Twig\Template>. It seems like you assume a child class of the class Twig_Template to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
52
        ]);
53
    }
54
55 10
    private function getController(): ?array
56
    {
57
        // this happens for example for exceptions (404 errors, etc.)
58 10
        if (null === $this->controller) {
59
            return null;
60
        }
61
62 10
        $method = $this->getCallableReflector($this->controller);
63
64 10
        $classCode = file($method->getFileName());
65 10
        $methodCode = array_slice($classCode, $method->getStartLine() - 1, $method->getEndLine() - $method->getStartLine() + 1);
66 10
        $controllerCode = '    '.$method->getDocComment()."\n".implode('', $methodCode);
67
68
        return [
69 10
            'file_path' => $method->getFileName(),
70 10
            'starting_line' => $method->getStartLine(),
71 10
            'source_code' => $this->unindentCode($controllerCode),
72
        ];
73
    }
74
75
    /**
76
     * Gets a reflector for a callable.
77
     *
78
     * This logic is copied from Symfony\Component\HttpKernel\Controller\ControllerResolver::getArguments
79
     */
80 10
    private function getCallableReflector(callable $callable): \ReflectionFunctionAbstract
81
    {
82 10
        if (is_array($callable)) {
83 10
            return new \ReflectionMethod($callable[0], $callable[1]);
84
        }
85
86
        if (is_object($callable) && !$callable instanceof \Closure) {
87
            $r = new \ReflectionObject($callable);
88
89
            return $r->getMethod('__invoke');
90
        }
91
92
        return new \ReflectionFunction($callable);
93
    }
94
95 10
    private function getTemplateSource(Template $template): array
96
    {
97 10
        $templateSource = $template->getSourceContext();
98
99
        return [
100
            // Twig templates are not always stored in files (they can be stored
101
            // in a database for example). However, for the needs of the Symfony
102
            // Demo app, we consider that all templates are stored in files and
103
            // that their file paths can be obtained through the source context.
104 10
            'file_path' => $templateSource->getPath(),
105 10
            'starting_line' => 1,
106 10
            'source_code' => $templateSource->getCode(),
107
        ];
108
    }
109
110
    /**
111
     * Utility method that "unindents" the given $code when all its lines start
112
     * with a tabulation of four white spaces.
113
     */
114 10
    private function unindentCode(string $code): string
115
    {
116 10
        $formattedCode = $code;
117 10
        $codeLines = explode("\n", $code);
118
119
        $indentedLines = array_filter($codeLines, function ($lineOfCode) {
120 10
            return '' === $lineOfCode || '    ' === mb_substr($lineOfCode, 0, 4);
121 10
        });
122
123 10
        if (count($indentedLines) === count($codeLines)) {
124 10
            $formattedCode = array_map(function ($lineOfCode) {
125 10
                return mb_substr($lineOfCode, 4);
126 10
            }, $codeLines);
127 10
            $formattedCode = implode("\n", $formattedCode);
128
        }
129
130 10
        return $formattedCode;
131
    }
132
}
133