Passed
Push — acc ( 8b9c60...aec456 )
by Herberto
09:02
created

SourceCodeExtension   A

Complexity

Total Complexity 13

Size/Duplication

Total Lines 105
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Test Coverage

Coverage 90.48%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 13
c 1
b 0
f 0
lcom 1
cbo 5
dl 0
loc 105
ccs 38
cts 42
cp 0.9048
rs 10

7 Methods

Rating   Name   Duplication   Size   Complexity  
A getFunctions() 0 6 1
A showSourceCode() 0 7 1
A getCallableReflector() 0 14 4
A getTemplateSource() 0 14 1
A setController() 0 4 1
A getController() 0 19 2
A unindentCode() 0 18 3
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
    }
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 14
    public function showSourceCode(Environment $twig, $template): string
48
    {
49 14
        return $twig->render('debug/source_code.html.twig', [
50 14
            'controller' => $this->getController(),
51 14
            'template' => $this->getTemplateSource($twig->resolveTemplate($template)),
0 ignored issues
show
Documentation introduced by
$twig->resolveTemplate($template) is of type object<Twig_Template>|ob...t<Twig_TemplateWrapper>, but the function expects a object<Twig\Template>.

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...
52
        ]);
53
    }
54
55 14
    private function getController(): ?array
56
    {
57
        // this happens for example for exceptions (404 errors, etc.)
58 14
        if (null === $this->controller) {
59 4
            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 14
    private function getTemplateSource(Template $template): array
96
    {
97 14
        $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 14
            'file_path' => $templateSource->getPath(),
105 14
            'starting_line' => 1,
106 14
            '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 10
        $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