Completed
Pull Request — master (#420)
by Daniel
03:14 queued 17s
created

TemplateHelper::dump()   B

Complexity

Conditions 3
Paths 3

Size

Total Lines 27
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 6
Bugs 1 Features 1
Metric Value
c 6
b 1
f 1
dl 0
loc 27
ccs 0
cts 15
cp 0
rs 8.8571
cc 3
eloc 14
nc 3
nop 1
crap 12
1
<?php
2
/**
3
 * Whoops - php errors for cool kids
4
 * @author Filipe Dobreira <http://github.com/filp>
5
 */
6
7
namespace Whoops\Util;
8
9
use Symfony\Component\VarDumper\Caster\Caster;
10
use Symfony\Component\VarDumper\Cloner\AbstractCloner;
11
use Symfony\Component\VarDumper\Cloner\VarCloner;
12
use Symfony\Component\VarDumper\Dumper\HtmlDumper;
13
use Whoops\Exception\Frame;
14
15
/**
16
 * Exposes useful tools for working with/in templates
17
 */
18
class TemplateHelper
19
{
20
    /**
21
     * An array of variables to be passed to all templates
22
     * @var array
23
     */
24
    private $variables = array();
25
26
    /**
27
     * @var HtmlDumper
28
     */
29
    private $htmlDumper;
30
31
    /**
32
     * @var HtmlDumperOutput
33
     */
34
    private $htmlDumperOutput;
35
36
    /**
37
     * @var AbstractCloner
38
     */
39
    private $cloner;
40
41
    /**
42
     * Escapes a string for output in an HTML document
43
     *
44
     * @param  string $raw
45
     * @return string
46
     */
47 2
    public function escape($raw)
48
    {
49 2
        $flags = ENT_QUOTES;
50
51
        // HHVM has all constants defined, but only ENT_IGNORE
52
        // works at the moment
53 2
        if (defined("ENT_SUBSTITUTE") && !defined("HHVM_VERSION")) {
54 2
            $flags |= ENT_SUBSTITUTE;
55 2
        } else {
56
            // This is for 5.3.
57
            // The documentation warns of a potential security issue,
58
            // but it seems it does not apply in our case, because
59
            // we do not blacklist anything anywhere.
60
            $flags |= ENT_IGNORE;
61
        }
62
63 2
        return htmlspecialchars($raw, $flags, "UTF-8");
64
    }
65
66
    /**
67
     * Escapes a string for output in an HTML document, but preserves
68
     * URIs within it, and converts them to clickable anchor elements.
69
     *
70
     * @param  string $raw
71
     * @return string
72
     */
73 1
    public function escapeButPreserveUris($raw)
74
    {
75 1
        $escaped = $this->escape($raw);
76 1
        return preg_replace(
77 1
            "@([A-z]+?://([-\w\.]+[-\w])+(:\d+)?(/([\w/_\.#-]*(\?\S+)?[^\.\s])?)?)@",
78 1
            "<a href=\"$1\" target=\"_blank\">$1</a>", $escaped
79 1
        );
80
    }
81
82
    private function getDumper()
83
    {
84
        if (!$this->htmlDumper && class_exists('Symfony\Component\VarDumper\Cloner\VarCloner')) {
85
            $this->htmlDumperOutput = new HtmlDumperOutput();
86
            // re-use the same var-dumper instance, so it won't re-render the global styles/scripts on each dump.
87
            $this->htmlDumper = new HtmlDumper($this->htmlDumperOutput);
88
89
            $styles = array(
90
                'default' => 'color:#FFFFFF; line-height:normal; font:12px "Inconsolata", "Fira Mono", "Source Code Pro", Monaco, Consolas, "Lucida Console", monospace !important; word-wrap: break-word; white-space: pre-wrap; position:relative; z-index:99999; word-break: normal',
91
                'num' => 'color:#BCD42A',
92
                'const' => 'color: #4bb1b1;',
93
                'str' => 'color:#BCD42A',
94
                'note' => 'color:#ef7c61',
95
                'ref' => 'color:#A0A0A0',
96
                'public' => 'color:#FFFFFF',
97
                'protected' => 'color:#FFFFFF',
98
                'private' => 'color:#FFFFFF',
99
                'meta' => 'color:#FFFFFF',
100
                'key' => 'color:#BCD42A',
101
                'index' => 'color:#ef7c61',
102
            );
103
            $this->htmlDumper->setStyles($styles);
104
        }
105
106
        return $this->htmlDumper;
107
    }
108
109
    /**
110
     * Format the given value into a human readable string.
111
     *
112
     * @param  mixed $value
113
     * @return string
114
     */
115
    public function dump($value)
116
    {
117
        $dumper = $this->getDumper();
118
119
        if ($dumper) {
120
            // re-use the same DumpOutput instance, so it won't re-render the global styles/scripts on each dump.
121
            // exclude verbose information (e.g. exception stack traces)
122
            if (class_exists('Symfony\Component\VarDumper\Caster\Caster')) {
123
                $cloner = $this->getCloner()->cloneVar($value, Caster::EXCLUDE_VERBOSE);
124
            // Symfony VarDumper 2.6 Caster class dont exist.
125
            } else {
126
                $cloner = $this->getCloner()->cloneVar($value);
127
            }
128
129
            $dumper->dump(
130
                $cloner,
131
                $this->htmlDumperOutput
132
            );
133
134
            $output = $this->htmlDumperOutput->getOutput();
135
            $this->htmlDumperOutput->clear();
136
137
            return $output;
138
        }
139
140
        return print_r($value, true);
141
    }
142
143
    /**
144
     * Format the args of the given Frame as a human readable html string
145
     *
146
     * @param  Frame $frame
147
     * @return string the rendered html
148
     */
149
    public function dumpArgs(Frame $frame)
150
    {
151
        // we support frame args only when the optional dumper is available
152
        if (!$this->getDumper()) {
153
            return '';
154
        }
155
156
        $html = '';
157
        $numFrames = count($frame->getArgs());
158
159
        if ($numFrames > 0) {
160
            $html = '<ol class="linenums">';
161
            foreach($frame->getArgs() as $j => $frameArg) {
162
                $html .= '<li>'. $this->dump($frameArg) .'</li>';
163
            }
164
            $html .= '</ol>';
165
        }
166
167
        return $html;
168
    }
169
170
    /**
171
     * Convert a string to a slug version of itself
172
     *
173
     * @param  string $original
174
     * @return string
175
     */
176 1
    public function slug($original)
177
    {
178 1
        $slug = str_replace(" ", "-", $original);
179 1
        $slug = preg_replace('/[^\w\d\-\_]/i', '', $slug);
180 1
        return strtolower($slug);
181
    }
182
183
    /**
184
     * Given a template path, render it within its own scope. This
185
     * method also accepts an array of additional variables to be
186
     * passed to the template.
187
     *
188
     * @param string $template
189
     * @param array  $additionalVariables
190
     */
191 1
    public function render($template, array $additionalVariables = null)
192
    {
193 1
        $variables = $this->getVariables();
194
195
        // Pass the helper to the template:
196 1
        $variables["tpl"] = $this;
197
198 1
        if ($additionalVariables !== null) {
199 1
            $variables = array_replace($variables, $additionalVariables);
200 1
        }
201
202 1
        call_user_func(function () {
203 1
            extract(func_get_arg(1));
0 ignored issues
show
Bug introduced by
func_get_arg(1) cannot be passed to extract() as the parameter $var_array expects a reference.
Loading history...
204 1
            require func_get_arg(0);
205 1
        }, $template, $variables);
206 1
    }
207
208
    /**
209
     * Sets the variables to be passed to all templates rendered
210
     * by this template helper.
211
     *
212
     * @param array $variables
213
     */
214 1
    public function setVariables(array $variables)
215
    {
216 1
        $this->variables = $variables;
217 1
    }
218
219
    /**
220
     * Sets a single template variable, by its name:
221
     *
222
     * @param string $variableName
223
     * @param mixd   $variableValue
224
     */
225 1
    public function setVariable($variableName, $variableValue)
226
    {
227 1
        $this->variables[$variableName] = $variableValue;
228 1
    }
229
230
    /**
231
     * Gets a single template variable, by its name, or
232
     * $defaultValue if the variable does not exist
233
     *
234
     * @param  string $variableName
235
     * @param  mixed  $defaultValue
236
     * @return mixed
237
     */
238 1
    public function getVariable($variableName, $defaultValue = null)
239
    {
240 1
        return isset($this->variables[$variableName]) ?
241 1
            $this->variables[$variableName] : $defaultValue;
242
    }
243
244
    /**
245
     * Unsets a single template variable, by its name
246
     *
247
     * @param string $variableName
248
     */
249 1
    public function delVariable($variableName)
250
    {
251 1
        unset($this->variables[$variableName]);
252 1
    }
253
254
    /**
255
     * Returns all variables for this helper
256
     *
257
     * @return array
258
     */
259 1
    public function getVariables()
260
    {
261 1
        return $this->variables;
262
    }
263
264
    /**
265
     * Set the cloner used for dumping variables.
266
     *
267
     * @param AbstractCloner $cloner
268
     */
269
    public function setCloner($cloner)
270
    {
271
        $this->cloner = $cloner;
272
    }
273
274
    /**
275
     * Get the cloner used for dumping variables.
276
     *
277
     * @return AbstractCloner
278
     */
279
    public function getCloner()
280
    {
281
        if (!$this->cloner) {
282
            $this->cloner = new VarCloner();
283
        }
284
        return $this->cloner;
285
    }
286
}
287