Completed
Branch master (f58c14)
by Andrey
07:50
created

DumpDataCollector::unserialize()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 1
dl 0
loc 8
rs 10
c 0
b 0
f 0
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 Symfony\Component\HttpKernel\DataCollector;
13
14
use Symfony\Component\HttpFoundation\Request;
15
use Symfony\Component\HttpFoundation\RequestStack;
16
use Symfony\Component\HttpFoundation\Response;
17
use Symfony\Component\Stopwatch\Stopwatch;
18
use Symfony\Component\VarDumper\Cloner\Data;
19
use Symfony\Component\VarDumper\Cloner\VarCloner;
20
use Symfony\Component\VarDumper\Dumper\CliDumper;
21
use Symfony\Component\VarDumper\Dumper\HtmlDumper;
22
use Symfony\Component\VarDumper\Dumper\DataDumperInterface;
23
use Twig\Template;
24
25
/**
26
 * @author Nicolas Grekas <[email protected]>
27
 */
28
class DumpDataCollector extends DataCollector implements DataDumperInterface
29
{
30
    private $stopwatch;
31
    private $fileLinkFormat;
32
    private $dataCount = 0;
33
    private $isCollected = true;
34
    private $clonesCount = 0;
35
    private $clonesIndex = 0;
36
    private $rootRefs;
37
    private $charset;
38
    private $requestStack;
39
    private $dumper;
40
    private $dumperIsInjected;
41
42
    public function __construct(Stopwatch $stopwatch = null, $fileLinkFormat = null, $charset = null, RequestStack $requestStack = null, DataDumperInterface $dumper = null)
43
    {
44
        $this->stopwatch = $stopwatch;
45
        $this->fileLinkFormat = $fileLinkFormat ?: ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format');
46
        $this->charset = $charset ?: ini_get('php.output_encoding') ?: ini_get('default_charset') ?: 'UTF-8';
47
        $this->requestStack = $requestStack;
48
        $this->dumper = $dumper;
49
        $this->dumperIsInjected = null !== $dumper;
50
51
        // All clones share these properties by reference:
52
        $this->rootRefs = array(
53
            &$this->data,
54
            &$this->dataCount,
55
            &$this->isCollected,
56
            &$this->clonesCount,
57
        );
58
    }
59
60
    public function __clone()
61
    {
62
        $this->clonesIndex = ++$this->clonesCount;
63
    }
64
65
    public function dump(Data $data)
66
    {
67
        if ($this->stopwatch) {
68
            $this->stopwatch->start('dump');
69
        }
70
        if ($this->isCollected) {
71
            $this->isCollected = false;
72
        }
73
74
        $trace = DEBUG_BACKTRACE_PROVIDE_OBJECT | DEBUG_BACKTRACE_IGNORE_ARGS;
75
        if (\PHP_VERSION_ID >= 50400) {
76
            $trace = debug_backtrace($trace, 7);
77
        } else {
78
            $trace = debug_backtrace($trace);
79
        }
80
81
        $file = $trace[0]['file'];
82
        $line = $trace[0]['line'];
83
        $name = false;
84
        $fileExcerpt = false;
85
86
        for ($i = 1; $i < 7; ++$i) {
87
            if (isset($trace[$i]['class'], $trace[$i]['function'])
88
                && 'dump' === $trace[$i]['function']
89
                && 'Symfony\Component\VarDumper\VarDumper' === $trace[$i]['class']
90
            ) {
91
                $file = $trace[$i]['file'];
92
                $line = $trace[$i]['line'];
93
94
                while (++$i < 7) {
95
                    if (isset($trace[$i]['function'], $trace[$i]['file']) && empty($trace[$i]['class']) && 0 !== strpos($trace[$i]['function'], 'call_user_func')) {
96
                        $file = $trace[$i]['file'];
97
                        $line = $trace[$i]['line'];
98
99
                        break;
100
                    } elseif (isset($trace[$i]['object']) && $trace[$i]['object'] instanceof Template) {
0 ignored issues
show
Bug introduced by
The class Twig\Template does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
101
                        $template = $trace[$i]['object'];
102
                        $name = $template->getTemplateName();
103
                        $src = method_exists($template, 'getSourceContext') ? $template->getSourceContext()->getCode() : (method_exists($template, 'getSource') ? $template->getSource() : false);
104
                        $info = $template->getDebugInfo();
105
                        if (isset($info[$trace[$i - 1]['line']])) {
106
                            $line = $info[$trace[$i - 1]['line']];
107
                            $file = method_exists($template, 'getSourceContext') ? $template->getSourceContext()->getPath() : false;
108
109
                            if ($src) {
110
                                $src = explode("\n", $src);
111
                                $fileExcerpt = array();
112
113
                                for ($i = max($line - 3, 1), $max = min($line + 3, count($src)); $i <= $max; ++$i) {
114
                                    $fileExcerpt[] = '<li'.($i === $line ? ' class="selected"' : '').'><code>'.$this->htmlEncode($src[$i - 1]).'</code></li>';
115
                                }
116
117
                                $fileExcerpt = '<ol start="'.max($line - 3, 1).'">'.implode("\n", $fileExcerpt).'</ol>';
118
                            }
119
                        }
120
                        break;
121
                    }
122
                }
123
                break;
124
            }
125
        }
126
127
        if (false === $name) {
128
            $name = str_replace('\\', '/', $file);
129
            $name = substr($name, strrpos($name, '/') + 1);
130
        }
131
132
        if ($this->dumper) {
133
            $this->doDump($data, $name, $file, $line);
134
        }
135
136
        $this->data[] = compact('data', 'name', 'file', 'line', 'fileExcerpt');
137
        ++$this->dataCount;
138
139
        if ($this->stopwatch) {
140
            $this->stopwatch->stop('dump');
141
        }
142
    }
143
144
    public function collect(Request $request, Response $response, \Exception $exception = null)
145
    {
146
        // Sub-requests and programmatic calls stay in the collected profile.
147
        if ($this->dumper || ($this->requestStack && $this->requestStack->getMasterRequest() !== $request) || $request->isXmlHttpRequest() || $request->headers->has('Origin')) {
148
            return;
149
        }
150
151
        // In all other conditions that remove the web debug toolbar, dumps are written on the output.
152
        if (!$this->requestStack
153
            || !$response->headers->has('X-Debug-Token')
154
            || $response->isRedirection()
155
            || ($response->headers->has('Content-Type') && false === strpos($response->headers->get('Content-Type'), 'html'))
156
            || 'html' !== $request->getRequestFormat()
157
            || false === strripos($response->getContent(), '</body>')
158
        ) {
159
            if ($response->headers->has('Content-Type') && false !== strpos($response->headers->get('Content-Type'), 'html')) {
160
                $this->dumper = new HtmlDumper('php://output', $this->charset);
161
            } else {
162
                $this->dumper = new CliDumper('php://output', $this->charset);
163
            }
164
165
            foreach ($this->data as $dump) {
166
                $this->doDump($dump['data'], $dump['name'], $dump['file'], $dump['line']);
167
            }
168
        }
169
    }
170
171
    public function serialize()
172
    {
173
        if ($this->clonesCount !== $this->clonesIndex) {
174
            return 'a:0:{}';
175
        }
176
177
        $this->data[] = $this->fileLinkFormat;
178
        $this->data[] = $this->charset;
179
        $ser = serialize($this->data);
180
        $this->data = array();
181
        $this->dataCount = 0;
182
        $this->isCollected = true;
183
        if (!$this->dumperIsInjected) {
184
            $this->dumper = null;
185
        }
186
187
        return $ser;
188
    }
189
190
    public function unserialize($data)
191
    {
192
        parent::unserialize($data);
193
        $charset = array_pop($this->data);
194
        $fileLinkFormat = array_pop($this->data);
195
        $this->dataCount = count($this->data);
196
        self::__construct($this->stopwatch, $fileLinkFormat, $charset);
197
    }
198
199
    public function getDumpsCount()
200
    {
201
        return $this->dataCount;
202
    }
203
204
    public function getDumps($format, $maxDepthLimit = -1, $maxItemsPerDepth = -1)
205
    {
206
        $data = fopen('php://memory', 'r+b');
207
208
        if ('html' === $format) {
209
            $dumper = new HtmlDumper($data, $this->charset);
210
        } else {
211
            throw new \InvalidArgumentException(sprintf('Invalid dump format: %s', $format));
212
        }
213
        $dumps = array();
214
215
        foreach ($this->data as $dump) {
216
            if (method_exists($dump['data'], 'withMaxDepth')) {
217
                $dumper->dump($dump['data']->withMaxDepth($maxDepthLimit)->withMaxItemsPerDepth($maxItemsPerDepth));
218
            } else {
219
                // getLimitedClone is @deprecated, to be removed in 3.0
220
                $dumper->dump($dump['data']->getLimitedClone($maxDepthLimit, $maxItemsPerDepth));
221
            }
222
            $dump['data'] = stream_get_contents($data, -1, 0);
223
            ftruncate($data, 0);
224
            rewind($data);
225
            $dumps[] = $dump;
226
        }
227
228
        return $dumps;
229
    }
230
231
    public function getName()
232
    {
233
        return 'dump';
234
    }
235
236
    public function __destruct()
237
    {
238
        if (0 === $this->clonesCount-- && !$this->isCollected && $this->data) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->data of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
239
            $this->clonesCount = 0;
240
            $this->isCollected = true;
241
242
            $h = headers_list();
243
            $i = count($h);
244
            array_unshift($h, 'Content-Type: '.ini_get('default_mimetype'));
245
            while (0 !== stripos($h[$i], 'Content-Type:')) {
246
                --$i;
247
            }
248
249
            if ('cli' !== PHP_SAPI && stripos($h[$i], 'html')) {
250
                $this->dumper = new HtmlDumper('php://output', $this->charset);
251
            } else {
252
                $this->dumper = new CliDumper('php://output', $this->charset);
253
            }
254
255
            foreach ($this->data as $i => $dump) {
256
                $this->data[$i] = null;
257
                $this->doDump($dump['data'], $dump['name'], $dump['file'], $dump['line']);
258
            }
259
260
            $this->data = array();
261
            $this->dataCount = 0;
262
        }
263
    }
264
265
    private function doDump($data, $name, $file, $line)
266
    {
267
        if (\PHP_VERSION_ID >= 50400 && $this->dumper instanceof CliDumper) {
0 ignored issues
show
Bug introduced by
The class Symfony\Component\VarDumper\Dumper\CliDumper does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
268
            $contextDumper = function ($name, $file, $line, $fileLinkFormat) {
269
                if ($this instanceof HtmlDumper) {
0 ignored issues
show
Bug introduced by
The class Symfony\Component\VarDumper\Dumper\HtmlDumper does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
270
                    if ('' !== $file) {
271
                        $s = $this->style('meta', '%s');
272
                        $name = strip_tags($this->style('', $name));
273
                        $file = strip_tags($this->style('', $file));
274
                        if ($fileLinkFormat) {
275
                            $link = strtr(strip_tags($this->style('', $fileLinkFormat)), array('%f' => $file, '%l' => (int) $line));
276
                            $name = sprintf('<a href="%s" title="%s">'.$s.'</a>', $link, $file, $name);
277
                        } else {
278
                            $name = sprintf('<abbr title="%s">'.$s.'</abbr>', $file, $name);
279
                        }
280
                    } else {
281
                        $name = $this->style('meta', $name);
282
                    }
283
                    $this->line = $name.' on line '.$this->style('meta', $line).':';
284
                } else {
285
                    $this->line = $this->style('meta', $name).' on line '.$this->style('meta', $line).':';
286
                }
287
                $this->dumpLine(0);
288
            };
289
            $contextDumper = $contextDumper->bindTo($this->dumper, $this->dumper);
290
            $contextDumper($name, $file, $line, $this->fileLinkFormat);
291
        } else {
292
            $cloner = new VarCloner();
293
            $this->dumper->dump($cloner->cloneVar($name.' on line '.$line.':'));
294
        }
295
        $this->dumper->dump($data);
296
    }
297
298
    private function htmlEncode($s)
299
    {
300
        $html = '';
301
302
        $dumper = new HtmlDumper(function ($line) use (&$html) { $html .= $line; }, $this->charset);
303
        $dumper->setDumpHeader('');
304
        $dumper->setDumpBoundaries('', '');
305
306
        $cloner = new VarCloner();
307
        $dumper->dump($cloner->cloneVar($s));
308
309
        return substr(strip_tags($html), 1, -1);
310
    }
311
}
312