Subversion-Projekte lars-tiefland.laravel_shop

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
199 lars 1
<?php namespace Clockwork\Request;
2
 
3
use Clockwork\Helpers\Serializer;
4
 
5
// Data structure representing a single application request
6
class Request
7
{
8
	// Unique request ID
9
	public $id;
10
 
11
	// Metadata version
12
	public $version = 1;
13
 
14
	// Request type (request, command, queue-job or test)
15
	public $type = 'request';
16
 
17
	// Request time
18
	public $time;
19
 
20
	// Request method
21
	public $method;
22
 
23
	// Request URL
24
	public $url;
25
 
26
	// Request URI
27
	public $uri;
28
 
29
	// Request headers
30
	public $headers = [];
31
 
32
	// Textual representation of the executed controller
33
	public $controller;
34
 
35
	// Request GET data
36
	public $getData = [];
37
 
38
	// Request POST data
39
	public $postData = [];
40
 
41
	// Request body data
42
	public $requestData = [];
43
 
44
	// Session data array
45
	public $sessionData = [];
46
 
47
	// Authenticated user
48
	public $authenticatedUser;
49
 
50
	// Request cookies
51
	public $cookies = [];
52
 
53
	// Response time
54
	public $responseTime;
55
 
56
	// Response processing time
57
	public $responseDuration;
58
 
59
	// Response status code
60
	public $responseStatus;
61
 
62
	// Peak memory usage in bytes
63
	public $memoryUsage;
64
 
65
	// Executed middleware
66
	public $middleware = [];
67
 
68
	// Database queries
69
	public $databaseQueries = [];
70
 
71
	// Database queries count
72
	public $databaseQueriesCount;
73
 
74
	// Database slow queries count
75
	public $databaseSlowQueries;
76
 
77
	// Database query counts of a particular type (selects, inserts, updates, deletes, others)
78
	public $databaseSelects;
79
	public $databaseInserts;
80
	public $databaseUpdates;
81
	public $databaseDeletes;
82
	public $databaseOthers;
83
	public $databaseDuration;
84
 
85
	// Cache queries
86
	public $cacheQueries = [];
87
 
88
	// Cache query counts of a particular type (reads, hits, writes, deletes)
89
	public $cacheReads;
90
	public $cacheHits;
91
	public $cacheWrites;
92
	public $cacheDeletes;
93
 
94
	// Cache queries execution time
95
	public $cacheTime;
96
 
97
	// Model actions
98
	public $modelsActions = [];
99
 
100
	// Model action counts by model
101
	public $modelsRetrieved = [];
102
	public $modelsCreated = [];
103
	public $modelsUpdated = [];
104
	public $modelsDeleted = [];
105
 
106
	// Redis commands
107
	public $redisCommands = [];
108
 
109
	// Dispatched queue jobs
110
	public $queueJobs = [];
111
 
112
	// Timeline events
113
	public $timelineData = [];
114
 
115
	// Log messages
116
	public $log = [];
117
 
118
	// Fired events
119
	public $events = [];
120
 
121
	// Application routes
122
	public $routes = [];
123
 
124
	// Sent notifications
125
	public $notifications = [];
126
 
127
	// Sent emails (legacy property replaced by notifications)
128
	public $emailsData = [];
129
 
130
	// Rendered views
131
	public $viewsData = [];
132
 
133
	// Custom user data
134
	public $userData = [];
135
 
136
	// Subrequests
137
	public $subrequests = [];
138
 
139
	// Xebug profiler data
140
	public $xdebug = [];
141
 
142
	// Command name
143
	public $commandName;
144
 
145
	// Command arguments passed in
146
	public $commandArguments = [];
147
 
148
	// Command arguments defaults
149
	public $commandArgumentsDefaults = [];
150
 
151
	// Command options passed in
152
	public $commandOptions = [];
153
 
154
	// Command options defaults
155
	public $commandOptionsDefaults = [];
156
 
157
	// Command exit code
158
	public $commandExitCode;
159
 
160
	// Command output
161
	public $commandOutput;
162
 
163
	// Queue job name
164
	public $jobName;
165
 
166
	// Queue job description
167
	public $jobDescription;
168
 
169
	// Queue job status
170
	public $jobStatus;
171
 
172
	// Queue job payload
173
	public $jobPayload = [];
174
 
175
	// Queue job queue name
176
	public $jobQueue;
177
 
178
	// Queue job connection name
179
	public $jobConnection;
180
 
181
	// Queue job additional options
182
	public $jobOptions = [];
183
 
184
	// Test name
185
	public $testName;
186
 
187
	// Test status
188
	public $testStatus;
189
 
190
	// Test status message (eg. in case of failure)
191
	public $testStatusMessage;
192
 
193
	// Ran test asserts
194
	public $testAsserts = [];
195
 
196
	// Client-side performance metrics in the form of [ metric => value ]
197
	public $clientMetrics = [];
198
 
199
	// Web vitals in the form of [ vital => value ]
200
	public $webVitals = [];
201
 
202
	// Parent request
203
	public $parent;
204
 
205
	// Token to update this request data
206
	public $updateToken;
207
 
208
	// Log instance for the current request
209
	protected $currentLog;
210
 
211
	// Timeline instance for the current request
212
	protected $currentTimeline;
213
 
214
	// Array of property values to override collected values from data sources
215
	protected $overrides = [];
216
 
217
	// Create a new request, if optional data array argument is provided, it will be used to populate the request object,
218
	// otherwise an empty request with current time, autogenerated ID and update token will be created
219
	public function __construct(array $data = [])
220
	{
221
		$this->id = isset($data['id']) ? $data['id'] : $this->generateRequestId();
222
		$this->time = microtime(true);
223
		$this->updateToken = isset($data['updateToken']) ? $data['updateToken'] : $this->generateUpdateToken();
224
 
225
		foreach ($data as $key => $val) $this->$key = $val;
226
 
227
		$this->currentLog = new Log($this->log);
228
		$this->currentTimeline = new Timeline\Timeline($this->timelineData);
229
	}
230
 
231
	// Compute the sum of durations of all database queries
232
	public function getDatabaseDuration()
233
	{
234
		return array_reduce($this->databaseQueries, function ($total, $query) {
235
			return isset($query['duration']) ? $total + $query['duration'] : $total;
236
		}, 0);
237
	}
238
 
239
	// Compute response duration in milliseconds
240
	public function getResponseDuration()
241
	{
242
		return ($this->responseTime - $this->time) * 1000;
243
	}
244
 
245
	// Get all request data as an array
246
	public function toArray()
247
	{
248
		return [
249
			'id'                       => $this->id,
250
			'version'                  => $this->version,
251
			'type'                     => $this->type,
252
			'time'                     => $this->time,
253
			'method'                   => $this->method,
254
			'url'                      => $this->url,
255
			'uri'                      => $this->uri,
256
			'headers'                  => $this->headers,
257
			'controller'               => $this->controller,
258
			'getData'                  => $this->getData,
259
			'postData'                 => $this->postData,
260
			'requestData'              => $this->requestData,
261
			'sessionData'              => $this->sessionData,
262
			'authenticatedUser'        => $this->authenticatedUser,
263
			'cookies'                  => $this->cookies,
264
			'responseTime'             => $this->responseTime,
265
			'responseStatus'           => $this->responseStatus,
266
			'responseDuration'         => $this->responseDuration ?: $this->getResponseDuration(),
267
			'memoryUsage'              => $this->memoryUsage,
268
			'middleware'               => $this->middleware,
269
			'databaseQueries'          => $this->databaseQueries,
270
			'databaseQueriesCount'     => $this->databaseQueriesCount,
271
			'databaseSlowQueries'      => $this->databaseSlowQueries,
272
			'databaseSelects'          => $this->databaseSelects,
273
			'databaseInserts'          => $this->databaseInserts,
274
			'databaseUpdates'          => $this->databaseUpdates,
275
			'databaseDeletes'          => $this->databaseDeletes,
276
			'databaseOthers'           => $this->databaseOthers,
277
			'databaseDuration'         => $this->getDatabaseDuration(),
278
			'cacheQueries'             => $this->cacheQueries,
279
			'cacheReads'               => $this->cacheReads,
280
			'cacheHits'                => $this->cacheHits,
281
			'cacheWrites'              => $this->cacheWrites,
282
			'cacheDeletes'             => $this->cacheDeletes,
283
			'cacheTime'                => $this->cacheTime,
284
			'modelsActions'            => $this->modelsActions,
285
			'modelsRetrieved'          => $this->modelsRetrieved,
286
			'modelsCreated'            => $this->modelsCreated,
287
			'modelsUpdated'            => $this->modelsUpdated,
288
			'modelsDeleted'            => $this->modelsDeleted,
289
			'redisCommands'            => $this->redisCommands,
290
			'queueJobs'                => $this->queueJobs,
291
			'timelineData'             => $this->timeline()->toArray(),
292
			'log'                      => $this->log()->toArray(),
293
			'events'                   => $this->events,
294
			'routes'                   => $this->routes,
295
			'notifications'            => $this->notifications,
296
			'emailsData'               => $this->emailsData,
297
			'viewsData'                => $this->viewsData,
298
			'userData'                 => array_map(function ($data) {
299
				return $data instanceof UserData ? $data->toArray() : $data;
300
			}, $this->userData),
301
			'subrequests'              => $this->subrequests,
302
			'xdebug'                   => $this->xdebug,
303
			'commandName'              => $this->commandName,
304
			'commandArguments'         => $this->commandArguments,
305
			'commandArgumentsDefaults' => $this->commandArgumentsDefaults,
306
			'commandOptions'           => $this->commandOptions,
307
			'commandOptionsDefaults'   => $this->commandOptionsDefaults,
308
			'commandExitCode'          => $this->commandExitCode,
309
			'commandOutput'            => $this->commandOutput,
310
			'jobName'                  => $this->jobName,
311
			'jobDescription'           => $this->jobDescription,
312
			'jobStatus'                => $this->jobStatus,
313
			'jobPayload'               => $this->jobPayload,
314
			'jobQueue'                 => $this->jobQueue,
315
			'jobConnection'            => $this->jobConnection,
316
			'jobOptions'               => $this->jobOptions,
317
			'testName'                 => $this->testName,
318
			'testStatus'               => $this->testStatus,
319
			'testStatusMessage'        => $this->testStatusMessage,
320
			'testAsserts'              => $this->testAsserts,
321
			'clientMetrics'            => $this->clientMetrics,
322
			'webVitals'                => $this->webVitals,
323
			'parent'                   => $this->parent,
324
			'updateToken'              => $this->updateToken
325
		];
326
	}
327
 
328
	// Get all request data as a JSON string
329
	public function toJson()
330
	{
331
		return json_encode($this->toArray(), \JSON_PARTIAL_OUTPUT_ON_ERROR);
332
	}
333
 
334
	// Return request data except specified keys as an array
335
	public function except($keys)
336
	{
337
		return array_filter($this->toArray(), function ($value, $key) use ($keys) {
338
			return ! in_array($key, $keys);
339
		}, ARRAY_FILTER_USE_BOTH);
340
	}
341
 
342
	// Return only request data with specified keys as an array
343
	public function only($keys)
344
	{
345
		return array_filter($this->toArray(), function ($value, $key) use ($keys) {
346
			return in_array($key, $keys);
347
		}, ARRAY_FILTER_USE_BOTH);
348
	}
349
 
350
	// Return log instance for the current request
351
	public function log()
352
	{
353
		return $this->currentLog;
354
	}
355
 
356
	// Return timeline instance for the current request
357
	public function timeline()
358
	{
359
		return $this->currentTimeline;
360
	}
361
 
362
	// Add a new overridden property
363
	public function override($property, $value)
364
	{
365
		$this->overrides[$property] = $value;
366
		return $this;
367
	}
368
 
369
	// Get or set all overrides at once
370
	public function overrides($overrides = null)
371
	{
372
		if (! $overrides) return $this->overrides;
373
 
374
		$this->overrides = $overrides;
375
		return $this;
376
	}
377
 
378
	// Add database query, takes query, bindings, duration (in ms) and additional data - connection (connection name),
379
	// time (when was the query executed), file (caller file name), line (caller line number), trace (serialized trace),
380
	// model (associated ORM model)
381
	public function addDatabaseQuery($query, $bindings = [], $duration = null, $data = [])
382
	{
383
		$this->databaseQueries[] = [
384
			'query'      => $query,
385
			'bindings'   => (new Serializer)->normalize($bindings),
386
			'duration'   => $duration,
387
			'connection' => isset($data['connection']) ? $data['connection'] : null,
388
			'time'       => isset($data['time']) ? $data['time'] : microtime(true) - ($duration ?: 0) / 1000,
389
			'file'       => isset($data['file']) ? $data['file'] : null,
390
			'line'       => isset($data['line']) ? $data['line'] : null,
391
			'trace'      => isset($data['trace']) ? $data['trace'] : null,
392
			'model'      => isset($data['model']) ? $data['model'] : null,
393
			'tags'       => array_merge(
394
				isset($data['tags']) ? $data['tags'] : [], isset($data['slow']) ? [ 'slow' ] : []
395
			)
396
		];
397
	}
398
 
399
	// Add model action, takes model, action and additional data - key, attributes, changes, time (when was the action
400
	// executed), query, duration (in ms), connection (connection name), trace (serialized trace), file (caller file
401
	// name), line (caller line number), tags
402
	public function addModelAction($model, $action, $data = [])
403
	{
404
		$this->modelActions[] = [
405
			'model'      => $model,
406
			'key'        => isset($data['key']) ? $data['key'] : null,
407
			'action'     => $action,
408
			'attributes' => isset($data['attributes']) ? $data['attributes'] : [],
409
			'changes'    => isset($data['changes']) ? $data['changes'] : [],
410
			'duration'   => $duration = isset($data['duration']) ? $data['duration'] : null,
411
			'time'       => isset($data['time']) ? $data['time'] : microtime(true) - ($duration ?: 0) / 1000,
412
			'query'      => isset($data['query']) ? $data['query'] : null,
413
			'connection' => isset($data['connection']) ? $data['connection'] : null,
414
			'trace'      => isset($data['trace']) ? $data['trace'] : null,
415
			'file'       => isset($data['file']) ? $data['file'] : null,
416
			'line'       => isset($data['line']) ? $data['line'] : null,
417
			'tags'       => isset($data['tags']) ? $data['tags'] : []
418
		];
419
	}
420
 
421
	// Add cache query, takes type, key, value, duration (in ms) and additional data - connection (connection name),
422
	// time (when was the query executed), file (caller file name), line (caller line number), trace (serialized trace),
423
	// expiration
424
	public function addCacheQuery($type, $key, $value = null, $duration = null, $data = [])
425
	{
426
		$this->cacheQueries[] = [
427
			'type'       => $type,
428
			'key'        => $key,
429
			'value'      => (new Serializer)->normalize($value),
430
			'duration'   => $duration,
431
			'connection' => isset($data['connection']) ? $data['connection'] : null,
432
			'time'       => isset($data['time']) ? $data['time'] : microtime(true) - ($duration ?: 0) / 1000,
433
			'file'       => isset($data['file']) ? $data['file'] : null,
434
			'line'       => isset($data['line']) ? $data['line'] : null,
435
			'trace'      => isset($data['trace']) ? $data['trace'] : null,
436
			'expiration' => isset($data['expiration']) ? $data['expiration'] : null
437
		];
438
	}
439
 
440
	// Add event, takes event name, data, time and additional data - listeners, duration (in ms), file (caller file
441
	// name), line (caller line number), trace (serialized trace)
442
	public function addEvent($event, $eventData = null, $time = null, $data = [])
443
	{
444
		$this->events[] = [
445
			'event'     => $event,
446
			'data'      => (new Serializer)->normalize($eventData),
447
			'duration'  => $duration = isset($data['duration']) ? $data['duration'] : null,
448
			'time'      => $time ? $time : microtime(true) - ($duration ?: 0) / 1000,
449
			'listeners' => isset($data['listeners']) ? $data['listeners'] : null,
450
			'file'      => isset($data['file']) ? $data['file'] : null,
451
			'line'      => isset($data['line']) ? $data['line'] : null,
452
			'trace'     => isset($data['trace']) ? $data['trace'] : null
453
		];
454
	}
455
 
456
	// Add route, takes method, uri, action and additional data - name, middleware, before (before filters), after
457
	// (after filters)
458
	public function addRoute($method, $uri, $action, $data = [])
459
	{
460
		$this->routes[] = [
461
			'method'     => $method,
462
			'uri'        => $uri,
463
			'action'     => $action,
464
			'name'       => isset($data['name']) ? $data['name'] : null,
465
			'middleware' => isset($data['middleware']) ? $data['middleware'] : null,
466
			'before'     => isset($data['before']) ? $data['before'] : null,
467
			'after'      => isset($data['after']) ? $data['after'] : null
468
		];
469
	}
470
 
471
	// Add sent notifucation, takes subject, recipient, sender, and additional data - time, duration, type, content, data
472
	public function addNotification($subject, $to, $from = null, $data = [])
473
	{
474
		$this->notifications[] = [
475
			'subject'  => $subject,
476
			'from'     => $from,
477
			'to'       => $to,
478
			'content'  => isset($data['content']) ? $data['content'] : null,
479
			'type'     => isset($data['type']) ? $data['type'] : null,
480
			'data'     => isset($data['data']) ? $data['data'] : [],
481
			'duration' => $duration = isset($data['duration']) ? $data['duration'] : null,
482
			'time'     => isset($data['time']) ? $data['time'] : microtime(true) - ($duration ?: 0) / 1000,
483
			'trace'    => isset($data['trace']) ? $data['trace'] : null,
484
			'file'     => isset($data['file']) ? $data['file'] : null,
485
			'line'     => isset($data['line']) ? $data['line'] : null
486
		];
487
	}
488
 
489
	// Add sent email, takes subject, recipient address, sender address, array of headers, and additional data - time
490
	// (when was the email sent), duration (sending time in ms)
491
	public function addEmail($subject, $to, $from = null, $headers = [], $data = [])
492
	{
493
		$this->emailsData[] = [
494
			'start'       => isset($data['time']) ? $data['time'] : null,
495
			'end'         => isset($data['time'], $data['duration']) ? $data['time'] + $data['duration'] / 1000 : null,
496
			'duration'    => isset($data['duration']) ? $data['duration'] : null,
497
			'description' => 'Sending an email message',
498
			'data'        => [
499
				'subject' => $subject,
500
				'to'      => $to,
501
				'from'    => $from,
502
				'headers' => (new Serializer)->normalize($headers)
503
			]
504
		];
505
	}
506
 
507
	// Add view, takes view name, view data and additional data - time (when was the view rendered), duration (sending
508
	// time in ms)
509
	public function addView($name, $viewData = [], $data = [])
510
	{
511
		$this->viewsData[] = [
512
			'start'       => isset($data['time']) ? $data['time'] : null,
513
			'end'         => isset($data['time'], $data['duration']) ? $data['time'] + $data['duration'] / 1000 : null,
514
			'duration'    => isset($data['duration']) ? $data['duration'] : null,
515
			'description' => 'Rendering a view',
516
			'data'        => [
517
				'name' => $name,
518
				'data' => (new Serializer)->normalize($viewData)
519
			]
520
		];
521
	}
522
 
523
	// Add executed subrequest, takes the requested url, subrequest Clockwork ID and additional data - path if non-default
524
	public function addSubrequest($url, $id, $data = [])
525
	{
526
		$this->subrequests[] = [
527
			'url'  => $url,
528
			'id'   => $id,
529
			'path' => isset($data['path']) ? $data['path'] : null
530
		];
531
	}
532
 
533
	// Set the authenticated user, takes a username, an id and additional data - email and name
534
	public function setAuthenticatedUser($username, $id = null, $data = [])
535
	{
536
		$this->authenticatedUser = [
537
			'id'       => $id,
538
			'username' => $username,
539
			'email'    => isset($data['email']) ? $data['email'] : null,
540
			'name'     => isset($data['name']) ? $data['name'] : null
541
		];
542
	}
543
 
544
	// Set parent request, takes the request id and additional options - url and path if non-default
545
	public function setParent($id, $data = [])
546
	{
547
		$this->parent = [
548
			'id'   => $id,
549
			'url'  => isset($data['url']) ? $data['url'] : null,
550
			'path' => isset($data['path']) ? $data['path'] : null
551
		];
552
	}
553
 
554
	// Add custom user data
555
	public function userData($key = null)
556
	{
557
		if ($key && isset($this->userData[$key])) {
558
			return $this->userData[$key];
559
		}
560
 
561
		$userData = (new UserData)->title($key);
562
 
563
		return $key ? $this->userData[$key] = $userData : $this->userData[] = $userData;
564
	}
565
 
566
	// Add a ran test assert, takes the assert name, arguments, whether it passed and trace as arguments
567
	public function addTestAssert($name, $arguments = null, $passed = true, $trace = null)
568
	{
569
		$this->testAsserts[] = [
570
			'name'      => $name,
571
			'arguments' => (new Serializer)->normalize($arguments),
572
			'trace'     => $trace,
573
			'passed'    => $passed
574
		];
575
	}
576
 
577
	// Generate unique request ID in the form of <current time>-<random number>
578
	protected function generateRequestId()
579
	{
580
		return str_replace('.', '-', sprintf('%.4F', microtime(true))) . '-' . mt_rand();
581
	}
582
 
583
	// Generate a random update token
584
	protected function generateUpdateToken()
585
	{
586
		$length = 8;
587
		$bytes = function_exists('random_bytes') ? random_bytes($length) : openssl_random_pseudo_bytes($length);
588
 
589
		return substr(bin2hex($bytes), 0, $length);
590
	}
591
}