diff options
Diffstat (limited to 'vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Extension/CodeExtension.php')
-rw-r--r-- | vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Extension/CodeExtension.php | 232 |
1 files changed, 232 insertions, 0 deletions
diff --git a/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Extension/CodeExtension.php b/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Extension/CodeExtension.php new file mode 100644 index 00000000..a6202b24 --- /dev/null +++ b/vendor/symfony/twig-bridge/Symfony/Bridge/Twig/Extension/CodeExtension.php | |||
@@ -0,0 +1,232 @@ | |||
1 | <?php | ||
2 | |||
3 | /* | ||
4 | * This file is part of the Symfony package. | ||
5 | * | ||
6 | * (c) Fabien Potencier <fabien@symfony.com> | ||
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 Symfony\Bridge\Twig\Extension; | ||
13 | |||
14 | if (!defined('ENT_SUBSTITUTE')) { | ||
15 | define('ENT_SUBSTITUTE', 8); | ||
16 | } | ||
17 | |||
18 | /** | ||
19 | * Twig extension relate to PHP code and used by the profiler and the default exception templates. | ||
20 | * | ||
21 | * @author Fabien Potencier <fabien@symfony.com> | ||
22 | */ | ||
23 | class CodeExtension extends \Twig_Extension | ||
24 | { | ||
25 | private $fileLinkFormat; | ||
26 | private $rootDir; | ||
27 | private $charset; | ||
28 | |||
29 | /** | ||
30 | * Constructor. | ||
31 | * | ||
32 | * @param string $fileLinkFormat The format for links to source files | ||
33 | * @param string $rootDir The project root directory | ||
34 | * @param string $charset The charset | ||
35 | */ | ||
36 | public function __construct($fileLinkFormat, $rootDir, $charset) | ||
37 | { | ||
38 | $this->fileLinkFormat = empty($fileLinkFormat) ? ini_get('xdebug.file_link_format') : $fileLinkFormat; | ||
39 | $this->rootDir = str_replace('\\', '/', $rootDir).'/'; | ||
40 | $this->charset = $charset; | ||
41 | } | ||
42 | |||
43 | /** | ||
44 | * {@inheritdoc} | ||
45 | */ | ||
46 | public function getFilters() | ||
47 | { | ||
48 | return array( | ||
49 | 'abbr_class' => new \Twig_Filter_Method($this, 'abbrClass', array('is_safe' => array('html'))), | ||
50 | 'abbr_method' => new \Twig_Filter_Method($this, 'abbrMethod', array('is_safe' => array('html'))), | ||
51 | 'format_args' => new \Twig_Filter_Method($this, 'formatArgs', array('is_safe' => array('html'))), | ||
52 | 'format_args_as_text' => new \Twig_Filter_Method($this, 'formatArgsAsText'), | ||
53 | 'file_excerpt' => new \Twig_Filter_Method($this, 'fileExcerpt', array('is_safe' => array('html'))), | ||
54 | 'format_file' => new \Twig_Filter_Method($this, 'formatFile', array('is_safe' => array('html'))), | ||
55 | 'format_file_from_text' => new \Twig_Filter_Method($this, 'formatFileFromText', array('is_safe' => array('html'))), | ||
56 | 'file_link' => new \Twig_Filter_Method($this, 'getFileLink', array('is_safe' => array('html'))), | ||
57 | ); | ||
58 | } | ||
59 | |||
60 | public function abbrClass($class) | ||
61 | { | ||
62 | $parts = explode('\\', $class); | ||
63 | $short = array_pop($parts); | ||
64 | |||
65 | return sprintf("<abbr title=\"%s\">%s</abbr>", $class, $short); | ||
66 | } | ||
67 | |||
68 | public function abbrMethod($method) | ||
69 | { | ||
70 | if (false !== strpos($method, '::')) { | ||
71 | list($class, $method) = explode('::', $method, 2); | ||
72 | $result = sprintf("%s::%s()", $this->abbrClass($class), $method); | ||
73 | } elseif ('Closure' === $method) { | ||
74 | $result = sprintf("<abbr title=\"%s\">%s</abbr>", $method, $method); | ||
75 | } else { | ||
76 | $result = sprintf("<abbr title=\"%s\">%s</abbr>()", $method, $method); | ||
77 | } | ||
78 | |||
79 | return $result; | ||
80 | } | ||
81 | |||
82 | /** | ||
83 | * Formats an array as a string. | ||
84 | * | ||
85 | * @param array $args The argument array | ||
86 | * | ||
87 | * @return string | ||
88 | */ | ||
89 | public function formatArgs($args) | ||
90 | { | ||
91 | $result = array(); | ||
92 | foreach ($args as $key => $item) { | ||
93 | if ('object' === $item[0]) { | ||
94 | $parts = explode('\\', $item[1]); | ||
95 | $short = array_pop($parts); | ||
96 | $formattedValue = sprintf("<em>object</em>(<abbr title=\"%s\">%s</abbr>)", $item[1], $short); | ||
97 | } elseif ('array' === $item[0]) { | ||
98 | $formattedValue = sprintf("<em>array</em>(%s)", is_array($item[1]) ? $this->formatArgs($item[1]) : $item[1]); | ||
99 | } elseif ('string' === $item[0]) { | ||
100 | $formattedValue = sprintf("'%s'", htmlspecialchars($item[1], ENT_QUOTES, $this->charset)); | ||
101 | } elseif ('null' === $item[0]) { | ||
102 | $formattedValue = '<em>null</em>'; | ||
103 | } elseif ('boolean' === $item[0]) { | ||
104 | $formattedValue = '<em>'.strtolower(var_export($item[1], true)).'</em>'; | ||
105 | } elseif ('resource' === $item[0]) { | ||
106 | $formattedValue = '<em>resource</em>'; | ||
107 | } else { | ||
108 | $formattedValue = str_replace("\n", '', var_export(htmlspecialchars((string) $item[1], ENT_QUOTES, $this->charset), true)); | ||
109 | } | ||
110 | |||
111 | $result[] = is_int($key) ? $formattedValue : sprintf("'%s' => %s", $key, $formattedValue); | ||
112 | } | ||
113 | |||
114 | return implode(', ', $result); | ||
115 | } | ||
116 | |||
117 | /** | ||
118 | * Formats an array as a string. | ||
119 | * | ||
120 | * @param array $args The argument array | ||
121 | * | ||
122 | * @return string | ||
123 | */ | ||
124 | public function formatArgsAsText($args) | ||
125 | { | ||
126 | return strip_tags($this->formatArgs($args)); | ||
127 | } | ||
128 | |||
129 | /** | ||
130 | * Returns an excerpt of a code file around the given line number. | ||
131 | * | ||
132 | * @param string $file A file path | ||
133 | * @param int $line The selected line number | ||
134 | * | ||
135 | * @return string An HTML string | ||
136 | */ | ||
137 | public function fileExcerpt($file, $line) | ||
138 | { | ||
139 | if (is_readable($file)) { | ||
140 | $code = highlight_file($file, true); | ||
141 | // remove main code/span tags | ||
142 | $code = preg_replace('#^<code.*?>\s*<span.*?>(.*)</span>\s*</code>#s', '\\1', $code); | ||
143 | $content = preg_split('#<br />#', $code); | ||
144 | |||
145 | $lines = array(); | ||
146 | for ($i = max($line - 3, 1), $max = min($line + 3, count($content)); $i <= $max; $i++) { | ||
147 | $lines[] = '<li'.($i == $line ? ' class="selected"' : '').'><code>'.self::fixCodeMarkup($content[$i - 1]).'</code></li>'; | ||
148 | } | ||
149 | |||
150 | return '<ol start="'.max($line - 3, 1).'">'.implode("\n", $lines).'</ol>'; | ||
151 | } | ||
152 | } | ||
153 | |||
154 | /** | ||
155 | * Formats a file path. | ||
156 | * | ||
157 | * @param string $file An absolute file path | ||
158 | * @param integer $line The line number | ||
159 | * @param string $text Use this text for the link rather than the file path | ||
160 | * | ||
161 | * @return string | ||
162 | */ | ||
163 | public function formatFile($file, $line, $text = null) | ||
164 | { | ||
165 | if (null === $text) { | ||
166 | $file = trim($file); | ||
167 | $text = $file; | ||
168 | if (0 === strpos($text, $this->rootDir)) { | ||
169 | $text = str_replace($this->rootDir, '', str_replace('\\', '/', $text)); | ||
170 | $text = sprintf('<abbr title="%s">kernel.root_dir</abbr>/%s', $this->rootDir, $text); | ||
171 | } | ||
172 | } | ||
173 | |||
174 | $text = "$text at line $line"; | ||
175 | |||
176 | if (false !== $link = $this->getFileLink($file, $line)) { | ||
177 | return sprintf('<a href="%s" title="Click to open this file" class="file_link">%s</a>', htmlspecialchars($link, ENT_QUOTES | ENT_SUBSTITUTE, $this->charset), $text); | ||
178 | } | ||
179 | |||
180 | return $text; | ||
181 | } | ||
182 | |||
183 | /** | ||
184 | * Returns the link for a given file/line pair. | ||
185 | * | ||
186 | * @param string $file An absolute file path | ||
187 | * @param integer $line The line number | ||
188 | * | ||
189 | * @return string A link of false | ||
190 | */ | ||
191 | public function getFileLink($file, $line) | ||
192 | { | ||
193 | if ($this->fileLinkFormat && is_file($file)) { | ||
194 | return strtr($this->fileLinkFormat, array('%f' => $file, '%l' => $line)); | ||
195 | } | ||
196 | |||
197 | return false; | ||
198 | } | ||
199 | |||
200 | public function formatFileFromText($text) | ||
201 | { | ||
202 | $that = $this; | ||
203 | |||
204 | return preg_replace_callback('/in ("|")?(.+?)\1(?: +(?:on|at))? +line (\d+)/s', function ($match) use ($that) { | ||
205 | return 'in '.$that->formatFile($match[2], $match[3]); | ||
206 | }, $text); | ||
207 | } | ||
208 | |||
209 | public function getName() | ||
210 | { | ||
211 | return 'code'; | ||
212 | } | ||
213 | |||
214 | protected static function fixCodeMarkup($line) | ||
215 | { | ||
216 | // </span> ending tag from previous line | ||
217 | $opening = strpos($line, '<span'); | ||
218 | $closing = strpos($line, '</span>'); | ||
219 | if (false !== $closing && (false === $opening || $closing < $opening)) { | ||
220 | $line = substr_replace($line, '', $closing, 7); | ||
221 | } | ||
222 | |||
223 | // missing </span> tag at the end of line | ||
224 | $opening = strpos($line, '<span'); | ||
225 | $closing = strpos($line, '</span>'); | ||
226 | if (false !== $opening && (false === $closing || $closing > $opening)) { | ||
227 | $line .= '</span>'; | ||
228 | } | ||
229 | |||
230 | return $line; | ||
231 | } | ||
232 | } | ||