Subversion-Projekte lars-tiefland.laravel_shop

Revision

Blame | Letzte Änderung | Log anzeigen | RSS feed

<?php namespace Clockwork\Request;

use Clockwork\Helpers\Serializer;

// Data structure representing a single application request
class Request
{
        // Unique request ID
        public $id;

        // Metadata version
        public $version = 1;

        // Request type (request, command, queue-job or test)
        public $type = 'request';

        // Request time
        public $time;

        // Request method
        public $method;

        // Request URL
        public $url;

        // Request URI
        public $uri;

        // Request headers
        public $headers = [];

        // Textual representation of the executed controller
        public $controller;

        // Request GET data
        public $getData = [];

        // Request POST data
        public $postData = [];

        // Request body data
        public $requestData = [];

        // Session data array
        public $sessionData = [];

        // Authenticated user
        public $authenticatedUser;

        // Request cookies
        public $cookies = [];

        // Response time
        public $responseTime;

        // Response processing time
        public $responseDuration;

        // Response status code
        public $responseStatus;

        // Peak memory usage in bytes
        public $memoryUsage;

        // Executed middleware
        public $middleware = [];

        // Database queries
        public $databaseQueries = [];

        // Database queries count
        public $databaseQueriesCount;

        // Database slow queries count
        public $databaseSlowQueries;

        // Database query counts of a particular type (selects, inserts, updates, deletes, others)
        public $databaseSelects;
        public $databaseInserts;
        public $databaseUpdates;
        public $databaseDeletes;
        public $databaseOthers;
        public $databaseDuration;

        // Cache queries
        public $cacheQueries = [];

        // Cache query counts of a particular type (reads, hits, writes, deletes)
        public $cacheReads;
        public $cacheHits;
        public $cacheWrites;
        public $cacheDeletes;

        // Cache queries execution time
        public $cacheTime;

        // Model actions
        public $modelsActions = [];

        // Model action counts by model
        public $modelsRetrieved = [];
        public $modelsCreated = [];
        public $modelsUpdated = [];
        public $modelsDeleted = [];

        // Redis commands
        public $redisCommands = [];

        // Dispatched queue jobs
        public $queueJobs = [];

        // Timeline events
        public $timelineData = [];

        // Log messages
        public $log = [];

        // Fired events
        public $events = [];

        // Application routes
        public $routes = [];

        // Sent notifications
        public $notifications = [];

        // Sent emails (legacy property replaced by notifications)
        public $emailsData = [];

        // Rendered views
        public $viewsData = [];

        // Custom user data
        public $userData = [];

        // Subrequests
        public $subrequests = [];

        // Xebug profiler data
        public $xdebug = [];

        // Command name
        public $commandName;

        // Command arguments passed in
        public $commandArguments = [];

        // Command arguments defaults
        public $commandArgumentsDefaults = [];

        // Command options passed in
        public $commandOptions = [];

        // Command options defaults
        public $commandOptionsDefaults = [];

        // Command exit code
        public $commandExitCode;

        // Command output
        public $commandOutput;

        // Queue job name
        public $jobName;

        // Queue job description
        public $jobDescription;

        // Queue job status
        public $jobStatus;

        // Queue job payload
        public $jobPayload = [];

        // Queue job queue name
        public $jobQueue;

        // Queue job connection name
        public $jobConnection;

        // Queue job additional options
        public $jobOptions = [];

        // Test name
        public $testName;

        // Test status
        public $testStatus;

        // Test status message (eg. in case of failure)
        public $testStatusMessage;

        // Ran test asserts
        public $testAsserts = [];

        // Client-side performance metrics in the form of [ metric => value ]
        public $clientMetrics = [];

        // Web vitals in the form of [ vital => value ]
        public $webVitals = [];

        // Parent request
        public $parent;

        // Token to update this request data
        public $updateToken;

        // Log instance for the current request
        protected $currentLog;

        // Timeline instance for the current request
        protected $currentTimeline;

        // Array of property values to override collected values from data sources
        protected $overrides = [];

        // Create a new request, if optional data array argument is provided, it will be used to populate the request object,
        // otherwise an empty request with current time, autogenerated ID and update token will be created
        public function __construct(array $data = [])
        {
                $this->id = isset($data['id']) ? $data['id'] : $this->generateRequestId();
                $this->time = microtime(true);
                $this->updateToken = isset($data['updateToken']) ? $data['updateToken'] : $this->generateUpdateToken();

                foreach ($data as $key => $val) $this->$key = $val;

                $this->currentLog = new Log($this->log);
                $this->currentTimeline = new Timeline\Timeline($this->timelineData);
        }

        // Compute the sum of durations of all database queries
        public function getDatabaseDuration()
        {
                return array_reduce($this->databaseQueries, function ($total, $query) {
                        return isset($query['duration']) ? $total + $query['duration'] : $total;
                }, 0);
        }

        // Compute response duration in milliseconds
        public function getResponseDuration()
        {
                return ($this->responseTime - $this->time) * 1000;
        }

        // Get all request data as an array
        public function toArray()
        {
                return [
                        'id'                       => $this->id,
                        'version'                  => $this->version,
                        'type'                     => $this->type,
                        'time'                     => $this->time,
                        'method'                   => $this->method,
                        'url'                      => $this->url,
                        'uri'                      => $this->uri,
                        'headers'                  => $this->headers,
                        'controller'               => $this->controller,
                        'getData'                  => $this->getData,
                        'postData'                 => $this->postData,
                        'requestData'              => $this->requestData,
                        'sessionData'              => $this->sessionData,
                        'authenticatedUser'        => $this->authenticatedUser,
                        'cookies'                  => $this->cookies,
                        'responseTime'             => $this->responseTime,
                        'responseStatus'           => $this->responseStatus,
                        'responseDuration'         => $this->responseDuration ?: $this->getResponseDuration(),
                        'memoryUsage'              => $this->memoryUsage,
                        'middleware'               => $this->middleware,
                        'databaseQueries'          => $this->databaseQueries,
                        'databaseQueriesCount'     => $this->databaseQueriesCount,
                        'databaseSlowQueries'      => $this->databaseSlowQueries,
                        'databaseSelects'          => $this->databaseSelects,
                        'databaseInserts'          => $this->databaseInserts,
                        'databaseUpdates'          => $this->databaseUpdates,
                        'databaseDeletes'          => $this->databaseDeletes,
                        'databaseOthers'           => $this->databaseOthers,
                        'databaseDuration'         => $this->getDatabaseDuration(),
                        'cacheQueries'             => $this->cacheQueries,
                        'cacheReads'               => $this->cacheReads,
                        'cacheHits'                => $this->cacheHits,
                        'cacheWrites'              => $this->cacheWrites,
                        'cacheDeletes'             => $this->cacheDeletes,
                        'cacheTime'                => $this->cacheTime,
                        'modelsActions'            => $this->modelsActions,
                        'modelsRetrieved'          => $this->modelsRetrieved,
                        'modelsCreated'            => $this->modelsCreated,
                        'modelsUpdated'            => $this->modelsUpdated,
                        'modelsDeleted'            => $this->modelsDeleted,
                        'redisCommands'            => $this->redisCommands,
                        'queueJobs'                => $this->queueJobs,
                        'timelineData'             => $this->timeline()->toArray(),
                        'log'                      => $this->log()->toArray(),
                        'events'                   => $this->events,
                        'routes'                   => $this->routes,
                        'notifications'            => $this->notifications,
                        'emailsData'               => $this->emailsData,
                        'viewsData'                => $this->viewsData,
                        'userData'                 => array_map(function ($data) {
                                return $data instanceof UserData ? $data->toArray() : $data;
                        }, $this->userData),
                        'subrequests'              => $this->subrequests,
                        'xdebug'                   => $this->xdebug,
                        'commandName'              => $this->commandName,
                        'commandArguments'         => $this->commandArguments,
                        'commandArgumentsDefaults' => $this->commandArgumentsDefaults,
                        'commandOptions'           => $this->commandOptions,
                        'commandOptionsDefaults'   => $this->commandOptionsDefaults,
                        'commandExitCode'          => $this->commandExitCode,
                        'commandOutput'            => $this->commandOutput,
                        'jobName'                  => $this->jobName,
                        'jobDescription'           => $this->jobDescription,
                        'jobStatus'                => $this->jobStatus,
                        'jobPayload'               => $this->jobPayload,
                        'jobQueue'                 => $this->jobQueue,
                        'jobConnection'            => $this->jobConnection,
                        'jobOptions'               => $this->jobOptions,
                        'testName'                 => $this->testName,
                        'testStatus'               => $this->testStatus,
                        'testStatusMessage'        => $this->testStatusMessage,
                        'testAsserts'              => $this->testAsserts,
                        'clientMetrics'            => $this->clientMetrics,
                        'webVitals'                => $this->webVitals,
                        'parent'                   => $this->parent,
                        'updateToken'              => $this->updateToken
                ];
        }

        // Get all request data as a JSON string
        public function toJson()
        {
                return json_encode($this->toArray(), \JSON_PARTIAL_OUTPUT_ON_ERROR);
        }

        // Return request data except specified keys as an array
        public function except($keys)
        {
                return array_filter($this->toArray(), function ($value, $key) use ($keys) {
                        return ! in_array($key, $keys);
                }, ARRAY_FILTER_USE_BOTH);
        }

        // Return only request data with specified keys as an array
        public function only($keys)
        {
                return array_filter($this->toArray(), function ($value, $key) use ($keys) {
                        return in_array($key, $keys);
                }, ARRAY_FILTER_USE_BOTH);
        }

        // Return log instance for the current request
        public function log()
        {
                return $this->currentLog;
        }

        // Return timeline instance for the current request
        public function timeline()
        {
                return $this->currentTimeline;
        }

        // Add a new overridden property
        public function override($property, $value)
        {
                $this->overrides[$property] = $value;
                return $this;
        }

        // Get or set all overrides at once
        public function overrides($overrides = null)
        {
                if (! $overrides) return $this->overrides;

                $this->overrides = $overrides;
                return $this;
        }

        // Add database query, takes query, bindings, duration (in ms) and additional data - connection (connection name),
        // time (when was the query executed), file (caller file name), line (caller line number), trace (serialized trace),
        // model (associated ORM model)
        public function addDatabaseQuery($query, $bindings = [], $duration = null, $data = [])
        {
                $this->databaseQueries[] = [
                        'query'      => $query,
                        'bindings'   => (new Serializer)->normalize($bindings),
                        'duration'   => $duration,
                        'connection' => isset($data['connection']) ? $data['connection'] : null,
                        'time'       => isset($data['time']) ? $data['time'] : microtime(true) - ($duration ?: 0) / 1000,
                        'file'       => isset($data['file']) ? $data['file'] : null,
                        'line'       => isset($data['line']) ? $data['line'] : null,
                        'trace'      => isset($data['trace']) ? $data['trace'] : null,
                        'model'      => isset($data['model']) ? $data['model'] : null,
                        'tags'       => array_merge(
                                isset($data['tags']) ? $data['tags'] : [], isset($data['slow']) ? [ 'slow' ] : []
                        )
                ];
        }

        // Add model action, takes model, action and additional data - key, attributes, changes, time (when was the action
        // executed), query, duration (in ms), connection (connection name), trace (serialized trace), file (caller file
        // name), line (caller line number), tags
        public function addModelAction($model, $action, $data = [])
        {
                $this->modelActions[] = [
                        'model'      => $model,
                        'key'        => isset($data['key']) ? $data['key'] : null,
                        'action'     => $action,
                        'attributes' => isset($data['attributes']) ? $data['attributes'] : [],
                        'changes'    => isset($data['changes']) ? $data['changes'] : [],
                        'duration'   => $duration = isset($data['duration']) ? $data['duration'] : null,
                        'time'       => isset($data['time']) ? $data['time'] : microtime(true) - ($duration ?: 0) / 1000,
                        'query'      => isset($data['query']) ? $data['query'] : null,
                        'connection' => isset($data['connection']) ? $data['connection'] : null,
                        'trace'      => isset($data['trace']) ? $data['trace'] : null,
                        'file'       => isset($data['file']) ? $data['file'] : null,
                        'line'       => isset($data['line']) ? $data['line'] : null,
                        'tags'       => isset($data['tags']) ? $data['tags'] : []
                ];
        }

        // Add cache query, takes type, key, value, duration (in ms) and additional data - connection (connection name),
        // time (when was the query executed), file (caller file name), line (caller line number), trace (serialized trace),
        // expiration
        public function addCacheQuery($type, $key, $value = null, $duration = null, $data = [])
        {
                $this->cacheQueries[] = [
                        'type'       => $type,
                        'key'        => $key,
                        'value'      => (new Serializer)->normalize($value),
                        'duration'   => $duration,
                        'connection' => isset($data['connection']) ? $data['connection'] : null,
                        'time'       => isset($data['time']) ? $data['time'] : microtime(true) - ($duration ?: 0) / 1000,
                        'file'       => isset($data['file']) ? $data['file'] : null,
                        'line'       => isset($data['line']) ? $data['line'] : null,
                        'trace'      => isset($data['trace']) ? $data['trace'] : null,
                        'expiration' => isset($data['expiration']) ? $data['expiration'] : null
                ];
        }

        // Add event, takes event name, data, time and additional data - listeners, duration (in ms), file (caller file
        // name), line (caller line number), trace (serialized trace)
        public function addEvent($event, $eventData = null, $time = null, $data = [])
        {
                $this->events[] = [
                        'event'     => $event,
                        'data'      => (new Serializer)->normalize($eventData),
                        'duration'  => $duration = isset($data['duration']) ? $data['duration'] : null,
                        'time'      => $time ? $time : microtime(true) - ($duration ?: 0) / 1000,
                        'listeners' => isset($data['listeners']) ? $data['listeners'] : null,
                        'file'      => isset($data['file']) ? $data['file'] : null,
                        'line'      => isset($data['line']) ? $data['line'] : null,
                        'trace'     => isset($data['trace']) ? $data['trace'] : null
                ];
        }

        // Add route, takes method, uri, action and additional data - name, middleware, before (before filters), after
        // (after filters)
        public function addRoute($method, $uri, $action, $data = [])
        {
                $this->routes[] = [
                        'method'     => $method,
                        'uri'        => $uri,
                        'action'     => $action,
                        'name'       => isset($data['name']) ? $data['name'] : null,
                        'middleware' => isset($data['middleware']) ? $data['middleware'] : null,
                        'before'     => isset($data['before']) ? $data['before'] : null,
                        'after'      => isset($data['after']) ? $data['after'] : null
                ];
        }

        // Add sent notifucation, takes subject, recipient, sender, and additional data - time, duration, type, content, data
        public function addNotification($subject, $to, $from = null, $data = [])
        {
                $this->notifications[] = [
                        'subject'  => $subject,
                        'from'     => $from,
                        'to'       => $to,
                        'content'  => isset($data['content']) ? $data['content'] : null,
                        'type'     => isset($data['type']) ? $data['type'] : null,
                        'data'     => isset($data['data']) ? $data['data'] : [],
                        'duration' => $duration = isset($data['duration']) ? $data['duration'] : null,
                        'time'     => isset($data['time']) ? $data['time'] : microtime(true) - ($duration ?: 0) / 1000,
                        'trace'    => isset($data['trace']) ? $data['trace'] : null,
                        'file'     => isset($data['file']) ? $data['file'] : null,
                        'line'     => isset($data['line']) ? $data['line'] : null
                ];
        }

        // Add sent email, takes subject, recipient address, sender address, array of headers, and additional data - time
        // (when was the email sent), duration (sending time in ms)
        public function addEmail($subject, $to, $from = null, $headers = [], $data = [])
        {
                $this->emailsData[] = [
                        'start'       => isset($data['time']) ? $data['time'] : null,
                        'end'         => isset($data['time'], $data['duration']) ? $data['time'] + $data['duration'] / 1000 : null,
                        'duration'    => isset($data['duration']) ? $data['duration'] : null,
                        'description' => 'Sending an email message',
                        'data'        => [
                                'subject' => $subject,
                                'to'      => $to,
                                'from'    => $from,
                                'headers' => (new Serializer)->normalize($headers)
                        ]
                ];
        }

        // Add view, takes view name, view data and additional data - time (when was the view rendered), duration (sending
        // time in ms)
        public function addView($name, $viewData = [], $data = [])
        {
                $this->viewsData[] = [
                        'start'       => isset($data['time']) ? $data['time'] : null,
                        'end'         => isset($data['time'], $data['duration']) ? $data['time'] + $data['duration'] / 1000 : null,
                        'duration'    => isset($data['duration']) ? $data['duration'] : null,
                        'description' => 'Rendering a view',
                        'data'        => [
                                'name' => $name,
                                'data' => (new Serializer)->normalize($viewData)
                        ]
                ];
        }

        // Add executed subrequest, takes the requested url, subrequest Clockwork ID and additional data - path if non-default
        public function addSubrequest($url, $id, $data = [])
        {
                $this->subrequests[] = [
                        'url'  => $url,
                        'id'   => $id,
                        'path' => isset($data['path']) ? $data['path'] : null
                ];
        }

        // Set the authenticated user, takes a username, an id and additional data - email and name
        public function setAuthenticatedUser($username, $id = null, $data = [])
        {
                $this->authenticatedUser = [
                        'id'       => $id,
                        'username' => $username,
                        'email'    => isset($data['email']) ? $data['email'] : null,
                        'name'     => isset($data['name']) ? $data['name'] : null
                ];
        }

        // Set parent request, takes the request id and additional options - url and path if non-default
        public function setParent($id, $data = [])
        {
                $this->parent = [
                        'id'   => $id,
                        'url'  => isset($data['url']) ? $data['url'] : null,
                        'path' => isset($data['path']) ? $data['path'] : null
                ];
        }

        // Add custom user data
        public function userData($key = null)
        {
                if ($key && isset($this->userData[$key])) {
                        return $this->userData[$key];
                }

                $userData = (new UserData)->title($key);

                return $key ? $this->userData[$key] = $userData : $this->userData[] = $userData;
        }

        // Add a ran test assert, takes the assert name, arguments, whether it passed and trace as arguments
        public function addTestAssert($name, $arguments = null, $passed = true, $trace = null)
        {
                $this->testAsserts[] = [
                        'name'      => $name,
                        'arguments' => (new Serializer)->normalize($arguments),
                        'trace'     => $trace,
                        'passed'    => $passed
                ];
        }

        // Generate unique request ID in the form of <current time>-<random number>
        protected function generateRequestId()
        {
                return str_replace('.', '-', sprintf('%.4F', microtime(true))) . '-' . mt_rand();
        }

        // Generate a random update token
        protected function generateUpdateToken()
        {
                $length = 8;
                $bytes = function_exists('random_bytes') ? random_bytes($length) : openssl_random_pseudo_bytes($length);

                return substr(bin2hex($bytes), 0, $length);
        }
}