Subversion-Projekte lars-tiefland.laravel_shop

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
148 lars 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\Component\HttpKernel\DataCollector;
13
 
14
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
15
use Symfony\Component\HttpFoundation\Cookie;
16
use Symfony\Component\HttpFoundation\ParameterBag;
17
use Symfony\Component\HttpFoundation\Request;
18
use Symfony\Component\HttpFoundation\RequestStack;
19
use Symfony\Component\HttpFoundation\Response;
20
use Symfony\Component\HttpFoundation\Session\SessionBagInterface;
21
use Symfony\Component\HttpFoundation\Session\SessionInterface;
22
use Symfony\Component\HttpKernel\Event\ControllerEvent;
23
use Symfony\Component\HttpKernel\Event\ResponseEvent;
24
use Symfony\Component\HttpKernel\KernelEvents;
25
use Symfony\Component\VarDumper\Cloner\Data;
26
 
27
/**
28
 * @author Fabien Potencier <fabien@symfony.com>
29
 *
30
 * @final
31
 */
32
class RequestDataCollector extends DataCollector implements EventSubscriberInterface, LateDataCollectorInterface
33
{
34
    /**
35
     * @var \SplObjectStorage<Request, callable>
36
     */
37
    private \SplObjectStorage $controllers;
38
    private array $sessionUsages = [];
39
    private ?RequestStack $requestStack;
40
 
41
    public function __construct(RequestStack $requestStack = null)
42
    {
43
        $this->controllers = new \SplObjectStorage();
44
        $this->requestStack = $requestStack;
45
    }
46
 
47
    public function collect(Request $request, Response $response, \Throwable $exception = null)
48
    {
49
        // attributes are serialized and as they can be anything, they need to be converted to strings.
50
        $attributes = [];
51
        $route = '';
52
        foreach ($request->attributes->all() as $key => $value) {
53
            if ('_route' === $key) {
54
                $route = \is_object($value) ? $value->getPath() : $value;
55
                $attributes[$key] = $route;
56
            } else {
57
                $attributes[$key] = $value;
58
            }
59
        }
60
 
61
        $content = $request->getContent();
62
 
63
        $sessionMetadata = [];
64
        $sessionAttributes = [];
65
        $flashes = [];
66
        if ($request->hasSession()) {
67
            $session = $request->getSession();
68
            if ($session->isStarted()) {
69
                $sessionMetadata['Created'] = date(\DATE_RFC822, $session->getMetadataBag()->getCreated());
70
                $sessionMetadata['Last used'] = date(\DATE_RFC822, $session->getMetadataBag()->getLastUsed());
71
                $sessionMetadata['Lifetime'] = $session->getMetadataBag()->getLifetime();
72
                $sessionAttributes = $session->all();
73
                $flashes = $session->getFlashBag()->peekAll();
74
            }
75
        }
76
 
77
        $statusCode = $response->getStatusCode();
78
 
79
        $responseCookies = [];
80
        foreach ($response->headers->getCookies() as $cookie) {
81
            $responseCookies[$cookie->getName()] = $cookie;
82
        }
83
 
84
        $dotenvVars = [];
85
        foreach (explode(',', $_SERVER['SYMFONY_DOTENV_VARS'] ?? $_ENV['SYMFONY_DOTENV_VARS'] ?? '') as $name) {
86
            if ('' !== $name && isset($_ENV[$name])) {
87
                $dotenvVars[$name] = $_ENV[$name];
88
            }
89
        }
90
 
91
        $this->data = [
92
            'method' => $request->getMethod(),
93
            'format' => $request->getRequestFormat(),
94
            'content_type' => $response->headers->get('Content-Type', 'text/html'),
95
            'status_text' => Response::$statusTexts[$statusCode] ?? '',
96
            'status_code' => $statusCode,
97
            'request_query' => $request->query->all(),
98
            'request_request' => $request->request->all(),
99
            'request_files' => $request->files->all(),
100
            'request_headers' => $request->headers->all(),
101
            'request_server' => $request->server->all(),
102
            'request_cookies' => $request->cookies->all(),
103
            'request_attributes' => $attributes,
104
            'route' => $route,
105
            'response_headers' => $response->headers->all(),
106
            'response_cookies' => $responseCookies,
107
            'session_metadata' => $sessionMetadata,
108
            'session_attributes' => $sessionAttributes,
109
            'session_usages' => array_values($this->sessionUsages),
110
            'stateless_check' => $this->requestStack?->getMainRequest()?->attributes->get('_stateless') ?? false,
111
            'flashes' => $flashes,
112
            'path_info' => $request->getPathInfo(),
113
            'controller' => 'n/a',
114
            'locale' => $request->getLocale(),
115
            'dotenv_vars' => $dotenvVars,
116
        ];
117
 
118
        if (isset($this->data['request_headers']['php-auth-pw'])) {
119
            $this->data['request_headers']['php-auth-pw'] = '******';
120
        }
121
 
122
        if (isset($this->data['request_server']['PHP_AUTH_PW'])) {
123
            $this->data['request_server']['PHP_AUTH_PW'] = '******';
124
        }
125
 
126
        if (isset($this->data['request_request']['_password'])) {
127
            $encodedPassword = rawurlencode($this->data['request_request']['_password']);
128
            $content = str_replace('_password='.$encodedPassword, '_password=******', $content);
129
            $this->data['request_request']['_password'] = '******';
130
        }
131
 
132
        $this->data['content'] = $content;
133
 
134
        foreach ($this->data as $key => $value) {
135
            if (!\is_array($value)) {
136
                continue;
137
            }
138
            if ('request_headers' === $key || 'response_headers' === $key) {
139
                $this->data[$key] = array_map(function ($v) { return isset($v[0]) && !isset($v[1]) ? $v[0] : $v; }, $value);
140
            }
141
        }
142
 
143
        if (isset($this->controllers[$request])) {
144
            $this->data['controller'] = $this->parseController($this->controllers[$request]);
145
            unset($this->controllers[$request]);
146
        }
147
 
148
        if ($request->attributes->has('_redirected') && $redirectCookie = $request->cookies->get('sf_redirect')) {
149
            $this->data['redirect'] = json_decode($redirectCookie, true);
150
 
151
            $response->headers->clearCookie('sf_redirect');
152
        }
153
 
154
        if ($response->isRedirect()) {
155
            $response->headers->setCookie(new Cookie(
156
                'sf_redirect',
157
                json_encode([
158
                    'token' => $response->headers->get('x-debug-token'),
159
                    'route' => $request->attributes->get('_route', 'n/a'),
160
                    'method' => $request->getMethod(),
161
                    'controller' => $this->parseController($request->attributes->get('_controller')),
162
                    'status_code' => $statusCode,
163
                    'status_text' => Response::$statusTexts[$statusCode],
164
                ]),
165
                0, '/', null, $request->isSecure(), true, false, 'lax'
166
            ));
167
        }
168
 
169
        $this->data['identifier'] = $this->data['route'] ?: (\is_array($this->data['controller']) ? $this->data['controller']['class'].'::'.$this->data['controller']['method'].'()' : $this->data['controller']);
170
 
171
        if ($response->headers->has('x-previous-debug-token')) {
172
            $this->data['forward_token'] = $response->headers->get('x-previous-debug-token');
173
        }
174
    }
175
 
176
    public function lateCollect()
177
    {
178
        $this->data = $this->cloneVar($this->data);
179
    }
180
 
181
    public function reset()
182
    {
183
        $this->data = [];
184
        $this->controllers = new \SplObjectStorage();
185
        $this->sessionUsages = [];
186
    }
187
 
188
    public function getMethod()
189
    {
190
        return $this->data['method'];
191
    }
192
 
193
    public function getPathInfo()
194
    {
195
        return $this->data['path_info'];
196
    }
197
 
198
    public function getRequestRequest()
199
    {
200
        return new ParameterBag($this->data['request_request']->getValue());
201
    }
202
 
203
    public function getRequestQuery()
204
    {
205
        return new ParameterBag($this->data['request_query']->getValue());
206
    }
207
 
208
    public function getRequestFiles()
209
    {
210
        return new ParameterBag($this->data['request_files']->getValue());
211
    }
212
 
213
    public function getRequestHeaders()
214
    {
215
        return new ParameterBag($this->data['request_headers']->getValue());
216
    }
217
 
218
    public function getRequestServer(bool $raw = false)
219
    {
220
        return new ParameterBag($this->data['request_server']->getValue($raw));
221
    }
222
 
223
    public function getRequestCookies(bool $raw = false)
224
    {
225
        return new ParameterBag($this->data['request_cookies']->getValue($raw));
226
    }
227
 
228
    public function getRequestAttributes()
229
    {
230
        return new ParameterBag($this->data['request_attributes']->getValue());
231
    }
232
 
233
    public function getResponseHeaders()
234
    {
235
        return new ParameterBag($this->data['response_headers']->getValue());
236
    }
237
 
238
    public function getResponseCookies()
239
    {
240
        return new ParameterBag($this->data['response_cookies']->getValue());
241
    }
242
 
243
    public function getSessionMetadata()
244
    {
245
        return $this->data['session_metadata']->getValue();
246
    }
247
 
248
    public function getSessionAttributes()
249
    {
250
        return $this->data['session_attributes']->getValue();
251
    }
252
 
253
    public function getStatelessCheck()
254
    {
255
        return $this->data['stateless_check'];
256
    }
257
 
258
    public function getSessionUsages()
259
    {
260
        return $this->data['session_usages'];
261
    }
262
 
263
    public function getFlashes()
264
    {
265
        return $this->data['flashes']->getValue();
266
    }
267
 
268
    public function getContent()
269
    {
270
        return $this->data['content'];
271
    }
272
 
273
    public function isJsonRequest()
274
    {
275
        return 1 === preg_match('{^application/(?:\w+\++)*json$}i', $this->data['request_headers']['content-type']);
276
    }
277
 
278
    public function getPrettyJson()
279
    {
280
        $decoded = json_decode($this->getContent());
281
 
282
        return \JSON_ERROR_NONE === json_last_error() ? json_encode($decoded, \JSON_PRETTY_PRINT) : null;
283
    }
284
 
285
    public function getContentType()
286
    {
287
        return $this->data['content_type'];
288
    }
289
 
290
    public function getStatusText()
291
    {
292
        return $this->data['status_text'];
293
    }
294
 
295
    public function getStatusCode()
296
    {
297
        return $this->data['status_code'];
298
    }
299
 
300
    public function getFormat()
301
    {
302
        return $this->data['format'];
303
    }
304
 
305
    public function getLocale()
306
    {
307
        return $this->data['locale'];
308
    }
309
 
310
    public function getDotenvVars()
311
    {
312
        return new ParameterBag($this->data['dotenv_vars']->getValue());
313
    }
314
 
315
    /**
316
     * Gets the route name.
317
     *
318
     * The _route request attributes is automatically set by the Router Matcher.
319
     */
320
    public function getRoute(): string
321
    {
322
        return $this->data['route'];
323
    }
324
 
325
    public function getIdentifier()
326
    {
327
        return $this->data['identifier'];
328
    }
329
 
330
    /**
331
     * Gets the route parameters.
332
     *
333
     * The _route_params request attributes is automatically set by the RouterListener.
334
     */
335
    public function getRouteParams(): array
336
    {
337
        return isset($this->data['request_attributes']['_route_params']) ? $this->data['request_attributes']['_route_params']->getValue() : [];
338
    }
339
 
340
    /**
341
     * Gets the parsed controller.
342
     *
343
     * @return array|string|Data The controller as a string or array of data
344
     *                           with keys 'class', 'method', 'file' and 'line'
345
     */
346
    public function getController(): array|string|Data
347
    {
348
        return $this->data['controller'];
349
    }
350
 
351
    /**
352
     * Gets the previous request attributes.
353
     *
354
     * @return array|Data|false A legacy array of data from the previous redirection response
355
     *                          or false otherwise
356
     */
357
    public function getRedirect(): array|Data|false
358
    {
359
        return $this->data['redirect'] ?? false;
360
    }
361
 
362
    public function getForwardToken()
363
    {
364
        return $this->data['forward_token'] ?? null;
365
    }
366
 
367
    public function onKernelController(ControllerEvent $event)
368
    {
369
        $this->controllers[$event->getRequest()] = $event->getController();
370
    }
371
 
372
    public function onKernelResponse(ResponseEvent $event)
373
    {
374
        if (!$event->isMainRequest()) {
375
            return;
376
        }
377
 
378
        if ($event->getRequest()->cookies->has('sf_redirect')) {
379
            $event->getRequest()->attributes->set('_redirected', true);
380
        }
381
    }
382
 
383
    public static function getSubscribedEvents(): array
384
    {
385
        return [
386
            KernelEvents::CONTROLLER => 'onKernelController',
387
            KernelEvents::RESPONSE => 'onKernelResponse',
388
        ];
389
    }
390
 
391
    public function getName(): string
392
    {
393
        return 'request';
394
    }
395
 
396
    public function collectSessionUsage(): void
397
    {
398
        $trace = debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS);
399
 
400
        $traceEndIndex = \count($trace) - 1;
401
        for ($i = $traceEndIndex; $i > 0; --$i) {
402
            if (null !== ($class = $trace[$i]['class'] ?? null) && (is_subclass_of($class, SessionInterface::class) || is_subclass_of($class, SessionBagInterface::class))) {
403
                $traceEndIndex = $i;
404
                break;
405
            }
406
        }
407
 
408
        if ((\count($trace) - 1) === $traceEndIndex) {
409
            return;
410
        }
411
 
412
        // Remove part of the backtrace that belongs to session only
413
        array_splice($trace, 0, $traceEndIndex);
414
 
415
        // Merge identical backtraces generated by internal call reports
416
        $name = sprintf('%s:%s', $trace[1]['class'] ?? $trace[0]['file'], $trace[0]['line']);
417
        if (!\array_key_exists($name, $this->sessionUsages)) {
418
            $this->sessionUsages[$name] = [
419
                'name' => $name,
420
                'file' => $trace[0]['file'],
421
                'line' => $trace[0]['line'],
422
                'trace' => $trace,
423
            ];
424
        }
425
    }
426
 
427
    /**
428
     * @return array|string An array of controller data or a simple string
429
     */
430
    private function parseController(array|object|string|null $controller): array|string
431
    {
432
        if (\is_string($controller) && str_contains($controller, '::')) {
433
            $controller = explode('::', $controller);
434
        }
435
 
436
        if (\is_array($controller)) {
437
            try {
438
                $r = new \ReflectionMethod($controller[0], $controller[1]);
439
 
440
                return [
441
                    'class' => \is_object($controller[0]) ? get_debug_type($controller[0]) : $controller[0],
442
                    'method' => $controller[1],
443
                    'file' => $r->getFileName(),
444
                    'line' => $r->getStartLine(),
445
                ];
446
            } catch (\ReflectionException) {
447
                if (\is_callable($controller)) {
448
                    // using __call or  __callStatic
449
                    return [
450
                        'class' => \is_object($controller[0]) ? get_debug_type($controller[0]) : $controller[0],
451
                        'method' => $controller[1],
452
                        'file' => 'n/a',
453
                        'line' => 'n/a',
454
                    ];
455
                }
456
            }
457
        }
458
 
459
        if ($controller instanceof \Closure) {
460
            $r = new \ReflectionFunction($controller);
461
 
462
            $controller = [
463
                'class' => $r->getName(),
464
                'method' => null,
465
                'file' => $r->getFileName(),
466
                'line' => $r->getStartLine(),
467
            ];
468
 
469
            if (str_contains($r->name, '{closure}')) {
470
                return $controller;
471
            }
472
            $controller['method'] = $r->name;
473
 
474
            if ($class = \PHP_VERSION_ID >= 80111 ? $r->getClosureCalledClass() : $r->getClosureScopeClass()) {
475
                $controller['class'] = $class->name;
476
            } else {
477
                return $r->name;
478
            }
479
 
480
            return $controller;
481
        }
482
 
483
        if (\is_object($controller)) {
484
            $r = new \ReflectionClass($controller);
485
 
486
            return [
487
                'class' => $r->getName(),
488
                'method' => null,
489
                'file' => $r->getFileName(),
490
                'line' => $r->getStartLine(),
491
            ];
492
        }
493
 
494
        return \is_string($controller) ? $controller : 'n/a';
495
    }
496
}