Completed
Pull Request — master (#337)
by Markus
02:20
created

TemplateHelper::dumpArgs()   B

Complexity

Conditions 5
Paths 3

Size

Total Lines 23
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 23
ccs 0
cts 17
cp 0
rs 8.5907
cc 5
eloc 14
nc 3
nop 1
crap 30
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\Cloner\VarCloner;
10
use Symfony\Component\VarDumper\Dumper\CliDumper;
11
use Symfony\Component\VarDumper\Dumper\HtmlDumper;
12
use Whoops\Exception\Frame;
13
14
/**
15
 * Exposes useful tools for working with/in templates
16
 */
17
class TemplateHelper
18
{
19
    /**
20
     * An array of variables to be passed to all templates
21
     * @var array
22
     */
23
    private $variables = array();
24
25
    /**
26
     * Escapes a string for output in an HTML document
27
     *
28
     * @param  string $raw
29
     * @return string
30
     */
31 2
    public function escape($raw)
32
    {
33 2
        $flags = ENT_QUOTES;
34
35
        // HHVM has all constants defined, but only ENT_IGNORE
36
        // works at the moment
37 2
        if (defined("ENT_SUBSTITUTE") && !defined("HHVM_VERSION")) {
38 2
            $flags |= ENT_SUBSTITUTE;
39 2
        } else {
40
            // This is for 5.3.
41
            // The documentation warns of a potential security issue,
42
            // but it seems it does not apply in our case, because
43
            // we do not blacklist anything anywhere.
44
            $flags |= ENT_IGNORE;
45
        }
46
47 2
        return htmlspecialchars($raw, $flags, "UTF-8");
48
    }
49
50
    /**
51
     * Escapes a string for output in an HTML document, but preserves
52
     * URIs within it, and converts them to clickable anchor elements.
53
     *
54
     * @param  string $raw
55
     * @return string
56
     */
57 1
    public function escapeButPreserveUris($raw)
58
    {
59 1
        $escaped = $this->escape($raw);
60 1
        return preg_replace(
61 1
            "@([A-z]+?://([-\w\.]+[-\w])+(:\d+)?(/([\w/_\.#-]*(\?\S+)?[^\.\s])?)?)@",
62 1
            "<a href=\"$1\" target=\"_blank\">$1</a>", $escaped
63 1
        );
64
    }
65
66
    private function getDumper() {
67
        static $dumper = null;
68
69
        if (!$dumper && class_exists('Symfony\Component\VarDumper\Cloner\VarCloner')) {
70
            // re-use the same var-dumper instance, so it won't re-render the global styles/scripts on each dump.
71
            $dumper = new HtmlDumper();
72
73
            $styles = array(
74
                'default' => '',
75
                'num' => '',
76
                'const' => '',
77
                'str' => '',
78
                'note' => '',
79
                'ref' => '',
80
                'public' => '',
81
                'protected' => '',
82
                'private' => '',
83
                'meta' => '',
84
                'key' => '',
85
                'index' => '',
86
            );
87
            $dumper->setStyles($styles);
88
        }
89
90
        return $dumper;
91
    }
92
93
    /**
94
     * Format the given value into a human readable string.
95
     *
96
     * @param  mixed $value
97
     * @return string
98
     */
99
    public function dump($value)
100
    {
101
        $dumper = $this->getDumper();
102
103
        if ($dumper) {
104
            $cloner = new VarCloner();
105
            $output = '';
106
            $dumper->dump($cloner->cloneVar($value),  function ($line, $depth) use (&$output) {
107
                // A negative depth means "end of dump"
108
                if ($depth >= 0) {
109
                    // Adds a two spaces indentation to the line
110
                    $output .= str_repeat('  ', $depth).$line."\n";
111
                }
112
            });
113
            return $output;
114
        }
115
116
        return print_r($value, true);
117
    }
118
119
    /**
120
     * Format the args of the given Frame as a human readable html string
121
     *
122
     * @param  Frame $frame
123
     * @return string the rendered html
124
     */
125
    public function dumpArgs(Frame $frame) {
126
        // we support frame args only when the optional dumper is available
127
        if (!$this->getDumper()) {
128
            return '';
129
        }
130
131
        $html = '';
132
        $numFrames = count($frame->getArgs());
133
134
        if ($numFrames > 0) {
135
            $html .= '(';
136
            foreach($frame->getArgs() as $j => $frameArg) {
137
                $class = 'frame-arg';
138
                if ($j != $numFrames - 1 ) {
139
                    $class .= ' frame-arg-separated';
140
                }
141
                $html .= '<span class="'. $class .'">'. $this->dump($frameArg) .'</span>';
142
            }
143
            $html .= ')';
144
        }
145
146
        return $html;
147
    }
148
149
    /**
150
     * Convert a string to a slug version of itself
151
     *
152
     * @param  string $original
153
     * @return string
154
     */
155 1
    public function slug($original)
156
    {
157 1
        $slug = str_replace(" ", "-", $original);
158 1
        $slug = preg_replace('/[^\w\d\-\_]/i', '', $slug);
159 1
        return strtolower($slug);
160
    }
161
162
    /**
163
     * Given a template path, render it within its own scope. This
164
     * method also accepts an array of additional variables to be
165
     * passed to the template.
166
     *
167
     * @param string $template
168
     * @param array  $additionalVariables
169
     */
170 1
    public function render($template, array $additionalVariables = null)
171
    {
172 1
        $variables = $this->getVariables();
173
174
        // Pass the helper to the template:
175 1
        $variables["tpl"] = $this;
176
177 1
        if ($additionalVariables !== null) {
178 1
            $variables = array_replace($variables, $additionalVariables);
179 1
        }
180
181 1
        call_user_func(function () {
182 1
            extract(func_get_arg(1));
183 1
            require func_get_arg(0);
184 1
        }, $template, $variables);
185 1
    }
186
187
    /**
188
     * Sets the variables to be passed to all templates rendered
189
     * by this template helper.
190
     *
191
     * @param array $variables
192
     */
193 1
    public function setVariables(array $variables)
194
    {
195 1
        $this->variables = $variables;
196 1
    }
197
198
    /**
199
     * Sets a single template variable, by its name:
200
     *
201
     * @param string $variableName
202
     * @param mixd   $variableValue
203
     */
204 1
    public function setVariable($variableName, $variableValue)
205
    {
206 1
        $this->variables[$variableName] = $variableValue;
207 1
    }
208
209
    /**
210
     * Gets a single template variable, by its name, or
211
     * $defaultValue if the variable does not exist
212
     *
213
     * @param  string $variableName
214
     * @param  mixed  $defaultValue
215
     * @return mixed
216
     */
217 1
    public function getVariable($variableName, $defaultValue = null)
218
    {
219 1
        return isset($this->variables[$variableName]) ?
220 1
            $this->variables[$variableName] : $defaultValue;
221
    }
222
223
    /**
224
     * Unsets a single template variable, by its name
225
     *
226
     * @param string $variableName
227
     */
228 1
    public function delVariable($variableName)
229
    {
230 1
        unset($this->variables[$variableName]);
231 1
    }
232
233
    /**
234
     * Returns all variables for this helper
235
     *
236
     * @return array
237
     */
238 1
    public function getVariables()
239
    {
240 1
        return $this->variables;
241
    }
242
}
243