Completed
Pull Request — develop (#675)
by Wachter
05:15 queued 02:53
created

SourceCodeExtension::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 1
1
<?php
2
3
/*
4
 * This file is part of Sulu.
5
 *
6
 * (c) MASSIVE ART WebServices GmbH
7
 *
8
 * This source file is subject to the MIT license that is bundled
9
 * with this source code in the file LICENSE.
10
 */
11
12
namespace CodeExplorerBundle\Twig;
13
14
/**
15
 * CAUTION: this is an extremely advanced Twig extension. It's used to get the
16
 * source code of the controller and the template used to render the current
17
 * page. If you are starting with Symfony, don't look at this code and consider
18
 * studying instead the code of the src/AppBundle/Twig/AppExtension.php extension.
19
 *
20
 * @author Ryan Weaver <[email protected]>
21
 * @author Javier Eguiluz <[email protected]>
22
 */
23
class SourceCodeExtension extends \Twig_Extension
24
{
25
    private $controller;
26
    private $kernelRootDir;
27
28
    public function __construct($kernelRootDir)
29
    {
30
        $this->kernelRootDir = $kernelRootDir;
31
    }
32
33
    public function setController($controller)
34
    {
35
        $this->controller = $controller;
36
    }
37
38
    /**
39
     * {@inheritdoc}
40
     */
41
    public function getFunctions()
42
    {
43
        return [
44
            new \Twig_SimpleFunction(
45
                'show_source_code',
46
                [$this, 'showSourceCode'],
47
                ['is_safe' => ['html'], 'needs_environment' => true]
48
            ),
49
        ];
50
    }
51
52
    public function showSourceCode(\Twig_Environment $twig, $template)
53
    {
54
        return $twig->render(
55
            '@CodeExplorer/source_code.html.twig',
56
            [
57
                'controller' => $this->getController(),
58
                'template' => $this->getTemplateSource($twig, $twig->resolveTemplate($template)),
59
            ]
60
        );
61
    }
62
63
    private function getController()
64
    {
65
        // this happens for example for exceptions (404 errors, etc.)
66
        if (null === $this->controller) {
67
            return;
68
        }
69
70
        $method = $this->getCallableReflector($this->controller);
71
72
        $classCode = file($method->getFileName());
73
        $methodCode = array_slice(
74
            $classCode,
75
            $method->getStartLine() - 1,
76
            $method->getEndLine() - $method->getStartLine() + 1
77
        );
78
        $controllerCode = '    ' . $method->getDocComment() . "\n" . implode('', $methodCode);
79
80
        return [
81
            'file_path' => $method->getFileName(),
82
            'starting_line' => $method->getStartLine(),
83
            'source_code' => trim($this->unindentCode($controllerCode)),
84
        ];
85
    }
86
87
    /**
88
     * Gets a reflector for a callable.
89
     *
90
     * This logic is copied from Symfony\Component\HttpKernel\Controller\ControllerResolver::getArguments
91
     *
92
     * @param callable $callable
93
     *
94
     * @return \ReflectionFunctionAbstract
95
     */
96
    private function getCallableReflector($callable)
97
    {
98
        if (is_array($callable)) {
99
            return new \ReflectionMethod($callable[0], $callable[1]);
100
        }
101
102
        if (is_object($callable) && !$callable instanceof \Closure) {
103
            $r = new \ReflectionObject($callable);
104
105
            return $r->getMethod('__invoke');
106
        }
107
108
        return new \ReflectionFunction($callable);
109
    }
110
111
    private function getTemplateSource(\Twig_Environment $twig, \Twig_Template $template)
112
    {
113
        return [
114
            'name' => $template->getTemplateName(),
115
            'source_code' => $twig->getLoader()->getSource($template->getTemplateName()),
116
        ];
117
    }
118
119
    /**
120
     * Utility method that "unindents" the given $code when all its lines start
121
     * with a tabulation of four white spaces.
122
     *
123
     * @param  string $code
124
     *
125
     * @return string
126
     */
127
    private function unindentCode($code)
128
    {
129
        $formattedCode = $code;
130
        $codeLines = explode("\n", $code);
131
132
        $indentedLines = array_filter(
133
            $codeLines,
134
            function ($lineOfCode) {
135
                return '' === $lineOfCode || '    ' === substr($lineOfCode, 0, 4);
136
            }
137
        );
138
139
        if (count($indentedLines) === count($codeLines)) {
140
            $formattedCode = array_map(
141
                function ($lineOfCode) {
142
                    return substr($lineOfCode, 4);
143
                },
144
                $codeLines
145
            );
146
            $formattedCode = implode("\n", $formattedCode);
147
        }
148
149
        return $formattedCode;
150
    }
151
152
    // the name of the Twig extension must be unique in the application
153
    public function getName()
154
    {
155
        return 'code_explorer_source_code';
156
    }
157
}
158