Blame | Letzte Änderung | Log anzeigen | RSS feed
<?php namespace Clockwork\Request;use Clockwork\Helpers\Serializer;// Data structure representing a single application requestclass Request{// Unique request IDpublic $id;// Metadata versionpublic $version = 1;// Request type (request, command, queue-job or test)public $type = 'request';// Request timepublic $time;// Request methodpublic $method;// Request URLpublic $url;// Request URIpublic $uri;// Request headerspublic $headers = [];// Textual representation of the executed controllerpublic $controller;// Request GET datapublic $getData = [];// Request POST datapublic $postData = [];// Request body datapublic $requestData = [];// Session data arraypublic $sessionData = [];// Authenticated userpublic $authenticatedUser;// Request cookiespublic $cookies = [];// Response timepublic $responseTime;// Response processing timepublic $responseDuration;// Response status codepublic $responseStatus;// Peak memory usage in bytespublic $memoryUsage;// Executed middlewarepublic $middleware = [];// Database queriespublic $databaseQueries = [];// Database queries countpublic $databaseQueriesCount;// Database slow queries countpublic $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 queriespublic $cacheQueries = [];// Cache query counts of a particular type (reads, hits, writes, deletes)public $cacheReads;public $cacheHits;public $cacheWrites;public $cacheDeletes;// Cache queries execution timepublic $cacheTime;// Model actionspublic $modelsActions = [];// Model action counts by modelpublic $modelsRetrieved = [];public $modelsCreated = [];public $modelsUpdated = [];public $modelsDeleted = [];// Redis commandspublic $redisCommands = [];// Dispatched queue jobspublic $queueJobs = [];// Timeline eventspublic $timelineData = [];// Log messagespublic $log = [];// Fired eventspublic $events = [];// Application routespublic $routes = [];// Sent notificationspublic $notifications = [];// Sent emails (legacy property replaced by notifications)public $emailsData = [];// Rendered viewspublic $viewsData = [];// Custom user datapublic $userData = [];// Subrequestspublic $subrequests = [];// Xebug profiler datapublic $xdebug = [];// Command namepublic $commandName;// Command arguments passed inpublic $commandArguments = [];// Command arguments defaultspublic $commandArgumentsDefaults = [];// Command options passed inpublic $commandOptions = [];// Command options defaultspublic $commandOptionsDefaults = [];// Command exit codepublic $commandExitCode;// Command outputpublic $commandOutput;// Queue job namepublic $jobName;// Queue job descriptionpublic $jobDescription;// Queue job statuspublic $jobStatus;// Queue job payloadpublic $jobPayload = [];// Queue job queue namepublic $jobQueue;// Queue job connection namepublic $jobConnection;// Queue job additional optionspublic $jobOptions = [];// Test namepublic $testName;// Test statuspublic $testStatus;// Test status message (eg. in case of failure)public $testStatusMessage;// Ran test assertspublic $testAsserts = [];// Client-side performance metrics in the form of [ metric => value ]public $clientMetrics = [];// Web vitals in the form of [ vital => value ]public $webVitals = [];// Parent requestpublic $parent;// Token to update this request datapublic $updateToken;// Log instance for the current requestprotected $currentLog;// Timeline instance for the current requestprotected $currentTimeline;// Array of property values to override collected values from data sourcesprotected $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 createdpublic 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 queriespublic function getDatabaseDuration(){return array_reduce($this->databaseQueries, function ($total, $query) {return isset($query['duration']) ? $total + $query['duration'] : $total;}, 0);}// Compute response duration in millisecondspublic function getResponseDuration(){return ($this->responseTime - $this->time) * 1000;}// Get all request data as an arraypublic 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 stringpublic function toJson(){return json_encode($this->toArray(), \JSON_PARTIAL_OUTPUT_ON_ERROR);}// Return request data except specified keys as an arraypublic 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 arraypublic 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 requestpublic function log(){return $this->currentLog;}// Return timeline instance for the current requestpublic function timeline(){return $this->currentTimeline;}// Add a new overridden propertypublic function override($property, $value){$this->overrides[$property] = $value;return $this;}// Get or set all overrides at oncepublic 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), tagspublic 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),// expirationpublic 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, datapublic 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-defaultpublic 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 namepublic 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-defaultpublic 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 datapublic 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 argumentspublic 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 tokenprotected function generateUpdateToken(){$length = 8;$bytes = function_exists('random_bytes') ? random_bytes($length) : openssl_random_pseudo_bytes($length);return substr(bin2hex($bytes), 0, $length);}}