592 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			592 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?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);
 | |
| 	}
 | |
| }
 | 
