clock-work
This commit is contained in:
		
							
								
								
									
										9
									
								
								vendor/itsgoingd/clockwork/.editorconfig
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								vendor/itsgoingd/clockwork/.editorconfig
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | ||||
| root = true | ||||
|  | ||||
| [*] | ||||
| charset = utf-8 | ||||
| end_of_line = lf | ||||
| indent_style = tab | ||||
|  | ||||
| [*.php] | ||||
| insert_final_newline = true | ||||
							
								
								
									
										1
									
								
								vendor/itsgoingd/clockwork/.gitattributes
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								vendor/itsgoingd/clockwork/.gitattributes
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| .github/ export-ignore | ||||
							
								
								
									
										706
									
								
								vendor/itsgoingd/clockwork/CHANGELOG.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										706
									
								
								vendor/itsgoingd/clockwork/CHANGELOG.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,706 @@ | ||||
| 5.1.12 | ||||
|  | ||||
| - improved Timeline event run method to stop the event in case of an exception (implemented by UlrichEckhardt, thanks!) | ||||
| - fixed some deprecation warnings on PHP 8.2 (implemented by faytekin, thanks!) | ||||
| - fixed some deprecation warnings on PHP 8.1 (implemented by villermen, thanks!) | ||||
|  | ||||
| 5.1.11 | ||||
|  | ||||
| - fixed crash when resolving authenticated user in Laravel without using Eloquent (reported by m-thalmann-athesia, thanks!) | ||||
|  | ||||
| 5.1.10 | ||||
|  | ||||
| - fixed crash when resolving authenticated user in Laravel (reported by LucaRed, thanks!) | ||||
|  | ||||
| 5.1.9 | ||||
|  | ||||
| - added support for Eloquent strict mode (reported by Sergiobop, thanks!) | ||||
|  | ||||
| 5.1.8 | ||||
|  | ||||
| - updated list of built-in Laravel commands to ignore when collecting commands and included Horizon commands | ||||
| - fixed collecting of Laravel queue jobs when used with Horizon | ||||
| - fixed collecting of authanticated user name when the User model includes name() method (implemented by devfrey, thanks!) | ||||
|  | ||||
| 5.1.7 | ||||
|  | ||||
| - added support for authentiaction in the Vanilla integration | ||||
| - added support for compressed Xdebug profiles | ||||
| - improved collecting of Laravel Artisan commands to support abbreviated commands (implemented by mike-peters90, thanks!) | ||||
| - fixed doubled backslashes in collected Laravel database query bindings (reported by pys1992, thanks!) | ||||
| - fixed compatibility with PostgreSQL in SQL storage (implemented by screw, thanks!) | ||||
| - fixed possible crash during file storage cleanup when used with Laravel Octane (reported by flexchar, thanks!) | ||||
| - fixed infinite loop when collecting queries in Doctrine 3.x (reported by N-M, thanks!) | ||||
|  | ||||
| 5.1.6 | ||||
|  | ||||
| - added Monolog 2.x compatible handler (idea by mahagr, thanks!) | ||||
| - improved log to handle all Throwable classes as exceptions (idea by EdmondDantes, thanks!) | ||||
| - fixed support for capturing console output in Laravel 9 (reported by mikerockett, thanks!) | ||||
|  | ||||
| 5.1.5 | ||||
|  | ||||
| - removed support for psr/log | ||||
| - fixed some typos (implemented by fridzema, thanks!) | ||||
|  | ||||
| *BREAKING* | ||||
|  | ||||
| - `Clockwork\Request\Log` no longer implements the PSR log interface, it is unlikely you are using this class directly | ||||
|  | ||||
| 5.1.4 | ||||
|  | ||||
| - added Laravel 9 support | ||||
| - added support for manually registering Clockwork middleware in Laravel | ||||
| - fixed some failing tests might not been collected in Laravel (reported by ajcastro, thanks!) | ||||
| - fixed not respecting the collect tests setting in Laravel (reported by SimBig, thanks!) | ||||
| - fixed some deprecation warnings on PHP 8.1 (implemented by usernotnull, thanks!) | ||||
|  | ||||
| 5.1.3 | ||||
|  | ||||
| - added PSR to the default filtered namespaces from stack traces in the Laravel integration | ||||
| - fixed not being able to log non-string values when using psr/log >=2.0 (reported by Wit3, thanks!) | ||||
| - fixed some deprecation warnings on PHP 8.1 (reported by Pinnokkio, thanks!) | ||||
| - fixed wrong redirect when accessing web ui with an url including a trailing slash (implemented by ssnepenthe, thanks!) | ||||
| - fixed update-token could be leaked via the Clockwork rest api (implemented by ssnepenthe, thanks!) | ||||
|  | ||||
| 5.1.2 | ||||
|  | ||||
| - fixed some deprecation warnings on PHP 8.1 (reported by Codomic, thanks!) | ||||
|  | ||||
| 5.1.1 | ||||
|  | ||||
| - added support for psr/log 2.0 (used in recent Laravel versions) (implemented by byGits, thanks!) | ||||
| - improved timeline api event run method to return the return value of passed closure | ||||
| - improved collecting Laravel database queries to not quote integers (implemented by thisiskj, thanks!) | ||||
| - improved toolbar details link to always be absolute and work with subdirectories (reported by superDuperCyberTechno, thanks!) | ||||
| - fixed some deprecation warnings on PHP 8.1 (implemented by gharlan, thanks!) | ||||
| - fixed collecting Laravel database queries to produce correct queries when bindings contain question marks (reported by woshixiaobai, thanks!) | ||||
| - fixed filtering collected and recorded requests by closure (implemented by ssnepenthe, thanks!) | ||||
| - fixed some inconsistencies in the Clockwork metadata api | ||||
| - fixed some web UI assets being server with wrong mime-types (implemented by ssnepenthe, thanks!) | ||||
| - fixed missing method on storage interface and missing default parameter value in sql storage (implemented by ssnepenthe, thanks!) | ||||
|  | ||||
| *BREAKING* | ||||
|  | ||||
| - timeline api event run method now returns the return value of passed closure instead of the event instance | ||||
|  | ||||
| 5.1 | ||||
|  | ||||
| - added initial support for Laravel Octane | ||||
| - added support for Web UI in the vanilla integration | ||||
| - added support for collecting Laravel cache queries without values (implemented by akalongman, thanks!) | ||||
| - added ability to filter Laravel routes from particular namespaces (idea by hailwood, thanks!) | ||||
| - improved collecting of request URL to include full URL including the query string | ||||
| - improved Clockwork Browser payload to include Web UI path | ||||
| - updated Clockwork App (5.1) | ||||
| - fixed logging falsy values via Clockwork::log (reported by Karmalakas, thanks!) | ||||
| - fixed PHP 8 incompatibility when processing some Laravel notifications (implemented by nanaya, thanks!) | ||||
| - fixed request body being collected even when already parsed into POST data | ||||
| - fixed collecting request URLs with non-standard ports | ||||
|  | ||||
| 5.0.8 | ||||
|  | ||||
| - fixed crash when collecting Laravel mailables built via MailMessage (implemented by cbl, thanks!) | ||||
| - fixed crash when collecting artisan command in Lumen (reported by 2Attack, thanks!) | ||||
| - fixed crash when collecting database queries in Laravel with connection implementation not using PDO (implemented by lenssoft, thanks!) | ||||
| - fixed crash when HTTP request body contains valid json which does not contain array (eg. a number) (reported by Mradxz, thanks!) | ||||
| - fixed collected jobs dispatched from other jobs not having a correct parent job set (implemented by josvar, thanks!) | ||||
|  | ||||
| 5.0.7 | ||||
|  | ||||
| - changed delay listening to events until the app is booted (improves comatibility with some other packages) | ||||
| - changed default settings to enable toolbar (separately installed component) | ||||
| - changed default except requests filter to include debugbar api (implemented by edgardmessias, thanks!) | ||||
| - fixed wrong type-hint for the timeline event run method (reported by hferradj, thanks!) | ||||
| - fixed on-demand mode not working in Laravel (reported by yemenifree, thanks!) | ||||
| - fixed crash when collecting Laravel notifications with recipient names (reported by iainheng, thanks!) | ||||
| - fixed possible crashes and other issues when collecting Laravel notifications (reported by beekmanbv, thanks!) | ||||
| - fixed crash when creating runnable queries in DBAL data source (implemented by N-M, thanks!) | ||||
|  | ||||
| 5.0.6 | ||||
|  | ||||
| - fixed vanilla integration overriding other cookies when used with a PSR-7 response (reported by leemason, thanks!) | ||||
|  | ||||
| 5.0.5 | ||||
|  | ||||
| - added support for toolbar in the vanilla integration (idea by reeslo, thanks!) | ||||
| - added support for client metrics in the vanilla integration | ||||
| - improved PSR-7 support in the vanilla integration | ||||
| - fixed toolbar might not work when not collecting database models | ||||
| - fixed crash collecting Slack and Nexmo notifications (reported by abalozz, thanks!) | ||||
| - fixed timeline api usage not being updated in the Slim integration leading to crash (reported by jiaojie1989, implemented by seanhamlin, thanks!) | ||||
| - fixed api path being interpreted as regex in the vanilla integration (implemented by pqr, thanks!) | ||||
| - fixed Symfony storage not being updated for latest storage api (implemented by auchanhub, thanks!) | ||||
|  | ||||
| 5.0.4 | ||||
|  | ||||
| - fixed Lumen integration crash (implemented by alexbegoon, thanks!) | ||||
| - fixed PHP 5.6 incompatibility (implemented by sanis, thanks!) | ||||
|  | ||||
| 5.0.3 | ||||
|  | ||||
| - fixed PHP 8.0 incompatibility in log (implemented by mtorromeo, thanks!) | ||||
|  | ||||
| 5.0.2 | ||||
|  | ||||
| - fixed data sources not being initialized for extended data requests (reported by tmishutin, thanks!) | ||||
| - fixed inconsistent handling of time and duration arguments in various Request::add* methods (reported by mahagr, thanks!) | ||||
| - updated Clockwork App (5.0.2) | ||||
|  | ||||
| 5.0.1 | ||||
|  | ||||
| - fixed performance issues related to collecting stack traces for Eloquent models actions (reported by mstaack, thanks!) | ||||
| - fixed collecting database and unsupported Laravel notifications (implemented by YannikFirre, thanks!) | ||||
| - fixed log and timeline sorting leading to invalid metadata format | ||||
| - updated Clockwork App (5.0.1) | ||||
|  | ||||
| 5.0 | ||||
|  | ||||
| - added collecting of client-metrics and web-vitals | ||||
| - added collecting of Eloquent models actions and retrieved, created, updated and deleted models counts | ||||
| - added collecting of Laravel notifications | ||||
| - added reworked timeline api | ||||
| - added configurable web ui path (default changed to /clockwork) | ||||
| - added toolbar support | ||||
| - added on-demand mode (with optional secret) | ||||
| - added option to collect error requests only (requests with 4xx and 5xx responses) | ||||
| - added option to specify slow threshold and collect slow requests only | ||||
| - added option to sample collected requests (collect only 1 in x requests) | ||||
| - added option to collect only specified urls | ||||
| - added option to not collect pre-flight requests (enabled by default) | ||||
| - added option to filter collected and recorded requests by closure | ||||
| - added Laravel controller timeline event | ||||
| - added support for updating existing requests | ||||
| - added Slim 4 support | ||||
| - updated to Clockwork App 5.0 | ||||
| - improved reworked the central Clockwork class api | ||||
| - improved requests recording to use a terminate callback | ||||
| - improved global log instance to live on the request instance | ||||
| - improved global timeline instance to live on the request instance | ||||
| - improved Symfony routes registration to register web ui paths only when enabled | ||||
| - improved SQL storage to be more compatible with different PDO error modes | ||||
| - improved Clockwork rest api with only/except filters | ||||
| - improved handling of corrupted index records in file storage | ||||
| - improved cleaned up the code-base, added and improved comments, use modern php features | ||||
| - removed Laravel total, initialization, boot and run timeline events | ||||
| - removed legacy clockwork.controller events | ||||
| - removed duplicate file/line information from collected metadata | ||||
| - fixed authentication route not being registered when web ui is disabled | ||||
| - fixed database queries not being collected for queue jobs | ||||
| - fixed multi-line database queries not being counted properly (implemented by edgardmessias, thanks!) | ||||
| - fixed StackFrame not processing Windows paths correctly | ||||
|  | ||||
| *BREAKING* | ||||
|  | ||||
| - multiple changes to the Laravel config file, please review and re-publish | ||||
| - minimal required PHP version is now 5.6 (previously 5.5) | ||||
| - the timeline api was reworked, please see documentation for details | ||||
| - the global log instance was moved to request instance, please see documentation for details | ||||
| - the central Clockwork class api was reworked, old api is available but deprecated | ||||
| - changed Slim middleware namespaces | ||||
|  | ||||
| 4.1.8 | ||||
|  | ||||
| - fixed handling of index file locking failures in file storage (reported by mahagr, thanks!) | ||||
|  | ||||
| 4.1.7 | ||||
|  | ||||
| - fixed a rare crash in Eloquent duplicate queries detection (reported by mstaack, thanks!) | ||||
| - fixed code-style in the Laravel config (implemented by fgilio, thanks!) | ||||
|  | ||||
| 4.1.6 | ||||
|  | ||||
| - added support for filtering collected requests by method to Laravel integration (options requests filtered by default) (idea by mortenscheel, thanks!) | ||||
| - added support for filtering collected requests by uri and method to vanilla integration | ||||
| - fixed handling of failed file operations on index file in file storage (reported by staabm, thanks!) | ||||
|  | ||||
| 4.1.5 | ||||
|  | ||||
| - fixed crash on initialization in Lumen apps using queue (reported by gramparallelo, thanks!) | ||||
|  | ||||
| 4.1.4 | ||||
|  | ||||
| - added support for a time property to the Request:add* apis, defaults to "current time - duration" | ||||
| - fixed crash when collecting console commands with array arguments or options in the Laravel integration (implemented by mortenscheel, thanks!) | ||||
| - fixed default storage directory being one level too deep in vanilla integration | ||||
|  | ||||
| 4.1.3 | ||||
|  | ||||
| - fixed file storage not unlocking index when cleanup has nothing to clean (implemented by Nacoma, thanks!) | ||||
|  | ||||
| 4.1.2 | ||||
|  | ||||
| - fixed interaction when making HTTP requests in feature tests when collecting tests in Laravel | ||||
| - updated to Clockwork App 4.1.1 | ||||
|  | ||||
| 4.1.1 | ||||
|  | ||||
| - added ext-json to composer.json require section (idea by staabm, thanks!) | ||||
| - fixed Clockwork being initialized too soon in Laravel integration leading to possible crashes (reported by tminich, thanks!) | ||||
|  | ||||
| 4.1 | ||||
|  | ||||
| - added support for command type requests with command specific metadata (commandName, commandArguments, commandArgumentsDefaults, commandOptions, commandOptionsDefaults, commandExitCode, commandOutput) | ||||
| - added support for collecting executed artisan commands in Laravel integration | ||||
| - added support for queue-job type requests with queue-job specific metadata (jobName, jobDescription, jobStatus, jobPayload, jobQueue, jobConnection, jobOptions) | ||||
| - added support for collecting executed queue-jobs in Laravel integration (also supports Laravel Horizon) | ||||
| - added support for test type requests with test specific metadata (testName, testStatus, testStatusMessage, testAsserts) | ||||
| - added support for collecting test runs in Laravel integration using PHPunit | ||||
| - added support for disabling collection of view data when collecting rendered views (new default is to collect views without data) | ||||
| - added Twig data source using the built-in Twig profiler to collect more precise Twig profiling data | ||||
| - added support for setting parent requests on requests | ||||
| - improved collecting of database queries, cache queries, dispatched queue jobs and redis commands to also collect time | ||||
| - improved the data sources filters api to allow multiple filter types | ||||
| - improved collecting of Laravel views to use a separate data source | ||||
| - improved Eloquent data source to have an additional "early" filter applied before the query is added to query counts | ||||
| - improved Eloquent data source now passes raw stack trace as second argument to filters | ||||
| - improved Laravel data source to work when response is not provided | ||||
| - improved Laravel events data source to include Laravel namespace in the default ignored events | ||||
| - improved Laravel views data source to strip view data prefixed with __ | ||||
| - improved PHP data source to not set request time for cli commands | ||||
| - improved serializer to omit data below depth limit, support debugInfo, jsonSerialize and toArray methods (partially implemented by mahagr, thanks!) | ||||
| - improved log to allow overriding serializer settings via context, no longer enabled toString by default | ||||
| - improved Request class now has pre-populated request time on creation | ||||
| - improved StackTrace helper with limit option, last method, fixed filter output keys | ||||
| - improved Lumen queue and redis feature detection | ||||
| - improved vanilla integration to allow manually sending the headers early (implemented by tminich, thanks!) | ||||
| - fixed Symfony support, added support for latest Symfony 5.x and 4.x (reported by llaville, thanks!) | ||||
| - removed dark theme for the web UI setting (now configurable in the Clockwork app itself) | ||||
| - updated to Clockwork App 4.1 | ||||
|  | ||||
| *BREAKING* | ||||
|  | ||||
| - multiple new settings were added to the Laravel config file | ||||
| - DataSourceInterface::reset method was added, default empty implementation is provided in the base DataSource class | ||||
| - LaravelDataSource constructor arguments changed to reflect removing the views collecting support | ||||
|  | ||||
| 4.0.17 | ||||
|  | ||||
| - improved performance and memory usage when doing file storage cleanup (reported by ikkez, thanks!) | ||||
| - fixed crash after running file storage cleanup | ||||
| - fixed typo in clockwork:clean argument description | ||||
|  | ||||
| 4.0.16 | ||||
|  | ||||
| - fixed Laravel middleware being registered too late, causing "collect data always" setting to not work (reported by Youniteus, thanks!) | ||||
|  | ||||
| 4.0.15 | ||||
|  | ||||
| - fixed cleanup not working with file storage (implemented by LucidTaZ, thanks!) | ||||
|  | ||||
| 4.0.14 | ||||
|  | ||||
| - fixed compatibility with Laravel 5.4 and earlier when resolving authenticated user | ||||
|  | ||||
| 4.0.13 | ||||
|  | ||||
| - fixed stack traces processing not handling call_user_func frames properly leading to wrong traces (reported by marcus-at-localhost, thanks!) | ||||
| - fixed wrong stack traces skip namespaces defaults leading to wrong traces | ||||
| - fixed vanilla integration config file missing and no longer used settings | ||||
|  | ||||
| 4.0.12 | ||||
|  | ||||
| - added a simple index file locking to the file storage | ||||
| - improved handling of invalid index data in the file storage (reported by nsbucky and tkaven, thanks!) | ||||
| - fixed Laravel data source crash when running without auth service (implemented by DrBenton, thanks!) | ||||
|  | ||||
| 4.0.11 | ||||
|  | ||||
| - updated web UI (Clockwork App 4.0.6) | ||||
|  | ||||
| 4.0.10 | ||||
|  | ||||
| - fixed wrong file:line for log messages (requires enabled stack traces atm) | ||||
|  | ||||
| 4.0.9 | ||||
|  | ||||
| - fixed duplicate queries detection reporting all relationship queries instead of only duplicates (reported by robclancy, thanks!) | ||||
| - improved the default .gitignore for metadata storage to ignore compressed metadata as well (implemented by clugg, thanks!) | ||||
|  | ||||
| 4.0.8 | ||||
|  | ||||
| - updated web UI (Clockwork App 4.0.5) | ||||
|  | ||||
| 4.0.7 | ||||
|  | ||||
| - updated web UI (Clockwork App 4.0.4) | ||||
|  | ||||
| 4.0.6 | ||||
|  | ||||
| - fixed possible crash in LaravelDataSource when resolving authenticated user in non-standard auth implementations (4.0 regression) (implemented by zarunet, thanks!) | ||||
| - fixed StackTrace::filter calling array_filter with swapped arguments (implemented by villermen, thanks!) | ||||
| - fixed PHP 5.x incompatibility tenaming the Storage\Search empty and notEmpty methods to isEmpty and isNotEmpty (reported by eduardodgarciac, thanks!) | ||||
| - updated web UI (Clockwork App 4.0.3) | ||||
|  | ||||
| 4.0.5 | ||||
|  | ||||
| - fixed multiple issues causing FileStorage cleanup to not delete old metadata or crash (partially implemented by jaumesala, reported by SerafimArts, thanks!) | ||||
| - updated web UI (Clockwork App 4.0.2) | ||||
|  | ||||
| 4.0.4 | ||||
|  | ||||
| - fixed web UI not working (4.0.2 regression) (reported by williamqian and lachlankrautz, thanks!) | ||||
|  | ||||
| 4.0.3 | ||||
|  | ||||
| - fixed crash when using SQL storage (reported by sebastiaanluca, thanks!) | ||||
|  | ||||
| 4.0.2 | ||||
|  | ||||
| - updated web UI (Clockwork App 4.0.1) | ||||
|  | ||||
| 4.0.1 | ||||
|  | ||||
| - fixed Lumen support (reported by Owlnofeathers, thanks!) | ||||
|  | ||||
| 4.0 | ||||
|  | ||||
| - added "features" configuration | ||||
| - added requests search (extended storage api) | ||||
| - added collecting request body data (idea by lkloon123, thanks!) | ||||
| - added collecting of dispatched queue jobs | ||||
| - added collecting Redis commands (idea by tillkruss, thanks!) | ||||
| - added collecting of database query stats separate from queries | ||||
| - added collecting of executed middleware | ||||
| - added ability to specify slow database query threshold | ||||
| - added ability to collect only slow database queries | ||||
| - added ability to disable collecting of database queries keeping database stats | ||||
| - added ability to disable collecting of cache queries keeping cache stats | ||||
| - added duplicate (N+1) database query detection (inspired by beyondcode/laravel-query-detector, thanks!) | ||||
| - added configuration to limit number of collected frames for stack traces (defaults to 10) | ||||
| - added configuration to specify skipped vendors, namespaces and files for stack traces | ||||
| - added index file to file storage | ||||
| - added support for compression in file storage | ||||
| - added new filters api to data sources | ||||
| - improved file and sql storage to support search api | ||||
| - improved symfony storage to work with file storage changes | ||||
| - improved log api to allow passing custom stack traces in context | ||||
| - improved refactored and cleaned up Laravel service provider | ||||
| - improved Lumen integration to share more code with Laravel integration | ||||
| - improved refactored sql storage a bit | ||||
| - improved timeline api, description is now optional and defaults to event name when calling startEvent (idea by robclancy, thanks!) | ||||
| - updated web UI | ||||
| - fixed regexp in vanilla integration Clockwork REST api processing | ||||
| - removed storage filter support (replaced by features configuration) | ||||
| - BREAKING configuration format changes, please re-deploy if using customized Clockwork config | ||||
| - NOTE metadata files from previous versions will need to be manually removed on upgrade | ||||
|  | ||||
| 3.1.4 | ||||
|  | ||||
| - improved DBALDataSource to work with custom types (thanks villermen) | ||||
|  | ||||
| 3.1.3 | ||||
|  | ||||
| - updated LaravelCacheDataSource to support Laravel 5.8 | ||||
|  | ||||
| 3.1.2 | ||||
|  | ||||
| - fixed missing use statement in vanilla integration (thanks micc83) | ||||
|  | ||||
| 3.1.1 | ||||
|  | ||||
| - exposed the Request::setAuthenticatedUser method on the main Clockwork class | ||||
| - fixed possible crash in LaravelDataSource when resolving authenticated user in non-standard auth implementations (thanks freshleafmedia, motia) | ||||
|  | ||||
| 3.1 | ||||
|  | ||||
| - added new integration for vanilla PHP (thanks martbean) | ||||
| - added support for collecting authenticated user info | ||||
| - added bunch of helper methods for adding data like database queries or events to Clockwork | ||||
| - added serializer options to the config files | ||||
| - updated web UI to match latest Chrome version | ||||
| - improved collecting of exceptions | ||||
| - improved filtered uris implementation in Laravel to no longer have any performance overhead (thanks marcusbetts) | ||||
| - improved compatibility with Laravel Telescope | ||||
| - fixed numeric keys being lost on serialization of arrays (thanks ametad) | ||||
| - fixed serialization of parent class private properties | ||||
| - fixed a possible crash when resolving stack traces (thanks mbardelmeijer) | ||||
| - deprecated Clockwork::subrequest method in favor of Clockwork::addSubrequest | ||||
|  | ||||
| 3.0.2 | ||||
|  | ||||
| - fixed infinite redirect if dark web theme is enabled on Laravel or Lumen <5.5 (thanks pixelskribe) | ||||
|  | ||||
| 3.0.1 | ||||
|  | ||||
| - improved LaravelDataSource to not collect views data if it is filtered (by default) | ||||
|  | ||||
| 3.0 | ||||
|  | ||||
| - updated web UI to match latest Chrome version | ||||
| - added new api for user-data (custom tabs in Clockwork app) | ||||
| - added support for authentication (thanks xiaohuilam) | ||||
| - added support for collecting stack traces for log messages, queries, etc. (thanks sisve) | ||||
| - added new api for recording subrequests (thanks L3o-pold) | ||||
| - added Symfony integration beta | ||||
| - added Xdebug profiler support | ||||
| - added collecting of full URLs for requests | ||||
| - added collecting of peak memory usage | ||||
| - added ability to use dark theme for the web UI | ||||
| - added new extend-api to data soruces for extending data when it's being sent to the application | ||||
| - improved data serialization implementation - handles recursion, unlimited depth, type metadata, clear marking for protected and private properties | ||||
| - improved data serialization with configurable defaults, limit and blackboxing of classes | ||||
| - improved handling of binary bindings in EloquentDataSource (thanks sergio91pt and coderNeos) | ||||
| - improved stack traces collection to resolve original view names | ||||
| - BREAKING improved Laravel integration to type-hint contracts instead of concrete implementations (thanks robclancy) | ||||
| - improved default configuration to not collect data for Laravel Horizon requests (thanks fgilio) | ||||
| - improved LaravelDataSource view data collecting to remove Laravel Twigbridge metadata | ||||
| - changed Laravel integration to register middleware in the boot method instead of register (thanks dionysiosarvanitis) | ||||
| - changed Laravel and Lumen integrations to use a single shared Log instance | ||||
| - fixed Clockwork HTTP API returning empty object instead of null if request was not found | ||||
| - fixed Clockwork routes not returning 404 when disabled on runtime with route cache enabled (thanks joskfg) | ||||
| - BREAKING dropped Laravel 4 support | ||||
| - BREAKING dropped PHP 5.4 support, requires PHP 5.5 | ||||
|  | ||||
| 2.2.5 | ||||
|  | ||||
| - changed SQL storage schema URI column type from VARCHAR to TEXT (thanks sumidatx) | ||||
| - fixed possible crash in file storage cleanup if the file was already deleted (thanks bcalik) | ||||
| - fixed event handling in Eloquent data source compatibility with some 3rd party packages (thanks erikgaal) | ||||
|  | ||||
| 2.2.4 | ||||
|  | ||||
| - drop support for collecting Laravel controller middleware (as this can have unexpected side-effects) (thanks phh) | ||||
|  | ||||
| 2.2.3 | ||||
|  | ||||
| - improved Server-Timing now uses the new header format (thanks kohenkatz) | ||||
| - fixed Laravel crash when gathering middleware if the controller class doesn't exist | ||||
|  | ||||
| 2.2.2 | ||||
|  | ||||
| - fixed compatibility with Laravel 5.2 (thanks peppeocchi) | ||||
|  | ||||
| 2.2.1 | ||||
|  | ||||
| - fixed Laravel 4.x support once again (thanks bcalik) | ||||
|  | ||||
| 2.2 | ||||
|  | ||||
| - added support for collecting route middleware (thanks Vercoutere) | ||||
| - added support for collecting routes and middleware in newer Lumen versions | ||||
| - updated Web UI to match Clockwork Chrome 2.2 | ||||
| - improved Laravel support to register most event handlers only when collecting data | ||||
| - fixed Lumen middleware not being registered automatically (thanks lucian-dragomir) | ||||
| - fixed published Lumen config not being loaded | ||||
|  | ||||
| 2.1.1 | ||||
|  | ||||
| - fixed Laravel 4.x support (added legacy version of the config file) (thanks bcalik) | ||||
|  | ||||
| 2.1 | ||||
|  | ||||
| - updated Web UI to match Clockwork Chrome 2.1 | ||||
| - improved Laravel support to load the default config and use env variables in the default config | ||||
| - improved Lumen support to use the standard config subsystem instead of directly accessing env variables (thanks davoaust, SunMar) | ||||
| - improved reliability of storing metadata in some cases (by using JSON_PARTIAL_OUTPUT_ON_ERROR when supported) | ||||
| - fixed wrong mime-type for javascript assets in Web UI causing it to not work in some browsers (thanks sleavitt) | ||||
| - fixed path checking in Web UI causing it to not work on Windows (thanks Malezha) | ||||
| - fixed parameters conversion in DBALDataSource (thanks andrzejenne) | ||||
|  | ||||
| 2.0.4 | ||||
|  | ||||
| - improved mkdir error handling in FileStorage (thanks FBnil) | ||||
| - fixed crash in LaravelEventsDataSource when firing events with associative array as payload | ||||
|  | ||||
| 2.0.3 | ||||
|  | ||||
| - fixed Clockwork now working when used with Laravel route cache | ||||
|  | ||||
| 2.0.2 | ||||
|  | ||||
| - fixed crash on attempt to clean up file storage if the project contains Clockwork 1.x metadata | ||||
|  | ||||
| 2.0.1 | ||||
|  | ||||
| - fixed Web UI not working in Firefox | ||||
|  | ||||
| 2.0 | ||||
|  | ||||
| - added Web UI | ||||
| - added new Laravel cache data source | ||||
| - added new Laravel events data source | ||||
| - added new more robust metadata storage API | ||||
| - added automatic metadata cleanup (defaults to 1 week) | ||||
| - added better metadata serialization including class names for objects | ||||
| - added PostgreSQL compatibility for the SQL storage (thanks oldskool73) | ||||
| - added Slim 3 middleware (thanks sperrichon) | ||||
| - added PSR message data source (thanks sperrichon) | ||||
| - added Doctrine DBAL data source (thanks sperrichon) | ||||
| - changed Clockwork request ids now use dashes instead of dots (thanks Tibbelit) | ||||
| - changed Laravel and Lumen integrations to no longer log data for console commands | ||||
| - changed simplified the clock Laravel helper (thanks Jergus Lejko) | ||||
| - fixed wrong version data logged in SQL storage | ||||
| - removed PHP 5.3 support, code style changes | ||||
| - removed CodeIgniter support | ||||
| - removed ability to register additional data sources via Clockwork config | ||||
|  | ||||
| UPGRADING | ||||
|  | ||||
| - update the required Clockwork version to ^2.0 in your composer.json | ||||
| - PHP 5.3 - no longer supported, you can continue using the latest 1.x version | ||||
| - CodeIgniter - no longer supported, you can continue using the latest 1.x version | ||||
| - Slim 2 - update the imported namespace from Clockwork\Support\Slim to Clockwork\Support\Slim\Legacy | ||||
| - ability to register additional data sources via Clockwork config was removed, please call app('clockwork')->addDataSource(...) in your own service provider | ||||
|  | ||||
| 1.14.5 | ||||
|  | ||||
| - fixed incompatibility with Laravel 4.1 an 4.2 (introduced in 1.14.3) | ||||
|  | ||||
| 1.14.4 | ||||
|  | ||||
| - added support for Lumen 5.5 (thanks nebez) | ||||
|  | ||||
| 1.14.3 | ||||
|  | ||||
| - added support for Laravel 5.5 package auto-discovery (thanks Omranic) | ||||
| - added automatic registration of the Laravel middleware (no need to edit your Http/Kernel.php anymore, existing installations don't need to be changed) | ||||
| - updated Laravel artisan clockwork:clean command for Laravel 5.5 (thanks rosswilson252) | ||||
| - fixed crash when retrieving all requests from Sql storage (thanks pies) | ||||
|  | ||||
| 1.14.2 | ||||
|  | ||||
| - fixed missing imports in Doctrine data source (thanks jenssegers) | ||||
|  | ||||
| 1.14.1 | ||||
| - fixed collecting Eloquent queries when using PDO_ODBC driver for real (thanks abhimanyu003) | ||||
|  | ||||
| 1.14 | ||||
| - added support for Server-Timing headers (thanks Garbee) | ||||
| - fixed compatibility with Lumen 5.4 (thanks Dimasdanz) | ||||
| - fixed collecting Eloquent queries with bindings containing backslashes (thanks fitztrev) | ||||
| - fixed collecting Eloquent queries when using PDO_ODBC driver (thanks abhimanyu003) | ||||
| - fixed collecting Doctrine queries with array bindings (thanks RolfJanssen) | ||||
| - replaced Doctrine bindings preparation code with more complete version from laravel-doctrine | ||||
| - fixed PHP 5.3 compatibility | ||||
|  | ||||
| 1.13.1 | ||||
| - fixed compatibility with Lumen 5.4 (thanks meanevo) | ||||
|  | ||||
| 1.13 | ||||
| - added support for Laravel 5.4 (thanks KKSzymanowski) | ||||
| - improved Laravel "clock" helper function now takes multiple arguments to be logged at once (eg. `clock($foo, $bar, $baz)`) | ||||
|  | ||||
| 1.12 | ||||
| - added collecting of caller file name and line number for queries and model name (Laravel 4.2+) for ORM queries to the Eloquent data source (thanks OmarMakled and fitztrev for the idea) | ||||
| - added collecting of context, caller file name and line number to the logger (thanks crissi for the idea) | ||||
| - fixed crash in Lumen data source when running unit tests with simulated requests on Lumen | ||||
| - fixed compatibility with Laravel 4.0 | ||||
|  | ||||
| 1.11.2 | ||||
| - switched to PSR-4 autoloading | ||||
| - fixed Swift data source crash when sending email with no from/to address specified (thanks marksecurelogin) | ||||
|  | ||||
| 1.11.1 | ||||
| - added support for DateTimeImmutable in Doctrine data source (thanks morfin) | ||||
| - fixed not being able to log null values via the "clock" helper function | ||||
| - fixed Laravel 4.2-dev not being properly detected as 4.2 release (thanks DemianD) | ||||
|  | ||||
| 1.11 | ||||
| - added support for Lumen 5.2 (thanks lukeed) | ||||
| - added "clock" helper function | ||||
| - fixed data sources being initialized too late (thanks morfin) | ||||
| - fixed code style in Doctrine data source | ||||
| - removed Laravel log dependency from Doctrine data source | ||||
| - NOTE laravel-doctrine provides ootb support for Clockwork, you should use this instead of included Doctrine data source with Laravel | ||||
|  | ||||
| 1.10.1 | ||||
| - fixed collecting of database queries in Laravel 5.2 (thanks sebastiandedeyne) | ||||
|  | ||||
| 1.10 | ||||
| - added Laravel 5.2 support (thanks jonphipps) | ||||
| - improved file storage to allow configuring directory permissions (thanks patrick-radius) | ||||
| - fixed interaction with PHPUnit in Lumen (thanks troyharvey) | ||||
| - removed "router dispatch" timeline event for now (due to Laravel 5.2 changes) | ||||
|  | ||||
| 1.9 | ||||
| - added Lumen support (thanks dawiyo) | ||||
| - added aliases for all Clockwork parts so they can be resolved by the IoC container in Laravel and Lumen | ||||
| - fixed Laravel framework initialisation, booting and running timeline events not being recorded properly (thanks HipsterJazzbo, sisve) | ||||
| - fixed how Laravel clockwork:clean artisan command is registered (thanks freekmurze) | ||||
| - removed Lumen framework initialisation, booting and running timeline events as they are not supported by Lumen | ||||
|  | ||||
| 1.8.1 | ||||
| - fixed SQL data storage initialization if PDO is set to throw exception on error (thanks YOzaz) | ||||
|  | ||||
| 1.8 | ||||
| - added SQL data storage implementation | ||||
| - added new config options for data storage for Laravel (please re-publish the config file) | ||||
| - fixed not being able to use the Larvel route caching when using Clockwork (thanks Garbee, kylestev, cbakker86) | ||||
|  | ||||
| 1.7 | ||||
| - added support for Laravel 5 (thanks Garbee, slovenianGooner) | ||||
| - improved support for Laravel 4.1 and 4.2, Clockwork data is now available for error responses | ||||
| - added Doctrine data source (thanks matiux) | ||||
| - fixed compatibility with some old PHP 5.3 versions (thanks hailwood) | ||||
| - updated Laravel data source to capture the context for log messages (thanks hermanzhu) | ||||
|  | ||||
| 1.6 | ||||
| - improved Eloquent data source to support multiple databases (thanks ingro) | ||||
| - improved compatibility with Laravel apps not using database | ||||
| - improved compatibility with various CodeIngiter installations | ||||
| - fixed a bug where log messages and timeline data might not be sorted correctly | ||||
| - fixed missing static keyword in CodeIgniter hook (thanks noevidenz) | ||||
| - changed Timeline::endEvent behavior to return false instead of throwing exception when called for non-existing event | ||||
|  | ||||
| 1.5 | ||||
| - improved Slim support to use DI container to share Clockwork instance instead of config | ||||
| - improved Slim support now adds all messages logged via Slim's log interface to Clockwork log as well | ||||
| - improved CodeIgniter support to make Clockwork available through the CI app (tnx BradEstey) | ||||
| - fixed Laravel support breaking flash messages (tnx hannesvdvreken) | ||||
| - fixed CodeIgniter support PSR-0 autoloading and other improvements (tnx pwhelan) | ||||
| - fixed file storage warning when recursive data is collected | ||||
|  | ||||
| 1.4.4 | ||||
| - changed Laravel support to disable permanent data collection by default (tnx jenssegers) | ||||
| - improved Laravel support to return Clockwork data with proper Content-Type (tnx maximebeaudoin) | ||||
| - fixed CodeIgniter support compatibility with PHP 5.3 (tnx BradEstey) | ||||
|  | ||||
| 1.4.3 | ||||
| - fixed incorrect requests ids being generated depending on set locale | ||||
|  | ||||
| 1.4.2 | ||||
| - fixed Laravel support compatibility with PHP 5.3 | ||||
|  | ||||
| 1.4.1 | ||||
| - fixed Laravel support compatibility with PHP 5.3 | ||||
|  | ||||
| 1.4 | ||||
| - added support for collecting emails and views data | ||||
| - added support for CodeIgniter 2.1 (tnx pwhelan) | ||||
| - added data source and plugin for collecting emails data from Swift mailer | ||||
| - added support for collecting emails and views data from Laravel | ||||
| - added --age argument to Laravel artisan clockwork::clean command, specifies how old the request data must be to be deleted (in hours) | ||||
| - improved Laravel service provider | ||||
| - fixed compatibility with latest Laravel 4.1 | ||||
|  | ||||
| 1.3 | ||||
| NOTE: Clockwork\Request\Log::log method arguments have been changed from log($message, $level) to log($level, $message), levels are now specified via Psr\Log\LogLevel class, it's recommended to use shortcut methods for various levels (emergency, alert, critical,  error, warning, notice, info and debug($message)) | ||||
| - clockwork log class now implements PSR logger interface, updated Laravel and Monolog support to use all available log levels | ||||
| - clockwork log now accepts objects and arrays as input and logs their json representation | ||||
| - added support for specifying additional headers on metadata requests (Laravel) (tnx philsturgeon) | ||||
|  | ||||
| 1.2 | ||||
| - added support for Laravel 4.1 | ||||
| - added facade for Laravel | ||||
| - added ability to disable collecting data about requests to specified URIs in Laravel | ||||
| - added clockwork:clean artisan command for cleaning request metadata for Laravel | ||||
| - added an easy way to add timeline events and log records via main Clockwork class | ||||
| - added support for Slim apps running in subdirs (requires Clockwork Chrome 1.1+) | ||||
| - file storage now creates default gitignore file for the request data when creating the storage dir | ||||
| - fixed a few bugs which might cause request data to not appear in Chrome extension | ||||
| - fixed a few bugs that could lead to PHP errors/exceptions | ||||
|  | ||||
| 1.1 | ||||
| - added support for Laravel 4 apps running in subdirs (requires Clockwork Chrome 1.1+) | ||||
| - added data-protocol version to the request data | ||||
| - updated Laravel 4 service provider to work with Clockwork Web | ||||
| - fixed a bug where Clockwork would break Laravel 4 apps not using database | ||||
| - fixed a bug where calling Timeline::endEvent after Timeline::finalize caused exception to be thrown | ||||
| - fixed a bug where using certain filters would store incorrect data | ||||
|  | ||||
| 0.9.1 | ||||
| - added support for application routes (ootb support for Laravel 4 only atm) | ||||
| - added configuration file for Laravel 4 | ||||
| - added support for filtering stored data in Storage | ||||
| - added library version constant Clockwork::VERSION | ||||
							
								
								
									
										13
									
								
								vendor/itsgoingd/clockwork/Clockwork/Authentication/AuthenticatorInterface.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								vendor/itsgoingd/clockwork/Clockwork/Authentication/AuthenticatorInterface.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| <?php namespace Clockwork\Authentication; | ||||
|  | ||||
| interface AuthenticatorInterface | ||||
| { | ||||
| 	const REQUIRES_USERNAME = 'username'; | ||||
| 	const REQUIRES_PASSWORD = 'password'; | ||||
|  | ||||
| 	public function attempt(array $credentials); | ||||
|  | ||||
| 	public function check($token); | ||||
|  | ||||
| 	public function requires(); | ||||
| } | ||||
							
								
								
									
										19
									
								
								vendor/itsgoingd/clockwork/Clockwork/Authentication/NullAuthenticator.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								vendor/itsgoingd/clockwork/Clockwork/Authentication/NullAuthenticator.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | ||||
| <?php namespace Clockwork\Authentication; | ||||
|  | ||||
| class NullAuthenticator implements AuthenticatorInterface | ||||
| { | ||||
| 	public function attempt(array $credentials) | ||||
| 	{ | ||||
| 		return true; | ||||
| 	} | ||||
|  | ||||
| 	public function check($token) | ||||
| 	{ | ||||
| 		return true; | ||||
| 	} | ||||
|  | ||||
| 	public function requires() | ||||
| 	{ | ||||
| 		return []; | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										34
									
								
								vendor/itsgoingd/clockwork/Clockwork/Authentication/SimpleAuthenticator.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								vendor/itsgoingd/clockwork/Clockwork/Authentication/SimpleAuthenticator.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | ||||
| <?php namespace Clockwork\Authentication; | ||||
|  | ||||
| class SimpleAuthenticator implements AuthenticatorInterface | ||||
| { | ||||
| 	protected $password; | ||||
|  | ||||
| 	public function __construct($password) | ||||
| 	{ | ||||
| 		$this->password = $password; | ||||
| 	} | ||||
|  | ||||
| 	public function attempt(array $credentials) | ||||
| 	{ | ||||
| 		if (! isset($credentials['password'])) { | ||||
| 			return false; | ||||
| 		} | ||||
|  | ||||
| 		if (! hash_equals($credentials['password'], $this->password)) { | ||||
| 			return false; | ||||
| 		} | ||||
|  | ||||
| 		return password_hash($this->password, \PASSWORD_DEFAULT); | ||||
| 	} | ||||
|  | ||||
| 	public function check($token) | ||||
| 	{ | ||||
| 		return password_verify($this->password, $token); | ||||
| 	} | ||||
|  | ||||
| 	public function requires() | ||||
| 	{ | ||||
| 		return [ AuthenticatorInterface::REQUIRES_PASSWORD ]; | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										283
									
								
								vendor/itsgoingd/clockwork/Clockwork/Clockwork.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										283
									
								
								vendor/itsgoingd/clockwork/Clockwork/Clockwork.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,283 @@ | ||||
| <?php namespace Clockwork; | ||||
|  | ||||
| use Clockwork\Authentication\AuthenticatorInterface; | ||||
| use Clockwork\Authentication\NullAuthenticator; | ||||
| use Clockwork\DataSource\DataSourceInterface; | ||||
| use Clockwork\Helpers\Serializer; | ||||
| use Clockwork\Request\Log; | ||||
| use Clockwork\Request\Request; | ||||
| use Clockwork\Request\RequestType; | ||||
| use Clockwork\Request\ShouldCollect; | ||||
| use Clockwork\Request\ShouldRecord; | ||||
| use Clockwork\Storage\StorageInterface; | ||||
| use Closure; | ||||
|  | ||||
| // A central class implementing the core flow of the library | ||||
| class Clockwork | ||||
| { | ||||
| 	// Clockwork library version | ||||
| 	const VERSION = '5.1.12'; | ||||
|  | ||||
| 	// Array of data sources, these objects collect metadata for the current application run | ||||
| 	protected $dataSources = []; | ||||
|  | ||||
| 	// Request object, data structure which stores metadata about the current application run | ||||
| 	protected $request; | ||||
|  | ||||
| 	// Storage object, provides implementation for storing and retrieving request objects | ||||
| 	protected $storage; | ||||
|  | ||||
| 	// Authenticator implementation, authenticates requests for clockwork metadata | ||||
| 	protected $authenticator; | ||||
|  | ||||
| 	// An object specifying the rules for collecting requests | ||||
| 	protected $shouldCollect; | ||||
|  | ||||
| 	// An object specifying the rules for recording requests | ||||
| 	protected $shouldRecord; | ||||
|  | ||||
| 	// Create a new Clockwork instance with default request object, a storage implementation has to be additionally set | ||||
| 	public function __construct() | ||||
| 	{ | ||||
| 		$this->request = new Request; | ||||
| 		$this->authenticator = new NullAuthenticator; | ||||
|  | ||||
| 		$this->shouldCollect = new ShouldCollect; | ||||
| 		$this->shouldRecord = new ShouldRecord; | ||||
| 	} | ||||
|  | ||||
| 	// Add a new data source | ||||
| 	public function addDataSource(DataSourceInterface $dataSource) | ||||
| 	{ | ||||
| 		$this->dataSources[] = $dataSource; | ||||
| 		return $this; | ||||
| 	} | ||||
|  | ||||
| 	// Resolve the current request, sending it through all data sources, finalizing log and timeline | ||||
| 	public function resolveRequest() | ||||
| 	{ | ||||
| 		foreach ($this->dataSources as $dataSource) { | ||||
| 			$dataSource->resolve($this->request); | ||||
| 		} | ||||
|  | ||||
| 		$this->request->log()->sort(); | ||||
| 		$this->request->timeline()->finalize($this->request->time); | ||||
|  | ||||
| 		return $this; | ||||
| 	} | ||||
|  | ||||
| 	// Resolve the current request as a "command" type request with command-specific data | ||||
| 	public function resolveAsCommand($name, $exitCode = null, $arguments = [], $options = [], $argumentsDefaults = [], $optionsDefaults = [], $output = null) | ||||
| 	{ | ||||
| 		$this->resolveRequest(); | ||||
|  | ||||
| 		$this->request->type = RequestType::COMMAND; | ||||
| 		$this->request->commandName = $name; | ||||
| 		$this->request->commandArguments = $arguments; | ||||
| 		$this->request->commandArgumentsDefaults = $argumentsDefaults; | ||||
| 		$this->request->commandOptions = $options; | ||||
| 		$this->request->commandOptionsDefaults = $optionsDefaults; | ||||
| 		$this->request->commandExitCode = $exitCode; | ||||
| 		$this->request->commandOutput = $output; | ||||
|  | ||||
| 		return $this; | ||||
| 	} | ||||
|  | ||||
| 	// Resolve the current request as a "queue-job" type request with queue-job-specific data | ||||
| 	public function resolveAsQueueJob($name, $description = null, $status = 'processed', $payload = [], $queue = null, $connection = null, $options = []) | ||||
| 	{ | ||||
| 		$this->resolveRequest(); | ||||
|  | ||||
| 		$this->request->type = RequestType::QUEUE_JOB; | ||||
| 		$this->request->jobName = $name; | ||||
| 		$this->request->jobDescription = $description; | ||||
| 		$this->request->jobStatus = $status; | ||||
| 		$this->request->jobPayload = (new Serializer)->normalize($payload); | ||||
| 		$this->request->jobQueue = $queue; | ||||
| 		$this->request->jobConnection = $connection; | ||||
| 		$this->request->jobOptions = (new Serializer)->normalizeEach($options); | ||||
|  | ||||
| 		return $this; | ||||
| 	} | ||||
|  | ||||
| 	// Resolve the current request as a "test" type request with test-specific data, accepts test name, status, status | ||||
| 	// message in case of failure and array of ran asserts | ||||
| 	public function resolveAsTest($name, $status = 'passed', $statusMessage = null, $asserts = []) | ||||
| 	{ | ||||
| 		$this->resolveRequest(); | ||||
|  | ||||
| 		$this->request->type = RequestType::TEST; | ||||
| 		$this->request->testName = $name; | ||||
| 		$this->request->testStatus = $status; | ||||
| 		$this->request->testStatusMessage = $statusMessage; | ||||
|  | ||||
| 		foreach ($asserts as $assert) { | ||||
| 			$this->request->addTestAssert($assert['name'], $assert['arguments'], $assert['passed'], $assert['trace']); | ||||
| 		} | ||||
|  | ||||
| 		return $this; | ||||
| 	} | ||||
|  | ||||
| 	// Extends the request with an additional data form all data sources, which is not required for normal use | ||||
| 	public function extendRequest(Request $request = null) | ||||
| 	{ | ||||
| 		foreach ($this->dataSources as $dataSource) { | ||||
| 			$dataSource->extend($request ?: $this->request); | ||||
| 		} | ||||
|  | ||||
| 		return $this; | ||||
| 	} | ||||
|  | ||||
| 	// Store the current request via configured storage implementation | ||||
| 	public function storeRequest() | ||||
| 	{ | ||||
| 		return $this->storage->store($this->request); | ||||
| 	} | ||||
|  | ||||
| 	// Reset all data sources to an empty state, clearing any collected data | ||||
| 	public function reset() | ||||
| 	{ | ||||
| 		foreach ($this->dataSources as $dataSource) { | ||||
| 			$dataSource->reset(); | ||||
| 		} | ||||
|  | ||||
| 		return $this; | ||||
| 	} | ||||
|  | ||||
| 	// Get or set the current request instance | ||||
| 	public function request(Request $request = null) | ||||
| 	{ | ||||
| 		if (! $request) return $this->request; | ||||
|  | ||||
| 		$this->request = $request; | ||||
| 		return $this; | ||||
| 	} | ||||
|  | ||||
| 	// Get the log instance for the current request or log a new message | ||||
| 	public function log($level = null, $message = null, array $context = []) | ||||
| 	{ | ||||
| 		if ($level) { | ||||
| 			return $this->request->log()->log($level, $message, $context); | ||||
| 		} | ||||
|  | ||||
| 		return $this->request->log(); | ||||
| 	} | ||||
|  | ||||
| 	// Get the timeline instance for the current request | ||||
| 	public function timeline() | ||||
| 	{ | ||||
| 		return $this->request->timeline(); | ||||
| 	} | ||||
|  | ||||
| 	// Shortcut to create a new event on the current timeline instance | ||||
| 	public function event($description, $data = []) | ||||
| 	{ | ||||
| 		return $this->request->timeline()->event($description, $data); | ||||
| 	} | ||||
|  | ||||
| 	// Configure which requests should be collected, can be called with arrey of options, a custom closure or with no | ||||
| 	// arguments for a fluent configuration api | ||||
| 	public function shouldCollect($shouldCollect = null) | ||||
| 	{ | ||||
| 		if ($shouldCollect instanceof Closure) return $this->shouldCollect->callback($shouldCollect); | ||||
|  | ||||
| 		if (is_array($shouldCollect)) return $this->shouldCollect->merge($shouldCollect); | ||||
|  | ||||
| 		return $this->shouldCollect; | ||||
| 	} | ||||
|  | ||||
| 	// Configure which requests should be recorded, can be called with arrey of options, a custom closure or with no | ||||
| 	// arguments for a fluent configuration api | ||||
| 	public function shouldRecord($shouldRecord = null) | ||||
| 	{ | ||||
| 		if ($shouldRecord instanceof Closure) return $this->shouldRecord->callback($shouldRecord); | ||||
|  | ||||
| 		if (is_array($shouldRecord)) return $this->shouldRecord->merge($shouldRecord); | ||||
|  | ||||
| 		return $this->shouldRecord; | ||||
| 	} | ||||
|  | ||||
| 	// Get or set all data sources at once | ||||
| 	public function dataSources($dataSources = null) | ||||
| 	{ | ||||
| 		if (! $dataSources) return $this->dataSources; | ||||
|  | ||||
| 		$this->dataSources = $dataSources; | ||||
| 		return $this; | ||||
| 	} | ||||
|  | ||||
| 	// Get or set a storage implementation | ||||
| 	public function storage(StorageInterface $storage = null) | ||||
| 	{ | ||||
| 		if (! $storage) return $this->storage; | ||||
|  | ||||
| 		$this->storage = $storage; | ||||
| 		return $this; | ||||
| 	} | ||||
|  | ||||
| 	// Get or set an authenticator implementation | ||||
| 	public function authenticator(AuthenticatorInterface $authenticator = null) | ||||
| 	{ | ||||
| 		if (! $authenticator) return $this->authenticator; | ||||
|  | ||||
| 		$this->authenticator = $authenticator; | ||||
| 		return $this; | ||||
| 	} | ||||
|  | ||||
| 	// Forward any other method calls to the current request and log instances | ||||
| 	public function __call($method, $args) | ||||
| 	{ | ||||
| 		if (in_array($method, [ 'emergency', 'alert', 'critical', 'error', 'warning', 'notice', 'info', 'debug' ])) { | ||||
| 			return $this->request->log()->$method(...$args); | ||||
| 		} | ||||
|  | ||||
| 		return $this->request->$method(...$args); | ||||
| 	} | ||||
|  | ||||
| 	// DEPRECATED The following apis are deprecated and will be removed in a future version | ||||
|  | ||||
| 	// Get all added data sources | ||||
| 	public function getDataSources() | ||||
| 	{ | ||||
| 		return $this->dataSources; | ||||
| 	} | ||||
|  | ||||
| 	// Get the current request instance | ||||
| 	public function getRequest() | ||||
| 	{ | ||||
| 		return $this->request; | ||||
| 	} | ||||
|  | ||||
| 	// Set the current request instance | ||||
| 	public function setRequest(Request $request) | ||||
| 	{ | ||||
| 		$this->request = $request; | ||||
| 		return $this; | ||||
| 	} | ||||
|  | ||||
| 	// Get a storage implementation | ||||
| 	public function getStorage() | ||||
| 	{ | ||||
| 		return $this->storage; | ||||
| 	} | ||||
|  | ||||
| 	// Set a storage implementation | ||||
| 	public function setStorage(StorageInterface $storage) | ||||
| 	{ | ||||
| 		$this->storage = $storage; | ||||
| 		return $this; | ||||
| 	} | ||||
|  | ||||
| 	// Get an authenticator implementation | ||||
| 	public function getAuthenticator() | ||||
| 	{ | ||||
| 		return $this->authenticator; | ||||
| 	} | ||||
|  | ||||
| 	// Set an authenticator implementation | ||||
| 	public function setAuthenticator(AuthenticatorInterface $authenticator) | ||||
| 	{ | ||||
| 		$this->authenticator = $authenticator; | ||||
| 		return $this; | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										64
									
								
								vendor/itsgoingd/clockwork/Clockwork/DataSource/Concerns/EloquentDetectDuplicateQueries.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								vendor/itsgoingd/clockwork/Clockwork/DataSource/Concerns/EloquentDetectDuplicateQueries.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,64 @@ | ||||
| <?php namespace Clockwork\DataSource\Concerns; | ||||
|  | ||||
| use Clockwork\Helpers\StackFilter; | ||||
| use Clockwork\Helpers\StackTrace; | ||||
| use Clockwork\Request\Log; | ||||
| use Clockwork\Request\Request; | ||||
|  | ||||
| // Duplicate (N+1) queries detection for EloquentDataSource, inspired by the beyondcode/laravel-query-detector package | ||||
| // by Marcel Pociot (https://github.com/beyondcode/laravel-query-detector) | ||||
| trait EloquentDetectDuplicateQueries | ||||
| { | ||||
| 	protected $duplicateQueries = []; | ||||
|  | ||||
| 	protected function appendDuplicateQueriesWarnings(Request $request) | ||||
| 	{ | ||||
| 		$log = new Log; | ||||
|  | ||||
| 		foreach ($this->duplicateQueries as $query) { | ||||
| 			if ($query['count'] <= 1) continue; | ||||
|  | ||||
| 			$log->warning( | ||||
| 				"N+1 queries: {$query['model']}::{$query['relation']} loaded {$query['count']} times.", | ||||
| 				[ 'performance' => true, 'trace' => $query['trace'] ] | ||||
| 			); | ||||
| 		} | ||||
|  | ||||
| 		$request->log()->merge($log); | ||||
| 	} | ||||
|  | ||||
| 	protected function detectDuplicateQuery(StackTrace $trace) | ||||
| 	{ | ||||
| 		$relationFrame = $trace->first(function ($frame) { | ||||
| 			return $frame->function == 'getRelationValue' | ||||
| 				|| $frame->class == \Illuminate\Database\Eloquent\Relations\Relation::class; | ||||
| 		}); | ||||
|  | ||||
| 		if (! $relationFrame || ! $relationFrame->object) return; | ||||
|  | ||||
| 		if ($relationFrame->class == \Illuminate\Database\Eloquent\Relations\Relation::class) { | ||||
| 			$model = get_class($relationFrame->object->getParent()); | ||||
| 			$relation = get_class($relationFrame->object->getRelated()); | ||||
| 		} else { | ||||
| 			$model = get_class($relationFrame->object); | ||||
| 			$relation = $relationFrame->args[0]; | ||||
| 		} | ||||
|  | ||||
| 		$shortTrace = $trace->skip(StackFilter::make() | ||||
| 			->isNotVendor([ 'itsgoingd', 'laravel', 'illuminate' ]) | ||||
| 			->isNotNamespace([ 'Clockwork', 'Illuminate' ])); | ||||
|  | ||||
| 		$hash = implode('-', [ $model, $relation, $shortTrace->first()->file, $shortTrace->first()->line ]); | ||||
|  | ||||
| 		if (! isset($this->duplicateQueries[$hash])) { | ||||
| 			$this->duplicateQueries[$hash] = [ | ||||
| 				'count'    => 0, | ||||
| 				'model'    => $model, | ||||
| 				'relation' => $relation, | ||||
| 				'trace'    => $trace | ||||
| 			]; | ||||
| 		} | ||||
|  | ||||
| 		$this->duplicateQueries[$hash]['count']++; | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										191
									
								
								vendor/itsgoingd/clockwork/Clockwork/DataSource/DBALDataSource.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										191
									
								
								vendor/itsgoingd/clockwork/Clockwork/DataSource/DBALDataSource.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,191 @@ | ||||
| <?php namespace Clockwork\DataSource; | ||||
|  | ||||
| use Clockwork\Request\Request; | ||||
|  | ||||
| use Doctrine\DBAL\Connection; | ||||
| use Doctrine\DBAL\Logging\LoggerChain; | ||||
| use Doctrine\DBAL\Logging\SQLLogger; | ||||
| use Doctrine\DBAL\Platforms\AbstractPlatform; | ||||
| use Doctrine\DBAL\Types\Type; | ||||
|  | ||||
| // Data source for DBAL, provides database queries | ||||
| class DBALDataSource extends DataSource implements SQLLogger | ||||
| { | ||||
| 	// Array of collected queries | ||||
| 	protected $queries = []; | ||||
|  | ||||
| 	// Current running query | ||||
| 	protected $query = null; | ||||
|  | ||||
| 	// DBAL connection | ||||
| 	protected $connection; | ||||
|  | ||||
| 	// DBAL connection name | ||||
| 	protected $connectionName; | ||||
|  | ||||
| 	// Create a new data source instance, takes a DBAL connection instance as an argument | ||||
| 	public function __construct(Connection $connection) | ||||
| 	{ | ||||
| 		$this->connection = $connection; | ||||
| 		$this->connectionName = $this->connection->getDatabase(); | ||||
|  | ||||
| 		$configuration = $this->connection->getConfiguration(); | ||||
| 		$currentLogger = $configuration->getSQLLogger(); | ||||
|  | ||||
| 		if ($currentLogger === null) { | ||||
| 			$configuration->setSQLLogger($this); | ||||
| 		} else { | ||||
| 			$loggerChain = new LoggerChain; | ||||
| 			$loggerChain->addLogger($currentLogger); | ||||
| 			$loggerChain->addLogger($this); | ||||
|  | ||||
| 			$configuration->setSQLLogger($loggerChain); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Adds executed database queries to the request | ||||
| 	public function resolve(Request $request) | ||||
| 	{ | ||||
| 		$request->databaseQueries = array_merge($request->databaseQueries, $this->queries); | ||||
|  | ||||
| 		return $request; | ||||
| 	} | ||||
|  | ||||
| 	// Reset the data source to an empty state, clearing any collected data | ||||
| 	public function reset() | ||||
| 	{ | ||||
| 		$this->queries = []; | ||||
| 		$this->query = null; | ||||
| 	} | ||||
|  | ||||
| 	// DBAL SQLLogger event | ||||
| 	public function startQuery($sql, array $params = null, array $types = null) | ||||
| 	{ | ||||
| 		$this->query = [ | ||||
| 			'query'  => $sql, | ||||
| 			'params' => $params, | ||||
| 			'types'  => $types, | ||||
| 			'time'   => microtime(true) | ||||
| 		]; | ||||
| 	} | ||||
|  | ||||
| 	// DBAL SQLLogger event | ||||
| 	public function stopQuery() | ||||
| 	{ | ||||
| 		$this->registerQuery($this->query); | ||||
| 		$this->query = null; | ||||
| 	} | ||||
|  | ||||
| 	// Collect an executed database query | ||||
| 	protected function registerQuery($query) | ||||
| 	{ | ||||
| 		$query = [ | ||||
| 			'query'      => $this->createRunnableQuery($query['query'], $query['params'], $query['types']), | ||||
| 			'bindings'   => $query['params'], | ||||
| 			'duration'   => (microtime(true) - $query['time']) * 1000, | ||||
| 			'connection' => $this->connectionName, | ||||
| 			'time'       => $query['time'] | ||||
| 		]; | ||||
|  | ||||
| 		if ($this->passesFilters([ $query ])) { | ||||
| 			$this->queries[] = $query; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Takes a query, an array of params and types as arguments, returns runnable query with upper-cased keywords | ||||
| 	protected function createRunnableQuery($query, $params, $types) | ||||
| 	{ | ||||
| 		// add params to query | ||||
| 		$query = $this->replaceParams($this->connection->getDatabasePlatform(), $query, $params, $types); | ||||
|  | ||||
| 		// highlight keywords | ||||
| 		$keywords = [ | ||||
| 			'select', 'insert', 'update', 'delete', 'into', 'values', 'set', 'where', 'from', 'limit', 'is', 'null', | ||||
| 			'having', 'group by', 'order by', 'asc', 'desc' | ||||
| 		]; | ||||
| 		$regexp = '/\b' . implode('\b|\b', $keywords) . '\b/i'; | ||||
|  | ||||
| 		return preg_replace_callback($regexp, function ($match) { return strtoupper($match[0]); }, $query); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Source at laravel-doctrine/orm LaravelDoctrine\ORM\Loggers\Formatters\ReplaceQueryParams::format(). | ||||
| 	 * | ||||
| 	 * @param AbstractPlatform $platform | ||||
| 	 * @param string           $sql | ||||
| 	 * @param array|null       $params | ||||
| 	 * @param array|null       $types | ||||
| 	 * | ||||
| 	 * | ||||
| 	 * @return string | ||||
| 	 */ | ||||
| 	public function replaceParams($platform, $sql, array $params = null, array $types = null) | ||||
| 	{ | ||||
| 		if (is_array($params)) { | ||||
| 			foreach ($params as $key => $param) { | ||||
| 				$type  = isset($types[$key]) ? $types[$key] : null; // Originally used null coalescing | ||||
| 				$param = $this->convertParam($platform, $param, $type); | ||||
| 				$sql   = preg_replace('/\?/', "$param", $sql, 1); | ||||
| 			} | ||||
| 		} | ||||
| 		return $sql; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Source at laravel-doctrine/orm LaravelDoctrine\ORM\Loggers\Formatters\ReplaceQueryParams::convertParam(). | ||||
| 	 * | ||||
| 	 * @param mixed $param | ||||
| 	 * | ||||
| 	 * @throws \Exception | ||||
| 	 * @return string | ||||
| 	 */ | ||||
| 	protected function convertParam($platform, $param, $type = null) | ||||
| 	{ | ||||
| 		if (is_object($param)) { | ||||
| 			if (!method_exists($param, '__toString')) { | ||||
| 				if ($param instanceof \DateTimeInterface) { | ||||
| 					$param = $param->format('Y-m-d H:i:s'); | ||||
| 				} elseif (Type::hasType($type)) { | ||||
| 					$type  = Type::getType($type); | ||||
| 					$param = $type->convertToDatabaseValue($param, $platform); | ||||
| 				} else { | ||||
| 					throw new \Exception('Given query param is an instance of ' . get_class($param) . ' and could not be converted to a string'); | ||||
| 				} | ||||
| 			} | ||||
| 		} elseif (is_array($param)) { | ||||
| 			if ($this->isNestedArray($param)) { | ||||
| 				$param = json_encode($param, JSON_UNESCAPED_UNICODE); | ||||
| 			} else { | ||||
| 				$param = implode( | ||||
| 					', ', | ||||
| 					array_map( | ||||
| 						function ($part) { | ||||
| 							return '"' . (string) $part . '"'; | ||||
| 						}, | ||||
| 						$param | ||||
| 					) | ||||
| 				); | ||||
| 				return '(' . $param . ')'; | ||||
| 			} | ||||
| 		} else { | ||||
| 			$param = htmlspecialchars((string) $param); // Originally used the e() Laravel helper | ||||
| 		} | ||||
| 		return '"' . (string) $param . '"'; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Source at laravel-doctrine/orm LaravelDoctrine\ORM\Loggers\Formatters\ReplaceQueryParams::isNestedArray(). | ||||
| 	 * | ||||
| 	 * @param  array $array | ||||
| 	 * @return bool | ||||
| 	 */ | ||||
| 	private function isNestedArray(array $array) | ||||
| 	{ | ||||
| 		foreach ($array as $key => $value) { | ||||
| 			if (is_array($value)) { | ||||
| 				return true; | ||||
| 			} | ||||
| 		} | ||||
| 		return false; | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										67
									
								
								vendor/itsgoingd/clockwork/Clockwork/DataSource/DataSource.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								vendor/itsgoingd/clockwork/Clockwork/DataSource/DataSource.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,67 @@ | ||||
| <?php namespace Clockwork\DataSource; | ||||
|  | ||||
| use Clockwork\Request\Request; | ||||
|  | ||||
| // Base data source class | ||||
| class DataSource implements DataSourceInterface | ||||
| { | ||||
| 	// Array of filter functions | ||||
| 	protected $filters = []; | ||||
|  | ||||
| 	// Adds collected data to the request and returns it, to be implemented by extending classes | ||||
| 	public function resolve(Request $request) | ||||
| 	{ | ||||
| 		return $request; | ||||
| 	} | ||||
|  | ||||
| 	// Extends the request with an additional data, which is not required for normal use | ||||
| 	public function extend(Request $request) | ||||
| 	{ | ||||
| 		return $request; | ||||
| 	} | ||||
|  | ||||
| 	// Reset the data source to an empty state, clearing any collected data | ||||
| 	public function reset() | ||||
| 	{ | ||||
| 	} | ||||
|  | ||||
| 	// Register a new filter | ||||
| 	public function addFilter(\Closure $filter, $type = 'default') | ||||
| 	{ | ||||
| 		$this->filters[$type] = isset($this->filters[$type]) | ||||
| 			? array_merge($this->filters[$type], [ $filter ]) : [ $filter ]; | ||||
|  | ||||
| 		return $this; | ||||
| 	} | ||||
|  | ||||
| 	// Clear all registered filters | ||||
| 	public function clearFilters() | ||||
| 	{ | ||||
| 		$this->filters = []; | ||||
|  | ||||
| 		return $this; | ||||
| 	} | ||||
|  | ||||
| 	// Returns boolean whether the filterable passes all registered filters | ||||
| 	protected function passesFilters($args, $type = 'default') | ||||
| 	{ | ||||
| 		$filters = isset($this->filters[$type]) ? $this->filters[$type] : []; | ||||
|  | ||||
| 		foreach ($filters as $filter) { | ||||
| 			if (! $filter(...$args)) return false; | ||||
| 		} | ||||
|  | ||||
| 		return true; | ||||
| 	} | ||||
|  | ||||
| 	// Censors passwords in an array, identified by key containing "pass" substring | ||||
| 	public function removePasswords(array $data) | ||||
| 	{ | ||||
| 		$keys = array_keys($data); | ||||
| 		$values = array_map(function ($value, $key) { | ||||
| 			return strpos($key, 'pass') !== false ? '*removed*' : $value; | ||||
| 		}, $data, $keys); | ||||
|  | ||||
| 		return array_combine($keys, $values); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										16
									
								
								vendor/itsgoingd/clockwork/Clockwork/DataSource/DataSourceInterface.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								vendor/itsgoingd/clockwork/Clockwork/DataSource/DataSourceInterface.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| <?php namespace Clockwork\DataSource; | ||||
|  | ||||
| use Clockwork\Request\Request; | ||||
|  | ||||
| // Data source interface, all data sources must implement this interface | ||||
| interface DataSourceInterface | ||||
| { | ||||
| 	// Adds collected data to the request and returns it | ||||
| 	public function resolve(Request $request); | ||||
|  | ||||
| 	// Extends the request with an additional data, which is not required for normal use | ||||
| 	public function extend(Request $request); | ||||
|  | ||||
| 	// Reset the data source to an empty state, clearing any collected data | ||||
| 	public function reset(); | ||||
| } | ||||
							
								
								
									
										12
									
								
								vendor/itsgoingd/clockwork/Clockwork/DataSource/DoctrineDataSource.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								vendor/itsgoingd/clockwork/Clockwork/DataSource/DoctrineDataSource.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| <?php namespace Clockwork\DataSource; | ||||
|  | ||||
| use Doctrine\ORM\EntityManager; | ||||
|  | ||||
| // Data source for Doctrine, provides database queries | ||||
| class DoctrineDataSource extends DBALDataSource | ||||
| { | ||||
| 	public function __construct(EntityManager $enm) | ||||
| 	{ | ||||
| 		parent::__construct($enm->getConnection()); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										329
									
								
								vendor/itsgoingd/clockwork/Clockwork/DataSource/EloquentDataSource.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										329
									
								
								vendor/itsgoingd/clockwork/Clockwork/DataSource/EloquentDataSource.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,329 @@ | ||||
| <?php namespace Clockwork\DataSource; | ||||
|  | ||||
| use Clockwork\Helpers\Serializer; | ||||
| use Clockwork\Helpers\StackTrace; | ||||
| use Clockwork\Request\Request; | ||||
| use Clockwork\Support\Laravel\Eloquent\ResolveModelLegacyScope; | ||||
| use Clockwork\Support\Laravel\Eloquent\ResolveModelScope; | ||||
|  | ||||
| use Illuminate\Database\ConnectionResolverInterface; | ||||
| use Illuminate\Contracts\Events\Dispatcher as EventDispatcher; | ||||
|  | ||||
| // Data source for Eloquent (Laravel ORM), provides database queries, stats, model actions and counts | ||||
| class EloquentDataSource extends DataSource | ||||
| { | ||||
| 	use Concerns\EloquentDetectDuplicateQueries; | ||||
|  | ||||
| 	// Database manager instance | ||||
| 	protected $databaseManager; | ||||
|  | ||||
| 	// Event dispatcher instance | ||||
| 	protected $eventDispatcher; | ||||
|  | ||||
| 	// Array of collected queries | ||||
| 	protected $queries = []; | ||||
|  | ||||
| 	// Query counts by type | ||||
| 	protected $count = [ | ||||
| 		'total' => 0, 'slow' => 0, 'select' => 0, 'insert' => 0, 'update' => 0, 'delete' => 0, 'other' => 0 | ||||
| 	]; | ||||
|  | ||||
| 	// Collected models actions | ||||
| 	protected $modelsActions = []; | ||||
|  | ||||
| 	// Model action counts by model, eg. [ 'retrieved' => [ User::class => 1 ] ] | ||||
| 	protected $modelsCount = [ | ||||
| 		'retrieved' => [], 'created' => [], 'updated' => [], 'deleted' => [] | ||||
| 	]; | ||||
|  | ||||
| 	// Whether we are collecting database queries or stats only | ||||
| 	protected $collectQueries = true; | ||||
|  | ||||
| 	// Whether we are collecting models actions or stats only | ||||
| 	protected $collectModelsActions = true; | ||||
|  | ||||
| 	// Whether we are collecting retrieved models as well when collecting models actions | ||||
| 	protected $collectModelsRetrieved = false; | ||||
|  | ||||
| 	// Query execution time threshold in ms after which the query is marked as slow | ||||
| 	protected $slowThreshold; | ||||
|  | ||||
| 	// Enable duplicate queries detection | ||||
| 	protected $detectDuplicateQueries = false; | ||||
|  | ||||
| 	// Model name to associate with the next executed query, used to map queries to models | ||||
| 	public $nextQueryModel; | ||||
|  | ||||
| 	// Create a new data source instance, takes a database manager, an event dispatcher as arguments and additional | ||||
| 	// options as arguments | ||||
| 	public function __construct(ConnectionResolverInterface $databaseManager, EventDispatcher $eventDispatcher, $collectQueries = true, $slowThreshold = null, $slowOnly = false, $detectDuplicateQueries = false, $collectModelsActions = true, $collectModelsRetrieved = false) | ||||
| 	{ | ||||
| 		$this->databaseManager = $databaseManager; | ||||
| 		$this->eventDispatcher = $eventDispatcher; | ||||
|  | ||||
| 		$this->collectQueries         = $collectQueries; | ||||
| 		$this->slowThreshold          = $slowThreshold; | ||||
| 		$this->detectDuplicateQueries = $detectDuplicateQueries; | ||||
| 		$this->collectModelsActions   = $collectModelsActions; | ||||
| 		$this->collectModelsRetrieved = $collectModelsRetrieved; | ||||
|  | ||||
| 		if ($slowOnly) $this->addFilter(function ($query) { return $query['duration'] > $this->slowThreshold; }); | ||||
| 	} | ||||
|  | ||||
| 	// Adds ran database queries, query counts, models actions and models counts to the request | ||||
| 	public function resolve(Request $request) | ||||
| 	{ | ||||
| 		$request->databaseQueries = array_merge($request->databaseQueries, $this->queries); | ||||
|  | ||||
| 		$request->databaseQueriesCount += $this->count['total']; | ||||
| 		$request->databaseSlowQueries  += $this->count['slow']; | ||||
| 		$request->databaseSelects      += $this->count['select']; | ||||
| 		$request->databaseInserts      += $this->count['insert']; | ||||
| 		$request->databaseUpdates      += $this->count['update']; | ||||
| 		$request->databaseDeletes      += $this->count['delete']; | ||||
| 		$request->databaseOthers       += $this->count['other']; | ||||
|  | ||||
| 		$request->modelsActions = array_merge($request->modelsActions, $this->modelsActions); | ||||
|  | ||||
| 		$request->modelsRetrieved = $this->modelsCount['retrieved']; | ||||
| 		$request->modelsCreated   = $this->modelsCount['created']; | ||||
| 		$request->modelsUpdated   = $this->modelsCount['updated']; | ||||
| 		$request->modelsDeleted   = $this->modelsCount['deleted']; | ||||
|  | ||||
| 		$this->appendDuplicateQueriesWarnings($request); | ||||
|  | ||||
| 		return $request; | ||||
| 	} | ||||
|  | ||||
| 	// Reset the data source to an empty state, clearing any collected data | ||||
| 	public function reset() | ||||
| 	{ | ||||
| 		$this->queries = []; | ||||
| 		$this->count = [ | ||||
| 			'total' => 0, 'slow' => 0, 'select' => 0, 'insert' => 0, 'update' => 0, 'delete' => 0, 'other' => 0 | ||||
| 		]; | ||||
|  | ||||
| 		$this->modelsActions = []; | ||||
| 		$this->modelsCount = [ | ||||
| 			'retrieved' => [], 'created' => [], 'updated' => [], 'deleted' => [] | ||||
| 		]; | ||||
|  | ||||
| 		$this->nextQueryModel = null; | ||||
| 	} | ||||
|  | ||||
| 	// Start listening to Eloquent events | ||||
| 	public function listenToEvents() | ||||
| 	{ | ||||
| 		if ($scope = $this->getModelResolvingScope()) { | ||||
| 			$this->eventDispatcher->listen('eloquent.booted: *', function ($model, $data = null) use ($scope) { | ||||
| 				if (is_string($model) && is_array($data)) { // Laravel 5.4 wildcard event | ||||
| 					$model = reset($data); | ||||
| 				} | ||||
|  | ||||
| 				$model->addGlobalScope($scope); | ||||
| 			}); | ||||
| 		} | ||||
|  | ||||
| 		if (class_exists(\Illuminate\Database\Events\QueryExecuted::class)) { | ||||
| 			// Laravel 5.2 and up | ||||
| 			$this->eventDispatcher->listen(\Illuminate\Database\Events\QueryExecuted::class, function ($event) { | ||||
| 				$this->registerQuery($event); | ||||
| 			}); | ||||
| 		} else { | ||||
| 			// Laravel 5.0 to 5.1 | ||||
| 			$this->eventDispatcher->listen('illuminate.query', function ($event) { | ||||
| 				$this->registerLegacyQuery($event); | ||||
| 			}); | ||||
| 		} | ||||
|  | ||||
| 		// register all event listeners individually so we don't have to regex the event type and support Laravel <5.4 | ||||
| 		$this->listenToModelEvent('retrieved'); | ||||
| 		$this->listenToModelEvent('created'); | ||||
| 		$this->listenToModelEvent('updated'); | ||||
| 		$this->listenToModelEvent('deleted'); | ||||
| 	} | ||||
|  | ||||
| 	// Register a listener collecting model events of specified type | ||||
| 	protected function listenToModelEvent($event) | ||||
| 	{ | ||||
| 		$this->eventDispatcher->listen("eloquent.{$event}: *", function ($model, $data = null) use ($event) { | ||||
| 			if (is_string($model) && is_array($data)) { // Laravel 5.4 wildcard event | ||||
| 				$model = reset($data); | ||||
| 			} | ||||
|  | ||||
| 			$this->collectModelEvent($event, $model); | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	// Collect an executed database query | ||||
| 	protected function registerQuery($event) | ||||
| 	{ | ||||
| 		$trace = StackTrace::get([ 'arguments' => $this->detectDuplicateQueries ])->resolveViewName(); | ||||
|  | ||||
| 		if ($this->detectDuplicateQueries) $this->detectDuplicateQuery($trace); | ||||
|  | ||||
| 		$query = [ | ||||
| 			'query'      => $this->createRunnableQuery($event->sql, $event->bindings, $event->connectionName), | ||||
| 			'duration'   => $event->time, | ||||
| 			'connection' => $event->connectionName, | ||||
| 			'time'       => microtime(true) - $event->time / 1000, | ||||
| 			'trace'      => (new Serializer)->trace($trace), | ||||
| 			'model'      => $this->nextQueryModel, | ||||
| 			'tags'       => $this->slowThreshold !== null && $event->time > $this->slowThreshold ? [ 'slow' ] : [] | ||||
| 		]; | ||||
|  | ||||
| 		$this->nextQueryModel = null; | ||||
|  | ||||
| 		if (! $this->passesFilters([ $query, $trace ], 'early')) return; | ||||
|  | ||||
| 		$this->incrementQueryCount($query); | ||||
|  | ||||
| 		if (! $this->collectQueries || ! $this->passesFilters([ $query, $trace ])) return; | ||||
|  | ||||
| 		$this->queries[] = $query; | ||||
| 	} | ||||
|  | ||||
| 	// Collect an executed database query (pre Laravel 5.2) | ||||
| 	protected function registerLegacyQuery($sql, $bindings, $time, $connection) | ||||
| 	{ | ||||
| 		return $this->registerQuery((object) [ | ||||
| 			'sql'            => $sql, | ||||
| 			'bindings'       => $bindings, | ||||
| 			'time'           => $time, | ||||
| 			'connectionName' => $connection | ||||
| 		]); | ||||
| 	} | ||||
|  | ||||
| 	// Collect a model event and update stats | ||||
| 	protected function collectModelEvent($event, $model) | ||||
| 	{ | ||||
| 		$lastQuery = ($queryCount = count($this->queries)) ? $this->queries[$queryCount - 1] : null; | ||||
|  | ||||
| 		$action = [ | ||||
| 			'model'      => $modelClass = get_class($model), | ||||
| 			'key'        => $this->getModelKey($model), | ||||
| 			'action'     => $event, | ||||
| 			'attributes' => $this->collectModelsRetrieved && $event == 'retrieved' ? $model->getOriginal() : [], | ||||
| 			'changes'    => $this->collectModelsActions ? $model->getChanges() : [], | ||||
| 			'time'       => microtime(true) / 1000, | ||||
| 			'query'      => $lastQuery ? $lastQuery['query'] : null, | ||||
| 			'duration'   => $lastQuery ? $lastQuery['duration'] : null, | ||||
| 			'connection' => $lastQuery ? $lastQuery['connection'] : null, | ||||
| 			'trace'      => null, | ||||
| 			'tags'       => [] | ||||
| 		]; | ||||
|  | ||||
| 		if ($lastQuery) $this->queries[$queryCount - 1]['model'] = $modelClass; | ||||
|  | ||||
| 		if (! $this->passesFilters([ $action ], 'models-early')) return; | ||||
|  | ||||
| 		$this->incrementModelsCount($action['action'], $action['model']); | ||||
|  | ||||
| 		if (! $this->collectModelsActions) return; | ||||
| 		if (! $this->collectModelsRetrieved && $event == 'retrieved') return; | ||||
| 		if (! $this->passesFilters([ $action ], 'models')) return; | ||||
|  | ||||
| 		$action['trace'] = (new Serializer)->trace(StackTrace::get()->resolveViewName()); | ||||
|  | ||||
| 		$this->modelsActions[] = $action; | ||||
| 	} | ||||
|  | ||||
| 	// Takes a query, an array of bindings and the connection as arguments, returns runnable query with upper-cased keywords | ||||
| 	protected function createRunnableQuery($query, $bindings, $connection) | ||||
| 	{ | ||||
| 		// add bindings to query | ||||
| 		$bindings = $this->databaseManager->connection($connection)->prepareBindings($bindings); | ||||
|  | ||||
| 		$index = 0; | ||||
| 		$query = preg_replace_callback('/\?/', function ($matches) use ($bindings, $connection, &$index) { | ||||
| 			$binding = $this->quoteBinding($bindings[$index++], $connection); | ||||
|  | ||||
| 			// convert binary bindings to hexadecimal representation | ||||
| 			if (! preg_match('//u', (string) $binding)) $binding = '0x' . bin2hex($binding); | ||||
|  | ||||
| 			// escape backslashes in the binding (preg_replace requires to do so) | ||||
| 			return (string) $binding; | ||||
| 		}, $query, count($bindings)); | ||||
|  | ||||
| 		// highlight keywords | ||||
| 		$keywords = [ | ||||
| 			'select', 'insert', 'update', 'delete', 'into', 'values', 'set', 'where', 'from', 'limit', 'is', 'null', | ||||
| 			'having', 'group by', 'order by', 'asc', 'desc' | ||||
| 		]; | ||||
| 		$regexp = '/\b' . implode('\b|\b', $keywords) . '\b/i'; | ||||
|  | ||||
| 		return preg_replace_callback($regexp, function ($match) { return strtoupper($match[0]); }, $query); | ||||
| 	} | ||||
|  | ||||
| 	// Takes a query binding and a connection name, returns a quoted binding value | ||||
| 	protected function quoteBinding($binding, $connection) | ||||
| 	{ | ||||
| 		$connection = $this->databaseManager->connection($connection); | ||||
|  | ||||
| 		if (! method_exists($connection, 'getPdo')) return; | ||||
|  | ||||
| 		$pdo = $connection->getPdo(); | ||||
|  | ||||
| 		if ($pdo === null) return; | ||||
|  | ||||
| 		if ($pdo->getAttribute(\PDO::ATTR_DRIVER_NAME) === 'odbc') { | ||||
| 			// PDO_ODBC driver doesn't support the quote method, apply simple MSSQL style quoting instead | ||||
| 			return "'" . str_replace("'", "''", $binding) . "'"; | ||||
| 		} | ||||
|  | ||||
| 		return is_string($binding) ? $pdo->quote($binding) : $binding; | ||||
| 	} | ||||
|  | ||||
| 	// Increment query counts for collected query | ||||
| 	protected function incrementQueryCount($query) | ||||
| 	{ | ||||
| 		$sql = ltrim($query['query']); | ||||
|  | ||||
| 		$this->count['total']++; | ||||
|  | ||||
| 		if (preg_match('/^select\b/i', $sql)) { | ||||
| 			$this->count['select']++; | ||||
| 		} elseif (preg_match('/^insert\b/i', $sql)) { | ||||
| 			$this->count['insert']++; | ||||
| 		} elseif (preg_match('/^update\b/i', $sql)) { | ||||
| 			$this->count['update']++; | ||||
| 		} elseif (preg_match('/^delete\b/i', $sql)) { | ||||
| 			$this->count['delete']++; | ||||
| 		} else { | ||||
| 			$this->count['other']++; | ||||
| 		} | ||||
|  | ||||
| 		if (in_array('slow', $query['tags'])) { | ||||
| 			$this->count['slow']++; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Increment model counts for collected model action | ||||
| 	protected function incrementModelsCount($action, $model) | ||||
| 	{ | ||||
| 		if (! isset($this->modelsCount[$action][$model])) { | ||||
| 			$this->modelsCount[$action][$model] = 0; | ||||
| 		} | ||||
|  | ||||
| 		$this->modelsCount[$action][$model]++; | ||||
| 	} | ||||
|  | ||||
| 	// Returns model resolving scope for the installed Laravel version | ||||
| 	protected function getModelResolvingScope() | ||||
| 	{ | ||||
| 		if (interface_exists(\Illuminate\Database\Eloquent\ScopeInterface::class)) { | ||||
| 			// Laravel 5.0 to 5.1 | ||||
| 			return new ResolveModelLegacyScope($this); | ||||
| 		} | ||||
|  | ||||
| 		return new ResolveModelScope($this); | ||||
| 	} | ||||
|  | ||||
| 	// Returns model key without crashing when using Eloquent strict mode and it's not loaded | ||||
| 	protected function getModelKey($model) | ||||
| 	{ | ||||
| 		try { | ||||
| 			return $model->getKey(); | ||||
| 		} catch (\Illuminate\Database\Eloquent\MissingAttributeException $e) {} | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										138
									
								
								vendor/itsgoingd/clockwork/Clockwork/DataSource/LaravelCacheDataSource.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										138
									
								
								vendor/itsgoingd/clockwork/Clockwork/DataSource/LaravelCacheDataSource.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,138 @@ | ||||
| <?php namespace Clockwork\DataSource; | ||||
|  | ||||
| use Clockwork\Helpers\Serializer; | ||||
| use Clockwork\Helpers\StackTrace; | ||||
| use Clockwork\Request\Request; | ||||
|  | ||||
| use Illuminate\Contracts\Events\Dispatcher as EventDispatcher; | ||||
|  | ||||
| // Data source for Laravel cache component, provides cache queries and stats | ||||
| class LaravelCacheDataSource extends DataSource | ||||
| { | ||||
| 	// Event dispatcher instance | ||||
| 	protected $eventDispatcher; | ||||
|  | ||||
| 	// Executed cache queries | ||||
| 	protected $queries = []; | ||||
|  | ||||
| 	// Query counts by type | ||||
| 	protected $count = [ | ||||
| 		'read' => 0, 'hit' => 0, 'write' => 0, 'delete' => 0 | ||||
| 	]; | ||||
|  | ||||
| 	// Whether we are collecting cache queries or stats only | ||||
| 	protected $collectQueries = true; | ||||
|  | ||||
| 	// Whether we are collecting values from cache queries | ||||
| 	protected $collectValues = true; | ||||
|  | ||||
| 	// Create a new data source instance, takes an event dispatcher and additional options as argument | ||||
| 	public function __construct(EventDispatcher $eventDispatcher, $collectQueries = true, $collectValues = true) | ||||
| 	{ | ||||
| 		$this->eventDispatcher = $eventDispatcher; | ||||
|  | ||||
| 		$this->collectQueries = $collectQueries; | ||||
| 		$this->collectValues = $collectValues; | ||||
| 	} | ||||
|  | ||||
| 	// Adds cache queries and stats to the request | ||||
| 	public function resolve(Request $request) | ||||
| 	{ | ||||
| 		$request->cacheQueries = array_merge($request->cacheQueries, $this->queries); | ||||
|  | ||||
| 		$request->cacheReads   += $this->count['read']; | ||||
| 		$request->cacheHits    += $this->count['hit']; | ||||
| 		$request->cacheWrites  += $this->count['write']; | ||||
| 		$request->cacheDeletes += $this->count['delete']; | ||||
|  | ||||
| 		return $request; | ||||
| 	} | ||||
|  | ||||
| 	// Reset the data source to an empty state, clearing any collected data | ||||
| 	public function reset() | ||||
| 	{ | ||||
| 		$this->queries = []; | ||||
|  | ||||
| 		$this->count = [ | ||||
| 			'read' => 0, 'hit' => 0, 'write' => 0, 'delete' => 0 | ||||
| 		]; | ||||
| 	} | ||||
|  | ||||
| 	// Start listening to cache events | ||||
| 	public function listenToEvents() | ||||
| 	{ | ||||
| 		if (class_exists(\Illuminate\Cache\Events\CacheHit::class)) { | ||||
| 			$this->eventDispatcher->listen(\Illuminate\Cache\Events\CacheHit::class, function ($event) { | ||||
| 				$this->registerQuery([ 'type' => 'hit', 'key' => $event->key, 'value' => $event->value ]); | ||||
| 			}); | ||||
| 			$this->eventDispatcher->listen(\Illuminate\Cache\Events\CacheMissed::class, function ($event) { | ||||
| 				$this->registerQuery([ 'type' => 'miss', 'key' => $event->key ]); | ||||
| 			}); | ||||
| 			$this->eventDispatcher->listen(\Illuminate\Cache\Events\KeyWritten::class, function ($event) { | ||||
| 				$this->registerQuery([ | ||||
| 					'type' => 'write', 'key' => $event->key, 'value' => $event->value, | ||||
| 					'expiration' => property_exists($event, 'seconds') ? $event->seconds : $event->minutes * 60 | ||||
| 				]); | ||||
| 			}); | ||||
| 			$this->eventDispatcher->listen(\Illuminate\Cache\Events\KeyForgotten::class, function ($event) { | ||||
| 				$this->registerQuery([ 'type' => 'delete', 'key' => $event->key ]); | ||||
| 			}); | ||||
| 		} else { | ||||
| 			// legacy Laravel 5.1 style events | ||||
| 			$this->eventDispatcher->listen('cache.hit', function ($key, $value) { | ||||
| 				$this->registerQuery([ 'type' => 'hit', 'key' => $key, 'value' => $value ]); | ||||
| 			}); | ||||
| 			$this->eventDispatcher->listen('cache.missed', function ($key) { | ||||
| 				$this->registerQuery([ 'type' => 'miss', 'key' => $key ]); | ||||
| 			}); | ||||
| 			$this->eventDispatcher->listen('cache.write', function ($key, $value, $minutes) { | ||||
| 				$this->registerQuery([ | ||||
| 					'type' => 'write', 'key' => $key, 'value' => $value, 'expiration' => $minutes * 60 | ||||
| 				]); | ||||
| 			}); | ||||
| 			$this->eventDispatcher->listen('cache.delete', function ($key) { | ||||
| 				$this->registerQuery([ 'type' => 'delete', 'key' => $key ]); | ||||
| 			}); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Collect an executed query | ||||
| 	protected function registerQuery(array $query) | ||||
| 	{ | ||||
| 		$trace = StackTrace::get()->resolveViewName(); | ||||
|  | ||||
| 		$record = [ | ||||
| 			'type'       => $query['type'], | ||||
| 			'key'        => $query['key'], | ||||
| 			'time'       => microtime(true), | ||||
| 			'connection' => null, | ||||
| 			'trace'      => (new Serializer)->trace($trace) | ||||
| 		]; | ||||
|  | ||||
| 		if ($this->collectValues && isset($query['value'])) { | ||||
| 			$record['value'] = (new Serializer)->normalize($query['value']); | ||||
| 		} | ||||
|  | ||||
| 		$this->incrementQueryCount($record); | ||||
|  | ||||
| 		if ($this->collectQueries && $this->passesFilters([ $record ])) { | ||||
| 			$this->queries[] = $record; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Increment query counts for collected query | ||||
| 	protected function incrementQueryCount($query) | ||||
| 	{ | ||||
| 		if ($query['type'] == 'write') { | ||||
| 			$this->count['write']++; | ||||
| 		} elseif ($query['type'] == 'delete') { | ||||
| 			$this->count['delete']++; | ||||
| 		} else { | ||||
| 			$this->count['read']++; | ||||
|  | ||||
| 			if ($query['type'] == 'hit') { | ||||
| 				$this->count['hit']++; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										223
									
								
								vendor/itsgoingd/clockwork/Clockwork/DataSource/LaravelDataSource.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										223
									
								
								vendor/itsgoingd/clockwork/Clockwork/DataSource/LaravelDataSource.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,223 @@ | ||||
| <?php namespace Clockwork\DataSource; | ||||
|  | ||||
| use Clockwork\DataSource\DataSource; | ||||
| use Clockwork\Helpers\Serializer; | ||||
| use Clockwork\Request\Log; | ||||
| use Clockwork\Request\Request; | ||||
|  | ||||
| use Illuminate\Contracts\Foundation\Application; | ||||
| use Symfony\Component\HttpFoundation\Response; | ||||
|  | ||||
| // Data source for Laravel framework, provides application log, request and response information | ||||
| class LaravelDataSource extends DataSource | ||||
| { | ||||
| 	// Laravel application instance | ||||
| 	protected $app; | ||||
|  | ||||
| 	// Laravel response instance | ||||
| 	protected $response; | ||||
|  | ||||
| 	// Whether we should collect log messages | ||||
| 	protected $collectLog = true; | ||||
|  | ||||
| 	// Whether we should collect routes | ||||
| 	protected $collectRoutes = false; | ||||
|  | ||||
| 	// Only collect routes from following list of namespaces (collect all if empty) | ||||
| 	protected $routesOnlyNamespaces = []; | ||||
|  | ||||
| 	// Clockwork log instance | ||||
| 	protected $log; | ||||
|  | ||||
| 	// Create a new data source, takes Laravel application instance and additional options as an arguments | ||||
| 	public function __construct(Application $app, $collectLog = true, $collectRoutes = false, $routesOnlyNamespaces = true) | ||||
| 	{ | ||||
| 		$this->app = $app; | ||||
|  | ||||
| 		$this->collectLog           = $collectLog; | ||||
| 		$this->collectRoutes        = $collectRoutes; | ||||
| 		$this->routesOnlyNamespaces = $routesOnlyNamespaces; | ||||
|  | ||||
| 		$this->log = new Log; | ||||
| 	} | ||||
|  | ||||
| 	// Adds request, response information, middleware, routes, session data, user and log entries to the request | ||||
| 	public function resolve(Request $request) | ||||
| 	{ | ||||
| 		$request->method         = $this->getRequestMethod(); | ||||
| 		$request->url            = $this->getRequestUrl(); | ||||
| 		$request->uri            = $this->getRequestUri(); | ||||
| 		$request->controller     = $this->getController(); | ||||
| 		$request->headers        = $this->getRequestHeaders(); | ||||
| 		$request->responseStatus = $this->getResponseStatus(); | ||||
| 		$request->middleware     = $this->getMiddleware(); | ||||
| 		$request->routes         = $this->getRoutes(); | ||||
| 		$request->sessionData    = $this->getSessionData(); | ||||
|  | ||||
| 		$this->resolveAuthenticatedUser($request); | ||||
|  | ||||
| 		$request->log()->merge($this->log); | ||||
|  | ||||
| 		return $request; | ||||
| 	} | ||||
|  | ||||
| 	// Reset the data source to an empty state, clearing any collected data | ||||
| 	public function reset() | ||||
| 	{ | ||||
| 		$this->log = new Log; | ||||
| 	} | ||||
|  | ||||
| 	// Set Laravel application instance for the current request | ||||
| 	public function setApplication(Application $app) | ||||
| 	{ | ||||
| 		$this->app = $app; | ||||
| 		return $this; | ||||
| 	} | ||||
|  | ||||
| 	// Set Laravel response instance for the current request | ||||
| 	public function setResponse(Response $response) | ||||
| 	{ | ||||
| 		$this->response = $response; | ||||
| 		return $this; | ||||
| 	} | ||||
|  | ||||
| 	// Listen for the log events | ||||
| 	public function listenToEvents() | ||||
| 	{ | ||||
| 		if (! $this->collectLog) return; | ||||
|  | ||||
| 		if (class_exists(\Illuminate\Log\Events\MessageLogged::class)) { | ||||
| 			// Laravel 5.4 | ||||
| 			$this->app['events']->listen(\Illuminate\Log\Events\MessageLogged::class, function ($event) { | ||||
| 				$this->log->log($event->level, $event->message, $event->context); | ||||
| 			}); | ||||
| 		} else { | ||||
| 			// Laravel 5.0 to 5.3 | ||||
| 			$this->app['events']->listen('illuminate.log', function ($level, $message, $context) { | ||||
| 				$this->log->log($level, $message, $context); | ||||
| 			}); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Get a textual representation of the current route's controller | ||||
| 	protected function getController() | ||||
| 	{ | ||||
| 		$router = $this->app['router']; | ||||
|  | ||||
| 		$route = $router->current(); | ||||
| 		$controller = $route ? $route->getActionName() : null; | ||||
|  | ||||
| 		if ($controller instanceof \Closure) { | ||||
| 			$controller = 'anonymous function'; | ||||
| 		} elseif (is_object($controller)) { | ||||
| 			$controller = 'instance of ' . get_class($controller); | ||||
| 		} elseif (is_array($controller) && count($controller) == 2) { | ||||
| 			if (is_object($controller[0])) { | ||||
| 				$controller = get_class($controller[0]) . '->' . $controller[1]; | ||||
| 			} else { | ||||
| 				$controller = $controller[0] . '::' . $controller[1]; | ||||
| 			} | ||||
| 		} elseif (! is_string($controller)) { | ||||
| 			$controller = null; | ||||
| 		} | ||||
|  | ||||
| 		return $controller; | ||||
| 	} | ||||
|  | ||||
| 	// Get the request headers | ||||
| 	protected function getRequestHeaders() | ||||
| 	{ | ||||
| 		return $this->app['request']->headers->all(); | ||||
| 	} | ||||
|  | ||||
| 	// Get the request method | ||||
| 	protected function getRequestMethod() | ||||
| 	{ | ||||
| 		return $this->app['request']->getMethod(); | ||||
| 	} | ||||
|  | ||||
| 	// Get the request URL | ||||
| 	protected function getRequestUrl() | ||||
| 	{ | ||||
| 		return $this->app['request']->fullUrl(); | ||||
| 	} | ||||
|  | ||||
| 	// Get the request URI | ||||
| 	protected function getRequestUri() | ||||
| 	{ | ||||
| 		return $this->app['request']->getRequestUri(); | ||||
| 	} | ||||
|  | ||||
| 	// Get the response status code | ||||
| 	protected function getResponseStatus() | ||||
| 	{ | ||||
| 		return $this->response ? $this->response->getStatusCode() : null; | ||||
| 	} | ||||
|  | ||||
| 	// Get an array of middleware for the matched route | ||||
| 	protected function getMiddleware() | ||||
| 	{ | ||||
| 		$route = $this->app['router']->current(); | ||||
|  | ||||
| 		if (! $route) return; | ||||
|  | ||||
| 		return method_exists($route, 'gatherMiddleware') ? $route->gatherMiddleware() : $route->middleware(); | ||||
| 	} | ||||
|  | ||||
| 	// Get an array of application routes | ||||
| 	protected function getRoutes() | ||||
| 	{ | ||||
| 		if (! $this->collectRoutes) return []; | ||||
|  | ||||
| 		return array_values(array_filter(array_map(function ($route) { | ||||
| 			$action = $route->getActionName() ?: 'anonymous function'; | ||||
| 			$namespace = strpos($action, '\\') !== false ? explode('\\', $action)[0] : null; | ||||
|  | ||||
| 			if (count($this->routesOnlyNamespaces) && ! in_array($namespace, $this->routesOnlyNamespaces)) return; | ||||
|  | ||||
| 			return [ | ||||
| 				'method'     => implode(', ', $route->methods()), | ||||
| 				'uri'        => $route->uri(), | ||||
| 				'name'       => $route->getName(), | ||||
| 				'action'     => $action, | ||||
| 				'middleware' => $route->middleware(), | ||||
| 				'before'     => method_exists($route, 'beforeFilters') ? implode(', ', array_keys($route->beforeFilters())) : '', | ||||
| 				'after'      => method_exists($route, 'afterFilters') ? implode(', ', array_keys($route->afterFilters())) : '' | ||||
| 			]; | ||||
| 		}, $this->app['router']->getRoutes()->getRoutes()))); | ||||
| 	} | ||||
|  | ||||
| 	// Get the session data (normalized with removed passwords) | ||||
| 	protected function getSessionData() | ||||
| 	{ | ||||
| 		if (! isset($this->app['session'])) return []; | ||||
|  | ||||
| 		return $this->removePasswords((new Serializer)->normalizeEach($this->app['session']->all())); | ||||
| 	} | ||||
|  | ||||
| 	// Add authenticated user data to the request | ||||
| 	protected function resolveAuthenticatedUser(Request $request) | ||||
| 	{ | ||||
| 		if (! isset($this->app['auth'])) return; | ||||
| 		if (! ($user = $this->app['auth']->user())) return; | ||||
|  | ||||
| 		if ($user instanceof \Illuminate\Database\Eloquent\Model) { | ||||
| 			// retrieve attributes in this awkward way to make sure we don't trigger exceptions with Eloquent strict mode on | ||||
| 			$keyName = method_exists($user, 'getAuthIdentifierName') ? $user->getAuthIdentifierName() : $user->getKeyName(); | ||||
| 			$user = $user->getAttributes(); | ||||
|  | ||||
| 			$userId = isset($user[$keyName]) ? $user[$keyName] : null; | ||||
| 			$userEmail = isset($user['email']) ? $user['email'] : $userId; | ||||
| 			$userName = isset($user['name']) ? $user['name'] : null; | ||||
| 		} else { | ||||
| 			$userId = $user->getAuthIdentifier(); | ||||
| 			$userEmail = isset($user->email) ? $user->email : $userId; | ||||
| 			$userName = isset($user->name) ? $user->name : null; | ||||
| 		} | ||||
|  | ||||
| 		$request->setAuthenticatedUser($userEmail, $userId, [ | ||||
| 			'email' => $userEmail, | ||||
| 			'name'  => $userName | ||||
| 		]); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										143
									
								
								vendor/itsgoingd/clockwork/Clockwork/DataSource/LaravelEventsDataSource.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								vendor/itsgoingd/clockwork/Clockwork/DataSource/LaravelEventsDataSource.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,143 @@ | ||||
| <?php namespace Clockwork\DataSource; | ||||
|  | ||||
| use Clockwork\Helpers\Serializer; | ||||
| use Clockwork\Helpers\StackTrace; | ||||
| use Clockwork\Request\Request; | ||||
|  | ||||
| use Illuminate\Contracts\Events\Dispatcher; | ||||
|  | ||||
| // Data source for Laravel events component, provides fired events | ||||
| class LaravelEventsDataSource extends DataSource | ||||
| { | ||||
| 	// Event dispatcher instance | ||||
| 	protected $dispatcher; | ||||
|  | ||||
| 	// Fired events | ||||
| 	protected $events = []; | ||||
|  | ||||
| 	// Whether framework events should be collected | ||||
| 	protected $ignoredEvents = false; | ||||
|  | ||||
| 	// Create a new data source instance, takes an event dispatcher and additional options as arguments | ||||
| 	public function __construct(Dispatcher $dispatcher, $ignoredEvents = []) | ||||
| 	{ | ||||
| 		$this->dispatcher = $dispatcher; | ||||
|  | ||||
| 		$this->ignoredEvents = is_array($ignoredEvents) | ||||
| 			? array_merge($ignoredEvents, $this->defaultIgnoredEvents()) : []; | ||||
| 	} | ||||
|  | ||||
| 	// Adds fired events to the request | ||||
| 	public function resolve(Request $request) | ||||
| 	{ | ||||
| 		$request->events = array_merge($request->events, $this->events); | ||||
|  | ||||
| 		return $request; | ||||
| 	} | ||||
|  | ||||
| 	// Reset the data source to an empty state, clearing any collected data | ||||
| 	public function reset() | ||||
| 	{ | ||||
| 		$this->events = []; | ||||
| 	} | ||||
|  | ||||
| 	// Start listening to the events | ||||
| 	public function listenToEvents() | ||||
| 	{ | ||||
| 		$this->dispatcher->listen('*', function ($event = null, $data = null) { | ||||
| 			if (method_exists($this->dispatcher, 'firing')) { // Laravel 5.0 - 5.3 | ||||
| 				$data = func_get_args(); | ||||
| 				$event = $this->dispatcher->firing(); | ||||
| 			} | ||||
|  | ||||
| 			$this->registerEvent($event, $data); | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	// Collect a fired event, prepares data for serialization and resolves registered listeners | ||||
| 	protected function registerEvent($event, array $data) | ||||
| 	{ | ||||
| 		if (! $this->shouldCollect($event)) return; | ||||
|  | ||||
| 		$trace = StackTrace::get()->resolveViewName(); | ||||
|  | ||||
| 		$event = [ | ||||
| 			'event'     => $event, | ||||
| 			'data'      => (new Serializer)->normalize(count($data) == 1 && isset($data[0]) ? $data[0] : $data), | ||||
| 			'time'      => microtime(true), | ||||
| 			'listeners' => $this->findListenersFor($event), | ||||
| 			'trace'     => (new Serializer)->trace($trace) | ||||
| 		]; | ||||
|  | ||||
| 		if ($this->passesFilters([ $event ])) { | ||||
| 			$this->events[] = $event; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Returns registered listeners for the specified event | ||||
| 	protected function findListenersFor($event) | ||||
| 	{ | ||||
| 		$listener = $this->dispatcher->getListeners($event)[0]; | ||||
|  | ||||
| 		return array_filter(array_map(function ($listener) { | ||||
| 			if ($listener instanceof \Closure) { | ||||
| 				// Laravel 5.4+ (and earlier versions in some cases) wrap the listener into a closure, | ||||
| 				// attempt to resolve the original listener | ||||
| 				$use = (new \ReflectionFunction($listener))->getStaticVariables(); | ||||
| 				$listener = isset($use['listener']) ? $use['listener'] : $listener; | ||||
| 			} | ||||
|  | ||||
| 			if (is_string($listener)) { | ||||
| 				return $listener; | ||||
| 			} elseif (is_array($listener) && count($listener) == 2) { | ||||
| 				if (is_object($listener[0])) { | ||||
| 					return get_class($listener[0]) . '@' . $listener[1]; | ||||
| 				} else { | ||||
| 					return $listener[0] . '::' . $listener[1]; | ||||
| 				} | ||||
| 			} elseif ($listener instanceof \Closure) { | ||||
| 				$listener = new \ReflectionFunction($listener); | ||||
|  | ||||
| 				if (strpos($listener->getNamespaceName(), 'Clockwork\\') === 0) { // skip our own listeners | ||||
| 					return; | ||||
| 				} | ||||
|  | ||||
| 				$filename = str_replace(base_path(), '', $listener->getFileName()); | ||||
| 				$startLine = $listener->getStartLine(); | ||||
| 				$endLine = $listener->getEndLine(); | ||||
|  | ||||
| 				return "Closure ({$filename}:{$startLine}-{$endLine})"; | ||||
| 			} | ||||
| 		}, $this->dispatcher->getListeners($event))); | ||||
| 	} | ||||
|  | ||||
| 	// Returns whether the event should be collected (depending on ignored events) | ||||
| 	protected function shouldCollect($event) | ||||
| 	{ | ||||
| 		return ! preg_match('/^(?:' . implode('|', $this->ignoredEvents) . ')$/', $event); | ||||
| 	} | ||||
|  | ||||
| 	// Returns default ignored events (framework-specific events) | ||||
| 	protected function defaultIgnoredEvents() | ||||
| 	{ | ||||
| 		return [ | ||||
| 			'Illuminate\\\\.+', | ||||
| 			'Laravel\\\\.+', | ||||
| 			'auth\.(?:attempt|login|logout)', | ||||
| 			'artisan\.start', | ||||
| 			'bootstrapped:.+', | ||||
| 			'composing:.+', | ||||
| 			'creating:.+', | ||||
| 			'illuminate\.query', | ||||
| 			'connection\..+', | ||||
| 			'eloquent\..+', | ||||
| 			'kernel\.handled', | ||||
| 			'illuminate\.log', | ||||
| 			'mailer\.sending', | ||||
| 			'router\.(?:before|after|matched)', | ||||
| 			'router.filter:.+', | ||||
| 			'locale\.changed', | ||||
| 			'clockwork\..+' | ||||
| 		]; | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										250
									
								
								vendor/itsgoingd/clockwork/Clockwork/DataSource/LaravelNotificationsDataSource.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										250
									
								
								vendor/itsgoingd/clockwork/Clockwork/DataSource/LaravelNotificationsDataSource.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,250 @@ | ||||
| <?php namespace Clockwork\DataSource; | ||||
|  | ||||
| use Clockwork\Helpers\Serializer; | ||||
| use Clockwork\Helpers\StackTrace; | ||||
| use Clockwork\Request\Request; | ||||
|  | ||||
| use Illuminate\Contracts\Events\Dispatcher; | ||||
| use Illuminate\Mail\Mailable; | ||||
| use Illuminate\Mail\Events\MessageSending; | ||||
| use Illuminate\Mail\Events\MessageSent; | ||||
| use Illuminate\Notifications\Events\NotificationSending; | ||||
| use Illuminate\Notifications\Events\NotificationSent; | ||||
|  | ||||
| // Data source for Laravel notifications and mail components, provides sent notifications and emails | ||||
| class LaravelNotificationsDataSource extends DataSource | ||||
| { | ||||
| 	// Event dispatcher instance | ||||
| 	protected $dispatcher; | ||||
|  | ||||
| 	// Sent notifications | ||||
| 	protected $notifications = []; | ||||
|  | ||||
| 	// Last collected notification | ||||
| 	protected $lastNotification; | ||||
|  | ||||
| 	// Create a new data source instance, takes an event dispatcher as argument | ||||
| 	public function __construct(Dispatcher $dispatcher) | ||||
| 	{ | ||||
| 		$this->dispatcher = $dispatcher; | ||||
| 	} | ||||
|  | ||||
| 	// Add sent notifications to the request | ||||
| 	public function resolve(Request $request) | ||||
| 	{ | ||||
| 		$request->notifications = array_merge($request->notifications, $this->notifications); | ||||
|  | ||||
| 		return $request; | ||||
| 	} | ||||
|  | ||||
| 	// Reset the data source to an empty state, clearing any collected data | ||||
| 	public function reset() | ||||
| 	{ | ||||
| 		$this->notifications = []; | ||||
| 	} | ||||
|  | ||||
| 	// Listen to the email and notification events | ||||
| 	public function listenToEvents() | ||||
| 	{ | ||||
| 		$this->dispatcher->listen(MessageSending::class, function ($event) { $this->sendingMessage($event); }); | ||||
| 		$this->dispatcher->listen(MessageSent::class, function ($event) { $this->sentMessage($event); }); | ||||
|  | ||||
| 		$this->dispatcher->listen(NotificationSending::class, function ($event) { $this->sendingNotification($event); }); | ||||
| 		$this->dispatcher->listen(NotificationSent::class, function ($event) { $this->sentNotification($event); }); | ||||
| 	} | ||||
|  | ||||
| 	// Collect a sent email | ||||
| 	protected function sendingMessage($event) | ||||
| 	{ | ||||
| 		$trace = StackTrace::get()->resolveViewName(); | ||||
|  | ||||
| 		$mailable = ($frame = $trace->first(function ($frame) { return is_subclass_of($frame->object, Mailable::class); })) | ||||
| 			? $frame->object : null; | ||||
|  | ||||
| 		$notification = (object) [ | ||||
| 			'subject' => $event->message->getSubject(), | ||||
| 			'from'    => $this->messageAddressToString($event->message->getFrom()), | ||||
| 			'to'      => $this->messageAddressToString($event->message->getTo()), | ||||
| 			'content' => $this->messageBody($event->message), | ||||
| 			'type'    => 'mail', | ||||
| 			'data'    => [ | ||||
| 				'cc'       => $this->messageAddressToString($event->message->getCc()), | ||||
| 				'bcc'      => $this->messageAddressToString($event->message->getBcc()), | ||||
| 				'replyTo'  => $this->messageAddressToString($event->message->getReplyTo()), | ||||
| 				'mailable' => (new Serializer)->normalize($mailable) | ||||
| 			], | ||||
| 			'time'    => microtime(true), | ||||
| 			'trace'   => (new Serializer)->trace($trace) | ||||
| 		]; | ||||
|  | ||||
| 		if ($this->updateLastNotification($notification)) return; | ||||
|  | ||||
| 		if ($this->passesFilters([ $notification ])) { | ||||
| 			$this->notifications[] = $this->lastNotification = $notification; | ||||
| 		} else { | ||||
| 			$this->lastNotification = null; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Update last notification with time taken to send it | ||||
| 	protected function sentMessage($event) | ||||
| 	{ | ||||
| 		if ($this->lastNotification) { | ||||
| 			$this->lastNotification->duration = (microtime(true) - $this->lastNotification->time) * 1000; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Collect a sent notification | ||||
| 	protected function sendingNotification($event) | ||||
| 	{ | ||||
| 		$trace = StackTrace::get()->resolveViewName(); | ||||
|  | ||||
| 		$channelSpecific = $this->resolveChannelSpecific($event); | ||||
|  | ||||
| 		$notification = (object) [ | ||||
| 			'subject' => $channelSpecific['subject'], | ||||
| 			'from'    => $channelSpecific['from'], | ||||
| 			'to'      => $channelSpecific['to'], | ||||
| 			'content' => $channelSpecific['content'], | ||||
| 			'type'    => $event->channel, | ||||
| 			'data'    => array_merge($channelSpecific['data'], [ | ||||
| 				'notification' => (new Serializer)->normalize($event->notification), | ||||
| 				'notifiable'   => (new Serializer)->normalize($event->notifiable) | ||||
| 			]), | ||||
| 			'time'    => microtime(true), | ||||
| 			'trace'   => (new Serializer)->trace($trace) | ||||
| 		]; | ||||
|  | ||||
| 		if ($this->passesFilters([ $notification ])) { | ||||
| 			$this->notifications[] = $this->lastNotification = $notification; | ||||
| 		} else { | ||||
| 			$this->lastNotification = null; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Update last notification with time taken to send it and response | ||||
| 	protected function sentNotification($event) | ||||
| 	{ | ||||
| 		if ($this->lastNotification) { | ||||
| 			$this->lastNotification->duration = (microtime(true) - $this->lastNotification->time) * 1000; | ||||
| 			$this->lastNotification->data['response'] = $event->response; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Update last sent email notification with additional data from the message sent event | ||||
| 	protected function updateLastNotification($notification) | ||||
| 	{ | ||||
| 		if (! $this->lastNotification) return false; | ||||
|  | ||||
| 		if ($this->lastNotification->to !== $notification->to) return false; | ||||
|  | ||||
| 		$this->lastNotification->subject = $notification->subject; | ||||
| 		$this->lastNotification->from    = $notification->from; | ||||
| 		$this->lastNotification->to      = $notification->to; | ||||
| 		$this->lastNotification->content = $notification->content; | ||||
|  | ||||
| 		$this->lastNotification->data = array_merge($this->lastNotification->data, $notification->data); | ||||
|  | ||||
| 		return true; | ||||
| 	} | ||||
|  | ||||
| 	// Resolve notification channel specific data | ||||
| 	protected function resolveChannelSpecific($event) | ||||
| 	{ | ||||
| 		if (method_exists($event->notification, 'toMail')) { | ||||
| 			$channelSpecific = $this->resolveMailChannelSpecific($event, $event->notification->toMail($event->notifiable)); | ||||
| 		} elseif (method_exists($event->notification, 'toSlack')) { | ||||
| 			$channelSpecific = $this->resolveSlackChannelSpecific($event, $event->notification->toSlack($event->notifiable)); | ||||
| 		} elseif (method_exists($event->notification, 'toNexmo')) { | ||||
| 			$channelSpecific = $this->resolveNexmoChannelSpecific($event, $event->notification->toNexmo($event->notifiable)); | ||||
| 		} elseif (method_exists($event->notification, 'toBroadcast')) { | ||||
| 			$channelSpecific = [ 'data' => [ 'data' => (new Serializer)->normalize($event->notification->toBroadcast($event->notifiable)) ] ]; | ||||
| 		} elseif (method_exists($event->notification, 'toArray')) { | ||||
| 			$channelSpecific = [ 'data' => [ 'data' => (new Serializer)->normalize($event->notification->toArray($event->notifiable)) ] ]; | ||||
| 		} else { | ||||
| 			$channelSpecific = []; | ||||
| 		} | ||||
|  | ||||
| 		return array_merge( | ||||
| 			[ 'subject' => null, 'from' => null, 'to' => null, 'content' => null, 'data' => [] ], $channelSpecific | ||||
| 		); | ||||
| 	} | ||||
|  | ||||
| 	// Resolve mail notification channel specific data | ||||
| 	protected function resolveMailChannelSpecific($event, $message) | ||||
| 	{ | ||||
| 		return [ | ||||
| 			'subject' => $message->subject ?: get_class($event->notification), | ||||
| 			'from'    => $this->notificationAddressToString($message->from), | ||||
| 			'to'      => $this->notificationAddressToString($event->notifiable->routeNotificationFor('mail', $event->notification)), | ||||
| 			'data'    => [ | ||||
| 				'cc'      => $this->notificationAddressToString($message->cc), | ||||
| 				'bcc'     => $this->notificationAddressToString($message->bcc), | ||||
| 				'replyTo' => $this->notificationAddressToString($message->replyTo) | ||||
| 			] | ||||
| 		]; | ||||
| 	} | ||||
|  | ||||
| 	// Resolve Slack notification channel specific data | ||||
| 	protected function resolveSlackChannelSpecific($event, $message) | ||||
| 	{ | ||||
| 		return [ | ||||
| 			'subject' => get_class($event->notification), | ||||
| 			'from'    => $message->username, | ||||
| 			'to'      => $message->channel, | ||||
| 			'content' => $message->content | ||||
| 		]; | ||||
| 	} | ||||
|  | ||||
| 	// Resolve Nexmo notification channel specific data | ||||
| 	protected function resolveNexmoChannelSpecific($event, $message) | ||||
| 	{ | ||||
| 		return [ | ||||
| 			'subject' => get_class($event->notification), | ||||
| 			'from'    => $message->from, | ||||
| 			'to'      => $event->notifiable->routeNotificationFor('nexmo', $event->notification), | ||||
| 			'content' => $message->content | ||||
| 		]; | ||||
| 	} | ||||
|  | ||||
| 	protected function messageAddressToString($address) | ||||
| 	{ | ||||
| 		if (! $address) return; | ||||
|  | ||||
| 		return array_map(function ($address, $key) { | ||||
| 			// Laravel 8 or earlier | ||||
| 			if (! ($address instanceof \Symfony\Component\Mime\Address)) { | ||||
| 				return $address ? "{$address} <{$key}>" : $key; | ||||
| 			} | ||||
|  | ||||
| 			// Laravel 9 or later | ||||
| 			return $address->toString(); | ||||
| 		}, $address, array_keys($address)); | ||||
| 	} | ||||
|  | ||||
| 	protected function messageBody($message) | ||||
| 	{ | ||||
| 		// Laravel 8 or earlier | ||||
| 		if (! ($message instanceof \Symfony\Component\Mime\Email)) { | ||||
| 			return $message->getBody(); | ||||
| 		} | ||||
|  | ||||
| 		// Laravel 9 or later | ||||
| 		return $message->getHtmlBody() ?: $message->getTextBody(); | ||||
| 	} | ||||
|  | ||||
| 	protected function notificationAddressToString($address) | ||||
| 	{ | ||||
| 		if (! $address) return; | ||||
| 		if (! is_array($address)) $address = [ $address ]; | ||||
|  | ||||
| 		return array_map(function ($address) { | ||||
| 			if (! is_array($address)) return $address; | ||||
|  | ||||
| 			$email = isset($address['address']) ? $address['address'] : $address[0]; | ||||
| 			$name = isset($address['name']) ? $address['name'] : $address[1]; | ||||
|  | ||||
| 			return $name ? "{$name} <{$email}>" : $email; | ||||
| 		}, $address); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										90
									
								
								vendor/itsgoingd/clockwork/Clockwork/DataSource/LaravelQueueDataSource.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								vendor/itsgoingd/clockwork/Clockwork/DataSource/LaravelQueueDataSource.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,90 @@ | ||||
| <?php namespace Clockwork\DataSource; | ||||
|  | ||||
| use Clockwork\Helpers\Serializer; | ||||
| use Clockwork\Helpers\StackTrace; | ||||
| use Clockwork\Request\Request; | ||||
|  | ||||
| use Illuminate\Queue\Queue; | ||||
|  | ||||
| // Data source for Laravel queue component, provides dispatched queue jobs | ||||
| class LaravelQueueDataSource extends DataSource | ||||
| { | ||||
| 	// Queue instance | ||||
| 	protected $queue; | ||||
|  | ||||
| 	// Dispatched queue jobs | ||||
| 	protected $jobs = []; | ||||
|  | ||||
| 	// Clockwork ID of the current request | ||||
| 	protected $currentRequestId; | ||||
|  | ||||
| 	// Create a new data source instance, takes a queue as an argument | ||||
| 	public function __construct(Queue $queue) | ||||
| 	{ | ||||
| 		$this->queue = $queue; | ||||
| 	} | ||||
|  | ||||
| 	// Adds dispatched queue jobs to the request | ||||
| 	public function resolve(Request $request) | ||||
| 	{ | ||||
| 		$request->queueJobs = array_merge($request->queueJobs, $this->getJobs()); | ||||
|  | ||||
| 		return $request; | ||||
| 	} | ||||
|  | ||||
| 	// Reset the data source to an empty state, clearing any collected data | ||||
| 	public function reset() | ||||
| 	{ | ||||
| 		$this->jobs = []; | ||||
| 	} | ||||
|  | ||||
| 	// Listen to the queue events | ||||
| 	public function listenToEvents() | ||||
| 	{ | ||||
| 		$this->queue->createPayloadUsing(function ($connection, $queue, $payload) { | ||||
| 			$this->registerJob([ | ||||
| 				'id'         => $id = (new Request)->id, | ||||
| 				'connection' => $connection, | ||||
| 				'queue'      => $queue, | ||||
| 				'name'       => $payload['displayName'], | ||||
| 				'data'       => isset($payload['data']['command']) ? $payload['data']['command'] : null, | ||||
| 				'maxTries'   => $payload['maxTries'], | ||||
| 				'timeout'    => $payload['timeout'], | ||||
| 				'time'       => microtime(true) | ||||
| 			]); | ||||
|  | ||||
| 			return [ 'clockwork_id' => $id, 'clockwork_parent_id' => $this->currentRequestId ]; | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	// Set Clockwork ID of the current request | ||||
| 	public function setCurrentRequestId($requestId) | ||||
| 	{ | ||||
| 		$this->currentRequestId = $requestId; | ||||
| 		return $this; | ||||
| 	} | ||||
|  | ||||
| 	// Collect a dispatched queue job | ||||
| 	protected function registerJob(array $job) | ||||
| 	{ | ||||
| 		$trace = StackTrace::get()->resolveViewName(); | ||||
|  | ||||
| 		$job = array_merge($job, [ | ||||
| 			'trace' => (new Serializer)->trace($trace) | ||||
| 		]); | ||||
|  | ||||
| 		if ($this->passesFilters([ $job ])) { | ||||
| 			$this->jobs[] = $job; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Get an array of dispatched queue jobs commands | ||||
| 	protected function getJobs() | ||||
| 	{ | ||||
| 		return array_map(function ($query) { | ||||
| 			return array_merge($query, [ | ||||
| 				'data' => isset($query['data']) ? (new Serializer)->normalize($query['data']) : null | ||||
| 			]); | ||||
| 		}, $this->jobs); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										86
									
								
								vendor/itsgoingd/clockwork/Clockwork/DataSource/LaravelRedisDataSource.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								vendor/itsgoingd/clockwork/Clockwork/DataSource/LaravelRedisDataSource.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,86 @@ | ||||
| <?php namespace Clockwork\DataSource; | ||||
|  | ||||
| use Clockwork\Helpers\Serializer; | ||||
| use Clockwork\Helpers\StackTrace; | ||||
| use Clockwork\Request\Request; | ||||
|  | ||||
| use Illuminate\Contracts\Events\Dispatcher as EventDispatcher; | ||||
|  | ||||
| // Data source for Laravel redis component, provides redis commands | ||||
| class LaravelRedisDataSource extends DataSource | ||||
| { | ||||
| 	// Event dispatcher instance | ||||
| 	protected $eventDispatcher; | ||||
|  | ||||
| 	// Executed redis commands | ||||
| 	protected $commands = []; | ||||
|  | ||||
| 	// Whether to skip Redis commands originating from Laravel cache Redis store | ||||
| 	protected $skipCacheCommands = true; | ||||
|  | ||||
| 	// Create a new data source instance, takes an event dispatcher and additional options as arguments | ||||
| 	public function __construct(EventDispatcher $eventDispatcher, $skipCacheCommands = true) | ||||
| 	{ | ||||
| 		$this->eventDispatcher = $eventDispatcher; | ||||
|  | ||||
| 		$this->skipCacheCommands = $skipCacheCommands; | ||||
|  | ||||
| 		if ($this->skipCacheCommands) { | ||||
| 			$this->addFilter(function ($command, $trace) { | ||||
| 				return ! $trace->first(function ($frame) { return $frame->class == 'Illuminate\Cache\RedisStore'; }); | ||||
| 			}); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Adds redis commands to the request | ||||
| 	public function resolve(Request $request) | ||||
| 	{ | ||||
| 		$request->redisCommands = array_merge($request->redisCommands, $this->getCommands()); | ||||
|  | ||||
| 		return $request; | ||||
| 	} | ||||
|  | ||||
| 	// Reset the data source to an empty state, clearing any collected data | ||||
| 	public function reset() | ||||
| 	{ | ||||
| 		$this->commands = []; | ||||
| 	} | ||||
|  | ||||
| 	// Listen to the cache events | ||||
| 	public function listenToEvents() | ||||
| 	{ | ||||
| 		$this->eventDispatcher->listen(\Illuminate\Redis\Events\CommandExecuted::class, function ($event) { | ||||
| 			$this->registerCommand([ | ||||
| 				'command'    => $event->command, | ||||
| 				'parameters' => $event->parameters, | ||||
| 				'duration'   => $event->time, | ||||
| 				'connection' => $event->connectionName, | ||||
| 				'time'       => microtime(true) - $event->time / 1000 | ||||
| 			]); | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	// Collect an executed command | ||||
| 	protected function registerCommand(array $command) | ||||
| 	{ | ||||
| 		$trace = StackTrace::get()->resolveViewName(); | ||||
|  | ||||
| 		$command = array_merge($command, [ | ||||
| 			'trace' => (new Serializer)->trace($trace) | ||||
| 		]); | ||||
|  | ||||
| 		if ($this->passesFilters([ $command, $trace ])) { | ||||
| 			$this->commands[] = $command; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Get an array of executed redis commands | ||||
| 	protected function getCommands() | ||||
| 	{ | ||||
| 		return array_map(function ($query) { | ||||
| 			return array_merge($query, [ | ||||
| 				'parameters' => isset($query['parameters']) ? (new Serializer)->normalize($query['parameters']) : null | ||||
| 			]); | ||||
| 		}, $this->commands); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										71
									
								
								vendor/itsgoingd/clockwork/Clockwork/DataSource/LaravelViewsDataSource.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								vendor/itsgoingd/clockwork/Clockwork/DataSource/LaravelViewsDataSource.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,71 @@ | ||||
| <?php namespace Clockwork\DataSource; | ||||
|  | ||||
| use Clockwork\DataSource\DataSource; | ||||
| use Clockwork\Helpers\Serializer; | ||||
| use Clockwork\Request\Request; | ||||
| use Clockwork\Request\Timeline\Timeline; | ||||
|  | ||||
| use Illuminate\Contracts\Events\Dispatcher; | ||||
|  | ||||
| // Data source for Laravel views component, provides rendered views | ||||
| class LaravelViewsDataSource extends DataSource | ||||
| { | ||||
| 	// Event dispatcher | ||||
| 	protected $dispatcher; | ||||
|  | ||||
| 	// Timeline data structure for collected views | ||||
| 	protected $views; | ||||
|  | ||||
| 	// Whether we should collect view data | ||||
| 	protected $collectData = false; | ||||
|  | ||||
| 	// Create a new data source instance, takes an event dispatcher as argument | ||||
| 	public function __construct(Dispatcher $dispatcher, $collectData = false) | ||||
| 	{ | ||||
| 		$this->dispatcher = $dispatcher; | ||||
|  | ||||
| 		$this->collectData = $collectData; | ||||
|  | ||||
| 		$this->views = new Timeline; | ||||
| 	} | ||||
|  | ||||
| 	// Adds rendered views to the request | ||||
| 	public function resolve(Request $request) | ||||
| 	{ | ||||
| 		$request->viewsData = array_merge($request->viewsData, $this->views->finalize()); | ||||
|  | ||||
| 		return $request; | ||||
| 	} | ||||
|  | ||||
| 	// Reset the data source to an empty state, clearing any collected data | ||||
| 	public function reset() | ||||
| 	{ | ||||
| 		$this->views = new Timeline; | ||||
| 	} | ||||
|  | ||||
| 	// Listen to the views events | ||||
| 	public function listenToEvents() | ||||
| 	{ | ||||
| 		$this->dispatcher->listen('composing:*', function ($view, $data = null) { | ||||
| 			if (is_string($view) && is_array($data)) { // Laravel 5.4 wildcard event | ||||
| 				$view = $data[0]; | ||||
| 			} | ||||
|  | ||||
| 			$data = array_filter( | ||||
| 				$this->collectData ? $view->getData() : [], | ||||
| 				function ($v, $k) { return strpos($k, '__') !== 0; }, | ||||
| 				\ARRAY_FILTER_USE_BOTH | ||||
| 			); | ||||
|  | ||||
| 			$this->views->event('Rendering a view', [ | ||||
| 				'name'  => 'view ' . $view->getName(), | ||||
| 				'start' => $time = microtime(true), | ||||
| 				'end'   => $time, | ||||
| 				'data'  => [ | ||||
| 					'name' => $view->getName(), | ||||
| 					'data' => (new Serializer)->normalize($data) | ||||
| 				] | ||||
| 			]); | ||||
| 		}); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										201
									
								
								vendor/itsgoingd/clockwork/Clockwork/DataSource/LumenDataSource.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										201
									
								
								vendor/itsgoingd/clockwork/Clockwork/DataSource/LumenDataSource.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,201 @@ | ||||
| <?php namespace Clockwork\DataSource; | ||||
|  | ||||
| use Clockwork\DataSource\DataSource; | ||||
| use Clockwork\Helpers\Serializer; | ||||
| use Clockwork\Request\Log; | ||||
| use Clockwork\Request\Request; | ||||
|  | ||||
| use Laravel\Lumen\Application; | ||||
| use Symfony\Component\HttpFoundation\Response; | ||||
|  | ||||
| // Data source for Lumen framework, provides application log, request and response information | ||||
| class LumenDataSource extends DataSource | ||||
| { | ||||
| 	// Lumen application instance | ||||
| 	protected $app; | ||||
|  | ||||
| 	// Lumen response instance | ||||
| 	protected $response; | ||||
|  | ||||
| 	// Whether we should collect log messages | ||||
| 	protected $collectLog = true; | ||||
|  | ||||
| 	// Whether we should collect routes | ||||
| 	protected $collectRoutes = false; | ||||
|  | ||||
| 	// Clockwork log instance | ||||
| 	protected $log; | ||||
|  | ||||
| 	// Create a new data source, takes Lumen application instance and additional options as arguments | ||||
| 	public function __construct(Application $app, $collectLog = true, $collectRoutes = false) | ||||
| 	{ | ||||
| 		$this->app = $app; | ||||
|  | ||||
| 		$this->collectLog    = $collectLog; | ||||
| 		$this->collectRoutes = $collectRoutes; | ||||
|  | ||||
| 		$this->log = new Log; | ||||
| 	} | ||||
|  | ||||
| 	// Adds request, response information, middleware, routes, session data, user and log entries to the request | ||||
| 	public function resolve(Request $request) | ||||
| 	{ | ||||
| 		$request->method         = $this->getRequestMethod(); | ||||
| 		$request->uri            = $this->getRequestUri(); | ||||
| 		$request->controller     = $this->getController(); | ||||
| 		$request->headers        = $this->getRequestHeaders(); | ||||
| 		$request->responseStatus = $this->getResponseStatus(); | ||||
| 		$request->routes         = $this->getRoutes(); | ||||
| 		$request->sessionData    = $this->getSessionData(); | ||||
|  | ||||
| 		$this->resolveAuthenticatedUser($request); | ||||
|  | ||||
| 		$request->log()->merge($this->log); | ||||
|  | ||||
| 		return $request; | ||||
| 	} | ||||
|  | ||||
| 	// Reset the data source to an empty state, clearing any collected data | ||||
| 	public function reset() | ||||
| 	{ | ||||
| 		$this->log = new Log; | ||||
| 	} | ||||
|  | ||||
| 	// Set Lumen response instance for the current request | ||||
| 	public function setResponse(Response $response) | ||||
| 	{ | ||||
| 		$this->response = $response; | ||||
| 		return $this; | ||||
| 	} | ||||
|  | ||||
| 	// Listen for the log events | ||||
| 	public function listenToEvents() | ||||
| 	{ | ||||
| 		if (! $this->collectLog) return; | ||||
|  | ||||
| 		if (class_exists(\Illuminate\Log\Events\MessageLogged::class)) { | ||||
| 			// Lumen 5.4 | ||||
| 			$this->app['events']->listen(\Illuminate\Log\Events\MessageLogged::class, function ($event) { | ||||
| 				$this->log->log($event->level, $event->message, $event->context); | ||||
| 			}); | ||||
| 		} else { | ||||
| 			// Lumen 5.0 to 5.3 | ||||
| 			$this->app['events']->listen('illuminate.log', function ($level, $message, $context) { | ||||
| 				$this->log->log($level, $message, $context); | ||||
| 			}); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Get a textual representation of current route's controller | ||||
| 	protected function getController() | ||||
| 	{ | ||||
| 		$routes = method_exists($this->app, 'getRoutes') ? $this->app->getRoutes() : []; | ||||
|  | ||||
| 		$method = $this->getRequestMethod(); | ||||
| 		$pathInfo = $this->getPathInfo(); | ||||
|  | ||||
| 		if (isset($routes[$method.$pathInfo]['action']['uses'])) { | ||||
| 			$controller = $routes[$method.$pathInfo]['action']['uses']; | ||||
| 		} elseif (isset($routes[$method.$pathInfo]['action'][0])) { | ||||
| 			$controller = $routes[$method.$pathInfo]['action'][0]; | ||||
| 		} else { | ||||
| 			$controller = null; | ||||
| 		} | ||||
|  | ||||
| 		if ($controller instanceof \Closure) { | ||||
| 			$controller = 'anonymous function'; | ||||
| 		} elseif (is_object($controller)) { | ||||
| 			$controller = 'instance of ' . get_class($controller); | ||||
| 		} elseif (! is_string($controller)) { | ||||
| 			$controller = null; | ||||
| 		} | ||||
|  | ||||
| 		return $controller; | ||||
| 	} | ||||
|  | ||||
| 	// Get the request headers | ||||
| 	protected function getRequestHeaders() | ||||
| 	{ | ||||
| 		return $this->app['request']->headers->all(); | ||||
| 	} | ||||
|  | ||||
| 	// Get the request method | ||||
| 	protected function getRequestMethod() | ||||
| 	{ | ||||
| 		if ($this->app->bound('request')) { | ||||
| 			return $this->app['request']->getMethod(); | ||||
| 		} elseif (isset($_POST['_method'])) { | ||||
| 			return strtoupper($_POST['_method']); | ||||
| 		} else { | ||||
| 			return $_SERVER['REQUEST_METHOD']; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Get the request URI | ||||
| 	protected function getRequestUri() | ||||
| 	{ | ||||
| 		return $this->app['request']->getRequestUri(); | ||||
| 	} | ||||
|  | ||||
| 	// Get the response status code | ||||
| 	protected function getResponseStatus() | ||||
| 	{ | ||||
| 		return $this->response ? $this->response->getStatusCode() : null; | ||||
| 	} | ||||
|  | ||||
| 	// Get an array of application routes | ||||
| 	protected function getRoutes() | ||||
| 	{ | ||||
| 		if (! $this->collectRoutes) return []; | ||||
|  | ||||
| 		if (isset($this->app->router)) { | ||||
| 			$routes = array_values($this->app->router->getRoutes()); | ||||
| 		} elseif (method_exists($this->app, 'getRoutes')) { | ||||
| 			$routes = array_values($this->app->getRoutes()); | ||||
| 		} else { | ||||
| 			$routes = []; | ||||
| 		} | ||||
|  | ||||
| 		return array_map(function ($route) { | ||||
| 			return [ | ||||
| 				'method' => $route['method'], | ||||
| 				'uri'    => $route['uri'], | ||||
| 				'name'   => isset($route['action']['as']) ? $route['action']['as'] : null, | ||||
| 				'action' => isset($route['action']['uses']) && is_string($route['action']['uses']) ? $route['action']['uses'] : 'anonymous function', | ||||
| 				'middleware' => isset($route['action']['middleware']) ? $route['action']['middleware'] : null, | ||||
| 			]; | ||||
| 		}, $routes); | ||||
| 	} | ||||
|  | ||||
| 	// Get the session data (normalized with passwords removed) | ||||
| 	protected function getSessionData() | ||||
| 	{ | ||||
| 		if (! isset($this->app['session'])) return []; | ||||
|  | ||||
| 		return $this->removePasswords((new Serializer)->normalizeEach($this->app['session']->all())); | ||||
| 	} | ||||
|  | ||||
| 	// Add authenticated user data to the request | ||||
| 	protected function resolveAuthenticatedUser(Request $request) | ||||
| 	{ | ||||
| 		if (! isset($this->app['auth'])) return; | ||||
| 		if (! ($user = $this->app['auth']->user())) return; | ||||
| 		if (! isset($user->email) || ! isset($user->id)) return; | ||||
|  | ||||
| 		$request->setAuthenticatedUser($user->email, $user->id, [ | ||||
| 			'email' => $user->email, | ||||
| 			'name'  => isset($user->name) ? $user->name : null | ||||
| 		]); | ||||
| 	} | ||||
|  | ||||
| 	// Get the request path info | ||||
| 	protected function getPathInfo() | ||||
| 	{ | ||||
| 		if ($this->app->bound('request')) { | ||||
| 			return $this->app['request']->getPathInfo(); | ||||
| 		} else { | ||||
| 			$query = isset($_SERVER['QUERY_STRING']) ? $_SERVER['QUERY_STRING'] : ''; | ||||
| 			return '/' . trim(str_replace("?{$query}", '', $_SERVER['REQUEST_URI']), '/'); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										37
									
								
								vendor/itsgoingd/clockwork/Clockwork/DataSource/MonologDataSource.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								vendor/itsgoingd/clockwork/Clockwork/DataSource/MonologDataSource.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | ||||
| <?php namespace Clockwork\DataSource; | ||||
|  | ||||
| use Clockwork\DataSource\DataSource; | ||||
| use Clockwork\Request\Log; | ||||
| use Clockwork\Request\Request; | ||||
| use Clockwork\Support\Monolog\Handler\ClockworkHandler; | ||||
|  | ||||
| use Monolog\Logger as Monolog; | ||||
|  | ||||
| // Data source for Monolog, provides application log | ||||
| class MonologDataSource extends DataSource | ||||
| { | ||||
| 	// Clockwork log instance | ||||
| 	protected $log; | ||||
|  | ||||
| 	// Create a new data source, takes Monolog instance as an argument | ||||
| 	public function __construct(Monolog $monolog) | ||||
| 	{ | ||||
| 		$this->log = new Log; | ||||
|  | ||||
| 		$monolog->pushHandler(new ClockworkHandler($this->log)); | ||||
| 	} | ||||
|  | ||||
| 	// Adds log entries to the request | ||||
| 	public function resolve(Request $request) | ||||
| 	{ | ||||
| 		$request->log()->merge($this->log); | ||||
|  | ||||
| 		return $request; | ||||
| 	} | ||||
|  | ||||
| 	// Reset the data source to an empty state, clearing any collected data | ||||
| 	public function reset() | ||||
| 	{ | ||||
| 		$this->log = new Log; | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										155
									
								
								vendor/itsgoingd/clockwork/Clockwork/DataSource/PhpDataSource.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										155
									
								
								vendor/itsgoingd/clockwork/Clockwork/DataSource/PhpDataSource.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,155 @@ | ||||
| <?php namespace Clockwork\DataSource; | ||||
|  | ||||
| use Clockwork\DataSource\DataSource; | ||||
| use Clockwork\Helpers\Serializer; | ||||
| use Clockwork\Request\Request; | ||||
|  | ||||
| // Data source providing data obtainable in vanilla PHP | ||||
| class PhpDataSource extends DataSource | ||||
| { | ||||
| 	// Adds request, response information, session data and peak memory usage to the request | ||||
| 	public function resolve(Request $request) | ||||
| 	{ | ||||
| 		$request->time           = PHP_SAPI !== 'cli' ? $this->getRequestTime() : $request->time; | ||||
| 		$request->method         = $this->getRequestMethod(); | ||||
| 		$request->url            = $this->getRequestUrl(); | ||||
| 		$request->uri            = $this->getRequestUri(); | ||||
| 		$request->headers        = $this->getRequestHeaders(); | ||||
| 		$request->getData        = $this->getGetData(); | ||||
| 		$request->postData       = $this->getPostData(); | ||||
| 		$request->requestData    = $this->getRequestData(); | ||||
| 		$request->sessionData    = $this->getSessionData(); | ||||
| 		$request->cookies        = $this->getCookies(); | ||||
| 		$request->responseStatus = $this->getResponseStatus(); | ||||
| 		$request->responseTime   = $this->getResponseTime(); | ||||
| 		$request->memoryUsage    = $this->getMemoryUsage(); | ||||
|  | ||||
| 		return $request; | ||||
| 	} | ||||
|  | ||||
| 	// Get the request cookies (normalized with passwords removed) | ||||
| 	protected function getCookies() | ||||
| 	{ | ||||
| 		return $this->removePasswords((new Serializer)->normalizeEach($_COOKIE)); | ||||
| 	} | ||||
|  | ||||
| 	// Get the request GET data (normalized with passwords removed) | ||||
| 	protected function getGetData() | ||||
| 	{ | ||||
| 		return $this->removePasswords((new Serializer)->normalizeEach($_GET)); | ||||
| 	} | ||||
|  | ||||
| 	// Get the request POST data (normalized with passwords removed) | ||||
| 	protected function getPostData() | ||||
| 	{ | ||||
| 		return $this->removePasswords((new Serializer)->normalizeEach($_POST)); | ||||
| 	} | ||||
|  | ||||
| 	// Get the request body data (attempt to parse as json, normalized with passwords removed) | ||||
| 	protected function getRequestData() | ||||
| 	{ | ||||
| 		// The data will already be parsed into POST data by PHP in case of application/x-www-form-urlencoded requests | ||||
| 		if (count($_POST)) return; | ||||
|  | ||||
| 		$requestData = file_get_contents('php://input'); | ||||
| 		$requestJsonData = json_decode($requestData, true); | ||||
|  | ||||
| 		return is_array($requestJsonData) | ||||
| 			? $this->removePasswords((new Serializer)->normalizeEach($requestJsonData)) | ||||
| 			: $requestData; | ||||
| 	} | ||||
|  | ||||
| 	// Get the request headers | ||||
| 	protected function getRequestHeaders() | ||||
| 	{ | ||||
| 		$headers = []; | ||||
|  | ||||
| 		foreach ($_SERVER as $key => $value) { | ||||
| 			if (substr($key, 0, 5) !== 'HTTP_') continue; | ||||
|  | ||||
| 			$header = substr($key, 5); | ||||
| 			$header = str_replace('_', ' ', $header); | ||||
| 			$header = ucwords(strtolower($header)); | ||||
| 			$header = str_replace(' ', '-', $header); | ||||
|  | ||||
| 			if (! isset($headers[$header])) { | ||||
| 				$headers[$header] = [ $value ]; | ||||
| 			} else { | ||||
| 				$headers[$header][] = $value; | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		ksort($headers); | ||||
|  | ||||
| 		return $headers; | ||||
| 	} | ||||
|  | ||||
| 	// Get the request method | ||||
| 	protected function getRequestMethod() | ||||
| 	{ | ||||
| 		if (isset($_SERVER['REQUEST_METHOD'])) { | ||||
| 			return $_SERVER['REQUEST_METHOD']; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Get the response time | ||||
| 	protected function getRequestTime() | ||||
| 	{ | ||||
| 		if (isset($_SERVER['REQUEST_TIME_FLOAT'])) { | ||||
| 			return $_SERVER['REQUEST_TIME_FLOAT']; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Get the request URL | ||||
| 	protected function getRequestUrl() | ||||
| 	{ | ||||
| 		$https = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on'; | ||||
| 		$host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : null; | ||||
| 		$addr = isset($_SERVER['SERVER_ADDR']) ? $_SERVER['SERVER_ADDR'] : null; | ||||
| 		$port = isset($_SERVER['SERVER_PORT']) ? $_SERVER['SERVER_PORT'] : null; | ||||
| 		$uri = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : null; | ||||
|  | ||||
| 		$scheme = $https ? 'https' : 'http'; | ||||
| 		$host = $host ?: $addr; | ||||
| 		$port = (! $https && $port != 80 || $https && $port != 443) ? ":{$port}" : ''; | ||||
|  | ||||
| 		// remove port number from the host | ||||
| 		$host = $host ? preg_replace('/:\d+$/', '', trim($host)) : null; | ||||
|  | ||||
| 		return "{$scheme}://{$host}{$port}{$uri}"; | ||||
| 	} | ||||
|  | ||||
| 	// Get the request URI | ||||
| 	protected function getRequestUri() | ||||
| 	{ | ||||
| 		if (isset($_SERVER['REQUEST_URI'])) { | ||||
| 			return $_SERVER['REQUEST_URI']; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Get the response status code | ||||
| 	protected function getResponseStatus() | ||||
| 	{ | ||||
| 		return http_response_code(); | ||||
| 	} | ||||
|  | ||||
| 	// Get the response time (current time, assuming most of the application code has already run at this point) | ||||
| 	protected function getResponseTime() | ||||
| 	{ | ||||
| 		return microtime(true); | ||||
| 	} | ||||
|  | ||||
| 	// Get the session data (normalized with passwords removed) | ||||
| 	protected function getSessionData() | ||||
| 	{ | ||||
| 		if (! isset($_SESSION)) return []; | ||||
|  | ||||
| 		return $this->removePasswords((new Serializer)->normalizeEach($_SESSION)); | ||||
| 	} | ||||
|  | ||||
| 	// Get the peak memory usage in bytes | ||||
| 	protected function getMemoryUsage() | ||||
| 	{ | ||||
| 		return memory_get_peak_usage(true); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										96
									
								
								vendor/itsgoingd/clockwork/Clockwork/DataSource/PsrMessageDataSource.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								vendor/itsgoingd/clockwork/Clockwork/DataSource/PsrMessageDataSource.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,96 @@ | ||||
| <?php namespace Clockwork\DataSource; | ||||
|  | ||||
| use Clockwork\DataSource\DataSource; | ||||
| use Clockwork\Helpers\Serializer; | ||||
| use Clockwork\Request\Request; | ||||
|  | ||||
| use Psr\Http\Message\ServerRequestInterface as PsrRequest; | ||||
| use Psr\Http\Message\ResponseInterface as PsrResponse; | ||||
|  | ||||
| // Data source providing data obtainable from the PSR-7 request and response interfaces | ||||
| class PsrMessageDataSource extends DataSource | ||||
| { | ||||
| 	// PSR Messages | ||||
| 	protected $psrRequest; | ||||
| 	protected $psrResponse; | ||||
|  | ||||
| 	// Create a new data source, takes PSR-7 request and response as arguments | ||||
| 	public function __construct(PsrRequest $psrRequest = null, PsrResponse $psrResponse = null) | ||||
| 	{ | ||||
| 		$this->psrRequest  = $psrRequest; | ||||
| 		$this->psrResponse = $psrResponse; | ||||
| 	} | ||||
|  | ||||
| 	// Adds request and response information to the request | ||||
| 	public function resolve(Request $request) | ||||
| 	{ | ||||
| 		if ($this->psrRequest) { | ||||
| 			$request->method   = $this->psrRequest->getMethod(); | ||||
| 			$request->uri      = $this->getRequestUri(); | ||||
| 			$request->headers  = $this->getRequestHeaders(); | ||||
| 			$request->getData  = $this->sanitize($this->psrRequest->getQueryParams()); | ||||
| 			$request->postData = $this->sanitize($this->psrRequest->getParsedBody()); | ||||
| 			$request->cookies  = $this->sanitize($this->psrRequest->getCookieParams()); | ||||
| 			$request->time     = $this->getRequestTime(); | ||||
| 		} | ||||
|  | ||||
| 		if ($this->psrResponse !== null) { | ||||
| 			$request->responseStatus = $this->psrResponse->getStatusCode(); | ||||
| 			$request->responseTime   = $this->getResponseTime(); | ||||
| 		} | ||||
|  | ||||
| 		return $request; | ||||
| 	} | ||||
|  | ||||
| 	// Normalize items in the array and remove passwords | ||||
| 	protected function sanitize($data) | ||||
| 	{ | ||||
| 		return is_array($data) ? $this->removePasswords((new Serializer)->normalizeEach($data)) : $data; | ||||
| 	} | ||||
|  | ||||
| 	// Get the response time, fetching it from ServerParams | ||||
| 	protected function getRequestTime() | ||||
| 	{ | ||||
| 		$env = $this->psrRequest->getServerParams(); | ||||
|  | ||||
| 		if (isset($env['REQUEST_TIME_FLOAT'])) { | ||||
| 			return $env['REQUEST_TIME_FLOAT']; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Get the response time (current time, assuming most of the application code has already run at this point) | ||||
| 	protected function getResponseTime() | ||||
| 	{ | ||||
| 		return microtime(true); | ||||
| 	} | ||||
|  | ||||
| 	// Get the request headers | ||||
| 	protected function getRequestHeaders() | ||||
| 	{ | ||||
| 		$headers = []; | ||||
|  | ||||
| 		foreach ($this->psrRequest->getHeaders() as $header => $values) { | ||||
| 			if (strtoupper(substr($header, 0, 5)) === 'HTTP_') { | ||||
| 				$header = substr($header, 5); | ||||
| 			} | ||||
|  | ||||
| 			$header = str_replace('_', ' ', $header); | ||||
| 			$header = ucwords(strtolower($header)); | ||||
| 			$header = str_replace(' ', '-', $header); | ||||
|  | ||||
| 			$headers[$header] = $values; | ||||
| 		} | ||||
|  | ||||
| 		ksort($headers); | ||||
|  | ||||
| 		return $headers; | ||||
| 	} | ||||
|  | ||||
| 	// Get the request URI | ||||
| 	protected function getRequestUri() | ||||
| 	{ | ||||
| 		$uri = $this->psrRequest->getUri(); | ||||
|  | ||||
| 		return $uri->getPath() . ($uri->getQuery() ? '?' . $uri->getQuery() : ''); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										104
									
								
								vendor/itsgoingd/clockwork/Clockwork/DataSource/SlimDataSource.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								vendor/itsgoingd/clockwork/Clockwork/DataSource/SlimDataSource.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,104 @@ | ||||
| <?php namespace Clockwork\DataSource; | ||||
|  | ||||
| use Clockwork\DataSource\DataSource; | ||||
| use Clockwork\Request\Request; | ||||
|  | ||||
| use Slim\Slim; | ||||
|  | ||||
| // Data source for Slim 2 framework, provides controller, request and response information | ||||
| class SlimDataSource extends DataSource | ||||
| { | ||||
| 	// Slim instance | ||||
| 	protected $slim; | ||||
|  | ||||
| 	// Create a new data source, takes Slim instance as an argument | ||||
| 	public function __construct(Slim $slim) | ||||
| 	{ | ||||
| 		$this->slim = $slim; | ||||
| 	} | ||||
|  | ||||
| 	// Adds request and response information to the request | ||||
| 	public function resolve(Request $request) | ||||
| 	{ | ||||
| 		$request->method         = $this->getRequestMethod(); | ||||
| 		$request->uri            = $this->getRequestUri(); | ||||
| 		$request->controller     = $this->getController(); | ||||
| 		$request->headers        = $this->getRequestHeaders(); | ||||
| 		$request->responseStatus = $this->getResponseStatus(); | ||||
|  | ||||
| 		return $request; | ||||
| 	} | ||||
|  | ||||
| 	// Get a textual representation of current route's controller | ||||
| 	protected function getController() | ||||
| 	{ | ||||
| 		$matchedRoutes = $this->slim->router()->getMatchedRoutes( | ||||
| 			$this->slim->request()->getMethod(), $this->slim->request()->getResourceUri() | ||||
| 		); | ||||
|  | ||||
| 		if (! count($matchedRoutes)) return; | ||||
|  | ||||
| 		$controller = end($matchedRoutes)->getCallable(); | ||||
|  | ||||
| 		if ($controller instanceof \Closure) { | ||||
| 			$controller = 'anonymous function'; | ||||
| 		} elseif (is_object($controller)) { | ||||
| 			$controller = 'instance of ' . get_class($controller); | ||||
| 		} elseif (is_array($controller) && count($controller) == 2) { | ||||
| 			if (is_object($controller[0])) { | ||||
| 				$controller = get_class($controller[0]) . '->' . $controller[1]; | ||||
| 			} else { | ||||
| 				$controller = $controller[0] . '::' . $controller[1]; | ||||
| 			} | ||||
| 		} elseif (! is_string($controller)) { | ||||
| 			$controller = null; | ||||
| 		} | ||||
|  | ||||
| 		return $controller; | ||||
| 	} | ||||
|  | ||||
| 	// Get the request headers | ||||
| 	protected function getRequestHeaders() | ||||
| 	{ | ||||
| 		$headers = []; | ||||
|  | ||||
| 		foreach ($_SERVER as $key => $value) { | ||||
| 			if (substr($key, 0, 5) !== 'HTTP_') continue; | ||||
|  | ||||
| 			$header = substr($key, 5); | ||||
| 			$header = str_replace('_', ' ', $header); | ||||
| 			$header = ucwords(strtolower($header)); | ||||
| 			$header = str_replace(' ', '-', $header); | ||||
|  | ||||
| 			$value = $this->slim->request()->headers($header, $value); | ||||
|  | ||||
| 			if (! isset($headers[$header])) { | ||||
| 				$headers[$header] = [ $value ]; | ||||
| 			} else { | ||||
| 				$headers[$header][] = $value; | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		ksort($headers); | ||||
|  | ||||
| 		return $headers; | ||||
| 	} | ||||
|  | ||||
| 	// Get the request method | ||||
| 	protected function getRequestMethod() | ||||
| 	{ | ||||
| 		return $this->slim->request()->getMethod(); | ||||
| 	} | ||||
|  | ||||
| 	// Get the request URI | ||||
| 	protected function getRequestUri() | ||||
| 	{ | ||||
| 		return $this->slim->request()->getPathInfo(); | ||||
| 	} | ||||
|  | ||||
| 	// Get the response status code | ||||
| 	protected function getResponseStatus() | ||||
| 	{ | ||||
| 		return $this->slim->response()->status(); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										45
									
								
								vendor/itsgoingd/clockwork/Clockwork/DataSource/SwiftDataSource.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								vendor/itsgoingd/clockwork/Clockwork/DataSource/SwiftDataSource.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,45 @@ | ||||
| <?php namespace Clockwork\DataSource; | ||||
|  | ||||
| use Clockwork\Request\Request; | ||||
| use Clockwork\Request\Timeline\Timeline; | ||||
| use Clockwork\Support\Swift\SwiftPluginClockworkTimeline; | ||||
|  | ||||
| use Swift_Mailer; | ||||
|  | ||||
| // Data source for Swift mailer, provides sent emails | ||||
| class SwiftDataSource extends DataSource | ||||
| { | ||||
| 	// Swift instance | ||||
| 	protected $swift; | ||||
|  | ||||
| 	// Clockwork timeline instance | ||||
| 	protected $timeline; | ||||
|  | ||||
| 	// Create a new data source, takes a Swift instance as an argument | ||||
| 	public function __construct(Swift_Mailer $swift) | ||||
| 	{ | ||||
| 		$this->swift = $swift; | ||||
|  | ||||
| 		$this->timeline = new Timeline; | ||||
| 	} | ||||
|  | ||||
| 	// Listen to the email events | ||||
| 	public function listenToEvents() | ||||
| 	{ | ||||
| 		$this->swift->registerPlugin(new SwiftPluginClockworkTimeline($this->timeline)); | ||||
| 	} | ||||
|  | ||||
| 	// Adds send emails to the request | ||||
| 	public function resolve(Request $request) | ||||
| 	{ | ||||
| 		$request->emailsData = array_merge($request->emailsData, $this->timeline->finalize()); | ||||
|  | ||||
| 		return $request; | ||||
| 	} | ||||
|  | ||||
| 	// Reset the data source to an empty state, clearing any collected data | ||||
| 	public function reset() | ||||
| 	{ | ||||
| 		$this->timeline = new Timeline; | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										40
									
								
								vendor/itsgoingd/clockwork/Clockwork/DataSource/TwigDataSource.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								vendor/itsgoingd/clockwork/Clockwork/DataSource/TwigDataSource.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,40 @@ | ||||
| <?php namespace Clockwork\DataSource; | ||||
|  | ||||
| use Clockwork\Request\Request; | ||||
| use Clockwork\Support\Twig\ProfilerClockworkDumper; | ||||
|  | ||||
| use Twig_Environment; | ||||
| use Twig_Extension_Profiler; | ||||
| use Twig_Profiler_Profile; | ||||
|  | ||||
| // Data source for Twig, provides rendered views | ||||
| class TwigDataSource extends DataSource | ||||
| { | ||||
| 	// Twig environment instance | ||||
| 	protected $twig; | ||||
|  | ||||
| 	// Twig profile instance | ||||
| 	protected $profile; | ||||
|  | ||||
| 	// Create a new data source, takes Twig instance as an argument | ||||
| 	public function __construct(Twig_Environment $twig) | ||||
| 	{ | ||||
| 		$this->twig = $twig; | ||||
| 	} | ||||
|  | ||||
| 	// Register the Twig profiler extension | ||||
| 	public function listenToEvents() | ||||
| 	{ | ||||
| 		$this->twig->addExtension(new Twig_Extension_Profiler($this->profile = new Twig_Profiler_Profile)); | ||||
| 	} | ||||
|  | ||||
| 	// Adds rendered views to the request | ||||
| 	public function resolve(Request $request) | ||||
| 	{ | ||||
| 		$timeline = (new ProfilerClockworkDumper)->dump($this->profile); | ||||
|  | ||||
| 		$request->viewsData = array_merge($request->viewsData, $timeline->finalize()); | ||||
|  | ||||
| 		return $request; | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										31
									
								
								vendor/itsgoingd/clockwork/Clockwork/DataSource/XdebugDataSource.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								vendor/itsgoingd/clockwork/Clockwork/DataSource/XdebugDataSource.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | ||||
| <?php namespace Clockwork\DataSource; | ||||
|  | ||||
| use Clockwork\Request\Request; | ||||
|  | ||||
| // Data source for Xdebug, provides profiling data | ||||
| class XdebugDataSource extends DataSource | ||||
| { | ||||
| 	// Adds profiling data path to the request | ||||
| 	public function resolve(Request $request) | ||||
| 	{ | ||||
| 		$request->xdebug = [ 'profile' => xdebug_get_profiler_filename() ]; | ||||
|  | ||||
| 		return $request; | ||||
| 	} | ||||
|  | ||||
| 	// Extends the request with full profiling data | ||||
| 	public function extend(Request $request) | ||||
| 	{ | ||||
| 		$profile = isset($request->xdebug['profile']) ? $request->xdebug['profile'] : null; | ||||
|  | ||||
| 		if ($profile && ! preg_match('/\.php$/', $profile) && is_readable($profile)) { | ||||
| 			$request->xdebug['profileData'] = file_get_contents($profile); | ||||
|  | ||||
| 			if (preg_match('/\.gz$/', $profile)) { | ||||
| 				$request->xdebug['profileData'] = gzdecode($request->xdebug['profileData']); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		return $request; | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										36
									
								
								vendor/itsgoingd/clockwork/Clockwork/Helpers/Concerns/ResolvesViewName.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								vendor/itsgoingd/clockwork/Clockwork/Helpers/Concerns/ResolvesViewName.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,36 @@ | ||||
| <?php namespace Clockwork\Helpers\Concerns; | ||||
|  | ||||
| use Clockwork\Helpers\StackFrame; | ||||
|  | ||||
| // Replaces the first stack frame rendering a Laravel view with a duplicate with a resolved original view path (instead | ||||
| // of the compiled view path) | ||||
| trait ResolvesViewName | ||||
| { | ||||
| 	public function resolveViewName() | ||||
| 	{ | ||||
| 		$viewFrame = $this->first(function ($frame) { | ||||
| 			return $frame->shortPath ? preg_match('#^/storage/framework/views/[a-z0-9]+\.php$#', $frame->shortPath) : false; | ||||
| 		}); | ||||
|  | ||||
| 		if (! $viewFrame) return $this; | ||||
|  | ||||
| 		$renderFrame = $this->first(function ($frame) { | ||||
| 			return $frame->call == 'Illuminate\View\View->getContents()' | ||||
| 				&& $frame->object instanceof \Illuminate\View\View; | ||||
| 		}); | ||||
|  | ||||
| 		if (! $renderFrame) return $this; | ||||
|  | ||||
| 		$resolvedViewFrame = new StackFrame( | ||||
| 			[ 'file' => $renderFrame->object->getPath(), 'line' => $viewFrame->line ], | ||||
| 			$this->basePath, | ||||
| 			$this->vendorPath | ||||
| 		); | ||||
|  | ||||
| 		return $this->copy(array_merge( | ||||
| 			array_slice($this->frames, 0, array_search($viewFrame, $this->frames)), | ||||
| 			[ $resolvedViewFrame ], | ||||
| 			array_slice($this->frames, array_search($viewFrame, $this->frames) + 2) | ||||
| 		)); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										139
									
								
								vendor/itsgoingd/clockwork/Clockwork/Helpers/Serializer.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										139
									
								
								vendor/itsgoingd/clockwork/Clockwork/Helpers/Serializer.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,139 @@ | ||||
| <?php namespace Clockwork\Helpers; | ||||
|  | ||||
| // Prepares various types of data for serialization | ||||
| class Serializer | ||||
| { | ||||
| 	// Serialized objects cache by object hash | ||||
| 	protected $cache = []; | ||||
|  | ||||
| 	// Options for the current instance | ||||
| 	protected $options = []; | ||||
|  | ||||
| 	// Default options for new instances | ||||
| 	protected static $defaults = [ | ||||
| 		'blackbox' => [ | ||||
| 			\Illuminate\Container\Container::class, | ||||
| 			\Illuminate\Foundation\Application::class, | ||||
| 			\Laravel\Lumen\Application::class | ||||
| 		], | ||||
| 		'limit' => 10, | ||||
| 		'toArray' => false, | ||||
| 		'toString' => false, | ||||
| 		'debugInfo' => true, | ||||
| 		'jsonSerialize' => false, | ||||
| 		'traces' => true, | ||||
| 		'tracesFilter' => null, | ||||
| 		'tracesSkip' => null, | ||||
| 		'tracesLimit' => null | ||||
| 	]; | ||||
|  | ||||
| 	// Create a new instance optionally with options overriding defaults | ||||
| 	public function __construct(array $options = []) | ||||
| 	{ | ||||
| 		$this->options = $options + static::$defaults; | ||||
| 	} | ||||
|  | ||||
| 	// Set default options for all new instances | ||||
| 	public static function defaults(array $defaults) | ||||
| 	{ | ||||
| 		static::$defaults = $defaults + static::$defaults; | ||||
| 	} | ||||
|  | ||||
| 	// Prepares the passed data to be ready for serialization, takes any kind of data to normalize as the first | ||||
| 	// argument, other arguments are used internally in recursion | ||||
| 	public function normalize($data, $context = null, $limit = null) | ||||
| 	{ | ||||
| 		if ($context === null) $context = [ 'references' => [] ]; | ||||
| 		if ($limit === null) $limit = $this->options['limit']; | ||||
|  | ||||
| 		if (is_array($data)) { | ||||
| 			if ($limit === 0) return [ '__type__' => 'array', '__omitted__' => 'limit' ]; | ||||
|  | ||||
| 			return [ '__type__' => 'array' ] + $this->normalizeEach($data, $context, $limit - 1); | ||||
| 		} elseif (is_object($data)) { | ||||
| 			if ($data instanceof \Closure) return [ '__type__' => 'anonymous function' ]; | ||||
|  | ||||
| 			$className = get_class($data); | ||||
| 			$objectHash = spl_object_hash($data); | ||||
|  | ||||
| 			if ($limit === 0) return [ '__class__' => $className, '__omitted__' => 'limit' ]; | ||||
|  | ||||
| 			if (isset($context['references'][$objectHash])) return [ '__type__' => 'recursion' ]; | ||||
|  | ||||
| 			$context['references'][$objectHash] = true; | ||||
|  | ||||
| 			if (isset($this->cache[$objectHash])) return $this->cache[$objectHash]; | ||||
|  | ||||
| 			if ($this->options['blackbox'] && in_array($className, $this->options['blackbox'])) { | ||||
| 				return $this->cache[$objectHash] = [ '__class__' => $className, '__omitted__' => 'blackbox' ]; | ||||
| 			} elseif ($this->options['toString'] && method_exists($data, '__toString')) { | ||||
| 				return $this->cache[$objectHash] = (string) $data; | ||||
| 			} | ||||
|  | ||||
| 			if ($this->options['debugInfo'] && method_exists($data, '__debugInfo')) { | ||||
| 				$data = (array) $data->__debugInfo(); | ||||
| 			} elseif ($this->options['jsonSerialize'] && method_exists($data, 'jsonSerialize')) { | ||||
| 				$data = (array) $data->jsonSerialize(); | ||||
| 			} elseif ($this->options['toArray'] && method_exists($data, 'toArray')) { | ||||
| 				$data = (array) $data->toArray(); | ||||
| 			} else { | ||||
| 				$data = (array) $data; | ||||
| 			} | ||||
|  | ||||
| 			$data = array_combine( | ||||
| 				array_map(function ($key) { | ||||
| 					// replace null-byte prefixes of protected and private properties used by php with * (protected) | ||||
| 					// and ~ (private) | ||||
| 					return preg_replace('/^\0.+?\0/', '~', str_replace("\0*\0", '*', $key)); | ||||
| 				}, array_keys($data)), | ||||
| 				$this->normalizeEach($data, $context, $limit - 1) | ||||
| 			); | ||||
|  | ||||
| 			return $this->cache[$objectHash] = [ '__class__' => $className ] + $data; | ||||
| 		} elseif (is_resource($data)) { | ||||
| 			return [ '__type__' => 'resource' ]; | ||||
| 		} | ||||
|  | ||||
| 		return $data; | ||||
| 	} | ||||
|  | ||||
| 	// Normalize each member of an array (doesn't add metadata for top level) | ||||
| 	public function normalizeEach($data, $context = null, $limit = null) { | ||||
| 		return array_map(function ($item) use ($context, $limit) { | ||||
| 			return $this->normalize($item, $context, $limit); | ||||
| 		}, $data); | ||||
| 	} | ||||
|  | ||||
| 	// Normalize a stack trace instance | ||||
| 	public function trace(StackTrace $trace) | ||||
| 	{ | ||||
| 		if (! $this->options['traces']) return null; | ||||
|  | ||||
| 		if ($this->options['tracesFilter']) $trace = $trace->filter($this->options['tracesFilter']); | ||||
| 		if ($this->options['tracesSkip']) $trace = $trace->skip($this->options['tracesSkip']); | ||||
| 		if ($this->options['tracesLimit']) $trace = $trace->limit($this->options['tracesLimit']); | ||||
|  | ||||
| 		return array_map(function ($frame) { | ||||
| 			return [ | ||||
| 				'call' => $frame->call, | ||||
| 				'file' => $frame->file, | ||||
| 				'line' => $frame->line, | ||||
| 				'isVendor' => (bool) $frame->vendor | ||||
| 			]; | ||||
| 		}, $trace->frames()); | ||||
| 	} | ||||
|  | ||||
| 	// Normalize an exception instance | ||||
| 	public function exception(/* Throwable */ $exception) | ||||
| 	{ | ||||
| 		return [ | ||||
| 			'type'     => get_class($exception), | ||||
| 			'message'  => $exception->getMessage(), | ||||
| 			'code'     => $exception->getCode(), | ||||
| 			'file'     => $exception->getFile(), | ||||
| 			'line'     => $exception->getLine(), | ||||
| 			'trace'    => (new Serializer([ 'tracesLimit' => false ]))->trace(StackTrace::from($exception->getTrace())), | ||||
| 			'previous' => $exception->getPrevious() ? $this->exception($exception->getPrevious()) : null | ||||
| 		]; | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										45
									
								
								vendor/itsgoingd/clockwork/Clockwork/Helpers/ServerTiming.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								vendor/itsgoingd/clockwork/Clockwork/Helpers/ServerTiming.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,45 @@ | ||||
| <?php namespace Clockwork\Helpers; | ||||
|  | ||||
| use Clockwork\Request\Request; | ||||
|  | ||||
| // Generates Server-Timing header value | ||||
| class ServerTiming | ||||
| { | ||||
| 	// Performance metrics to include | ||||
| 	protected $metrics = []; | ||||
|  | ||||
| 	// Add a performance metric | ||||
| 	public function add($metric, $value, $description) | ||||
| 	{ | ||||
| 		$this->metrics[] = [ 'metric' => $metric, 'value' => $value, 'description' => $description ]; | ||||
|  | ||||
| 		return $this; | ||||
| 	} | ||||
|  | ||||
| 	// Generate the header value | ||||
| 	public function value() | ||||
| 	{ | ||||
| 		return implode(', ', array_map(function ($metric) { | ||||
| 			return "{$metric['metric']}; dur={$metric['value']}; desc=\"{$metric['description']}\""; | ||||
| 		}, $this->metrics)); | ||||
| 	} | ||||
|  | ||||
| 	// Create a new instance from a Clockwork request | ||||
| 	public static function fromRequest(Request $request, $eventsCount = 10) | ||||
| 	{ | ||||
| 		$header = new static; | ||||
|  | ||||
| 		$header->add('app', $request->getResponseDuration(), 'Application'); | ||||
|  | ||||
| 		if ($request->getDatabaseDuration()) { | ||||
| 			$header->add('db', $request->getDatabaseDuration(), 'Database'); | ||||
| 		} | ||||
|  | ||||
| 		// add timeline events limited to a set number so the header doesn't get too large | ||||
| 		foreach (array_slice($request->timeline()->events, 0, $eventsCount) as $i => $event) { | ||||
| 			$header->add("timeline-event-{$i}", $event->duration(), $event->description); | ||||
| 		} | ||||
|  | ||||
| 		return $header; | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										148
									
								
								vendor/itsgoingd/clockwork/Clockwork/Helpers/StackFilter.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										148
									
								
								vendor/itsgoingd/clockwork/Clockwork/Helpers/StackFilter.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,148 @@ | ||||
| <?php namespace Clockwork\Helpers; | ||||
|  | ||||
| // Filter stack traces | ||||
| class StackFilter | ||||
| { | ||||
| 	protected $classes = []; | ||||
| 	protected $notClasses = []; | ||||
|  | ||||
| 	protected $files = []; | ||||
| 	protected $notFiles = []; | ||||
|  | ||||
| 	protected $functions = []; | ||||
| 	protected $notFunctions = []; | ||||
|  | ||||
| 	protected $namespaces = []; | ||||
| 	protected $notNamespaces = []; | ||||
|  | ||||
| 	protected $vendors = []; | ||||
| 	protected $notVendors = []; | ||||
|  | ||||
| 	public static function make() | ||||
| 	{ | ||||
| 		return new static; | ||||
| 	} | ||||
|  | ||||
| 	public function isClass($classes) | ||||
| 	{ | ||||
| 		$this->classes = array_merge($this->classes, is_array($classes) ? $classes : [ $classes ]); | ||||
| 		return $this; | ||||
| 	} | ||||
|  | ||||
| 	public function isNotClass($classes) | ||||
| 	{ | ||||
| 		$this->notClasses = array_merge($this->notClasses, is_array($classes) ? $classes : [ $classes ]); | ||||
| 		return $this; | ||||
| 	} | ||||
|  | ||||
| 	public function isFile($files) | ||||
| 	{ | ||||
| 		$this->files = array_merge($this->files, is_array($files) ? $files : [ $files ]); | ||||
| 		return $this; | ||||
| 	} | ||||
|  | ||||
| 	public function isNotFile($files) | ||||
| 	{ | ||||
| 		$this->notFiles = array_merge($this->notFiles, is_array($files) ? $files : [ $files ]); | ||||
| 		return $this; | ||||
| 	} | ||||
|  | ||||
| 	public function isFunction($functions) | ||||
| 	{ | ||||
| 		$this->functions = array_merge($this->functions, is_array($functions) ? $functions : [ $functions ]); | ||||
| 		return $this; | ||||
| 	} | ||||
|  | ||||
| 	public function isNotFunction($functions) | ||||
| 	{ | ||||
| 		$this->notFunctions = array_merge($this->notFunctions, is_array($functions) ? $functions : [ $functions ]); | ||||
| 		return $this; | ||||
| 	} | ||||
|  | ||||
| 	public function isNamespace($namespaces) | ||||
| 	{ | ||||
| 		$this->namespaces = array_merge($this->namespaces, is_array($namespaces) ? $namespaces : [ $namespaces ]); | ||||
| 		return $this; | ||||
| 	} | ||||
|  | ||||
| 	public function isNotNamespace($namespaces) | ||||
| 	{ | ||||
| 		$this->notNamespaces = array_merge($this->notNamespaces, is_array($namespaces) ? $namespaces : [ $namespaces ]); | ||||
| 		return $this; | ||||
| 	} | ||||
|  | ||||
| 	public function isVendor($vendors) | ||||
| 	{ | ||||
| 		$this->vendors = array_merge($this->vendors, is_array($vendors) ? $vendors : [ $vendors ]); | ||||
| 		return $this; | ||||
| 	} | ||||
|  | ||||
| 	public function isNotVendor($vendors) | ||||
| 	{ | ||||
| 		$this->notVendors = array_merge($this->notVendors, is_array($vendors) ? $vendors : [ $vendors ]); | ||||
| 		return $this; | ||||
| 	} | ||||
|  | ||||
| 	// Apply the filter to a stack frame | ||||
| 	public function filter(StackFrame $frame) | ||||
| 	{ | ||||
| 		return $this->matchesClass($frame) | ||||
| 			&& $this->matchesFile($frame) | ||||
| 			&& $this->matchesFunction($frame) | ||||
| 			&& $this->matchesNamespace($frame) | ||||
| 			&& $this->matchesVendor($frame); | ||||
| 	} | ||||
|  | ||||
| 	// Return a closure calling this filter | ||||
| 	public function closure() | ||||
| 	{ | ||||
| 		return function ($frame) { return $this->filter($frame); }; | ||||
| 	} | ||||
|  | ||||
| 	protected function matchesClass(StackFrame $frame) | ||||
| 	{ | ||||
| 		if (count($this->classes) && ! in_array($frame->class, $this->classes)) return false; | ||||
| 		if (count($this->notClasses) && in_array($frame->class, $this->notClasses)) return false; | ||||
|  | ||||
| 		return true; | ||||
| 	} | ||||
|  | ||||
| 	protected function matchesFile(StackFrame $frame) | ||||
| 	{ | ||||
| 		if (count($this->files) && ! in_array($frame->file, $this->files)) return false; | ||||
| 		if (count($this->notFiles) && in_array($frame->file, $this->notFiles)) return false; | ||||
|  | ||||
| 		return true; | ||||
| 	} | ||||
|  | ||||
| 	protected function matchesFunction(StackFrame $frame) | ||||
| 	{ | ||||
| 		if (count($this->functions) && ! in_array($frame->function, $this->functions)) return false; | ||||
| 		if (count($this->notFunctions) && in_array($frame->function, $this->notFunctions)) return false; | ||||
|  | ||||
| 		return true; | ||||
| 	} | ||||
|  | ||||
| 	protected function matchesNamespace(StackFrame $frame) | ||||
| 	{ | ||||
| 		foreach ($this->notNamespaces as $namespace) { | ||||
| 			if ($frame->class !== null && strpos($frame->class, "{$namespace}\\") !== false) return false; | ||||
| 		} | ||||
|  | ||||
| 		if (! count($this->namespaces)) return true; | ||||
|  | ||||
| 		foreach ($this->namespaces as $namespace) { | ||||
| 			if ($frame->class !== null && strpos($frame->class, "{$namespace}\\") !== false) return true; | ||||
| 		} | ||||
|  | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	protected function matchesVendor(StackFrame $frame) | ||||
| 	{ | ||||
| 		if (count($this->vendors) && ! in_array($frame->vendor, $this->vendors)) return false; | ||||
| 		if (count($this->notVendors) && in_array($frame->vendor, $this->notVendors)) return false; | ||||
|  | ||||
| 		return true; | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										38
									
								
								vendor/itsgoingd/clockwork/Clockwork/Helpers/StackFrame.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								vendor/itsgoingd/clockwork/Clockwork/Helpers/StackFrame.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | ||||
| <?php namespace Clockwork\Helpers; | ||||
|  | ||||
| // A single frame of a stack trace | ||||
| class StackFrame | ||||
| { | ||||
| 	public $call; | ||||
| 	public $function; | ||||
| 	public $line; | ||||
| 	public $file; | ||||
| 	public $class; | ||||
| 	public $object; | ||||
| 	public $type; | ||||
| 	public $args = []; | ||||
| 	public $shortPath; | ||||
| 	public $vendor; | ||||
|  | ||||
| 	public function __construct(array $data = [], $basePath = '', $vendorPath = '') | ||||
| 	{ | ||||
| 		foreach ($data as $key => $value) { | ||||
| 			$this->$key = $value; | ||||
| 		} | ||||
|  | ||||
| 		$this->call = $this->formatCall(); | ||||
|  | ||||
| 		$this->shortPath = $this->file ? str_replace($basePath, '', $this->file) : null; | ||||
| 		$this->vendor = ($this->file && strpos($this->file, $vendorPath) === 0) | ||||
| 			? explode(DIRECTORY_SEPARATOR, str_replace($vendorPath, '', $this->file))[0] : null; | ||||
| 	} | ||||
|  | ||||
| 	protected function formatCall() | ||||
| 	{ | ||||
| 		if ($this->class) { | ||||
| 			return "{$this->class}{$this->type}{$this->function}()"; | ||||
| 		} else { | ||||
| 			return "{$this->function}()"; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										127
									
								
								vendor/itsgoingd/clockwork/Clockwork/Helpers/StackTrace.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										127
									
								
								vendor/itsgoingd/clockwork/Clockwork/Helpers/StackTrace.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,127 @@ | ||||
| <?php namespace Clockwork\Helpers; | ||||
|  | ||||
| // A stack trace | ||||
| class StackTrace | ||||
| { | ||||
| 	use Concerns\ResolvesViewName; | ||||
|  | ||||
| 	protected $frames; | ||||
|  | ||||
| 	protected $basePath; | ||||
| 	protected $vendorPath; | ||||
|  | ||||
| 	// Capture a new stack trace, accepts an array of options, "arguments" to include arguments in the trace and "limit" | ||||
| 	// to limit the trace length | ||||
| 	public static function get($options = []) | ||||
| 	{ | ||||
| 		$backtraceOptions = isset($options['arguments']) | ||||
| 			? DEBUG_BACKTRACE_PROVIDE_OBJECT : DEBUG_BACKTRACE_PROVIDE_OBJECT | DEBUG_BACKTRACE_IGNORE_ARGS; | ||||
| 		$limit = isset($options['limit']) ? $options['limit'] : 0; | ||||
|  | ||||
| 		return static::from(debug_backtrace($backtraceOptions, $limit)); | ||||
| 	} | ||||
|  | ||||
| 	// Create a stack trace from an existing debug_backtrace output | ||||
| 	public static function from(array $trace) | ||||
| 	{ | ||||
| 		$basePath = static::resolveBasePath(); | ||||
| 		$vendorPath = static::resolveVendorPath(); | ||||
|  | ||||
| 		return new static(array_map(function ($frame, $index) use ($basePath, $vendorPath, $trace) { | ||||
| 			return new StackFrame( | ||||
| 				static::fixCallUserFuncFrame($frame, $trace, $index), $basePath, $vendorPath | ||||
| 			); | ||||
| 		}, $trace, array_keys($trace)), $basePath, $vendorPath); | ||||
| 	} | ||||
|  | ||||
| 	public function __construct(array $frames, $basePath, $vendorPath) | ||||
| 	{ | ||||
| 		$this->frames = $frames; | ||||
| 		$this->basePath = $basePath; | ||||
| 		$this->vendorPath = $vendorPath; | ||||
| 	} | ||||
|  | ||||
| 	// Get all frames | ||||
| 	public function frames() | ||||
| 	{ | ||||
| 		return $this->frames; | ||||
| 	} | ||||
|  | ||||
| 	// Get the first frame, optionally filtered by a stack filter or a closure | ||||
| 	public function first($filter = null) | ||||
| 	{ | ||||
| 		if (! $filter) return reset($this->frames); | ||||
|  | ||||
| 		if ($filter instanceof StackFilter) $filter = $filter->closure(); | ||||
|  | ||||
| 		foreach ($this->frames as $frame) { | ||||
| 			if ($filter($frame)) return $frame; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Get the last frame, optionally filtered by a stack filter or a closure | ||||
| 	public function last($filter = null) | ||||
| 	{ | ||||
| 		if (! $filter) return $this->frames[count($this->frames) - 1]; | ||||
|  | ||||
| 		if ($filter instanceof StackFilter) $filter = $filter->closure(); | ||||
|  | ||||
| 		foreach (array_reverse($this->frames) as $frame) { | ||||
| 			if ($filter($frame)) return $frame; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Get trace filtered by a stack filter or a closure | ||||
| 	public function filter($filter = null) | ||||
| 	{ | ||||
| 		if ($filter instanceof StackFilter) $filter = $filter->closure(); | ||||
|  | ||||
| 		return $this->copy(array_values(array_filter($this->frames, $filter))); | ||||
| 	} | ||||
|  | ||||
| 	// Get trace skipping a number of frames or frames matching a stack filter or a closure | ||||
| 	public function skip($count = null) | ||||
| 	{ | ||||
| 		if ($count instanceof StackFilter) $count = $count->closure(); | ||||
| 		if ($count instanceof \Closure) $count = array_search($this->first($count), $this->frames); | ||||
|  | ||||
| 		return $this->copy(array_slice($this->frames, $count)); | ||||
| 	} | ||||
|  | ||||
| 	// Get trace with a number of frames from the top | ||||
| 	public function limit($count = null) | ||||
| 	{ | ||||
| 		return $this->copy(array_slice($this->frames, 0, $count)); | ||||
| 	} | ||||
|  | ||||
| 	// Get a copy of the trace | ||||
| 	public function copy($frames = null) | ||||
| 	{ | ||||
| 		return new static($frames ?: $this->frames, $this->basePath, $this->vendorPath); | ||||
| 	} | ||||
|  | ||||
| 	protected static function resolveBasePath() | ||||
| 	{ | ||||
| 		return substr(__DIR__, 0, strpos(__DIR__, DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR)); | ||||
| 	} | ||||
|  | ||||
| 	protected static function resolveVendorPath() | ||||
| 	{ | ||||
| 		return static::resolveBasePath() . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR; | ||||
| 	} | ||||
|  | ||||
| 	// Fixes call_user_func stack frames missing file and line | ||||
| 	protected static function fixCallUserFuncFrame($frame, array $trace, $index) | ||||
| 	{ | ||||
| 		if (isset($frame['file'])) return $frame; | ||||
|  | ||||
| 		$nextFrame = isset($trace[$index + 1]) ? $trace[$index + 1] : null; | ||||
|  | ||||
| 		if (! $nextFrame || ! in_array($nextFrame['function'], [ 'call_user_func', 'call_user_func_array' ])) return $frame; | ||||
|  | ||||
| 		$frame['file'] = $nextFrame['file']; | ||||
| 		$frame['line'] = $nextFrame['line']; | ||||
|  | ||||
| 		return $frame; | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										20
									
								
								vendor/itsgoingd/clockwork/Clockwork/Request/IncomingRequest.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								vendor/itsgoingd/clockwork/Clockwork/Request/IncomingRequest.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | ||||
| <?php namespace Clockwork\Request; | ||||
|  | ||||
| // Incoming HTTP request | ||||
| class IncomingRequest | ||||
| { | ||||
| 	// Method | ||||
| 	public $method; | ||||
| 	// URI | ||||
| 	public $uri; | ||||
|  | ||||
| 	// GET and POST data | ||||
| 	public $input = []; | ||||
| 	// Cookies | ||||
| 	public $cookies = []; | ||||
|  | ||||
| 	public function __construct(array $data = []) | ||||
| 	{ | ||||
| 		foreach ($data as $key => $val) $this->$key = $val; | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										125
									
								
								vendor/itsgoingd/clockwork/Clockwork/Request/Log.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										125
									
								
								vendor/itsgoingd/clockwork/Clockwork/Request/Log.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,125 @@ | ||||
| <?php namespace Clockwork\Request; | ||||
|  | ||||
| use Clockwork\Helpers\Serializer; | ||||
| use Clockwork\Helpers\StackTrace; | ||||
| use Clockwork\Helpers\StackFilter; | ||||
|  | ||||
| // Data structure representing a log with timestamped messages | ||||
| class Log | ||||
| { | ||||
| 	// Array of logged messages | ||||
| 	public $messages = []; | ||||
|  | ||||
| 	// Create a new log, optionally with existing messages | ||||
| 	public function __construct($messages = []) | ||||
| 	{ | ||||
| 		$this->messages = $messages; | ||||
| 	} | ||||
|  | ||||
| 	// Log a new message, with a level and context, context can be used to override serializer defaults, | ||||
| 	// $context['trace'] = true can be used to force collecting a stack trace | ||||
| 	public function log($level = LogLevel::INFO, $message = null, array $context = []) | ||||
| 	{ | ||||
| 		$trace = $this->hasTrace($context) ? $context['trace'] : StackTrace::get()->resolveViewName(); | ||||
|  | ||||
| 		$this->messages[] = [ | ||||
| 			'message'   => (new Serializer($context))->normalize($message), | ||||
| 			'exception' => $this->formatException($context), | ||||
| 			'context'   => $this->formatContext($context), | ||||
| 			'level'     => $level, | ||||
| 			'time'      => microtime(true), | ||||
| 			'trace'     => (new Serializer(! empty($context['trace']) ? [ 'traces' => true ] : []))->trace($trace) | ||||
| 		]; | ||||
| 	} | ||||
|  | ||||
| 	public function emergency($message, array $context = []) | ||||
| 	{ | ||||
| 		$this->log(LogLevel::EMERGENCY, $message, $context); | ||||
| 	} | ||||
|  | ||||
| 	public function alert($message, array $context = []) | ||||
| 	{ | ||||
| 		$this->log(LogLevel::ALERT, $message, $context); | ||||
| 	} | ||||
|  | ||||
| 	public function critical($message, array $context = []) | ||||
| 	{ | ||||
| 		$this->log(LogLevel::CRITICAL, $message, $context); | ||||
| 	} | ||||
|  | ||||
| 	public function error($message, array $context = []) | ||||
| 	{ | ||||
| 		$this->log(LogLevel::ERROR, $message, $context); | ||||
| 	} | ||||
|  | ||||
| 	public function warning($message, array $context = []) | ||||
| 	{ | ||||
| 		$this->log(LogLevel::WARNING, $message, $context); | ||||
| 	} | ||||
|  | ||||
| 	public function notice($message, array $context = []) | ||||
| 	{ | ||||
| 		$this->log(LogLevel::NOTICE, $message, $context); | ||||
| 	} | ||||
|  | ||||
| 	public function info($message, array $context = []) | ||||
| 	{ | ||||
| 		$this->log(LogLevel::INFO, $message, $context); | ||||
| 	} | ||||
|  | ||||
| 	public function debug($message, array $context = []) | ||||
| 	{ | ||||
| 		$this->log(LogLevel::DEBUG, $message, $context); | ||||
| 	} | ||||
|  | ||||
| 	// Merge another log instance into the current log | ||||
| 	public function merge(Log $log) | ||||
| 	{ | ||||
| 		$this->messages = array_merge($this->messages, $log->messages); | ||||
|  | ||||
| 		return $this; | ||||
| 	} | ||||
|  | ||||
| 	// Sort the log messages by timestamp | ||||
| 	public function sort() | ||||
| 	{ | ||||
| 		usort($this->messages, function ($a, $b) { return $a['time'] * 1000 - $b['time'] * 1000; }); | ||||
| 	} | ||||
|  | ||||
| 	// Get all messages as an array | ||||
| 	public function toArray() | ||||
| 	{ | ||||
| 		return $this->messages; | ||||
| 	} | ||||
|  | ||||
| 	// Format message context, removes exception and trace if we are serializing them | ||||
| 	protected function formatContext($context) | ||||
| 	{ | ||||
| 		if ($this->hasException($context)) unset($context['exception']); | ||||
| 		if ($this->hasTrace($context)) unset($context['trace']); | ||||
|  | ||||
| 		return (new Serializer)->normalize($context); | ||||
| 	} | ||||
|  | ||||
| 	// Format exception if present in the context | ||||
| 	protected function formatException($context) | ||||
| 	{ | ||||
| 		if ($this->hasException($context)) { | ||||
| 			return (new Serializer)->exception($context['exception']); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Check if context has serializable trace | ||||
| 	protected function hasTrace($context) | ||||
| 	{ | ||||
| 		return ! empty($context['trace']) && $context['trace'] instanceof StackTrace && empty($context['raw']); | ||||
| 	} | ||||
|  | ||||
| 	// Check if context has serializable exception | ||||
| 	protected function hasException($context) | ||||
| 	{ | ||||
| 		return ! empty($context['exception']) | ||||
| 			&& ($context['exception'] instanceof \Throwable || $context['exception'] instanceof \Exception) | ||||
| 			&& empty($context['raw']); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										13
									
								
								vendor/itsgoingd/clockwork/Clockwork/Request/LogLevel.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								vendor/itsgoingd/clockwork/Clockwork/Request/LogLevel.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| <?php namespace Clockwork\Request; | ||||
|  | ||||
| class LogLevel | ||||
| { | ||||
| 	const EMERGENCY = 'emergency'; | ||||
| 	const ALERT     = 'alert'; | ||||
| 	const CRITICAL  = 'critical'; | ||||
| 	const ERROR     = 'error'; | ||||
| 	const WARNING   = 'warning'; | ||||
| 	const NOTICE    = 'notice'; | ||||
| 	const INFO      = 'info'; | ||||
| 	const DEBUG     = 'debug'; | ||||
| } | ||||
							
								
								
									
										591
									
								
								vendor/itsgoingd/clockwork/Clockwork/Request/Request.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										591
									
								
								vendor/itsgoingd/clockwork/Clockwork/Request/Request.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,591 @@ | ||||
| <?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); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										10
									
								
								vendor/itsgoingd/clockwork/Clockwork/Request/RequestType.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								vendor/itsgoingd/clockwork/Clockwork/Request/RequestType.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| <?php namespace Clockwork\Request; | ||||
|  | ||||
| // Supported request types | ||||
| class RequestType | ||||
| { | ||||
| 	const REQUEST = 'request'; | ||||
| 	const COMMAND = 'command'; | ||||
| 	const QUEUE_JOB = 'queue-job'; | ||||
| 	const TEST = 'test'; | ||||
| } | ||||
							
								
								
									
										120
									
								
								vendor/itsgoingd/clockwork/Clockwork/Request/ShouldCollect.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								vendor/itsgoingd/clockwork/Clockwork/Request/ShouldCollect.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,120 @@ | ||||
| <?php namespace Clockwork\Request; | ||||
|  | ||||
| // Filter incoming requests before collecting data | ||||
| class ShouldCollect | ||||
| { | ||||
| 	// Enable on-demand mode, boolean or the secret value | ||||
| 	protected $onDemand = false; | ||||
| 	// Enable sampling, chance to be sampled (eg. 100 to collect 1 in 100 requests) | ||||
| 	protected $sample = false; | ||||
|  | ||||
| 	// List of URIs that should not be collected, can contain regexes | ||||
| 	protected $except = []; | ||||
| 	// List of URIs that should only be collected, can contain regexes (only used if non-empty) | ||||
| 	protected $only = []; | ||||
|  | ||||
| 	// Disable collection of OPTIONS method requests (most commonly used for CORS pre-flight requests) | ||||
| 	protected $exceptPreflight = false; | ||||
|  | ||||
| 	// Custom filter callback | ||||
| 	protected $callback; | ||||
|  | ||||
| 	// Append one or more except URIs | ||||
| 	public function except($uris) | ||||
| 	{ | ||||
| 		$this->except = array_merge($this->except, is_array($uris) ? $uris : [ $uris ]); | ||||
|  | ||||
| 		return $this; | ||||
| 	} | ||||
|  | ||||
| 	// Append one or more only URIs | ||||
| 	public function only($uris) | ||||
| 	{ | ||||
| 		$this->only = array_merge($this->only, is_array($uris) ? $uris : [ $uris ]); | ||||
|  | ||||
| 		return $this; | ||||
| 	} | ||||
|  | ||||
| 	// Merge multiple settings from array | ||||
| 	public function merge(array $data = []) | ||||
| 	{ | ||||
| 		foreach ($data as $key => $val) $this->$key = $val; | ||||
| 	} | ||||
|  | ||||
| 	// Apply the filter to an incoming request | ||||
| 	public function filter(IncomingRequest $request) | ||||
| 	{ | ||||
| 		return $this->passOnDemand($request) | ||||
| 			&& $this->passSampling() | ||||
| 			&& $this->passExcept($request) | ||||
| 			&& $this->passOnly($request) | ||||
| 			&& $this->passExceptPreflight($request) | ||||
| 			&& $this->passCallback($request); | ||||
| 	} | ||||
|  | ||||
| 	protected function passOnDemand(IncomingRequest $request) | ||||
| 	{ | ||||
| 		if (! $this->onDemand) return true; | ||||
|  | ||||
| 		if ($this->onDemand !== true) { | ||||
| 			$input = isset($request->input['clockwork-profile']) ? $request->input['clockwork-profile'] : ''; | ||||
| 			$cookie = isset($request->cookies['clockwork-profile']) ? $request->cookies['clockwork-profile'] : ''; | ||||
|  | ||||
| 			return hash_equals($this->onDemand, $input) || hash_equals($this->onDemand, $cookie); | ||||
| 		} | ||||
|  | ||||
| 		return isset($request->input['clockwork-profile']) || isset($request->cookies['clockwork-profile']); | ||||
| 	} | ||||
|  | ||||
| 	protected function passSampling() | ||||
| 	{ | ||||
| 		if (! $this->sample) return true; | ||||
|  | ||||
| 		return mt_rand(0, $this->sample) == $this->sample; | ||||
| 	} | ||||
|  | ||||
| 	protected function passExcept(IncomingRequest $request) | ||||
| 	{ | ||||
| 		if (! count($this->except)) return true; | ||||
|  | ||||
| 		foreach ($this->except as $pattern) { | ||||
| 			if (preg_match('#' . str_replace('#', '\#', $pattern) . '#', $request->uri)) return false; | ||||
| 		} | ||||
|  | ||||
| 		return true; | ||||
| 	} | ||||
|  | ||||
| 	protected function passOnly(IncomingRequest $request) | ||||
| 	{ | ||||
| 		if (! count($this->only)) return true; | ||||
|  | ||||
| 		foreach ($this->only as $pattern) { | ||||
| 			if (preg_match('#' . str_replace('#', '\#', $pattern) . '#', $request->uri)) return true; | ||||
| 		} | ||||
|  | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	protected function passExceptPreflight(IncomingRequest $request) | ||||
| 	{ | ||||
| 		if (! $this->exceptPreflight) return true; | ||||
|  | ||||
| 		return strtoupper($request->method) != 'OPTIONS'; | ||||
| 	} | ||||
|  | ||||
| 	protected function passCallback(IncomingRequest $request) | ||||
| 	{ | ||||
| 		if (! $this->callback) return true; | ||||
|  | ||||
| 		return call_user_func($this->callback, $request); | ||||
| 	} | ||||
|  | ||||
| 	public function __call($method, $parameters) | ||||
| 	{ | ||||
| 		if (! count($parameters)) return $this->$method; | ||||
|  | ||||
| 		$this->$method = count($parameters) ? $parameters[0] : true; | ||||
|  | ||||
| 		return $this; | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										58
									
								
								vendor/itsgoingd/clockwork/Clockwork/Request/ShouldRecord.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								vendor/itsgoingd/clockwork/Clockwork/Request/ShouldRecord.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,58 @@ | ||||
| <?php namespace Clockwork\Request; | ||||
|  | ||||
| // Filter requests before recording | ||||
| class ShouldRecord | ||||
| { | ||||
| 	// Enable collecting of errors only (requests with 4xx or 5xx responses) | ||||
| 	protected $errorsOnly = false; | ||||
| 	// Enable collecting of slow requests only, slow response time threshold in ms | ||||
| 	protected $slowOnly = false; | ||||
|  | ||||
| 	// Custom filter callback | ||||
| 	protected $callback; | ||||
|  | ||||
| 	// Merge multiple settings from array | ||||
| 	public function merge(array $data = []) | ||||
| 	{ | ||||
| 		foreach ($data as $key => $val) $this->$key = $val; | ||||
| 	} | ||||
|  | ||||
| 	// Apply the filter to a request | ||||
| 	public function filter(Request $request) | ||||
| 	{ | ||||
| 		return $this->passErrorsOnly($request) | ||||
| 			&& $this->passSlowOnly($request) | ||||
| 			&& $this->passCallback($request); | ||||
| 	} | ||||
|  | ||||
| 	protected function passErrorsOnly(Request $request) | ||||
| 	{ | ||||
| 		if (! $this->errorsOnly) return true; | ||||
|  | ||||
| 		return 400 <= $request->responseStatus && $request->responseStatus <= 599; | ||||
| 	} | ||||
|  | ||||
| 	protected function passSlowOnly(Request $request) | ||||
| 	{ | ||||
| 		if (! $this->slowOnly) return true; | ||||
|  | ||||
| 		return $request->getResponseDuration() >= $this->slowOnly; | ||||
| 	} | ||||
|  | ||||
| 	protected function passCallback(Request $request) | ||||
| 	{ | ||||
| 		if (! $this->callback) return true; | ||||
|  | ||||
| 		return call_user_func($this->callback, $request); | ||||
| 	} | ||||
|  | ||||
| 	// Fluent API | ||||
| 	public function __call($method, $parameters) | ||||
| 	{ | ||||
| 		if (! count($parameters)) return $this->$method; | ||||
|  | ||||
| 		$this->$method = count($parameters) ? $parameters[0] : true; | ||||
|  | ||||
| 		return $this; | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										105
									
								
								vendor/itsgoingd/clockwork/Clockwork/Request/Timeline/Event.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								vendor/itsgoingd/clockwork/Clockwork/Request/Timeline/Event.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,105 @@ | ||||
| <?php namespace Clockwork\Request\Timeline; | ||||
|  | ||||
| // Data structure representing a single timeline event with fluent API | ||||
| class Event | ||||
| { | ||||
| 	// Event description | ||||
| 	public $description; | ||||
| 	// Unique event name | ||||
| 	public $name; | ||||
|  | ||||
| 	// Start time | ||||
| 	public $start; | ||||
| 	// End time | ||||
| 	public $end; | ||||
|  | ||||
| 	// Color (blue, red, green, purple, grey) | ||||
| 	public $color; | ||||
| 	// Additional event data | ||||
| 	public $data; | ||||
|  | ||||
| 	public function __construct($description, $data = []) | ||||
| 	{ | ||||
| 		$this->description = $description; | ||||
| 		$this->name = isset($data['name']) ? $data['name'] : $description; | ||||
|  | ||||
| 		$this->start = isset($data['start']) ? $data['start'] : null; | ||||
| 		$this->end = isset($data['end']) ? $data['end'] : null; | ||||
|  | ||||
| 		$this->color = isset($data['color']) ? $data['color'] : null; | ||||
| 		$this->data = isset($data['data']) ? $data['data'] : null; | ||||
| 	} | ||||
|  | ||||
| 	// Begin the event at current time | ||||
| 	public function begin() | ||||
| 	{ | ||||
| 		$this->start = microtime(true); | ||||
|  | ||||
| 		return $this; | ||||
| 	} | ||||
|  | ||||
| 	// End the event at current time | ||||
| 	public function end() | ||||
| 	{ | ||||
| 		$this->end = microtime(true); | ||||
|  | ||||
| 		return $this; | ||||
| 	} | ||||
|  | ||||
| 	// Begin the event, execute the passed in closure and end the event, returns the closure return value | ||||
| 	public function run(\Closure $closure, ...$args) | ||||
| 	{ | ||||
| 		$this->begin(); | ||||
| 		try { | ||||
| 			return $closure(...$args); | ||||
| 		} finally { | ||||
| 			$this->end(); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Set or retrieve event duration (in ms), event can be defined with both start and end time or just a single time and duration | ||||
| 	public function duration($duration = null) | ||||
| 	{ | ||||
| 		if (! $duration) return ($this->start && $this->end) ? ($this->end - $this->start) * 1000 : 0; | ||||
|  | ||||
| 		if ($this->start) $this->end = $this->start + $duration / 1000; | ||||
| 		if ($this->end) $this->start = $this->end - $duration / 1000; | ||||
|  | ||||
| 		return $this; | ||||
| 	} | ||||
|  | ||||
| 	// Finalize the event, ends the event, fills in start time if empty and limits the start and end time | ||||
| 	public function finalize($start = null, $end = null) | ||||
| 	{ | ||||
| 		$end = $end ?: microtime(true); | ||||
|  | ||||
| 		$this->start = $this->start ?: $start; | ||||
| 		$this->end = $this->end ?: $end; | ||||
|  | ||||
| 		if ($this->start < $start) $this->start = $start; | ||||
| 		if ($this->end > $end) $this->end = $end; | ||||
| 	} | ||||
|  | ||||
| 	// Fluent API | ||||
| 	public function __call($method, $parameters) | ||||
| 	{ | ||||
| 		if (! count($parameters)) return $this->$method; | ||||
|  | ||||
| 		$this->$method = $parameters[0]; | ||||
|  | ||||
| 		return $this; | ||||
| 	} | ||||
|  | ||||
| 	// Return an array representation of the event | ||||
| 	public function toArray() | ||||
| 	{ | ||||
| 		return [ | ||||
| 			'description' => $this->description, | ||||
| 			'start'       => $this->start, | ||||
| 			'end'         => $this->end, | ||||
| 			'duration'    => $this->duration(), | ||||
| 			'color'       => $this->color, | ||||
| 			'data'        => $this->data | ||||
| 		]; | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										72
									
								
								vendor/itsgoingd/clockwork/Clockwork/Request/Timeline/Timeline.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								vendor/itsgoingd/clockwork/Clockwork/Request/Timeline/Timeline.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,72 @@ | ||||
| <?php namespace Clockwork\Request\Timeline; | ||||
|  | ||||
| // Data structure representing collection of time-based events | ||||
| class Timeline | ||||
| { | ||||
| 	// Timeline events | ||||
| 	public $events = []; | ||||
|  | ||||
| 	// Create a new timeline, optionally with existing events | ||||
| 	public function __construct($events = []) | ||||
| 	{ | ||||
| 		foreach ($events as $event) { | ||||
| 			$this->create($event['description'], $event); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Find or create a new event, takes description and optional data - name, start, end, duration, color, data | ||||
| 	public function event($description, $data = []) | ||||
| 	{ | ||||
| 		$name = isset($data['name']) ? $data['name'] : $description; | ||||
|  | ||||
| 		if ($event = $this->find($name)) return $event; | ||||
|  | ||||
| 		return $this->create($description, $data); | ||||
| 	} | ||||
|  | ||||
| 	// Create a new event, takes description and optional data - name, start, end, duration, color, data | ||||
| 	public function create($description, $data = []) | ||||
| 	{ | ||||
| 		return $this->events[] = new Event($description, $data); | ||||
| 	} | ||||
|  | ||||
| 	// Find event by name | ||||
| 	public function find($name) | ||||
| 	{ | ||||
| 		foreach ($this->events as $event) { | ||||
| 			if ($event->name == $name) return $event; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Merge another timeline instance into the current timeline | ||||
| 	public function merge(Timeline $timeline) | ||||
| 	{ | ||||
| 		$this->events = array_merge($this->events, $timeline->events); | ||||
|  | ||||
| 		return $this; | ||||
| 	} | ||||
|  | ||||
| 	// Finalize timeline, ends all events, sorts them and returns as an array | ||||
| 	public function finalize($start = null, $end = null) | ||||
| 	{ | ||||
| 		foreach ($this->events as $event) { | ||||
| 			$event->finalize($start, $end); | ||||
| 		} | ||||
|  | ||||
| 		$this->sort(); | ||||
|  | ||||
| 		return $this->toArray(); | ||||
| 	} | ||||
|  | ||||
| 	// Sort the timeline events by start time | ||||
| 	public function sort() | ||||
| 	{ | ||||
| 		usort($this->events, function ($a, $b) { return $a->start * 1000 - $b->start * 1000; }); | ||||
| 	} | ||||
|  | ||||
| 	// Return events as an array | ||||
| 	public function toArray() | ||||
| 	{ | ||||
| 		return array_map(function ($event) { return $event->toArray(); }, $this->events); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										52
									
								
								vendor/itsgoingd/clockwork/Clockwork/Request/UserData.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								vendor/itsgoingd/clockwork/Clockwork/Request/UserData.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,52 @@ | ||||
| <?php namespace Clockwork\Request; | ||||
|  | ||||
| // Data structure representing custom user data | ||||
| class UserData | ||||
| { | ||||
| 	// Data items | ||||
| 	protected $data = []; | ||||
|  | ||||
| 	// Data title | ||||
| 	protected $title; | ||||
|  | ||||
| 	// Add generic user data | ||||
| 	public function data(array $data, $key = null) | ||||
| 	{ | ||||
| 		if ($key !== null) { | ||||
| 			return $this->data[$key] = new UserDataItem($data); | ||||
| 		} | ||||
|  | ||||
| 		return $this->data[] = new UserDataItem($data); | ||||
| 	} | ||||
|  | ||||
| 	// Add user data shown as counters | ||||
| 	public function counters(array $data) | ||||
| 	{ | ||||
| 		return $this->data($data) | ||||
| 			->showAs('counters'); | ||||
| 	} | ||||
|  | ||||
| 	// Add user data shown as table | ||||
| 	public function table($title, array $data) | ||||
| 	{ | ||||
| 		return $this->data($data) | ||||
| 			->showAs('table') | ||||
| 			->title($title); | ||||
| 	} | ||||
|  | ||||
| 	// Set data title | ||||
| 	public function title($title) | ||||
| 	{ | ||||
| 		$this->title = $title; | ||||
| 		return $this; | ||||
| 	} | ||||
|  | ||||
| 	// Transform data and all contents to a serializable array with metadata | ||||
| 	public function toArray() | ||||
| 	{ | ||||
| 		return array_merge( | ||||
| 			array_map(function ($data) { return $data->toArray(); }, $this->data), | ||||
| 			[ '__meta' => array_filter([ 'title' => $this->title ]) ] | ||||
| 		); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										55
									
								
								vendor/itsgoingd/clockwork/Clockwork/Request/UserDataItem.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								vendor/itsgoingd/clockwork/Clockwork/Request/UserDataItem.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,55 @@ | ||||
| <?php namespace Clockwork\Request; | ||||
|  | ||||
| // Data structure representing custom user data item (shown as counters or table) | ||||
| class UserDataItem | ||||
| { | ||||
| 	// Data contents (labels and values or table rows) | ||||
| 	protected $data; | ||||
|  | ||||
| 	// Describes how the data should be presented ("counters" or "table") | ||||
| 	protected $showAs; | ||||
|  | ||||
| 	// Data title (shown as table title in the official app) | ||||
| 	protected $title; | ||||
|  | ||||
| 	// Map of human-readable labels for the data contents | ||||
| 	protected $labels; | ||||
|  | ||||
| 	public function __construct(array $data) | ||||
| 	{ | ||||
| 		$this->data = $data; | ||||
| 	} | ||||
|  | ||||
| 	// Set how the item should be presented ("counters" or "table") | ||||
| 	public function showAs($showAs) | ||||
| 	{ | ||||
| 		$this->showAs = $showAs; | ||||
| 		return $this; | ||||
| 	} | ||||
|  | ||||
| 	// Set data title (shown as table title in the official app) | ||||
| 	public function title($title) | ||||
| 	{ | ||||
| 		$this->title = $title; | ||||
| 		return $this; | ||||
| 	} | ||||
|  | ||||
| 	// Set a map of human-readable labels for the data contents | ||||
| 	public function labels($labels) | ||||
| 	{ | ||||
| 		$this->labels = $labels; | ||||
| 		return $this; | ||||
| 	} | ||||
|  | ||||
| 	// Transform contents to a serializable array with metadata | ||||
| 	public function toArray() | ||||
| 	{ | ||||
| 		return array_merge($this->data, [ | ||||
| 			'__meta' => array_filter([ | ||||
| 				'showAs' => $this->showAs, | ||||
| 				'title'  => $this->title, | ||||
| 				'labels' => $this->labels | ||||
| 			]) | ||||
| 		]); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										331
									
								
								vendor/itsgoingd/clockwork/Clockwork/Storage/FileStorage.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										331
									
								
								vendor/itsgoingd/clockwork/Clockwork/Storage/FileStorage.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,331 @@ | ||||
| <?php namespace Clockwork\Storage; | ||||
|  | ||||
| use Clockwork\Request\Request; | ||||
| use Clockwork\Storage\Storage; | ||||
|  | ||||
| // File based storage for requests | ||||
| class FileStorage extends Storage | ||||
| { | ||||
| 	// Path where files are stored | ||||
| 	protected $path; | ||||
|  | ||||
| 	// Metadata expiration time in minutes | ||||
| 	protected $expiration; | ||||
|  | ||||
| 	// Compress the files using gzip | ||||
| 	protected $compress; | ||||
|  | ||||
| 	// Metadata cleanup chance | ||||
| 	protected $cleanupChance = 100; | ||||
|  | ||||
| 	// Index file handle | ||||
| 	protected $indexHandle; | ||||
|  | ||||
| 	// Return new storage, takes path where to store files as argument, throws exception if path is not writable | ||||
| 	public function __construct($path, $dirPermissions = 0700, $expiration = null, $compress = false) | ||||
| 	{ | ||||
| 		if (! file_exists($path)) { | ||||
| 			// directory doesn't exist, try to create one | ||||
| 			if (! @mkdir($path, $dirPermissions, true)) { | ||||
| 				throw new \Exception("Directory \"{$path}\" does not exist."); | ||||
| 			} | ||||
|  | ||||
| 			// create default .gitignore, to ignore stored json files | ||||
| 			file_put_contents("{$path}/.gitignore", "*.json\n*.json.gz\nindex\n"); | ||||
| 		} elseif (! is_writable($path)) { | ||||
| 			throw new \Exception("Path \"{$path}\" is not writable."); | ||||
| 		} | ||||
|  | ||||
| 		if (! file_exists($indexFile = "{$path}/index")) { | ||||
| 			file_put_contents($indexFile, ''); | ||||
| 		} elseif (! is_writable($indexFile)) { | ||||
| 			throw new \Exception("Path \"{$indexFile}\" is not writable."); | ||||
| 		} | ||||
|  | ||||
| 		$this->path = $path; | ||||
| 		$this->expiration = $expiration === null ? 60 * 24 * 7 : $expiration; | ||||
| 		$this->compress = $compress; | ||||
| 	} | ||||
|  | ||||
| 	// Returns all requests | ||||
| 	public function all(Search $search = null) | ||||
| 	{ | ||||
| 		return $this->loadRequests($this->searchIndexForward($search)); | ||||
| 	} | ||||
|  | ||||
| 	// Return a single request by id | ||||
| 	public function find($id) | ||||
| 	{ | ||||
| 		return $this->loadRequest($id); | ||||
| 	} | ||||
|  | ||||
| 	// Return the latest request | ||||
| 	public function latest(Search $search = null) | ||||
| 	{ | ||||
| 		$requests = $this->loadRequests($this->searchIndexBackward($search, null, 1)); | ||||
| 		return reset($requests); | ||||
| 	} | ||||
|  | ||||
| 	// Return requests received before specified id, optionally limited to specified count | ||||
| 	public function previous($id, $count = null, Search $search = null) | ||||
| 	{ | ||||
| 		return $this->loadRequests($this->searchIndexBackward($search, $id, $count)); | ||||
| 	} | ||||
|  | ||||
| 	// Return requests received after specified id, optionally limited to specified count | ||||
| 	public function next($id, $count = null, Search $search = null) | ||||
| 	{ | ||||
| 		return $this->loadRequests($this->searchIndexForward($search, $id, $count)); | ||||
| 	} | ||||
|  | ||||
| 	// Store request, requests are stored in JSON representation in files named <request id>.json in storage path | ||||
| 	public function store(Request $request, $skipIndex = false) | ||||
| 	{ | ||||
| 		$path = "{$this->path}/{$request->id}.json"; | ||||
| 		$data = @json_encode($request->toArray(), \JSON_PARTIAL_OUTPUT_ON_ERROR); | ||||
|  | ||||
| 		$this->compress | ||||
| 			? file_put_contents("{$path}.gz", gzcompress($data)) | ||||
| 			: file_put_contents($path, $data); | ||||
|  | ||||
| 		if (! $skipIndex) $this->updateIndex($request); | ||||
|  | ||||
| 		$this->cleanup(); | ||||
| 	} | ||||
|  | ||||
| 	// Update existing request | ||||
| 	public function update(Request $request) | ||||
| 	{ | ||||
| 		return $this->store($request, true); | ||||
| 	} | ||||
|  | ||||
| 	// Cleanup old requests | ||||
| 	public function cleanup($force = false) | ||||
| 	{ | ||||
| 		if ($this->expiration === false || (! $force && rand(1, $this->cleanupChance) != 1)) return; | ||||
|  | ||||
| 		$this->openIndex('start', true, true); // reopen index with lock | ||||
|  | ||||
| 		$expirationTime = time() - ($this->expiration * 60); | ||||
|  | ||||
| 		$old = $this->searchIndexForward( | ||||
| 			new Search([ 'received' => [ '<' . date('c', $expirationTime) ] ], [ 'stopOnFirstMismatch' => true ]) | ||||
| 		); | ||||
|  | ||||
| 		if (! count($old)) return $this->closeIndex(true); | ||||
|  | ||||
| 		$this->readPreviousIndex(); | ||||
| 		$this->trimIndex(); | ||||
| 		$this->closeIndex(true); // explicitly close index to unlock asap | ||||
|  | ||||
| 		foreach ($old as $id) { | ||||
| 			$path = "{$this->path}/{$id}.json"; | ||||
| 			@unlink($this->compress ? "{$path}.gz" : $path); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Load a single request by id from filesystem | ||||
| 	protected function loadRequest($id) | ||||
| 	{ | ||||
| 		$path = "{$this->path}/{$id}.json"; | ||||
|  | ||||
| 		if (! is_readable($this->compress ? "{$path}.gz" : $path)) return; | ||||
|  | ||||
| 		$data = file_get_contents($this->compress ? "{$path}.gz" : $path); | ||||
|  | ||||
| 		return new Request(json_decode($this->compress ? gzuncompress($data) : $data, true)); | ||||
| 	} | ||||
|  | ||||
| 	// Load multiple requests by ids from filesystem | ||||
| 	protected function loadRequests($ids) | ||||
| 	{ | ||||
| 		return array_filter(array_map(function ($id) { return $this->loadRequest($id); }, $ids)); | ||||
| 	} | ||||
|  | ||||
| 	// Search index backward from specified ID or last record, with optional results count limit | ||||
| 	protected function searchIndexBackward(Search $search = null, $id = null, $count = null) | ||||
| 	{ | ||||
| 		return $this->searchIndex('previous', $search, $id, $count); | ||||
| 	} | ||||
|  | ||||
| 	// Search index forward from specified ID or last record, with optional results count limit | ||||
| 	protected function searchIndexForward(Search $search = null, $id = null, $count = null) | ||||
| 	{ | ||||
| 		return $this->searchIndex('next', $search, $id, $count); | ||||
| 	} | ||||
|  | ||||
| 	// Search index in specified direction from specified ID or last record, with optional results count limit | ||||
| 	protected function searchIndex($direction, Search $search = null, $id = null, $count = null) | ||||
| 	{ | ||||
| 		$this->openIndex($direction == 'previous' ? 'end' : 'start', false, true); | ||||
|  | ||||
| 		if ($id) { | ||||
| 			while ($request = $this->readIndex($direction)) { if ($request->id == $id) break; } | ||||
| 		} | ||||
|  | ||||
| 		$found = []; | ||||
|  | ||||
| 		while ($request = $this->readIndex($direction)) { | ||||
| 			if (! $search || $search->matches($request)) { | ||||
| 				$found[] = $request->id; | ||||
| 			} elseif ($search->stopOnFirstMismatch) { | ||||
| 				break; | ||||
| 			} | ||||
|  | ||||
| 			if ($count && count($found) == $count) break; | ||||
| 		} | ||||
|  | ||||
| 		return $direction == 'next' ? $found : array_reverse($found); | ||||
| 	} | ||||
|  | ||||
| 	// Open index file, optionally lock or move file pointer to the end, existing handle will be returned by default | ||||
| 	protected function openIndex($position = 'start', $lock = false, $force = false) | ||||
| 	{ | ||||
| 		if ($this->indexHandle) { | ||||
| 			if (! $force) return; | ||||
| 			$this->closeIndex(); | ||||
| 		} | ||||
|  | ||||
| 		$this->indexHandle = fopen("{$this->path}/index", 'r'); | ||||
|  | ||||
| 		if ($lock) flock($this->indexHandle, LOCK_EX); | ||||
| 		if ($position == 'end') fseek($this->indexHandle, 0, SEEK_END); | ||||
| 	} | ||||
|  | ||||
| 	// Close index file, optionally unlock | ||||
| 	protected function closeIndex($lock = false) | ||||
| 	{ | ||||
| 		if ($lock) flock($this->indexHandle, LOCK_UN); | ||||
| 		fclose($this->indexHandle); | ||||
|  | ||||
| 		$this->indexHandle = null; | ||||
| 	} | ||||
|  | ||||
| 	// Read a line from index in the specified direction (next or previous) | ||||
| 	protected function readIndex($direction) | ||||
| 	{ | ||||
| 		return $direction == 'next' ? $this->readNextIndex() : $this->readPreviousIndex(); | ||||
| 	} | ||||
|  | ||||
| 	// Read previous line from index | ||||
| 	protected function readPreviousIndex() | ||||
| 	{ | ||||
| 		$position = ftell($this->indexHandle) - 1; | ||||
|  | ||||
| 		if ($position <= 0) return; | ||||
|  | ||||
| 		$line = ''; | ||||
|  | ||||
| 		// reads 1024B chunks of the file backwards from the current position, until a newline is found or we reach the top | ||||
| 		while ($position > 0) { | ||||
| 			// find next position to read from, make sure we don't read beyond file boundary | ||||
| 			$position -= $chunkSize = min($position, 1024); | ||||
|  | ||||
| 			// read the chunk from the position | ||||
| 			fseek($this->indexHandle, $position); | ||||
| 			$chunk = fread($this->indexHandle, $chunkSize); | ||||
|  | ||||
| 			// if a newline is found, append only the part after the last newline, otherwise we can append the whole chunk | ||||
| 			$line = ($newline = strrpos($chunk, "\n")) === false | ||||
| 				? $chunk . $line : substr($chunk, $newline + 1) . $line; | ||||
|  | ||||
| 			// if a newline was found, fix the position so we read from that newline next time | ||||
| 			if ($newline !== false) $position += $newline + 1; | ||||
|  | ||||
| 			// move file pointer to the correct position (revert fread, apply newline fix) | ||||
| 			fseek($this->indexHandle, $position); | ||||
|  | ||||
| 			// if we reached a newline and put together a non-empty line we are done | ||||
| 			if ($newline !== false) break; | ||||
| 		} | ||||
|  | ||||
| 		return $this->makeRequestFromIndex(str_getcsv($line)); | ||||
| 	} | ||||
|  | ||||
| 	// Read next line from index | ||||
| 	protected function readNextIndex() | ||||
| 	{ | ||||
| 		if (feof($this->indexHandle)) return; | ||||
|  | ||||
| 		// File pointer is always at the start of the line, call extra fgets to skip current line | ||||
| 		fgets($this->indexHandle); | ||||
| 		$line = fgets($this->indexHandle); | ||||
|  | ||||
| 		// Check if we read an empty line or reached the end of file | ||||
| 		if ($line === false) return; | ||||
|  | ||||
| 		// Reset the file pointer to the start of the read line | ||||
| 		fseek($this->indexHandle, ftell($this->indexHandle) - strlen($line)); | ||||
|  | ||||
| 		return $this->makeRequestFromIndex(str_getcsv($line)); | ||||
| 	} | ||||
|  | ||||
| 	// Trim index file from beginning to current position (including) | ||||
| 	protected function trimIndex() | ||||
| 	{ | ||||
| 		// File pointer is always at the start of the line, call extra fgets to skip current line | ||||
| 		fgets($this->indexHandle); | ||||
|  | ||||
| 		// Read the rest of the index file | ||||
| 		$trimmedLength = filesize("{$this->path}/index") - ftell($this->indexHandle); | ||||
| 		$trimmed = $trimmedLength > 0 ? fread($this->indexHandle, $trimmedLength) : ''; | ||||
|  | ||||
| 		// Rewrite the index file with a trimmed version | ||||
| 		file_put_contents("{$this->path}/index", $trimmed); | ||||
| 	} | ||||
|  | ||||
| 	// Create an incomplete request from index data | ||||
| 	protected function makeRequestFromIndex($record) | ||||
| 	{ | ||||
| 		$type = isset($record[7]) ? $record[7] : 'response'; | ||||
|  | ||||
| 		if ($type == 'command') { | ||||
| 			$nameField = 'commandName'; | ||||
| 		} elseif ($type == 'queue-job') { | ||||
| 			$nameField = 'jobName'; | ||||
| 		} elseif ($type == 'test') { | ||||
| 			$nameField = 'testName'; | ||||
| 		} else { | ||||
| 			$nameField = 'uri'; | ||||
| 		} | ||||
|  | ||||
| 		return new Request(array_combine( | ||||
| 			[ 'id', 'time', 'method', $nameField, 'controller', 'responseStatus', 'responseDuration', 'type' ], | ||||
| 			array_slice($record, 0, 8) + [ null, null, null, null, null, null, null, 'response' ] | ||||
| 		)); | ||||
| 	} | ||||
|  | ||||
| 	// Update index with a new request | ||||
| 	protected function updateIndex(Request $request) | ||||
| 	{ | ||||
| 		$handle = fopen("{$this->path}/index", 'a'); | ||||
|  | ||||
| 		if (! $handle) return; | ||||
|  | ||||
| 		if (! flock($handle, LOCK_EX)) return fclose($handle); | ||||
|  | ||||
| 		if ($request->type == 'command') { | ||||
| 			$nameField = 'commandName'; | ||||
| 		} elseif ($request->type == 'queue-job') { | ||||
| 			$nameField = 'jobName'; | ||||
| 		} elseif ($request->type == 'test') { | ||||
| 			$nameField = 'testName'; | ||||
| 		} else { | ||||
| 			$nameField = 'uri'; | ||||
| 		} | ||||
|  | ||||
| 		fputcsv($handle, [ | ||||
| 			$request->id, | ||||
| 			$request->time, | ||||
| 			$request->method, | ||||
| 			$request->$nameField, | ||||
| 			$request->controller, | ||||
| 			$request->responseStatus, | ||||
| 			$request->getResponseDuration(), | ||||
| 			$request->type | ||||
| 		]); | ||||
|  | ||||
| 		flock($handle, LOCK_UN); | ||||
| 		fclose($handle); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										166
									
								
								vendor/itsgoingd/clockwork/Clockwork/Storage/Search.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										166
									
								
								vendor/itsgoingd/clockwork/Clockwork/Storage/Search.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,166 @@ | ||||
| <?php namespace Clockwork\Storage; | ||||
|  | ||||
| use Clockwork\Request\Request; | ||||
| use Clockwork\Request\RequestType; | ||||
|  | ||||
| // Rules for searching requests | ||||
| class Search | ||||
| { | ||||
| 	// Search parameters | ||||
| 	public $uri = []; | ||||
| 	public $controller = []; | ||||
| 	public $method = []; | ||||
| 	public $status = []; | ||||
| 	public $time = []; | ||||
| 	public $received = []; | ||||
| 	public $name = []; | ||||
| 	public $type = []; | ||||
|  | ||||
| 	// Whether to stop search on the first not matching request | ||||
| 	public $stopOnFirstMismatch = false; | ||||
|  | ||||
| 	// Create a new instance, takes search parameters and additional options | ||||
| 	public function __construct($search = [], $options = []) | ||||
| 	{ | ||||
| 		foreach ([ 'uri', 'controller', 'method', 'status', 'time', 'received', 'name', 'type' ] as $condition) { | ||||
| 			$this->$condition = isset($search[$condition]) ? $search[$condition] : []; | ||||
| 		} | ||||
|  | ||||
| 		foreach ([ 'stopOnFirstMismatch' ] as $option) { | ||||
| 			$this->$option = isset($options[$option]) ? $options[$option] : $this->$condition; | ||||
| 		} | ||||
|  | ||||
| 		$this->method = array_map('strtolower', $this->method); | ||||
| 	} | ||||
|  | ||||
| 	// Create a new instance from request input | ||||
| 	public static function fromRequest($data = []) | ||||
| 	{ | ||||
| 		return new static($data); | ||||
| 	} | ||||
|  | ||||
| 	// Check whether the request matches current search parameters | ||||
| 	public function matches(Request $request) | ||||
| 	{ | ||||
| 		if ($request->type == RequestType::COMMAND) { | ||||
| 			return $this->matchesCommand($request); | ||||
| 		} elseif ($request->type == RequestType::QUEUE_JOB) { | ||||
| 			return $this->matchesQueueJob($request); | ||||
| 		} elseif ($request->type == RequestType::TEST) { | ||||
| 			return $this->matchesTest($request); | ||||
| 		} else { | ||||
| 			return $this->matchesRequest($request); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Check whether a request type request matches | ||||
| 	protected function matchesRequest(Request $request) | ||||
| 	{ | ||||
| 		return $this->matchesString($this->type, RequestType::REQUEST) | ||||
| 			&& $this->matchesString($this->uri, $request->uri) | ||||
| 			&& $this->matchesString($this->controller, $request->controller) | ||||
| 			&& $this->matchesExact($this->method, strtolower($request->method)) | ||||
| 			&& $this->matchesNumber($this->status, $request->responseStatus) | ||||
| 			&& $this->matchesNumber($this->time, $request->responseDuration) | ||||
| 			&& $this->matchesDate($this->received, $request->time); | ||||
| 	} | ||||
|  | ||||
| 	// Check whether a command type request matches | ||||
| 	protected function matchesCommand(Request $request) | ||||
| 	{ | ||||
| 		return $this->matchesString($this->type, RequestType::COMMAND) | ||||
| 			&& $this->matchesString($this->name, $request->commandName) | ||||
| 			&& $this->matchesNumber($this->status, $request->commandExitCode) | ||||
| 			&& $this->matchesNumber($this->time, $request->responseDuration) | ||||
| 			&& $this->matchesDate($this->received, $request->time); | ||||
| 	} | ||||
|  | ||||
| 	// Check whether a queue-job type request matches | ||||
| 	protected function matchesQueueJob(Request $request) | ||||
| 	{ | ||||
| 		return $this->matchesString($this->type, RequestType::QUEUE_JOB) | ||||
| 			&& $this->matchesString($this->name, $request->jobName) | ||||
| 			&& $this->matchesString($this->status, $request->jobStatus) | ||||
| 			&& $this->matchesNumber($this->time, $request->responseDuration) | ||||
| 			&& $this->matchesDate($this->received, $request->time); | ||||
| 	} | ||||
|  | ||||
| 	// Check whether a test type request matches | ||||
| 	protected function matchesTest(Request $request) | ||||
| 	{ | ||||
| 		return $this->matchesString($this->type, RequestType::TEST) | ||||
| 			&& $this->matchesString($this->name, $request->testName) | ||||
| 			&& $this->matchesString($this->status, $request->testStatus) | ||||
| 			&& $this->matchesNumber($this->time, $request->responseDuration) | ||||
| 			&& $this->matchesDate($this->received, $request->time); | ||||
| 	} | ||||
|  | ||||
| 	// Check if there are no search parameters specified | ||||
| 	public function isEmpty() | ||||
| 	{ | ||||
| 		return ! count($this->uri) && ! count($this->controller) && ! count($this->method) && ! count($this->status) | ||||
| 			&& ! count($this->time) && ! count($this->received) && ! count($this->name) && ! count($this->type); | ||||
| 	} | ||||
|  | ||||
| 	// Check if there are some search parameters specified | ||||
| 	public function isNotEmpty() | ||||
| 	{ | ||||
| 		return ! $this->isEmpty(); | ||||
| 	} | ||||
|  | ||||
| 	// Check if the value matches date type search parameter | ||||
| 	protected function matchesDate($inputs, $value) | ||||
| 	{ | ||||
| 		if (! count($inputs)) return true; | ||||
|  | ||||
| 		foreach ($inputs as $input) { | ||||
| 			if (preg_match('/^<(.+)$/', $input, $match)) { | ||||
| 				if ($value < strtotime($match[1])) return true; | ||||
| 			} elseif (preg_match('/^>(.+)$/', $input, $match)) { | ||||
| 				if ($value > strtotime($match[1])) return true; | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	// Check if the value matches exact type search parameter | ||||
| 	protected function matchesExact($inputs, $value) | ||||
| 	{ | ||||
| 		if (! count($inputs)) return true; | ||||
|  | ||||
| 		return in_array($value, $inputs); | ||||
| 	} | ||||
|  | ||||
| 	// Check if the value matches number type search parameter | ||||
| 	protected function matchesNumber($inputs, $value) | ||||
| 	{ | ||||
| 		if (! count($inputs)) return true; | ||||
|  | ||||
| 		foreach ($inputs as $input) { | ||||
| 			if (preg_match('/^<(\d+(?:\.\d+)?)$/', $input, $match)) { | ||||
| 				if ($value < $match[1]) return true; | ||||
| 			} elseif (preg_match('/^>(\d+(?:\.\d+)?)$/', $input, $match)) { | ||||
| 				if ($value > $match[1]) return true; | ||||
| 			} elseif (preg_match('/^(\d+(?:\.\d+)?)-(\d+(?:\.\d+)?)$/', $input, $match)) { | ||||
| 				if ($match[1] < $value && $value < $match[2]) return true; | ||||
| 			} else { | ||||
| 				if ($value == $input) return true; | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	// Check if the value matches string type search parameter | ||||
| 	protected function matchesString($inputs, $value) | ||||
| 	{ | ||||
| 		if (! count($inputs)) return true; | ||||
|  | ||||
| 		foreach ($inputs as $input) { | ||||
| 			if (strpos($value, $input) !== false) return true; | ||||
| 		} | ||||
|  | ||||
| 		return false; | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										164
									
								
								vendor/itsgoingd/clockwork/Clockwork/Storage/SqlSearch.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										164
									
								
								vendor/itsgoingd/clockwork/Clockwork/Storage/SqlSearch.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,164 @@ | ||||
| <?php namespace Clockwork\Storage; | ||||
|  | ||||
| use Clockwork\Request\Request; | ||||
|  | ||||
| use PDO; | ||||
|  | ||||
| // Rules for searching requests using SQL storage, builds the SQL query conditions | ||||
| class SqlSearch extends Search | ||||
| { | ||||
| 	// Generated SQL query and bindings | ||||
| 	public $query; | ||||
| 	public $bindings; | ||||
|  | ||||
| 	// Internal representation of the SQL where conditions | ||||
| 	protected $conditions; | ||||
|  | ||||
| 	// PDO instance | ||||
| 	protected $pdo; | ||||
|  | ||||
| 	// Create a new instance, takes search parameters | ||||
| 	public function __construct($search = [], PDO $pdo = null) | ||||
| 	{ | ||||
| 		parent::__construct($search); | ||||
|  | ||||
| 		$this->pdo = $pdo; | ||||
|  | ||||
| 		list($this->conditions, $this->bindings) = $this->resolveConditions(); | ||||
|  | ||||
| 		$this->buildQuery(); | ||||
| 	} | ||||
|  | ||||
| 	// Creates a new instance from a base Search class instance | ||||
| 	public static function fromBase(Search $search = null, PDO $pdo = null) | ||||
| 	{ | ||||
| 		return new static((array) $search, $pdo); | ||||
| 	} | ||||
|  | ||||
| 	// Add an additional where condition, takes the SQL condition and array of bindings | ||||
| 	public function addCondition($condition, $bindings = []) | ||||
| 	{ | ||||
| 		$this->conditions = array_merge([ $condition ], $this->conditions); | ||||
| 		$this->bindings = array_merge($bindings, $this->bindings); | ||||
| 		$this->buildQuery(); | ||||
|  | ||||
| 		return $this; | ||||
| 	} | ||||
|  | ||||
| 	// Resolve SQL conditions and bindings based on the search parameters | ||||
| 	protected function resolveConditions() | ||||
| 	{ | ||||
| 		if ($this->isEmpty()) return [ [], [] ]; | ||||
|  | ||||
| 		$conditions = array_filter([ | ||||
| 			$this->resolveStringCondition([ 'type' ], $this->type), | ||||
| 			$this->resolveStringCondition([ 'uri', 'commandName', 'jobName', 'testName' ], array_merge($this->uri, $this->name)), | ||||
| 			$this->resolveStringCondition([ 'controller' ], $this->controller), | ||||
| 			$this->resolveExactCondition([ 'method' ], $this->method), | ||||
| 			$this->resolveNumberCondition([ 'responseStatus', 'commandExitCode', 'jobStatus', 'testStatus' ], $this->status), | ||||
| 			$this->resolveNumberCondition([ 'responseDuration' ], $this->time), | ||||
| 			$this->resolveDateCondition([ 'time' ], $this->received) | ||||
| 		]); | ||||
|  | ||||
| 		$sql = array_map(function ($condition) { return $condition[0]; }, $conditions); | ||||
| 		$bindings = array_reduce($conditions, function ($bindings, $condition) { | ||||
| 			return array_merge($bindings, $condition[1]); | ||||
| 		}, []); | ||||
|  | ||||
| 		return [ $sql, $bindings ]; | ||||
| 	} | ||||
|  | ||||
| 	// Resolve a date type condition and bindings | ||||
| 	protected function resolveDateCondition($fields, $inputs) | ||||
| 	{ | ||||
| 		if (! count($inputs)) return null; | ||||
|  | ||||
| 		$bindings = []; | ||||
| 		$conditions = implode(' OR ', array_map(function ($field) use ($inputs, &$bindings) { | ||||
| 			return implode(' OR ', array_map(function ($input, $index) use ($field, &$bindings) { | ||||
| 				if (preg_match('/^<(.+)$/', $input, $match)) { | ||||
| 					$bindings["{$field}{$index}"] = $match[1]; | ||||
| 					return $this->quote($field) . " < :{$field}{$index}"; | ||||
| 				} elseif (preg_match('/^>(.+)$/', $input, $match)) { | ||||
| 					$bindings["{$field}{$index}"] = $match[1]; | ||||
| 					return $this->quote($field). " > :{$field}{$index}"; | ||||
| 				} | ||||
| 			}, $inputs, array_keys($inputs))); | ||||
| 		}, $fields)); | ||||
|  | ||||
| 		return [ "({$conditions})", $bindings ]; | ||||
| 	} | ||||
|  | ||||
| 	// Resolve an exact type condition and bindings | ||||
| 	protected function resolveExactCondition($fields, $inputs) | ||||
| 	{ | ||||
| 		if (! count($inputs)) return null; | ||||
|  | ||||
| 		$bindings = []; | ||||
| 		$values = implode(' OR ', array_map(function ($field) use ($inputs, &$bindings) { | ||||
| 			return implode(', ', array_map(function ($input, $index) use ($field, &$bindings) { | ||||
| 				$bindings["{$field}{$index}"] = $input; | ||||
| 				return ":{$field}{$index}"; | ||||
| 			}, $inputs, array_keys($inputs))); | ||||
| 		}, $fields)); | ||||
|  | ||||
| 		return [ $this->quote($field) . " IN ({$values})", $bindings ]; | ||||
| 	} | ||||
|  | ||||
| 	// Resolve a number type condition and bindings | ||||
| 	protected function resolveNumberCondition($fields, $inputs) | ||||
| 	{ | ||||
| 		if (! count($inputs)) return null; | ||||
|  | ||||
| 		$bindings = []; | ||||
| 		$conditions = implode(' OR ', array_map(function ($field) use ($inputs, &$bindings) { | ||||
| 			return implode(' OR ', array_map(function ($input, $index) use ($field, &$bindings) { | ||||
| 				if (preg_match('/^<(\d+(?:\.\d+)?)$/', $input, $match)) { | ||||
| 					$bindings["{$field}{$index}"] = $match[1]; | ||||
| 					return $this->quote($field) . " < :{$field}{$index}"; | ||||
| 				} elseif (preg_match('/^>(\d+(?:\.\d+)?)$/', $input, $match)) { | ||||
| 					$bindings["{$field}{$index}"] = $match[1]; | ||||
| 					return $this->quote($field) . " > :{$field}{$index}"; | ||||
| 				} elseif (preg_match('/^(\d+(?:\.\d+)?)-(\d+(?:\.\d+)?)$/', $input, $match)) { | ||||
| 					$bindings["{$field}{$index}from"] = $match[1]; | ||||
| 					$bindings["{$field}{$index}to"] = $match[2]; | ||||
| 					$quotedField = $this->quote($field); | ||||
| 					return "({$quotedField} > :{$field}{$index}from AND {$quotedField} < :{$field}{$index}to)"; | ||||
| 				} else { | ||||
| 					$bindings["{$field}{$index}"] = $input; | ||||
| 					return $this->quote($field) . " = :{$field}{$index}"; | ||||
| 				} | ||||
| 			}, $inputs, array_keys($inputs))); | ||||
| 		}, $fields)); | ||||
|  | ||||
| 		return [ "({$conditions})", $bindings ]; | ||||
| 	} | ||||
|  | ||||
| 	// Resolve a string type condition and bindings | ||||
| 	protected function resolveStringCondition($fields, $inputs) | ||||
| 	{ | ||||
| 		if (! count($inputs)) return null; | ||||
|  | ||||
| 		$bindings = []; | ||||
| 		$conditions = implode(' OR ', array_map(function ($field) use ($inputs, &$bindings) { | ||||
| 			return implode(' OR ', array_map(function ($input, $index) use ($field, &$bindings) { | ||||
| 				$bindings["{$field}{$index}"] = $input; | ||||
| 				return $this->quote($field) . " LIKE :{$field}{$index}"; | ||||
| 			}, $inputs, array_keys($inputs))); | ||||
| 		}, $fields)); | ||||
|  | ||||
| 		return [ "({$conditions})", $bindings ]; | ||||
| 	} | ||||
|  | ||||
| 	// Build the where part of the SQL query | ||||
| 	protected function buildQuery() | ||||
| 	{ | ||||
| 		$this->query = count($this->conditions) ? 'WHERE ' . implode(' AND ', $this->conditions) : ''; | ||||
| 	} | ||||
|  | ||||
| 	// Quotes SQL identifier name properly for the current database | ||||
| 	protected function quote($identifier) | ||||
| 	{ | ||||
| 		return $this->pdo && $this->pdo->getAttribute(PDO::ATTR_DRIVER_NAME) == 'mysql' ? "`{$identifier}`" : "\"{$identifier}\""; | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										296
									
								
								vendor/itsgoingd/clockwork/Clockwork/Storage/SqlStorage.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										296
									
								
								vendor/itsgoingd/clockwork/Clockwork/Storage/SqlStorage.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,296 @@ | ||||
| <?php namespace Clockwork\Storage; | ||||
|  | ||||
| use Clockwork\Clockwork; | ||||
| use Clockwork\Request\Request; | ||||
|  | ||||
| use PDO; | ||||
|  | ||||
| // SQL storage for requests using PDO | ||||
| class SqlStorage extends Storage | ||||
| { | ||||
| 	// PDO instance | ||||
| 	protected $pdo; | ||||
|  | ||||
| 	// Name of the table with Clockwork requests metadata | ||||
| 	protected $table; | ||||
|  | ||||
| 	// Metadata expiration time in minutes | ||||
| 	protected $expiration; | ||||
|  | ||||
| 	// Schema for the Clockwork requests table | ||||
| 	protected $fields = [ | ||||
| 		'id'                       => 'VARCHAR(100) PRIMARY KEY', | ||||
| 		'version'                  => 'INTEGER', | ||||
| 		'type'                     => 'VARCHAR(100) NULL', | ||||
| 		'time'                     => 'DOUBLE PRECISION NULL', | ||||
| 		'method'                   => 'VARCHAR(10) NULL', | ||||
| 		'url'                      => 'TEXT NULL', | ||||
| 		'uri'                      => 'TEXT NULL', | ||||
| 		'headers'                  => 'TEXT NULL', | ||||
| 		'controller'               => 'VARCHAR(250) NULL', | ||||
| 		'getData'                  => 'TEXT NULL', | ||||
| 		'postData'                 => 'TEXT NULL', | ||||
| 		'requestData'              => 'TEXT NULL', | ||||
| 		'sessionData'              => 'TEXT NULL', | ||||
| 		'authenticatedUser'        => 'TEXT NULL', | ||||
| 		'cookies'                  => 'TEXT NULL', | ||||
| 		'responseTime'             => 'DOUBLE PRECISION NULL', | ||||
| 		'responseStatus'           => 'INTEGER NULL', | ||||
| 		'responseDuration'         => 'DOUBLE PRECISION NULL', | ||||
| 		'memoryUsage'              => 'DOUBLE PRECISION NULL', | ||||
| 		'middleware'               => 'TEXT NULL', | ||||
| 		'databaseQueries'          => 'TEXT NULL', | ||||
| 		'databaseQueriesCount'     => 'INTEGER NULL', | ||||
| 		'databaseSlowQueries'      => 'INTEGER NULL', | ||||
| 		'databaseSelects'          => 'INTEGER NULL', | ||||
| 		'databaseInserts'          => 'INTEGER NULL', | ||||
| 		'databaseUpdates'          => 'INTEGER NULL', | ||||
| 		'databaseDeletes'          => 'INTEGER NULL', | ||||
| 		'databaseOthers'           => 'INTEGER NULL', | ||||
| 		'databaseDuration'         => 'DOUBLE PRECISION NULL', | ||||
| 		'cacheQueries'             => 'TEXT NULL', | ||||
| 		'cacheReads'               => 'INTEGER NULL', | ||||
| 		'cacheHits'                => 'INTEGER NULL', | ||||
| 		'cacheWrites'              => 'INTEGER NULL', | ||||
| 		'cacheDeletes'             => 'INTEGER NULL', | ||||
| 		'cacheTime'                => 'DOUBLE PRECISION NULL', | ||||
| 		'modelsActions'            => 'TEXT NULL', | ||||
| 		'modelsRetrieved'          => 'TEXT NULL', | ||||
| 		'modelsCreated'            => 'TEXT NULL', | ||||
| 		'modelsUpdated'            => 'TEXT NULL', | ||||
| 		'modelsDeleted'            => 'TEXT NULL', | ||||
| 		'redisCommands'            => 'TEXT NULL', | ||||
| 		'queueJobs'                => 'TEXT NULL', | ||||
| 		'timelineData'             => 'TEXT NULL', | ||||
| 		'log'                      => 'TEXT NULL', | ||||
| 		'events'                   => 'TEXT NULL', | ||||
| 		'routes'                   => 'TEXT NULL', | ||||
| 		'notifications'            => 'TEXT NULL', | ||||
| 		'emailsData'               => 'TEXT NULL', | ||||
| 		'viewsData'                => 'TEXT NULL', | ||||
| 		'userData'                 => 'TEXT NULL', | ||||
| 		'subrequests'              => 'TEXT NULL', | ||||
| 		'xdebug'                   => 'TEXT NULL', | ||||
| 		'commandName'              => 'TEXT NULL', | ||||
| 		'commandArguments'         => 'TEXT NULL', | ||||
| 		'commandArgumentsDefaults' => 'TEXT NULL', | ||||
| 		'commandOptions'           => 'TEXT NULL', | ||||
| 		'commandOptionsDefaults'   => 'TEXT NULL', | ||||
| 		'commandExitCode'          => 'INTEGER NULL', | ||||
| 		'commandOutput'            => 'TEXT NULL', | ||||
| 		'jobName'                  => 'TEXT NULL', | ||||
| 		'jobDescription'           => 'TEXT NULL', | ||||
| 		'jobStatus'                => 'TEXT NULL', | ||||
| 		'jobPayload'               => 'TEXT NULL', | ||||
| 		'jobQueue'                 => 'TEXT NULL', | ||||
| 		'jobConnection'            => 'TEXT NULL', | ||||
| 		'jobOptions'               => 'TEXT NULL', | ||||
| 		'testName'                 => 'TEXT NULL', | ||||
| 		'testStatus'               => 'TEXT NULL', | ||||
| 		'testStatusMessage'        => 'TEXT NULL', | ||||
| 		'testAsserts'              => 'TEXT NULL', | ||||
| 		'clientMetrics'            => 'TEXT NULL', | ||||
| 		'webVitals'                => 'TEXT NULL', | ||||
| 		'parent'                   => 'TEXT NULL', | ||||
| 		'updateToken'              => 'VARCHAR(100) NULL' | ||||
| 	]; | ||||
|  | ||||
| 	// List of Request keys that need to be serialized before they can be stored in database | ||||
| 	protected $needsSerialization = [ | ||||
| 		'headers', 'getData', 'postData', 'requestData', 'sessionData', 'authenticatedUser', 'cookies', 'middleware', | ||||
| 		'databaseQueries', 'cacheQueries', 'modelsActions', 'modelsRetrieved', 'modelsCreated', 'modelsUpdated', | ||||
| 		'modelsDeleted', 'redisCommands', 'queueJobs', 'timelineData', 'log', 'events', 'routes', 'notifications', | ||||
| 		'emailsData', 'viewsData', 'userData', 'subrequests', 'xdebug', 'commandArguments', 'commandArgumentsDefaults', | ||||
| 		'commandOptions', 'commandOptionsDefaults', 'jobPayload', 'jobOptions', 'testAsserts', 'parent', | ||||
| 		'clientMetrics', 'webVitals' | ||||
| 	]; | ||||
|  | ||||
| 	// Return a new storage, takes PDO object or DSN and optionally a table name and database credentials as arguments | ||||
| 	public function __construct($dsn, $table = 'clockwork', $username = null, $password = null, $expiration = null) | ||||
| 	{ | ||||
| 		$this->pdo = $dsn instanceof PDO ? $dsn : new PDO($dsn, $username, $password); | ||||
| 		$this->table = $table; | ||||
| 		$this->expiration = $expiration === null ? 60 * 24 * 7 : $expiration; | ||||
| 	} | ||||
|  | ||||
| 	// Returns all requests | ||||
| 	public function all(Search $search = null) | ||||
| 	{ | ||||
| 		$fields = implode(', ', array_map(function ($field) { return $this->quote($field); }, array_keys($this->fields))); | ||||
| 		$search = SqlSearch::fromBase($search, $this->pdo); | ||||
| 		$result = $this->query("SELECT {$fields} FROM {$this->table} {$search->query}", $search->bindings); | ||||
|  | ||||
| 		return $this->resultsToRequests($result); | ||||
| 	} | ||||
|  | ||||
| 	// Return a single request by id | ||||
| 	public function find($id) | ||||
| 	{ | ||||
| 		$fields = implode(', ', array_map(function ($field) { return $this->quote($field); }, array_keys($this->fields))); | ||||
| 		$result = $this->query("SELECT {$fields} FROM {$this->table} WHERE id = :id", [ 'id' => $id ]); | ||||
|  | ||||
| 		$requests = $this->resultsToRequests($result); | ||||
| 		return end($requests); | ||||
| 	} | ||||
|  | ||||
| 	// Return the latest request | ||||
| 	public function latest(Search $search = null) | ||||
| 	{ | ||||
| 		$fields = implode(', ', array_map(function ($field) { return $this->quote($field); }, array_keys($this->fields))); | ||||
| 		$search = SqlSearch::fromBase($search, $this->pdo); | ||||
| 		$result = $this->query( | ||||
| 			"SELECT {$fields} FROM {$this->table} {$search->query} ORDER BY id DESC LIMIT 1", $search->bindings | ||||
| 		); | ||||
|  | ||||
| 		$requests = $this->resultsToRequests($result); | ||||
| 		return end($requests); | ||||
| 	} | ||||
|  | ||||
| 	// Return requests received before specified id, optionally limited to specified count | ||||
| 	public function previous($id, $count = null, Search $search = null) | ||||
| 	{ | ||||
| 		$count = (int) $count; | ||||
|  | ||||
| 		$fields = implode(', ', array_map(function ($field) { return $this->quote($field); }, array_keys($this->fields))); | ||||
| 		$search = SqlSearch::fromBase($search, $this->pdo)->addCondition('id < :id', [ 'id' => $id ]); | ||||
| 		$limit = $count ? "LIMIT {$count}" : ''; | ||||
| 		$result = $this->query( | ||||
| 			"SELECT {$fields} FROM {$this->table} {$search->query} ORDER BY id DESC {$limit}", $search->bindings | ||||
| 		); | ||||
|  | ||||
| 		return array_reverse($this->resultsToRequests($result)); | ||||
| 	} | ||||
|  | ||||
| 	// Return requests received after specified id, optionally limited to specified count | ||||
| 	public function next($id, $count = null, Search $search = null) | ||||
| 	{ | ||||
| 		$count = (int) $count; | ||||
|  | ||||
| 		$fields = implode(', ', array_map(function ($field) { return $this->quote($field); }, array_keys($this->fields))); | ||||
| 		$search = SqlSearch::fromBase($search, $this->pdo)->addCondition('id > :id', [ 'id' => $id ]); | ||||
| 		$limit = $count ? "LIMIT {$count}" : ''; | ||||
| 		$result = $this->query( | ||||
| 			"SELECT {$fields} FROM {$this->table} {$search->query} ORDER BY id ASC {$limit}", $search->bindings | ||||
| 		); | ||||
|  | ||||
| 		return $this->resultsToRequests($result); | ||||
| 	} | ||||
|  | ||||
| 	// Store the request in the database | ||||
| 	public function store(Request $request) | ||||
| 	{ | ||||
| 		$data = $request->toArray(); | ||||
|  | ||||
| 		foreach ($this->needsSerialization as $key) { | ||||
| 			$data[$key] = @json_encode($data[$key], \JSON_PARTIAL_OUTPUT_ON_ERROR); | ||||
| 		} | ||||
|  | ||||
| 		$fields = implode(', ', array_map(function ($field) { return $this->quote($field); }, array_keys($this->fields))); | ||||
| 		$bindings = implode(', ', array_map(function ($field) { return ":{$field}"; }, array_keys($this->fields))); | ||||
|  | ||||
| 		$this->query("INSERT INTO {$this->table} ($fields) VALUES ($bindings)", $data); | ||||
|  | ||||
| 		$this->cleanup(); | ||||
| 	} | ||||
|  | ||||
| 	// Update an existing request in the database | ||||
| 	public function update(Request $request) | ||||
| 	{ | ||||
| 		$data = $request->toArray(); | ||||
|  | ||||
| 		foreach ($this->needsSerialization as $key) { | ||||
| 			$data[$key] = @json_encode($data[$key], \JSON_PARTIAL_OUTPUT_ON_ERROR); | ||||
| 		} | ||||
|  | ||||
| 		$values = implode(', ', array_map(function ($field) { | ||||
| 			return $this->quote($field) . " = :{$field}"; | ||||
| 		}, array_keys($this->fields))); | ||||
|  | ||||
| 		$this->query("UPDATE {$this->table} SET {$values} WHERE id = :id", $data); | ||||
|  | ||||
| 		$this->cleanup(); | ||||
| 	} | ||||
|  | ||||
| 	// Cleanup old requests | ||||
| 	public function cleanup() | ||||
| 	{ | ||||
| 		if ($this->expiration === false) return; | ||||
|  | ||||
| 		$this->query("DELETE FROM {$this->table} WHERE time < :time", [ 'time' => time() - ($this->expiration * 60) ]); | ||||
| 	} | ||||
|  | ||||
| 	// Create or update the Clockwork metadata table | ||||
| 	protected function initialize() | ||||
| 	{ | ||||
| 		// first we get rid of existing table if it exists by renaming it so we won't lose any data | ||||
| 		try { | ||||
| 			$table = $this->quote($this->table); | ||||
| 			$backupTableName = $this->quote("{$this->table}_backup_" . date('Ymd')); | ||||
| 			$this->pdo->exec("ALTER TABLE {$table} RENAME TO {$backupTableName};"); | ||||
| 		} catch (\PDOException $e) { | ||||
| 			// this just means the table doesn't yet exist, nothing to do here | ||||
| 		} | ||||
|  | ||||
| 		// create the metadata table | ||||
| 		$this->pdo->exec($this->buildSchema($table)); | ||||
|  | ||||
| 		$indexName = $this->quote("{$this->table}_time_index"); | ||||
| 		$this->pdo->exec("CREATE INDEX {$indexName} ON {$table} (". $this->quote('time') .')'); | ||||
| 	} | ||||
|  | ||||
| 	// Builds the query to create Clockwork database table | ||||
| 	protected function buildSchema($table) | ||||
| 	{ | ||||
| 		$textType = $this->pdo->getAttribute(PDO::ATTR_DRIVER_NAME) == 'mysql' ? 'MEDIUMTEXT' : 'TEXT'; | ||||
|  | ||||
| 		$columns = implode(', ', array_map(function ($field, $type) use ($textType) { | ||||
| 			return $this->quote($field) . ' ' . str_replace('TEXT', $textType, $type); | ||||
| 		}, array_keys($this->fields), array_values($this->fields))); | ||||
|  | ||||
| 		return "CREATE TABLE {$table} ({$columns});"; | ||||
| 	} | ||||
|  | ||||
| 	// Executes an sql query, lazily initiates the clockwork database schema if it's old or doesn't exist yet, returns | ||||
| 	// executed statement or false on error | ||||
| 	protected function query($query, array $bindings = [], $firstTry = true) | ||||
| 	{ | ||||
| 		try { | ||||
| 			if ($stmt = $this->pdo->prepare($query)) { | ||||
| 				if ($stmt->execute($bindings)) return $stmt; | ||||
| 				throw new \PDOException; | ||||
| 			} | ||||
| 		} catch (\PDOException $e) { | ||||
| 			$stmt = false; | ||||
| 		} | ||||
|  | ||||
| 		// the query failed to execute, assume it's caused by missing or old schema, try to reinitialize database | ||||
| 		if (! $stmt && $firstTry) { | ||||
| 			$this->initialize(); | ||||
| 			return $this->query($query, $bindings, false); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Quotes SQL identifier name properly for the current database | ||||
| 	protected function quote($identifier) | ||||
| 	{ | ||||
| 		return $this->pdo->getAttribute(PDO::ATTR_DRIVER_NAME) == 'mysql' ? "`{$identifier}`" : "\"{$identifier}\""; | ||||
| 	} | ||||
|  | ||||
| 	// Returns array of Requests instances from the executed PDO statement | ||||
| 	protected function resultsToRequests($stmt) | ||||
| 	{ | ||||
| 		return array_map(function ($data) { | ||||
| 			return $this->dataToRequest($data); | ||||
| 		}, $stmt->fetchAll(PDO::FETCH_ASSOC)); | ||||
| 	} | ||||
|  | ||||
| 	// Returns a Request instance from a single database record | ||||
| 	protected function dataToRequest($data) | ||||
| 	{ | ||||
| 		foreach ($this->needsSerialization as $key) { | ||||
| 			$data[$key] = json_decode($data[$key], true); | ||||
| 		} | ||||
|  | ||||
| 		return new Request($data); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										12
									
								
								vendor/itsgoingd/clockwork/Clockwork/Storage/Storage.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								vendor/itsgoingd/clockwork/Clockwork/Storage/Storage.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| <?php namespace Clockwork\Storage; | ||||
|  | ||||
| use Clockwork\Storage\StorageInterface; | ||||
| use Clockwork\Request\Request; | ||||
|  | ||||
| abstract class Storage implements StorageInterface | ||||
| { | ||||
| 	// Update existing request | ||||
| 	public function update(Request $request) | ||||
| 	{ | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										31
									
								
								vendor/itsgoingd/clockwork/Clockwork/Storage/StorageInterface.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								vendor/itsgoingd/clockwork/Clockwork/Storage/StorageInterface.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | ||||
| <?php namespace Clockwork\Storage; | ||||
|  | ||||
| use Clockwork\Request\Request; | ||||
|  | ||||
| // Interface for requests storage implementations | ||||
| interface StorageInterface | ||||
| { | ||||
| 	// Returns all requests | ||||
| 	public function all(Search $search = null); | ||||
|  | ||||
| 	// Return a single request by id | ||||
| 	public function find($id); | ||||
|  | ||||
| 	// Return the latest request | ||||
| 	public function latest(Search $search = null); | ||||
|  | ||||
| 	// Return requests received before specified id, optionally limited to specified count | ||||
| 	public function previous($id, $count = null, Search $search = null); | ||||
|  | ||||
| 	// Return requests received after specified id, optionally limited to specified count | ||||
| 	public function next($id, $count = null, Search $search = null); | ||||
|  | ||||
| 	// Store request | ||||
| 	public function store(Request $request); | ||||
|  | ||||
| 	// Update existing request | ||||
| 	public function update(Request $request); | ||||
|  | ||||
| 	// Cleanup old requests | ||||
| 	public function cleanup(); | ||||
| } | ||||
							
								
								
									
										55
									
								
								vendor/itsgoingd/clockwork/Clockwork/Storage/SymfonyStorage.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								vendor/itsgoingd/clockwork/Clockwork/Storage/SymfonyStorage.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,55 @@ | ||||
| <?php namespace Clockwork\Storage; | ||||
|  | ||||
| use Clockwork\Request\Request; | ||||
| use Clockwork\Support\Symfony\ProfileTransformer; | ||||
|  | ||||
| use Symfony\Component\HttpKernel\Profiler\Profiler; | ||||
|  | ||||
| // Storage wrapping Symfony profiler | ||||
| class SymfonyStorage extends FileStorage | ||||
| { | ||||
| 	// Symfony profiler instance | ||||
| 	protected $profiler; | ||||
|  | ||||
| 	// Symfony profiler path | ||||
| 	protected $path; | ||||
|  | ||||
| 	// Create a new instance, takes Symfony profiler instance and path as argument | ||||
| 	public function __construct(Profiler $profiler, $path) | ||||
| 	{ | ||||
| 		$this->profiler = $profiler; | ||||
| 		$this->path = $path; | ||||
| 	} | ||||
|  | ||||
| 	// Store request, no-op since this is read-only storage implementation | ||||
| 	public function store(Request $request, $skipIndex = false) | ||||
| 	{ | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	// Cleanup old requests, no-op since this is read-only storage implementation | ||||
| 	public function cleanup($force = false) | ||||
| 	{ | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	protected function loadRequest($token) | ||||
| 	{ | ||||
| 		return ($profile = $this->profiler->loadProfile($token)) ? (new ProfileTransformer)->transform($profile) : null; | ||||
| 	} | ||||
|  | ||||
| 	// Open index file, optionally move file pointer to the end | ||||
| 	protected function openIndex($position = 'start', $lock = null, $force = null) | ||||
| 	{ | ||||
| 		$this->indexHandle = fopen("{$this->path}/index.csv", 'r'); | ||||
|  | ||||
| 		if ($position == 'end') fseek($this->indexHandle, 0, SEEK_END); | ||||
| 	} | ||||
|  | ||||
| 	protected function makeRequestFromIndex($record) | ||||
| 	{ | ||||
| 		return new Request(array_combine( | ||||
| 			[ 'id', 'ip', 'method', 'uri', 'time', 'parent', 'responseStatus' ], $record | ||||
| 		)); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										43
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Laravel/ClockworkCleanCommand.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Laravel/ClockworkCleanCommand.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,43 @@ | ||||
| <?php namespace Clockwork\Support\Laravel; | ||||
|  | ||||
| use Illuminate\Console\Command; | ||||
| use Symfony\Component\Console\Input\InputOption; | ||||
|  | ||||
| // Console command for cleaning old requests metadata | ||||
| class ClockworkCleanCommand extends Command | ||||
| { | ||||
| 	// Command name | ||||
| 	protected $name = 'clockwork:clean'; | ||||
|  | ||||
| 	// Command description | ||||
| 	protected $description = 'Cleans Clockwork request metadata'; | ||||
|  | ||||
| 	// Command options | ||||
| 	public function getOptions() | ||||
| 	{ | ||||
| 		return [ | ||||
| 			[ 'all', 'a', InputOption::VALUE_NONE, 'cleans all data' ], | ||||
| 			[ 'expiration', 'e', InputOption::VALUE_REQUIRED, 'cleans data older than specified value in minutes' ] | ||||
| 		]; | ||||
| 	} | ||||
|  | ||||
| 	// Execute the console command | ||||
| 	public function handle() | ||||
| 	{ | ||||
| 		if ($this->option('all')) { | ||||
| 			$this->laravel['config']->set('clockwork.storage_expiration', 0); | ||||
| 		} elseif ($expiration = $this->option('expiration')) { | ||||
| 			$this->laravel['config']->set('clockwork.storage_expiration', $expiration); | ||||
| 		} | ||||
|  | ||||
| 		$this->laravel['clockwork.support']->makeStorage()->cleanup($force = true); | ||||
|  | ||||
| 		$this->info('Metadata cleaned successfully.'); | ||||
| 	} | ||||
|  | ||||
| 	// Compatibility for the old Laravel versions | ||||
| 	public function fire() | ||||
| 	{ | ||||
| 		return $this->handle(); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										91
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Laravel/ClockworkController.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Laravel/ClockworkController.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,91 @@ | ||||
| <?php namespace Clockwork\Support\Laravel; | ||||
|  | ||||
| use Clockwork\Clockwork; | ||||
| use Clockwork\Support\Laravel\ClockworkSupport; | ||||
| use Illuminate\Contracts\Foundation\Application; | ||||
| use Illuminate\Http\JsonResponse; | ||||
| use Illuminate\Http\RedirectResponse; | ||||
| use Illuminate\Http\Request; | ||||
| use Illuminate\Routing\Controller; | ||||
| use Laravel\Telescope\Telescope; | ||||
|  | ||||
| // Clockwork api and app controller | ||||
| class ClockworkController extends Controller | ||||
| { | ||||
| 	public function __construct() | ||||
| 	{ | ||||
| 		$this->middleware(config('clockwork.middlewares')); | ||||
| 	} | ||||
|  | ||||
| 	// Authantication endpoint | ||||
| 	public function authenticate(Clockwork $clockwork, ClockworkSupport $clockworkSupport, Request $request) | ||||
| 	{ | ||||
| 		$this->ensureClockworkIsEnabled($clockworkSupport); | ||||
|  | ||||
| 		$token = $clockwork->authenticator()->attempt( | ||||
| 			$request->only([ 'username', 'password' ]) | ||||
| 		); | ||||
|  | ||||
| 		return new JsonResponse([ 'token' => $token ], $token ? 200 : 403); | ||||
| 	} | ||||
|  | ||||
| 	// Metadata retrieving endpoint | ||||
| 	public function getData(ClockworkSupport $clockworkSupport, Request $request, $id = null, $direction = null, $count = null) | ||||
| 	{ | ||||
| 		$this->ensureClockworkIsEnabled($clockworkSupport); | ||||
|  | ||||
| 		return $clockworkSupport->getData( | ||||
| 			$id, $direction, $count, $request->only([ 'only', 'except' ]) | ||||
| 		); | ||||
| 	} | ||||
|  | ||||
| 	// Extended metadata retrieving endpoint | ||||
| 	public function getExtendedData(ClockworkSupport $clockworkSupport, Request $request, $id = null) | ||||
| 	{ | ||||
| 		$this->ensureClockworkIsEnabled($clockworkSupport); | ||||
|  | ||||
| 		return $clockworkSupport->getExtendedData( | ||||
| 			$id, $request->only([ 'only', 'except' ]) | ||||
| 		); | ||||
| 	} | ||||
|  | ||||
| 	// Metadata updating endpoint | ||||
| 	public function updateData(ClockworkSupport $clockworkSupport, Request $request, $id = null) | ||||
| 	{ | ||||
| 		$this->ensureClockworkIsEnabled($clockworkSupport); | ||||
|  | ||||
| 		return $clockworkSupport->updateData($id, $request->json()->all()); | ||||
| 	} | ||||
|  | ||||
| 	// App index | ||||
| 	public function webIndex(ClockworkSupport $clockworkSupport) | ||||
| 	{ | ||||
| 		$this->ensureClockworkIsEnabled($clockworkSupport); | ||||
|  | ||||
| 		return $clockworkSupport->getWebAsset('index.html'); | ||||
| 	} | ||||
|  | ||||
| 	// App assets serving | ||||
| 	public function webAsset(ClockworkSupport $clockworkSupport, $path) | ||||
| 	{ | ||||
| 		$this->ensureClockworkIsEnabled($clockworkSupport); | ||||
|  | ||||
| 		return $clockworkSupport->getWebAsset($path); | ||||
| 	} | ||||
|  | ||||
| 	// App redirect (/clockwork -> /clockwork/app) | ||||
| 	public function webRedirect(ClockworkSupport $clockworkSupport, Request $request) | ||||
| 	{ | ||||
| 		$this->ensureClockworkIsEnabled($clockworkSupport); | ||||
|  | ||||
| 		return new RedirectResponse('/' . $request->path() . '/app'); | ||||
| 	} | ||||
|  | ||||
| 	// Ensure Clockwork is still enabled at this point and stop Telescope recording if present | ||||
| 	protected function ensureClockworkIsEnabled(ClockworkSupport $clockworkSupport) | ||||
| 	{ | ||||
| 		if (class_exists(Telescope::class)) Telescope::stopRecording(); | ||||
|  | ||||
| 		if (! $clockworkSupport->isEnabled()) abort(404); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										38
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Laravel/ClockworkMiddleware.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Laravel/ClockworkMiddleware.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | ||||
| <?php namespace Clockwork\Support\Laravel; | ||||
|  | ||||
| use Illuminate\Contracts\Debug\ExceptionHandler; | ||||
| use Illuminate\Contracts\Foundation\Application; | ||||
|  | ||||
| // Clockwork Laravel middleware | ||||
| class ClockworkMiddleware | ||||
| { | ||||
| 	// Laravel application instance | ||||
| 	protected $app; | ||||
|  | ||||
| 	// Create a new middleware instance | ||||
| 	public function __construct(Application $app) | ||||
| 	{ | ||||
| 		$this->app = $app; | ||||
| 	} | ||||
|  | ||||
| 	// Handle an incoming request | ||||
| 	public function handle($request, \Closure $next) | ||||
| 	{ | ||||
| 		$this->app['clockwork']->event('Controller')->begin(); | ||||
|  | ||||
| 		try { | ||||
| 			$response = $next($request); | ||||
| 		} catch (\Exception $e) { | ||||
| 			$this->app[ExceptionHandler::class]->report($e); | ||||
| 			$response = $this->app[ExceptionHandler::class]->render($request, $e); | ||||
| 		} | ||||
|  | ||||
| 		return $this->app['clockwork.support']->processRequest($request, $response); | ||||
| 	} | ||||
|  | ||||
| 	// Record the current request after a response is sent | ||||
| 	public function terminate() | ||||
| 	{ | ||||
| 		$this->app['clockwork.support']->recordRequest(); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										274
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Laravel/ClockworkServiceProvider.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										274
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Laravel/ClockworkServiceProvider.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,274 @@ | ||||
| <?php namespace Clockwork\Support\Laravel; | ||||
|  | ||||
| use Clockwork\Clockwork; | ||||
| use Clockwork\Authentication\AuthenticatorInterface; | ||||
| use Clockwork\DataSource\EloquentDataSource; | ||||
| use Clockwork\DataSource\LaravelCacheDataSource; | ||||
| use Clockwork\DataSource\LaravelDataSource; | ||||
| use Clockwork\DataSource\LaravelEventsDataSource; | ||||
| use Clockwork\DataSource\LaravelNotificationsDataSource; | ||||
| use Clockwork\DataSource\LaravelQueueDataSource; | ||||
| use Clockwork\DataSource\LaravelRedisDataSource; | ||||
| use Clockwork\DataSource\LaravelTwigDataSource; | ||||
| use Clockwork\DataSource\LaravelViewsDataSource; | ||||
| use Clockwork\DataSource\SwiftDataSource; | ||||
| use Clockwork\DataSource\TwigDataSource; | ||||
| use Clockwork\DataSource\XdebugDataSource; | ||||
| use Clockwork\Helpers\StackFilter; | ||||
| use Clockwork\Request\Request; | ||||
| use Clockwork\Storage\StorageInterface; | ||||
|  | ||||
| use Illuminate\Support\ServiceProvider; | ||||
|  | ||||
| class ClockworkServiceProvider extends ServiceProvider | ||||
| { | ||||
| 	public function boot() | ||||
| 	{ | ||||
| 		if ($this->app['clockwork.support']->isCollectingData()) { | ||||
| 			$this->registerEventListeners(); | ||||
| 			$this->registerMiddleware(); | ||||
| 		} | ||||
|  | ||||
| 		$this->app['clockwork.support']->handleArtisanEvents(); | ||||
| 		$this->app['clockwork.support']->handleOctaneEvents(); | ||||
|  | ||||
| 		// If Clockwork is disabled, return before registering middleware or routes | ||||
| 		if (! $this->app['clockwork.support']->isEnabled()) return; | ||||
|  | ||||
| 		$this->registerRoutes(); | ||||
|  | ||||
| 		// register the Clockwork Web UI routes | ||||
| 		if ($this->app['clockwork.support']->isWebEnabled()) { | ||||
| 			$this->registerWebRoutes(); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	public function register() | ||||
| 	{ | ||||
| 		$this->registerConfiguration(); | ||||
| 		$this->registerClockwork(); | ||||
| 		$this->registerCommands(); | ||||
| 		$this->registerDataSources(); | ||||
| 		$this->registerAliases(); | ||||
|  | ||||
| 		$this->app->make('clockwork.request'); // instantiate the request to have id and time available as early as possible | ||||
|  | ||||
| 		$this->app['clockwork.support'] | ||||
| 			->configureSerializer() | ||||
| 			->configureShouldCollect() | ||||
| 			->configureShouldRecord(); | ||||
|  | ||||
| 		if ($this->app['clockwork.support']->getConfig('register_helpers', true)) { | ||||
| 			require __DIR__ . '/helpers.php'; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Register the configuration file | ||||
| 	protected function registerConfiguration() | ||||
| 	{ | ||||
| 		$this->publishes([ __DIR__ . '/config/clockwork.php' => config_path('clockwork.php') ]); | ||||
| 		$this->mergeConfigFrom(__DIR__ . '/config/clockwork.php', 'clockwork'); | ||||
| 	} | ||||
|  | ||||
| 	// Register core Clockwork components | ||||
| 	protected function registerClockwork() | ||||
| 	{ | ||||
| 		$this->app->singleton('clockwork', function ($app) { | ||||
| 			return (new Clockwork) | ||||
| 				->authenticator($app['clockwork.authenticator']) | ||||
| 				->request($app['clockwork.request']) | ||||
| 				->storage($app['clockwork.storage']); | ||||
| 		}); | ||||
|  | ||||
| 		$this->app->singleton('clockwork.authenticator', function ($app) { | ||||
| 			return $app['clockwork.support']->makeAuthenticator(); | ||||
| 		}); | ||||
|  | ||||
| 		$this->app->singleton('clockwork.request', function ($app) { | ||||
| 			return new Request; | ||||
| 		}); | ||||
|  | ||||
| 		$this->app->singleton('clockwork.storage', function ($app) { | ||||
| 			return $app['clockwork.support']->makeStorage(); | ||||
| 		}); | ||||
|  | ||||
| 		$this->app->singleton('clockwork.support', function ($app) { | ||||
| 			return new ClockworkSupport($app); | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	// Register the artisan commands | ||||
| 	protected function registerCommands() | ||||
| 	{ | ||||
| 		$this->commands([ | ||||
| 			ClockworkCleanCommand::class | ||||
| 		]); | ||||
| 	} | ||||
|  | ||||
| 	// Register Clockwork data sources | ||||
| 	protected function registerDataSources() | ||||
| 	{ | ||||
| 		$this->app->singleton('clockwork.cache', function ($app) { | ||||
| 			return (new LaravelCacheDataSource( | ||||
| 				$app['events'], | ||||
| 				$app['clockwork.support']->getConfig('features.cache.collect_queries'), | ||||
| 				$app['clockwork.support']->getConfig('features.cache.collect_values') | ||||
| 			)); | ||||
| 		}); | ||||
|  | ||||
| 		$this->app->singleton('clockwork.eloquent', function ($app) { | ||||
| 			$dataSource = (new EloquentDataSource( | ||||
| 				$app['db'], | ||||
| 				$app['events'], | ||||
| 				$app['clockwork.support']->getConfig('features.database.collect_queries'), | ||||
| 				$app['clockwork.support']->getConfig('features.database.slow_threshold'), | ||||
| 				$app['clockwork.support']->getConfig('features.database.slow_only'), | ||||
| 				$app['clockwork.support']->getConfig('features.database.detect_duplicate_queries'), | ||||
| 				$app['clockwork.support']->getConfig('features.database.collect_models_actions'), | ||||
| 				$app['clockwork.support']->getConfig('features.database.collect_models_retrieved') | ||||
| 			)); | ||||
|  | ||||
| 			// if we are collecting queue jobs, filter out queries caused by the database queue implementation | ||||
| 			if ($app['clockwork.support']->isCollectingQueueJobs()) { | ||||
| 				$dataSource->addFilter(function ($query, $trace) { | ||||
| 					return ! $trace->first(StackFilter::make()->isClass(\Illuminate\Queue\DatabaseQueue::class)); | ||||
| 				}, 'early'); | ||||
| 			} | ||||
|  | ||||
| 			if ($app->runningUnitTests()) { | ||||
| 				$dataSource->addFilter(function ($query, $trace) { | ||||
| 					return ! $trace->first(StackFilter::make()->isClass([ | ||||
| 						\Illuminate\Database\Migrations\Migrator::class, | ||||
| 						\Illuminate\Database\Console\Migrations\MigrateCommand::class | ||||
| 					])); | ||||
| 				}); | ||||
| 			} | ||||
|  | ||||
| 			return $dataSource; | ||||
| 		}); | ||||
|  | ||||
| 		$this->app->singleton('clockwork.events', function ($app) { | ||||
| 			return (new LaravelEventsDataSource( | ||||
| 				$app['events'], | ||||
| 				$app['clockwork.support']->getConfig('features.events.ignored_events', []) | ||||
| 			)); | ||||
| 		}); | ||||
|  | ||||
| 		$this->app->singleton('clockwork.laravel', function ($app) { | ||||
| 			return (new LaravelDataSource( | ||||
| 				$app, | ||||
| 				$app['clockwork.support']->isFeatureEnabled('log'), | ||||
| 				$app['clockwork.support']->isFeatureEnabled('routes'), | ||||
| 				$app['clockwork.support']->getConfig('features.routes.only_namespaces', []) | ||||
| 			)); | ||||
| 		}); | ||||
|  | ||||
| 		$this->app->singleton('clockwork.notifications', function ($app) { | ||||
| 			return new LaravelNotificationsDataSource($app['events']); | ||||
| 		}); | ||||
|  | ||||
| 		$this->app->singleton('clockwork.queue', function ($app) { | ||||
| 			return (new LaravelQueueDataSource($app['queue']->connection())); | ||||
| 		}); | ||||
|  | ||||
| 		$this->app->singleton('clockwork.redis', function ($app) { | ||||
| 			$dataSource = new LaravelRedisDataSource($app['events']); | ||||
|  | ||||
| 			// if we are collecting queue jobs, filter out commands executed by the redis queue implementation | ||||
| 			if ($app['clockwork.support']->isCollectingQueueJobs()) { | ||||
| 				$dataSource->addFilter(function ($query, $trace) { | ||||
| 					return ! $trace->first(StackFilter::make()->isClass([ | ||||
| 						\Illuminate\Queue\RedisQueue::class, | ||||
| 						\Laravel\Horizon\Repositories\RedisJobRepository::class, | ||||
| 						\Laravel\Horizon\Repositories\RedisTagRepository::class, | ||||
| 						\Laravel\Horizon\Repositories\RedisMetricsRepository::class | ||||
| 					])); | ||||
| 				}); | ||||
| 			} | ||||
|  | ||||
| 			return $dataSource; | ||||
| 		}); | ||||
|  | ||||
| 		$this->app->singleton('clockwork.swift', function ($app) { | ||||
| 			return new SwiftDataSource($app['mailer']->getSwiftMailer()); | ||||
| 		}); | ||||
|  | ||||
| 		$this->app->singleton('clockwork.twig', function ($app) { | ||||
| 			return new TwigDataSource($app['twig']); | ||||
| 		}); | ||||
|  | ||||
| 		$this->app->singleton('clockwork.views', function ($app) { | ||||
| 			return new LaravelViewsDataSource( | ||||
| 				$app['events'], | ||||
| 				$app['clockwork.support']->getConfig('features.views.collect_data') | ||||
| 			); | ||||
| 		}); | ||||
|  | ||||
| 		$this->app->singleton('clockwork.xdebug', function ($app) { | ||||
| 			return new XdebugDataSource; | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	// Set up aliases for all Clockwork parts so they can be resolved by type-hinting | ||||
| 	protected function registerAliases() | ||||
| 	{ | ||||
| 		$this->app->alias('clockwork', Clockwork::class); | ||||
|  | ||||
| 		$this->app->alias('clockwork.authenticator', AuthenticatorInterface::class); | ||||
| 		$this->app->alias('clockwork.storage', StorageInterface::class); | ||||
| 		$this->app->alias('clockwork.support', ClockworkSupport::class); | ||||
|  | ||||
| 		$this->app->alias('clockwork.cache', LaravelCacheDataSource::class); | ||||
| 		$this->app->alias('clockwork.eloquent', EloquentDataSource::class); | ||||
| 		$this->app->alias('clockwork.events', LaravelEventsDataSource::class); | ||||
| 		$this->app->alias('clockwork.laravel', LaravelDataSource::class); | ||||
| 		$this->app->alias('clockwork.notifications', LaravelNotificationsDataSource::class); | ||||
| 		$this->app->alias('clockwork.queue', LaravelQueueDataSource::class); | ||||
| 		$this->app->alias('clockwork.redis', LaravelRedisDataSource::class); | ||||
| 		$this->app->alias('clockwork.swift', SwiftDataSource::class); | ||||
| 		$this->app->alias('clockwork.xdebug', XdebugDataSource::class); | ||||
| 	} | ||||
|  | ||||
| 	// Register event listeners | ||||
| 	protected function registerEventListeners() | ||||
| 	{ | ||||
| 		$this->app->booted(function () { | ||||
| 			$this->app['clockwork.support']->addDataSources()->listenToEvents(); | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	// Register middleware | ||||
| 	protected function registerMiddleware() | ||||
| 	{ | ||||
| 		$kernel = $this->app[\Illuminate\Contracts\Http\Kernel::class]; | ||||
|  | ||||
| 		if (method_exists($kernel, 'hasMiddleware') && $kernel->hasMiddleware(ClockworkMiddleware::class)) return; | ||||
|  | ||||
| 		$kernel->prependMiddleware(ClockworkMiddleware::class); | ||||
| 	} | ||||
|  | ||||
| 	protected function registerRoutes() | ||||
| 	{ | ||||
| 		$this->app['router']->get('/__clockwork/{id}/extended', 'Clockwork\Support\Laravel\ClockworkController@getExtendedData') | ||||
| 			->where('id', '([0-9-]+|latest)'); | ||||
| 		$this->app['router']->get('/__clockwork/{id}/{direction?}/{count?}', 'Clockwork\Support\Laravel\ClockworkController@getData') | ||||
| 			->where('id', '([0-9-]+|latest)')->where('direction', '(next|previous)')->where('count', '\d+'); | ||||
| 		$this->app['router']->put('/__clockwork/{id}', 'Clockwork\Support\Laravel\ClockworkController@updateData'); | ||||
| 		$this->app['router']->post('/__clockwork/auth', 'Clockwork\Support\Laravel\ClockworkController@authenticate'); | ||||
| 	} | ||||
|  | ||||
| 	protected function registerWebRoutes() | ||||
| 	{ | ||||
| 		$this->app['clockwork.support']->webPaths()->each(function ($path) { | ||||
| 			$this->app['router']->get("{$path}", 'Clockwork\Support\Laravel\ClockworkController@webRedirect'); | ||||
| 			$this->app['router']->get("{$path}/app", 'Clockwork\Support\Laravel\ClockworkController@webIndex'); | ||||
| 			$this->app['router']->get("{$path}/{path}", 'Clockwork\Support\Laravel\ClockworkController@webAsset') | ||||
| 				->where('path', '.+'); | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	public function provides() | ||||
| 	{ | ||||
| 		return [ 'clockwork' ]; | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										734
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Laravel/ClockworkSupport.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										734
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Laravel/ClockworkSupport.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,734 @@ | ||||
| <?php namespace Clockwork\Support\Laravel; | ||||
|  | ||||
| use Clockwork\Clockwork; | ||||
| use Clockwork\Authentication\NullAuthenticator; | ||||
| use Clockwork\Authentication\SimpleAuthenticator; | ||||
| use Clockwork\DataSource\PhpDataSource; | ||||
| use Clockwork\Helpers\Serializer; | ||||
| use Clockwork\Helpers\ServerTiming; | ||||
| use Clockwork\Helpers\StackFilter; | ||||
| use Clockwork\Helpers\StackTrace; | ||||
| use Clockwork\Request\IncomingRequest; | ||||
| use Clockwork\Request\Request; | ||||
| use Clockwork\Storage\FileStorage; | ||||
| use Clockwork\Storage\Search; | ||||
| use Clockwork\Storage\SqlStorage; | ||||
| use Clockwork\Web\Web; | ||||
|  | ||||
| use Illuminate\Contracts\Foundation\Application; | ||||
| use Illuminate\Contracts\Console\Kernel as ConsoleKernel; | ||||
| use Illuminate\Http\JsonResponse; | ||||
| use Illuminate\Http\Response; | ||||
| use Illuminate\Redis\RedisManager; | ||||
| use Symfony\Component\HttpFoundation\BinaryFileResponse; | ||||
| use Symfony\Component\HttpFoundation\Cookie; | ||||
| use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; | ||||
|  | ||||
| // Support class for the Laravel integration | ||||
| class ClockworkSupport | ||||
| { | ||||
| 	// Laravel application instance | ||||
| 	protected $app; | ||||
|  | ||||
| 	// Laravel artisan (console application) instance | ||||
| 	protected $artisan; | ||||
|  | ||||
| 	// Incoming request instance | ||||
| 	protected $incomingRequest; | ||||
|  | ||||
| 	public function __construct(Application $app) | ||||
| 	{ | ||||
| 		$this->app = $app; | ||||
| 	} | ||||
|  | ||||
| 	// Get a value form the Clockwork config | ||||
| 	public function getConfig($key, $default = null) | ||||
| 	{ | ||||
| 		return $this->app['config']->get("clockwork.{$key}", $default); | ||||
| 	} | ||||
|  | ||||
| 	// Retrieve metadata | ||||
| 	public function getData($id = null, $direction = null, $count = null, $filter = [], $extended = false) | ||||
| 	{ | ||||
| 		if (isset($this->app['session'])) $this->app['session.store']->reflash(); | ||||
|  | ||||
| 		$authenticator = $this->app['clockwork']->authenticator(); | ||||
| 		$storage = $this->app['clockwork']->storage(); | ||||
|  | ||||
| 		$authenticated = $authenticator->check($this->app['request']->header('X-Clockwork-Auth')); | ||||
|  | ||||
| 		if ($authenticated !== true) { | ||||
| 			return new JsonResponse([ 'message' => $authenticated, 'requires' => $authenticator->requires() ], 403); | ||||
| 		} | ||||
|  | ||||
| 		if ($direction == 'previous') { | ||||
| 			$data = $storage->previous($id, $count, Search::fromRequest($this->app['request']->all())); | ||||
| 		} elseif ($direction == 'next') { | ||||
| 			$data = $storage->next($id, $count, Search::fromRequest($this->app['request']->all())); | ||||
| 		} elseif ($id == 'latest') { | ||||
| 			$data = $storage->latest(Search::fromRequest($this->app['request']->all())); | ||||
| 		} else { | ||||
| 			$data = $storage->find($id); | ||||
| 		} | ||||
|  | ||||
| 		if ($extended) { | ||||
| 			$this->addDataSources(); | ||||
| 			$this->app['clockwork']->extendRequest($data); | ||||
| 		} | ||||
|  | ||||
| 		$except = isset($filter['except']) ? explode(',', $filter['except']) : []; | ||||
| 		$only = isset($filter['only']) ? explode(',', $filter['only']) : null; | ||||
|  | ||||
| 		if (is_array($data)) { | ||||
| 			$data = array_map(function ($request) use ($except, $only) { | ||||
| 				return $only ? $request->only(array_diff($only, [ 'updateToken' ])) : $request->except(array_merge($except, [ 'updateToken' ])); | ||||
| 			}, $data); | ||||
| 		} elseif ($data) { | ||||
| 			$data = $only ? $data->only(array_diff($only, [ 'updateToken' ])) : $data->except(array_merge($except, [ 'updateToken' ])); | ||||
| 		} | ||||
|  | ||||
| 		return new JsonResponse($data); | ||||
| 	} | ||||
|  | ||||
| 	// Retrieve extended metadata | ||||
| 	public function getExtendedData($id, $filter = []) | ||||
| 	{ | ||||
| 		return $this->getData($id, null, null, $filter, true); | ||||
| 	} | ||||
|  | ||||
| 	// Update metadata | ||||
| 	public function updateData($id, $input = []) | ||||
| 	{ | ||||
| 		if (isset($this->app['session'])) $this->app['session.store']->reflash(); | ||||
|  | ||||
| 		if (! $this->isCollectingClientMetrics()) { | ||||
| 			throw new NotFoundHttpException; | ||||
| 		} | ||||
|  | ||||
| 		$storage = $this->app['clockwork']->storage(); | ||||
|  | ||||
| 		$request = $storage->find($id); | ||||
|  | ||||
| 		if (! $request) { | ||||
| 			return new JsonResponse([ 'message' => 'Request not found.' ], 404); | ||||
| 		} | ||||
|  | ||||
| 		$token = isset($input['_token']) ? $input['_token'] : ''; | ||||
|  | ||||
| 		if (! $request->updateToken || ! hash_equals($request->updateToken, $token)) { | ||||
| 			return new JsonResponse([ 'message' => 'Invalid update token.' ], 403); | ||||
| 		} | ||||
|  | ||||
| 		foreach ($input as $key => $value) { | ||||
| 			if (in_array($key, [ 'clientMetrics', 'webVitals' ])) { | ||||
| 				$request->$key = $value; | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		$storage->update($request); | ||||
| 	} | ||||
|  | ||||
| 	// Return an asset for web ui based on it's path, resolves correct mime-type and protectes from accessing files | ||||
| 	// outside of Clockwork public dir | ||||
| 	public function getWebAsset($path) | ||||
| 	{ | ||||
| 		$asset = (new Web)->asset($path); | ||||
|  | ||||
| 		if (! $asset) throw new NotFoundHttpException; | ||||
|  | ||||
| 		return new BinaryFileResponse($asset['path'], 200, [ 'Content-Type' => $asset['mime'] ]); | ||||
| 	} | ||||
|  | ||||
| 	// Add enabled data sources | ||||
| 	public function addDataSources() | ||||
| 	{ | ||||
| 		$clockwork = $this->app['clockwork']; | ||||
|  | ||||
| 		$clockwork | ||||
| 			->addDataSource(new PhpDataSource) | ||||
| 			->addDataSource($this->frameworkDataSource()); | ||||
|  | ||||
| 		if ($this->isFeatureEnabled('database')) $clockwork->addDataSource($this->app['clockwork.eloquent']); | ||||
| 		if ($this->isFeatureEnabled('cache')) $clockwork->addDataSource($this->app['clockwork.cache']); | ||||
| 		if ($this->isFeatureEnabled('redis')) $clockwork->addDataSource($this->app['clockwork.redis']); | ||||
| 		if ($this->isFeatureEnabled('queue')) $clockwork->addDataSource($this->app['clockwork.queue']); | ||||
| 		if ($this->isFeatureEnabled('events')) $clockwork->addDataSource($this->app['clockwork.events']); | ||||
| 		if ($this->isFeatureEnabled('notifications')) { | ||||
| 			$clockwork->addDataSource( | ||||
| 				$this->isFeatureAvailable('notifications-events') | ||||
| 					? $this->app['clockwork.notifications'] : $this->app['clockwork.swift'] | ||||
| 			); | ||||
| 		} | ||||
| 		if ($this->isFeatureAvailable('xdebug')) $clockwork->addDataSource($this->app['clockwork.xdebug']); | ||||
| 		if ($this->isFeatureEnabled('views')) { | ||||
| 			$clockwork->addDataSource( | ||||
| 				$this->getConfig('features.views.use_twig_profiler', false) | ||||
| 					? $this->app['clockwork.twig'] : $this->app['clockwork.views'] | ||||
| 			); | ||||
| 		} | ||||
|  | ||||
| 		return $this; | ||||
| 	} | ||||
|  | ||||
| 	// Start listening to events | ||||
| 	public function listenToEvents() | ||||
| 	{ | ||||
| 		$this->frameworkDataSource()->listenToEvents(); | ||||
|  | ||||
| 		if ($this->isFeatureEnabled('cache')) $this->app['clockwork.cache']->listenToEvents(); | ||||
| 		if ($this->isFeatureEnabled('database')) $this->app['clockwork.eloquent']->listenToEvents(); | ||||
| 		if ($this->isFeatureEnabled('events')) $this->app['clockwork.events']->listenToEvents(); | ||||
| 		if ($this->isFeatureEnabled('notifications')) { | ||||
| 			$this->isFeatureAvailable('notifications-events') | ||||
| 				? $this->app['clockwork.notifications']->listenToEvents() : $this->app['clockwork.swift']->listenToEvents(); | ||||
| 		} | ||||
| 		if ($this->isFeatureEnabled('queue')) { | ||||
| 			$this->app['clockwork.queue']->listenToEvents(); | ||||
| 			$this->app['clockwork.queue']->setCurrentRequestId($this->app['clockwork.request']->id); | ||||
| 		} | ||||
| 		if ($this->isFeatureEnabled('redis')) { | ||||
| 			$this->app[RedisManager::class]->enableEvents(); | ||||
| 			$this->app['clockwork.redis']->listenToEvents(); | ||||
| 		} | ||||
| 		if ($this->isFeatureEnabled('views')) { | ||||
| 			$this->getConfig('features.views.use_twig_profiler', false) | ||||
| 				? $this->app['clockwork.twig']->listenToEvents() : $this->app['clockwork.views']->listenToEvents(); | ||||
| 		} | ||||
|  | ||||
| 		if ($this->isCollectingCommands()) $this->collectCommands(); | ||||
| 		if ($this->isCollectingQueueJobs()) $this->collectQueueJobs(); | ||||
|  | ||||
| 		return $this; | ||||
| 	} | ||||
|  | ||||
| 	// Resolves the framework data source from the container | ||||
| 	protected function frameworkDataSource() | ||||
| 	{ | ||||
| 		return $this->app['clockwork.laravel']; | ||||
| 	} | ||||
|  | ||||
| 	public function handleArtisanEvents() | ||||
| 	{ | ||||
| 		if (class_exists(\Illuminate\Console\Events\ArtisanStarting::class)) { | ||||
| 			$this->app['events']->listen(\Illuminate\Console\Events\ArtisanStarting::class, function ($event) { | ||||
| 				$this->artisan = $event->artisan; | ||||
| 			}); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	public function handleOctaneEvents() | ||||
| 	{ | ||||
| 		$this->app['events']->listen(\Laravel\Octane\Events\RequestReceived::class, function ($event) { | ||||
| 			$this->app = $event->sandbox; | ||||
| 			$this->incomingRequest = null; | ||||
|  | ||||
| 			$this->app->forgetInstance('clockwork.request'); | ||||
| 			$request = $this->app->make('clockwork.request')->override('requestTime', microtime(true)); | ||||
|  | ||||
| 			$this->app['clockwork']->reset()->request($request); | ||||
| 			$this->app['clockwork.laravel']->setApplication($this->app); | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	// Make a storage instance based on the current configuration | ||||
| 	public function makeStorage() | ||||
| 	{ | ||||
| 		$expiration = $this->getConfig('storage_expiration'); | ||||
|  | ||||
| 		if ($this->getConfig('storage', 'files') == 'sql') { | ||||
| 			$database = $this->getConfig('storage_sql_database', storage_path('clockwork.sqlite')); | ||||
| 			$table = $this->getConfig('storage_sql_table', 'clockwork'); | ||||
|  | ||||
| 			if ($this->app['config']->get("database.connections.{$database}")) { | ||||
| 				$database = $this->app['db']->connection($database)->getPdo(); | ||||
| 			} else { | ||||
| 				$database = "sqlite:{$database}"; | ||||
| 			} | ||||
|  | ||||
| 			return new SqlStorage($database, $table, null, null, $expiration); | ||||
| 		} else { | ||||
| 			return new FileStorage( | ||||
| 				$this->getConfig('storage_files_path', storage_path('clockwork')), | ||||
| 				0700, | ||||
| 				$expiration, | ||||
| 				$this->getConfig('storage_files_compress', false) | ||||
| 			); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Make an authenticator instance based on the current configuration | ||||
| 	public function makeAuthenticator() | ||||
| 	{ | ||||
| 		$authenticator = $this->getConfig('authentication'); | ||||
|  | ||||
| 		if (is_string($authenticator)) { | ||||
| 			return $this->app->make($authenticator); | ||||
| 		} elseif ($authenticator) { | ||||
| 			return new SimpleAuthenticator($this->getConfig('authentication_password')); | ||||
| 		} else { | ||||
| 			return new NullAuthenticator; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Set up collecting of executed artisan commands | ||||
| 	public function collectCommands() | ||||
| 	{ | ||||
| 		$this->app['events']->listen(\Illuminate\Console\Events\CommandStarting::class, function ($event) { | ||||
| 			// only collect commands ran through artisan cli, other commands are recorded as part of respective request | ||||
| 			if (basename(StackTrace::get()->last()->file) != 'artisan') return; | ||||
|  | ||||
| 			if (! $this->getConfig('artisan.collect_output')) return; | ||||
| 			if (! $event->command || $this->isCommandFiltered($event->command)) return; | ||||
|  | ||||
| 			$event->output->setFormatter( | ||||
| 				version_compare(\Illuminate\Foundation\Application::VERSION, '9.0.0', '<') | ||||
| 					? new Console\CapturingLegacyFormatter($event->output->getFormatter()) | ||||
| 					: new Console\CapturingFormatter($event->output->getFormatter()) | ||||
| 			); | ||||
| 		}); | ||||
|  | ||||
| 		$this->app['events']->listen(\Illuminate\Console\Events\CommandFinished::class, function ($event) { | ||||
| 			// only collect commands ran through artisan cli, other commands are recorded as part of respective request | ||||
| 			if (basename(StackTrace::get()->last()->file) != 'artisan') return; | ||||
|  | ||||
| 			if (! $event->command || $this->isCommandFiltered($event->command)) return; | ||||
|  | ||||
| 			$command = $this->artisan->find($event->command); | ||||
|  | ||||
| 			$allArguments = $event->input->getArguments(); | ||||
| 			$allOptions = $event->input->getOptions(); | ||||
|  | ||||
| 			$defaultArguments = $command->getDefinition()->getArgumentDefaults(); | ||||
| 			$defaultOptions = $command->getDefinition()->getOptionDefaults(); | ||||
|  | ||||
| 			$this->app->make('clockwork') | ||||
| 				->resolveAsCommand( | ||||
| 					$event->command, | ||||
| 					$event->exitCode, | ||||
| 					array_udiff_assoc($allArguments, $defaultArguments, function ($a, $b) { return $a == $b ? 0 : 1; }), | ||||
| 					array_udiff_assoc($allOptions, $defaultOptions, function ($a, $b) { return $a == $b ? 0 : 1; }), | ||||
| 					$defaultArguments, | ||||
| 					$defaultOptions, | ||||
| 					$this->getConfig('artisan.collect_output') ? $event->output->getFormatter()->capturedOutput() : null | ||||
| 				) | ||||
| 				->storeRequest(); | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	// Set up collecting of executed queue jobs | ||||
| 	public function collectQueueJobs() | ||||
| 	{ | ||||
| 		$this->app['events']->listen(\Illuminate\Queue\Events\JobProcessing::class, function ($event) { | ||||
| 			// sync jobs are recorded as part of the parent request | ||||
| 			if ($event->job instanceof \Illuminate\Queue\Jobs\SyncJob) return; | ||||
|  | ||||
| 			$payload = $event->job->payload(); | ||||
|  | ||||
| 			if (! isset($payload['clockwork_id']) || $this->isQueueJobFiltered($payload['displayName'])) return; | ||||
|  | ||||
| 			$request = new Request([ 'id' => $payload['clockwork_id'] ]); | ||||
| 			if (isset($payload['clockwork_parent_id'])) $request->setParent($payload['clockwork_parent_id']); | ||||
|  | ||||
| 			$this->app->make('clockwork')->reset()->request($request); | ||||
|  | ||||
| 			$this->app['clockwork.queue']->setCurrentRequestId($request->id); | ||||
| 		}); | ||||
|  | ||||
| 		$this->app['events']->listen(\Illuminate\Queue\Events\JobProcessed::class, function ($event) { | ||||
| 			$this->processQueueJob($event->job); | ||||
| 		}); | ||||
|  | ||||
| 		$this->app['events']->listen(\Illuminate\Queue\Events\JobFailed::class, function ($event) { | ||||
| 			$this->processQueueJob($event->job, $event->exception); | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	// Process an executed queue job, resolves and records the current request | ||||
| 	protected function processQueueJob($job, $exception = null) | ||||
| 	{ | ||||
| 		// sync jobs are recorded as part of the parent request | ||||
| 		if ($job instanceof \Illuminate\Queue\Jobs\SyncJob) return; | ||||
|  | ||||
| 		$payload = $job->payload(); | ||||
|  | ||||
| 		if (! isset($payload['clockwork_id'])) return; | ||||
|  | ||||
| 		$unserialized = isset($payload['data']['command']) ? unserialize($payload['data']['command']) : null; | ||||
|  | ||||
| 		if (! $unserialized || $this->isQueueJobFiltered(get_class($unserialized))) return; | ||||
|  | ||||
| 		if ($exception) { | ||||
| 			$this->app->make('clockwork')->error($exception->getMessage(), [ 'exception' => $exception ]); | ||||
| 		} | ||||
|  | ||||
| 		$this->app->make('clockwork') | ||||
| 			->resolveAsQueueJob( | ||||
| 				get_class($unserialized), | ||||
| 				$payload['displayName'], | ||||
| 				$job->hasFailed() ? 'failed' : ($job->isReleased() ? 'released' : 'done'), | ||||
| 				$unserialized, | ||||
| 				$job->getQueue(), | ||||
| 				$job->getConnectionName(), | ||||
| 				array_filter([ | ||||
| 					'maxTries'     => isset($payload['maxTries']) ? $payload['maxTries'] : null, | ||||
| 					'delaySeconds' => isset($payload['delaySeconds']) ? $payload['delaySeconds'] : null, | ||||
| 					'timeout'      => isset($payload['timeout']) ? $payload['timeout'] : null, | ||||
| 					'timeoutAt'    => isset($payload['timeoutAt']) ? $payload['timeoutAt'] : null | ||||
| 				]) | ||||
| 			) | ||||
| 			->storeRequest(); | ||||
| 	} | ||||
|  | ||||
| 	// Process an executed http request, resolves the current request, sets Clockwork headers and cookies | ||||
| 	public function processRequest($request, $response) | ||||
| 	{ | ||||
| 		if (! $this->isCollectingRequests()) { | ||||
| 			return $response; // Clockwork is not collecting data, additional check when the middleware is enabled manually | ||||
| 		} | ||||
|  | ||||
| 		$clockwork = $this->app['clockwork']; | ||||
| 		$clockworkRequest = $clockwork->request(); | ||||
|  | ||||
| 		$clockwork->event('Controller')->end(); | ||||
|  | ||||
| 		$this->setResponse($response); | ||||
|  | ||||
| 		$clockwork->resolveRequest(); | ||||
|  | ||||
| 		if (! $this->isEnabled() || ! $this->isRecording($clockworkRequest)) { | ||||
| 			return $response; // Clockwork is disabled or we are not recording this request | ||||
| 		} | ||||
|  | ||||
| 		$response->headers->set('X-Clockwork-Id', $clockworkRequest->id, true); | ||||
| 		$response->headers->set('X-Clockwork-Version', Clockwork::VERSION, true); | ||||
|  | ||||
| 		if ($request->getBasePath()) { | ||||
| 			$response->headers->set('X-Clockwork-Path', $request->getBasePath() . '/__clockwork/', true); | ||||
| 		} | ||||
|  | ||||
| 		foreach ($this->getConfig('headers', []) as $headerName => $headerValue) { | ||||
| 			$response->headers->set("X-Clockwork-Header-{$headerName}", $headerValue); | ||||
| 		} | ||||
|  | ||||
| 		foreach ($clockwork->request()->subrequests as $subrequest) { | ||||
| 			$url = urlencode($subrequest['url']); | ||||
| 			$path = urlencode($subrequest['path']); | ||||
|  | ||||
| 			$response->headers->set('X-Clockwork-Subrequest', "{$subrequest['id']};{$url};{$path}", false); | ||||
| 		} | ||||
|  | ||||
| 		$this->appendServerTimingHeader($response, $clockworkRequest); | ||||
|  | ||||
| 		if (! ($response instanceof Response)) { | ||||
| 			return $response; | ||||
| 		} | ||||
|  | ||||
| 		if ($this->isCollectingClientMetrics() || $this->isToolbarEnabled()) { | ||||
| 			$clockworkBrowser = [ | ||||
| 				'requestId' => $clockworkRequest->id, | ||||
| 				'version'   => Clockwork::VERSION, | ||||
| 				'path'      => $request->getBasePath() . '/__clockwork/', | ||||
| 				'webPath'   => $request->getBasePath() . '/' . $this->webPaths()[0] . '/app', | ||||
| 				'token'     => $clockworkRequest->updateToken, | ||||
| 				'metrics'   => $this->isCollectingClientMetrics(), | ||||
| 				'toolbar'   => $this->isToolbarEnabled() | ||||
| 			]; | ||||
|  | ||||
| 			$response->cookie( | ||||
| 				new Cookie('x-clockwork', json_encode($clockworkBrowser), time() + 60, null, null, null, false) | ||||
| 			); | ||||
| 		} | ||||
|  | ||||
| 		return $response; | ||||
| 	} | ||||
|  | ||||
| 	// Records the current http request | ||||
| 	public function recordRequest() | ||||
| 	{ | ||||
| 		if (! $this->isCollectingRequests()) { | ||||
| 			return; // Clockwork is not collecting data, additional check when the middleware is enabled manually | ||||
| 		} | ||||
|  | ||||
| 		$clockwork = $this->app['clockwork']; | ||||
|  | ||||
| 		if (! $this->isRecording($clockwork->request())) { | ||||
| 			return; // Collecting data is disabled, return immediately | ||||
| 		} | ||||
|  | ||||
| 		$clockwork->storeRequest(); | ||||
| 	} | ||||
|  | ||||
| 	// Set current http response on the framework data source | ||||
| 	protected function setResponse($response) | ||||
| 	{ | ||||
| 		$this->app['clockwork.laravel']->setResponse($response); | ||||
| 	} | ||||
|  | ||||
| 	// Configure serializer defaults | ||||
| 	public function configureSerializer() | ||||
| 	{ | ||||
| 		Serializer::defaults([ | ||||
| 			'limit'       => $this->getConfig('serialization_depth'), | ||||
| 			'blackbox'    => $this->getConfig('serialization_blackbox'), | ||||
| 			'traces'      => $this->getConfig('stack_traces.enabled', true), | ||||
| 			'tracesSkip'  => StackFilter::make() | ||||
| 				->isNotVendor(array_merge( | ||||
| 					$this->getConfig('stack_traces.skip_vendors', []), | ||||
| 					[ 'itsgoingd', 'laravel', 'illuminate', 'psr' ] | ||||
| 				)) | ||||
| 				->isNotNamespace($this->getConfig('stack_traces.skip_namespaces', [])) | ||||
| 				->isNotFunction([ 'call_user_func', 'call_user_func_array' ]) | ||||
| 				->isNotClass($this->getConfig('stack_traces.skip_classes', [])), | ||||
| 			'tracesLimit' => $this->getConfig('stack_traces.limit', 10) | ||||
| 		]); | ||||
|  | ||||
| 		return $this; | ||||
| 	} | ||||
|  | ||||
| 	// Configure should collect rules | ||||
| 	public function configureShouldCollect() | ||||
| 	{ | ||||
| 		$this->app['clockwork']->shouldCollect([ | ||||
| 			'onDemand'        => $this->getConfig('requests.on_demand', false), | ||||
| 			'sample'          => $this->getConfig('requests.sample', false), | ||||
| 			'except'          => $this->getConfig('requests.except', []), | ||||
| 			'only'            => $this->getConfig('requests.only', []), | ||||
| 			'exceptPreflight' => $this->getConfig('requests.except_preflight', []) | ||||
| 		]); | ||||
|  | ||||
| 		// don't collect data for Clockwork requests | ||||
| 		$webPath = $this->webPaths()[0]; | ||||
| 		$this->app['clockwork']->shouldCollect()->except([ '/__clockwork(?:/.*)?', "/{$webPath}(?:/.*)?" ]); | ||||
|  | ||||
| 		return $this; | ||||
| 	} | ||||
|  | ||||
| 	// Configure should record rules | ||||
| 	public function configureShouldRecord() | ||||
| 	{ | ||||
| 		$this->app['clockwork']->shouldRecord([ | ||||
| 			'errorsOnly' => $this->getConfig('requests.errors_only', false), | ||||
| 			'slowOnly'   => $this->getConfig('requests.slow_only', false) ? $this->getConfig('requests.slow_threshold') : false | ||||
| 		]); | ||||
|  | ||||
| 		return $this; | ||||
| 	} | ||||
|  | ||||
| 	// Check whether Clockwork is enabled at all | ||||
| 	public function isEnabled() | ||||
| 	{ | ||||
| 		return $this->getConfig('enable') | ||||
| 			|| $this->getConfig('enable') === null && $this->app['config']->get('app.debug'); | ||||
| 	} | ||||
|  | ||||
| 	// Check whether we are collecting data | ||||
| 	public function isCollectingData() | ||||
| 	{ | ||||
| 		return $this->isCollectingCommands() | ||||
| 			|| $this->isCollectingQueueJobs() | ||||
| 			|| $this->isCollectingRequests() | ||||
| 			|| $this->isCollectingTests(); | ||||
| 	} | ||||
|  | ||||
| 	// Check whether we are collecting artisan commands | ||||
| 	public function isCollectingCommands() | ||||
| 	{ | ||||
| 		return ($this->isEnabled() || $this->getConfig('collect_data_always', false)) | ||||
| 			&& $this->app->runningInConsole() | ||||
| 			&& $this->getConfig('artisan.collect', false); | ||||
| 	} | ||||
|  | ||||
| 	// Check whether we are collecting queue jobs | ||||
| 	public function isCollectingQueueJobs() | ||||
| 	{ | ||||
| 		return ($this->isEnabled() || $this->getConfig('collect_data_always', false)) | ||||
| 			&& $this->app->runningInConsole() | ||||
| 			&& $this->getConfig('queue.collect', false); | ||||
| 	} | ||||
|  | ||||
| 	// Check whether we are collecting http requests | ||||
| 	public function isCollectingRequests() | ||||
| 	{ | ||||
| 		return ($this->isEnabled() || $this->getConfig('collect_data_always', false)) | ||||
| 			&& ! $this->app->runningInConsole() | ||||
| 			&& $this->app['clockwork']->shouldCollect()->filter($this->incomingRequest()); | ||||
| 	} | ||||
|  | ||||
| 	// Check whether we are collecting tests | ||||
| 	public function isCollectingTests() | ||||
| 	{ | ||||
| 		return ($this->isEnabled() || $this->getConfig('collect_data_always', false)) | ||||
| 			&& $this->app->runningInConsole() | ||||
| 			&& $this->getConfig('tests.collect', false); | ||||
| 	} | ||||
|  | ||||
| 	// Check whether we are recording the passed request | ||||
| 	public function isRecording($incomingRequest) | ||||
| 	{ | ||||
| 		return ($this->isEnabled() || $this->getConfig('collect_data_always', false)) | ||||
| 			&& $this->app['clockwork']->shouldRecord()->filter($incomingRequest); | ||||
| 	} | ||||
|  | ||||
| 	// Check whether a feature is enabled | ||||
| 	public function isFeatureEnabled($feature) | ||||
| 	{ | ||||
| 		return $this->getConfig("features.{$feature}.enabled") && $this->isFeatureAvailable($feature); | ||||
| 	} | ||||
|  | ||||
| 	// Check whether a feature is available | ||||
| 	public function isFeatureAvailable($feature) | ||||
| 	{ | ||||
| 		if ($feature == 'database') { | ||||
| 			return $this->app['config']->get('database.default'); | ||||
| 		} elseif ($feature == 'notifications-events') { | ||||
| 			return class_exists(\Illuminate\Mail\Events\MessageSent::class) | ||||
| 				&& class_exists(\Illuminate\Notifications\Events\NotificationSent::class); | ||||
| 		} elseif ($feature == 'redis') { | ||||
| 			return method_exists(\Illuminate\Redis\RedisManager::class, 'enableEvents'); | ||||
| 		} elseif ($feature == 'queue') { | ||||
| 			return method_exists(\Illuminate\Queue\Queue::class, 'createPayloadUsing'); | ||||
| 		} elseif ($feature == 'xdebug') { | ||||
| 			return in_array('xdebug', get_loaded_extensions()); | ||||
| 		} | ||||
|  | ||||
| 		return true; | ||||
| 	} | ||||
|  | ||||
| 	// Check whether we are collecting client metrics | ||||
| 	public function isCollectingClientMetrics() | ||||
| 	{ | ||||
| 		return $this->getConfig('features.performance.client_metrics', true); | ||||
| 	} | ||||
|  | ||||
| 	// Check whether the toolbar is enabled | ||||
| 	public function isToolbarEnabled() | ||||
| 	{ | ||||
| 		return $this->getConfig('toolbar', false); | ||||
| 	} | ||||
|  | ||||
| 	// Check whether the web ui is enabled | ||||
| 	public function isWebEnabled() | ||||
| 	{ | ||||
| 		return $this->getConfig('web', true); | ||||
| 	} | ||||
|  | ||||
| 	// Check whether a command should not be collected | ||||
| 	protected function isCommandFiltered($command) | ||||
| 	{ | ||||
| 		$only = $this->getConfig('artisan.only', []); | ||||
|  | ||||
| 		if (count($only)) return ! in_array($command, $only); | ||||
|  | ||||
| 		$except = $this->getConfig('artisan.except', []); | ||||
|  | ||||
| 		if ($this->getConfig('artisan.except_laravel_commands', true)) { | ||||
| 			$except = array_merge($except, $this->builtinLaravelCommands()); | ||||
| 		} | ||||
|  | ||||
| 		$except = array_merge($except, $this->builtinClockworkCommands()); | ||||
|  | ||||
| 		return in_array($command, $except); | ||||
| 	} | ||||
|  | ||||
| 	// Check whether a queue job should not be collected | ||||
| 	protected function isQueueJobFiltered($queueJob) | ||||
| 	{ | ||||
| 		$only = $this->getConfig('queue.only', []); | ||||
|  | ||||
| 		if (count($only)) return ! in_array($queueJob, $only); | ||||
|  | ||||
| 		$except = $this->getConfig('queue.except', []); | ||||
|  | ||||
| 		return in_array($queueJob, $except); | ||||
| 	} | ||||
|  | ||||
| 	// Check whether a test should not be collected | ||||
| 	public function isTestFiltered($test) | ||||
| 	{ | ||||
| 		$except = $this->getConfig('tests.except', []); | ||||
|  | ||||
| 		return in_array($test, $except); | ||||
| 	} | ||||
|  | ||||
| 	// Append server timing headers from a Clockwork request to a http response | ||||
| 	protected function appendServerTimingHeader($response, $request) | ||||
| 	{ | ||||
| 		if (($eventsCount = $this->getConfig('server_timing', 10)) !== false) { | ||||
| 			$response->headers->set('Server-Timing', ServerTiming::fromRequest($request, $eventsCount)->value()); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Make an incoming request instance | ||||
| 	protected function incomingRequest() | ||||
| 	{ | ||||
| 		if ($this->incomingRequest) return $this->incomingRequest; | ||||
|  | ||||
| 		return $this->incomingRequest = new IncomingRequest([ | ||||
| 			'method'  => $this->app['request']->getMethod(), | ||||
| 			'uri'     => $this->app['request']->getRequestUri(), | ||||
| 			'input'   => $this->app['request']->input(), | ||||
| 			'cookies' => $this->app['request']->cookie() | ||||
| 		]); | ||||
| 	} | ||||
|  | ||||
| 	// Return an array of web ui paths | ||||
| 	public function webPaths() | ||||
| 	{ | ||||
| 		$path = $this->getConfig('web', true); | ||||
|  | ||||
| 		if (is_string($path)) return collect([ trim($path, '/') ]); | ||||
|  | ||||
| 		return collect([ 'clockwork', '__clockwork' ]); | ||||
| 	} | ||||
|  | ||||
| 	// Return an array of built-in Laravel commands | ||||
| 	protected function builtinLaravelCommands() | ||||
| 	{ | ||||
| 		return [ | ||||
| 			'clear-compiled', 'completion', 'db', 'down', 'dump-server', 'env', 'help', 'list', 'migrate', 'optimize', | ||||
| 			'preset', 'serve', 'test', 'tinker', 'up', | ||||
| 			'app:name', | ||||
| 			'auth:clear-resets', | ||||
| 			'cache:clear', 'cache:forget', 'cache:table', | ||||
| 			'config:cache', 'config:clear', | ||||
| 			'db:seed', 'db:wipe', | ||||
| 			'event:cache', 'event:clear', 'event:generate', 'event:list', | ||||
| 			'horizon', 'horizon:clear', 'horizon:continue', 'horizon:continue-supervisor', 'horizon:forget', | ||||
| 			'horizon:install', 'horizon:list', 'horizon:pause', 'horizon:pause-supervisor', 'horizon:publish', | ||||
| 			'horizon:purge', 'horizon:snapshot', 'horizon:status', 'horizon:supervisors', 'horizon:terminate', | ||||
| 			'horizon:work', | ||||
| 			'key:generate', | ||||
| 			'make:auth', 'make:cast', 'make:channel', 'make:command', 'make:component', 'make:controller', 'make:event', | ||||
| 			'make:exception', 'make:factory', 'make:job', 'make:listener', 'make:mail', 'make:middleware', | ||||
| 			'make:migration', 'make:model', 'make:notification', 'make:observer', 'make:policy', 'make:provider', | ||||
| 			'make:request', 'make:resource', 'make:rule', 'make:scope', 'make:seeder', 'make:test', | ||||
| 			'migrate:fresh', 'migrate:install', 'migrate:refresh', 'migrate:reset', 'migrate:rollback', | ||||
| 			'migrate:status', | ||||
| 			'model:prune', | ||||
| 			'notifications:table', | ||||
| 			'octane:install', 'octane:reload', 'octane:start', 'octane:status', 'octane:stop', | ||||
| 			'optimize:clear', | ||||
| 			'package:discover', | ||||
| 			'queue:batches-table', 'queue:clear', 'queue:failed', 'queue:failed-table', 'queue:flush', 'queue:forget', | ||||
| 			'queue:listen', 'queue:monitor', 'queue:prune-batches', 'queue:prune-failed', 'queue:restart', | ||||
| 			'queue:retry', 'queue:retry-batch', 'queue:table', 'queue:work', | ||||
| 			'route:cache', 'route:clear', 'route:list', | ||||
| 			'sail:install', 'sail:publish', | ||||
| 			'schedule:clear-cache', 'schedule:list', 'schedule:run', 'schedule:test', 'schedule:work', | ||||
| 			'schema:dump', | ||||
| 			'session:table', | ||||
| 			'storage:link', | ||||
| 			'stub:publish', | ||||
| 			'vendor:publish', | ||||
| 			'view:cache', 'view:clear' | ||||
| 		]; | ||||
| 	} | ||||
|  | ||||
| 	// Return an array of built-in Clockwork commands | ||||
| 	protected function builtinClockworkCommands() | ||||
| 	{ | ||||
| 		return [ | ||||
| 			'clockwork:clean' | ||||
| 		]; | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										70
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Laravel/Console/CapturingFormatter.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Laravel/Console/CapturingFormatter.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,70 @@ | ||||
| <?php namespace Clockwork\Support\Laravel\Console; | ||||
|  | ||||
| use Symfony\Component\Console\Formatter\OutputFormatterInterface; | ||||
| use Symfony\Component\Console\Formatter\OutputFormatterStyleInterface; | ||||
|  | ||||
| // Formatter wrapping around a "real" formatter, capturing the formatted output (Symfony 6.x and later) | ||||
| class CapturingFormatter implements OutputFormatterInterface | ||||
| { | ||||
| 	protected $formatter; | ||||
|  | ||||
| 	protected $capturedOutput; | ||||
|  | ||||
| 	public function __construct(OutputFormatterInterface $formatter) | ||||
| 	{ | ||||
| 		$this->formatter = $formatter; | ||||
| 	} | ||||
|  | ||||
| 	public function capturedOutput() | ||||
| 	{ | ||||
| 		$capturedOutput = $this->capturedOutput; | ||||
|  | ||||
| 		$this->capturedOutput = null; | ||||
|  | ||||
| 		return $capturedOutput; | ||||
| 	} | ||||
|  | ||||
| 	public function setDecorated(bool $decorated) | ||||
| 	{ | ||||
| 		return $this->formatter->setDecorated($decorated); | ||||
| 	} | ||||
|  | ||||
| 	public function isDecorated(): bool | ||||
| 	{ | ||||
| 		return $this->formatter->isDecorated(); | ||||
| 	} | ||||
|  | ||||
| 	public function setStyle(string $name, OutputFormatterStyleInterface $style) | ||||
| 	{ | ||||
| 		return $this->formatter->setStyle($name, $style); | ||||
| 	} | ||||
|  | ||||
| 	public function hasStyle(string $name): bool | ||||
| 	{ | ||||
| 		return $this->formatter->hasStyle($name); | ||||
| 	} | ||||
|  | ||||
| 	public function getStyle(string $name): OutputFormatterStyleInterface | ||||
| 	{ | ||||
| 		return $this->formatter->getStyle($name); | ||||
| 	} | ||||
|  | ||||
| 	public function format(?string $message): ?string | ||||
| 	{ | ||||
| 		$formatted = $this->formatter->format($message); | ||||
|  | ||||
| 		$this->capturedOutput .= $formatted; | ||||
|  | ||||
| 		return $formatted; | ||||
| 	} | ||||
|  | ||||
| 	public function __call($method, $args) | ||||
| 	{ | ||||
| 		return $this->formatter->$method(...$args); | ||||
| 	} | ||||
|  | ||||
| 	public function __clone() | ||||
| 	{ | ||||
| 		$this->formatter = clone $this->formatter; | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										70
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Laravel/Console/CapturingLegacyFormatter.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Laravel/Console/CapturingLegacyFormatter.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,70 @@ | ||||
| <?php namespace Clockwork\Support\Laravel\Console; | ||||
|  | ||||
| use Symfony\Component\Console\Formatter\OutputFormatterInterface; | ||||
| use Symfony\Component\Console\Formatter\OutputFormatterStyleInterface; | ||||
|  | ||||
| // Formatter wrapping around a "real" formatter, capturing the formatted output (Symfony 5.x and earlier) | ||||
| class CapturingLegacyFormatter implements OutputFormatterInterface | ||||
| { | ||||
| 	protected $formatter; | ||||
|  | ||||
| 	protected $capturedOutput; | ||||
|  | ||||
| 	public function __construct(OutputFormatterInterface $formatter) | ||||
| 	{ | ||||
| 		$this->formatter = $formatter; | ||||
| 	} | ||||
|  | ||||
| 	public function capturedOutput() | ||||
| 	{ | ||||
| 		$capturedOutput = $this->capturedOutput; | ||||
|  | ||||
| 		$this->capturedOutput = null; | ||||
|  | ||||
| 		return $capturedOutput; | ||||
| 	} | ||||
|  | ||||
| 	public function setDecorated($decorated) | ||||
| 	{ | ||||
| 		return $this->formatter->setDecorated($decorated); | ||||
| 	} | ||||
|  | ||||
| 	public function isDecorated() | ||||
| 	{ | ||||
| 		return $this->formatter->isDecorated(); | ||||
| 	} | ||||
|  | ||||
| 	public function setStyle($name, OutputFormatterStyleInterface $style) | ||||
| 	{ | ||||
| 		return $this->formatter->setStyle($name, $style); | ||||
| 	} | ||||
|  | ||||
| 	public function hasStyle($name) | ||||
| 	{ | ||||
| 		return $this->formatter->hasStyle($name); | ||||
| 	} | ||||
|  | ||||
| 	public function getStyle($name) | ||||
| 	{ | ||||
| 		return $this->formatter->getStyle($name); | ||||
| 	} | ||||
|  | ||||
| 	public function format($message) | ||||
| 	{ | ||||
| 		$formatted = $this->formatter->format($message); | ||||
|  | ||||
| 		$this->capturedOutput .= $formatted; | ||||
|  | ||||
| 		return $formatted; | ||||
| 	} | ||||
|  | ||||
| 	public function __call($method, $args) | ||||
| 	{ | ||||
| 		return $this->formatter->$method(...$args); | ||||
| 	} | ||||
|  | ||||
| 	public function __clone() | ||||
| 	{ | ||||
| 		$this->formatter = clone $this->formatter; | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										27
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Laravel/Eloquent/ResolveModelLegacyScope.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Laravel/Eloquent/ResolveModelLegacyScope.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| <?php namespace Clockwork\Support\Laravel\Eloquent; | ||||
|  | ||||
| use Clockwork\DataSource\EloquentDataSource; | ||||
|  | ||||
| use Illuminate\Database\Eloquent\Builder; | ||||
| use Illuminate\Database\Eloquent\Model; | ||||
| use Illuminate\Database\Eloquent\ScopeInterface; | ||||
|  | ||||
| class ResolveModelLegacyScope implements ScopeInterface | ||||
| { | ||||
| 	protected $dataSource; | ||||
|  | ||||
| 	public function __construct(EloquentDataSource $dataSource) | ||||
| 	{ | ||||
| 		$this->dataSource = $dataSource; | ||||
| 	} | ||||
|  | ||||
| 	public function apply(Builder $builder, Model $model) | ||||
| 	{ | ||||
| 		$this->dataSource->nextQueryModel = get_class($model); | ||||
| 	} | ||||
|  | ||||
| 	public function remove(Builder $builder, Model $model) | ||||
| 	{ | ||||
| 		// nothing to do here | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										22
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Laravel/Eloquent/ResolveModelScope.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Laravel/Eloquent/ResolveModelScope.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | ||||
| <?php namespace Clockwork\Support\Laravel\Eloquent; | ||||
|  | ||||
| use Clockwork\DataSource\EloquentDataSource; | ||||
|  | ||||
| use Illuminate\Database\Eloquent\Builder; | ||||
| use Illuminate\Database\Eloquent\Model; | ||||
| use Illuminate\Database\Eloquent\Scope; | ||||
|  | ||||
| class ResolveModelScope implements Scope | ||||
| { | ||||
| 	protected $dataSource; | ||||
|  | ||||
| 	public function __construct(EloquentDataSource $dataSource) | ||||
| 	{ | ||||
| 		$this->dataSource = $dataSource; | ||||
| 	} | ||||
|  | ||||
| 	public function apply(Builder $builder, Model $model) | ||||
| 	{ | ||||
| 		$this->dataSource->nextQueryModel = get_class($model); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										9
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Laravel/Facade.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Laravel/Facade.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | ||||
| <?php namespace Clockwork\Support\Laravel; | ||||
|  | ||||
| use Illuminate\Support\Facades\Facade as IlluminateFacade; | ||||
|  | ||||
| // Clockwork facade | ||||
| class Facade extends IlluminateFacade | ||||
| { | ||||
| 	protected static function getFacadeAccessor() { return 'clockwork'; } | ||||
| } | ||||
							
								
								
									
										87
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Laravel/Tests/UsesClockwork.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Laravel/Tests/UsesClockwork.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,87 @@ | ||||
| <?php namespace Clockwork\Support\Laravel\Tests; | ||||
|  | ||||
| use Clockwork\Helpers\Serializer; | ||||
| use Clockwork\Helpers\StackFilter; | ||||
| use Clockwork\Helpers\StackTrace; | ||||
|  | ||||
| use PHPUnit\Framework\Constraint\Constraint; | ||||
| use PHPUnit\Runner\BaseTestRunner; | ||||
|  | ||||
| // Trait to include in PHPUnit tests to collect executed tests | ||||
| trait UsesClockwork | ||||
| { | ||||
| 	// Clockwork phpunit metadata collected while executing tests | ||||
| 	protected static $clockwork = [ | ||||
| 		'asserts' => [] | ||||
| 	]; | ||||
|  | ||||
| 	// Set up Clockwork in this test case, should be called from the PHPUnit setUp method | ||||
| 	protected function setUpClockwork() | ||||
| 	{ | ||||
| 		if (! $this->app->make('clockwork.support')->isCollectingTests()) return; | ||||
|  | ||||
| 		$this->beforeApplicationDestroyed(function () { | ||||
| 			if ($this->app->make('clockwork.support')->isTestFiltered($this->toString())) return; | ||||
|  | ||||
| 			$this->app->make('clockwork') | ||||
| 				->resolveAsTest( | ||||
| 					$this->toString(), | ||||
| 					$this->resolveClockworkStatus(), | ||||
| 					$this->getStatusMessage(), | ||||
| 					$this->resolveClockworkAsserts() | ||||
| 				) | ||||
| 				->storeRequest(); | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	// Resolve Clockwork test status | ||||
| 	protected function resolveClockworkStatus() | ||||
| 	{ | ||||
| 		$status = $this->getStatus(); | ||||
|  | ||||
| 		$statuses = [ | ||||
| 			BaseTestRunner::STATUS_UNKNOWN    => 'unknown', | ||||
| 			BaseTestRunner::STATUS_PASSED     => 'passed', | ||||
| 			BaseTestRunner::STATUS_SKIPPED    => 'skipped', | ||||
| 			BaseTestRunner::STATUS_INCOMPLETE => 'incomplete', | ||||
| 			BaseTestRunner::STATUS_FAILURE    => 'failed', | ||||
| 			BaseTestRunner::STATUS_ERROR      => 'error', | ||||
| 			BaseTestRunner::STATUS_RISKY      => 'passed', | ||||
| 			BaseTestRunner::STATUS_WARNING    => 'warning' | ||||
| 		]; | ||||
|  | ||||
| 		return isset($statuses[$status]) ? $statuses[$status] : null; | ||||
| 	} | ||||
|  | ||||
| 	// Resolve executed asserts | ||||
| 	protected function resolveClockworkAsserts() | ||||
| 	{ | ||||
| 		$asserts = static::$clockwork['asserts']; | ||||
|  | ||||
| 		if ($this->getStatus() == BaseTestRunner::STATUS_FAILURE && count($asserts)) { | ||||
| 			$asserts[count($asserts) - 1]['passed'] = false; | ||||
| 		} | ||||
|  | ||||
| 		static::$clockwork['asserts'] = []; | ||||
|  | ||||
| 		return $asserts; | ||||
| 	} | ||||
|  | ||||
| 	// Overload the main PHPUnit assert method to collect executed asserts | ||||
| 	public static function assertThat($value, Constraint $constraint, string $message = ''): void | ||||
| 	{ | ||||
| 		$trace = StackTrace::get([ 'arguments' => true, 'limit' => 10 ]); | ||||
|  | ||||
| 		$assertFrame = $trace->filter(function ($frame) { return strpos($frame->function, 'assert') === 0; })->last(); | ||||
| 		$trace = $trace->skip(StackFilter::make()->isNotVendor([ 'itsgoingd', 'phpunit' ]))->limit(3); | ||||
|  | ||||
| 		static::$clockwork['asserts'][] = [ | ||||
| 			'name'      => $assertFrame->function, | ||||
| 			'arguments' => $assertFrame->args, | ||||
| 			'trace'     => (new Serializer)->trace($trace), | ||||
| 			'passed'    => true | ||||
| 		]; | ||||
|  | ||||
| 		parent::assertThat($value, $constraint, $message); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										416
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Laravel/config/clockwork.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										416
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Laravel/config/clockwork.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,416 @@ | ||||
| <?php | ||||
|  | ||||
| return [ | ||||
|  | ||||
| 	/* | ||||
| 	|------------------------------------------------------------------------------------------------------------------ | ||||
| 	| Enable Clockwork | ||||
| 	|------------------------------------------------------------------------------------------------------------------ | ||||
| 	| | ||||
| 	| Clockwork is enabled by default only when your application is in debug mode. Here you can explicitly enable or | ||||
| 	| disable Clockwork. When disabled, no data is collected and the api and web ui are inactive. | ||||
| 	| | ||||
| 	*/ | ||||
|  | ||||
| 	'enable' => env('CLOCKWORK_ENABLE', null), | ||||
|  | ||||
| 	/* | ||||
| 	|------------------------------------------------------------------------------------------------------------------ | ||||
| 	| Features | ||||
| 	|------------------------------------------------------------------------------------------------------------------ | ||||
| 	| | ||||
| 	| You can enable or disable various Clockwork features here. Some features have additional settings (eg. slow query | ||||
| 	| threshold for database queries). | ||||
| 	| | ||||
| 	*/ | ||||
|  | ||||
| 	'features' => [ | ||||
|  | ||||
| 		// Cache usage stats and cache queries including results | ||||
| 		'cache' => [ | ||||
| 			'enabled' => env('CLOCKWORK_CACHE_ENABLED', true), | ||||
|  | ||||
| 			// Collect cache queries | ||||
| 			'collect_queries' => env('CLOCKWORK_CACHE_QUERIES', true), | ||||
|  | ||||
| 			// Collect values from cache queries (high performance impact with a very high number of queries) | ||||
| 			'collect_values' => env('CLOCKWORK_CACHE_COLLECT_VALUES', false) | ||||
| 		], | ||||
|  | ||||
| 		// Database usage stats and queries | ||||
| 		'database' => [ | ||||
| 			'enabled' => env('CLOCKWORK_DATABASE_ENABLED', true), | ||||
|  | ||||
| 			// Collect database queries (high performance impact with a very high number of queries) | ||||
| 			'collect_queries' => env('CLOCKWORK_DATABASE_COLLECT_QUERIES', true), | ||||
|  | ||||
| 			// Collect details of models updates (high performance impact with a lot of model updates) | ||||
| 			'collect_models_actions' => env('CLOCKWORK_DATABASE_COLLECT_MODELS_ACTIONS', true), | ||||
|  | ||||
| 			// Collect details of retrieved models (very high performance impact with a lot of models retrieved) | ||||
| 			'collect_models_retrieved' => env('CLOCKWORK_DATABASE_COLLECT_MODELS_RETRIEVED', false), | ||||
|  | ||||
| 			// Query execution time threshold in milliseconds after which the query will be marked as slow | ||||
| 			'slow_threshold' => env('CLOCKWORK_DATABASE_SLOW_THRESHOLD'), | ||||
|  | ||||
| 			// Collect only slow database queries | ||||
| 			'slow_only' => env('CLOCKWORK_DATABASE_SLOW_ONLY', false), | ||||
|  | ||||
| 			// Detect and report duplicate queries | ||||
| 			'detect_duplicate_queries' => env('CLOCKWORK_DATABASE_DETECT_DUPLICATE_QUERIES', false) | ||||
| 		], | ||||
|  | ||||
| 		// Dispatched events | ||||
| 		'events' => [ | ||||
| 			'enabled' => env('CLOCKWORK_EVENTS_ENABLED', true), | ||||
|  | ||||
| 			// Ignored events (framework events are ignored by default) | ||||
| 			'ignored_events' => [ | ||||
| 				// App\Events\UserRegistered::class, | ||||
| 				// 'user.registered' | ||||
| 			], | ||||
| 		], | ||||
|  | ||||
| 		// Laravel log (you can still log directly to Clockwork with laravel log disabled) | ||||
| 		'log' => [ | ||||
| 			'enabled' => env('CLOCKWORK_LOG_ENABLED', true) | ||||
| 		], | ||||
|  | ||||
| 		// Sent notifications | ||||
| 		'notifications' => [ | ||||
| 			'enabled' => env('CLOCKWORK_NOTIFICATIONS_ENABLED', true), | ||||
| 		], | ||||
|  | ||||
| 		// Performance metrics | ||||
| 		'performance' => [ | ||||
| 			// Allow collecting of client metrics. Requires separate clockwork-browser npm package. | ||||
| 			'client_metrics' => env('CLOCKWORK_PERFORMANCE_CLIENT_METRICS', true) | ||||
| 		], | ||||
|  | ||||
| 		// Dispatched queue jobs | ||||
| 		'queue' => [ | ||||
| 			'enabled' => env('CLOCKWORK_QUEUE_ENABLED', true) | ||||
| 		], | ||||
|  | ||||
| 		// Redis commands | ||||
| 		'redis' => [ | ||||
| 			'enabled' => env('CLOCKWORK_REDIS_ENABLED', true) | ||||
| 		], | ||||
|  | ||||
| 		// Routes list | ||||
| 		'routes' => [ | ||||
| 			'enabled' => env('CLOCKWORK_ROUTES_ENABLED', false), | ||||
|  | ||||
| 			// Collect only routes from particular namespaces (only application routes by default) | ||||
| 			'only_namespaces' => [ 'App' ] | ||||
| 		], | ||||
|  | ||||
| 		// Rendered views | ||||
| 		'views' => [ | ||||
| 			'enabled' => env('CLOCKWORK_VIEWS_ENABLED', true), | ||||
|  | ||||
| 			// Collect views including view data (high performance impact with a high number of views) | ||||
| 			'collect_data' => env('CLOCKWORK_VIEWS_COLLECT_DATA', false), | ||||
|  | ||||
| 			// Use Twig profiler instead of Laravel events for apps using laravel-twigbridge (more precise, but does | ||||
| 			// not support collecting view data) | ||||
| 			'use_twig_profiler' => env('CLOCKWORK_VIEWS_USE_TWIG_PROFILER', false) | ||||
| 		] | ||||
|  | ||||
| 	], | ||||
|  | ||||
| 	/* | ||||
| 	|------------------------------------------------------------------------------------------------------------------ | ||||
| 	| Enable web UI | ||||
| 	|------------------------------------------------------------------------------------------------------------------ | ||||
| 	| | ||||
| 	| Clockwork comes with a web UI accessible via http://your.app/clockwork. Here you can enable or disable this | ||||
| 	| feature. You can also set a custom path for the web UI. | ||||
| 	| | ||||
| 	*/ | ||||
|  | ||||
| 	'web' => env('CLOCKWORK_WEB', true), | ||||
|  | ||||
| 	/* | ||||
| 	|------------------------------------------------------------------------------------------------------------------ | ||||
| 	| Enable toolbar | ||||
| 	|------------------------------------------------------------------------------------------------------------------ | ||||
| 	| | ||||
| 	| Clockwork can show a toolbar with basic metrics on all responses. Here you can enable or disable this feature. | ||||
| 	| Requires a separate clockwork-browser npm library. | ||||
| 	| For installation instructions see https://underground.works/clockwork/#docs-viewing-data | ||||
| 	| | ||||
| 	*/ | ||||
|  | ||||
| 	'toolbar' => env('CLOCKWORK_TOOLBAR', true), | ||||
|  | ||||
| 	/* | ||||
| 	|------------------------------------------------------------------------------------------------------------------ | ||||
| 	| HTTP requests collection | ||||
| 	|------------------------------------------------------------------------------------------------------------------ | ||||
| 	| | ||||
| 	| Clockwork collects data about HTTP requests to your app. Here you can choose which requests should be collected. | ||||
| 	| | ||||
| 	*/ | ||||
|  | ||||
| 	'requests' => [ | ||||
| 		// With on-demand mode enabled, Clockwork will only profile requests when the browser extension is open or you | ||||
| 		// manually pass a "clockwork-profile" cookie or get/post data key. | ||||
| 		// Optionally you can specify a "secret" that has to be passed as the value to enable profiling. | ||||
| 		'on_demand' => env('CLOCKWORK_REQUESTS_ON_DEMAND', false), | ||||
|  | ||||
| 		// Collect only errors (requests with HTTP 4xx and 5xx responses) | ||||
| 		'errors_only' => env('CLOCKWORK_REQUESTS_ERRORS_ONLY', false), | ||||
|  | ||||
| 		// Response time threshold in milliseconds after which the request will be marked as slow | ||||
| 		'slow_threshold' => env('CLOCKWORK_REQUESTS_SLOW_THRESHOLD'), | ||||
|  | ||||
| 		// Collect only slow requests | ||||
| 		'slow_only' => env('CLOCKWORK_REQUESTS_SLOW_ONLY', false), | ||||
|  | ||||
| 		// Sample the collected requests (e.g. set to 100 to collect only 1 in 100 requests) | ||||
| 		'sample' => env('CLOCKWORK_REQUESTS_SAMPLE', false), | ||||
|  | ||||
| 		// List of URIs that should not be collected | ||||
| 		'except' => [ | ||||
| 			'/horizon/.*', // Laravel Horizon requests | ||||
| 			'/telescope/.*', // Laravel Telescope requests | ||||
| 			'/_debugbar/.*', // Laravel DebugBar requests | ||||
| 		], | ||||
|  | ||||
| 		// List of URIs that should be collected, any other URI will not be collected if not empty | ||||
| 		'only' => [ | ||||
| 			// '/api/.*' | ||||
| 		], | ||||
|  | ||||
| 		// Don't collect OPTIONS requests, mostly used in the CSRF pre-flight requests and are rarely of interest | ||||
| 		'except_preflight' => env('CLOCKWORK_REQUESTS_EXCEPT_PREFLIGHT', true) | ||||
| 	], | ||||
|  | ||||
| 	/* | ||||
| 	|------------------------------------------------------------------------------------------------------------------ | ||||
| 	| Artisan commands collection | ||||
| 	|------------------------------------------------------------------------------------------------------------------ | ||||
| 	| | ||||
| 	| Clockwork can collect data about executed artisan commands. Here you can enable and configure which commands | ||||
| 	| should be collected. | ||||
| 	| | ||||
| 	*/ | ||||
|  | ||||
| 	'artisan' => [ | ||||
| 		// Enable or disable collection of executed Artisan commands | ||||
| 		'collect' => env('CLOCKWORK_ARTISAN_COLLECT', false), | ||||
|  | ||||
| 		// List of commands that should not be collected (built-in commands are not collected by default) | ||||
| 		'except' => [ | ||||
| 			// 'inspire' | ||||
| 		], | ||||
|  | ||||
| 		// List of commands that should be collected, any other command will not be collected if not empty | ||||
| 		'only' => [ | ||||
| 			// 'inspire' | ||||
| 		], | ||||
|  | ||||
| 		// Enable or disable collection of command output | ||||
| 		'collect_output' => env('CLOCKWORK_ARTISAN_COLLECT_OUTPUT', false), | ||||
|  | ||||
| 		// Enable or disable collection of built-in Laravel commands | ||||
| 		'except_laravel_commands' => env('CLOCKWORK_ARTISAN_EXCEPT_LARAVEL_COMMANDS', true) | ||||
| 	], | ||||
|  | ||||
| 	/* | ||||
| 	|------------------------------------------------------------------------------------------------------------------ | ||||
| 	| Queue jobs collection | ||||
| 	|------------------------------------------------------------------------------------------------------------------ | ||||
| 	| | ||||
| 	| Clockwork can collect data about executed queue jobs. Here you can enable and configure which queue jobs should | ||||
| 	| be collected. | ||||
| 	| | ||||
| 	*/ | ||||
|  | ||||
| 	'queue' => [ | ||||
| 		// Enable or disable collection of executed queue jobs | ||||
| 		'collect' => env('CLOCKWORK_QUEUE_COLLECT', false), | ||||
|  | ||||
| 		// List of queue jobs that should not be collected | ||||
| 		'except' => [ | ||||
| 			// App\Jobs\ExpensiveJob::class | ||||
| 		], | ||||
|  | ||||
| 		// List of queue jobs that should be collected, any other queue job will not be collected if not empty | ||||
| 		'only' => [ | ||||
| 			// App\Jobs\BuggyJob::class | ||||
| 		] | ||||
| 	], | ||||
|  | ||||
| 	/* | ||||
| 	|------------------------------------------------------------------------------------------------------------------ | ||||
| 	| Tests collection | ||||
| 	|------------------------------------------------------------------------------------------------------------------ | ||||
| 	| | ||||
| 	| Clockwork can collect data about executed tests. Here you can enable and configure which tests should be | ||||
| 	| collected. | ||||
| 	| | ||||
| 	*/ | ||||
|  | ||||
| 	'tests' => [ | ||||
| 		// Enable or disable collection of ran tests | ||||
| 		'collect' => env('CLOCKWORK_TESTS_COLLECT', false), | ||||
|  | ||||
| 		// List of tests that should not be collected | ||||
| 		'except' => [ | ||||
| 			// Tests\Unit\ExampleTest::class | ||||
| 		] | ||||
| 	], | ||||
|  | ||||
| 	/* | ||||
| 	|------------------------------------------------------------------------------------------------------------------ | ||||
| 	| Enable data collection when Clockwork is disabled | ||||
| 	|------------------------------------------------------------------------------------------------------------------ | ||||
| 	| | ||||
| 	| You can enable this setting to collect data even when Clockwork is disabled, e.g. for future analysis. | ||||
| 	| | ||||
| 	*/ | ||||
|  | ||||
| 	'collect_data_always' => env('CLOCKWORK_COLLECT_DATA_ALWAYS', false), | ||||
|  | ||||
| 	/* | ||||
| 	|------------------------------------------------------------------------------------------------------------------ | ||||
| 	| Metadata storage | ||||
| 	|------------------------------------------------------------------------------------------------------------------ | ||||
| 	| | ||||
| 	| Configure how is the metadata collected by Clockwork stored. Two options are available: | ||||
| 	|   - files - A simple fast storage implementation storing data in one-per-request files. | ||||
| 	|   - sql - Stores requests in a sql database. Supports MySQL, PostgreSQL and SQLite. Requires PDO. | ||||
| 	| | ||||
| 	*/ | ||||
|  | ||||
| 	'storage' => env('CLOCKWORK_STORAGE', 'files'), | ||||
|  | ||||
| 	// Path where the Clockwork metadata is stored | ||||
| 	'storage_files_path' => env('CLOCKWORK_STORAGE_FILES_PATH', storage_path('clockwork')), | ||||
|  | ||||
| 	// Compress the metadata files using gzip, trading a little bit of performance for lower disk usage | ||||
| 	'storage_files_compress' => env('CLOCKWORK_STORAGE_FILES_COMPRESS', false), | ||||
|  | ||||
| 	// SQL database to use, can be a name of database configured in database.php or a path to a SQLite file | ||||
| 	'storage_sql_database' => env('CLOCKWORK_STORAGE_SQL_DATABASE', storage_path('clockwork.sqlite')), | ||||
|  | ||||
| 	// SQL table name to use, the table is automatically created and updated when needed | ||||
| 	'storage_sql_table' => env('CLOCKWORK_STORAGE_SQL_TABLE', 'clockwork'), | ||||
|  | ||||
| 	// Maximum lifetime of collected metadata in minutes, older requests will automatically be deleted, false to disable | ||||
| 	'storage_expiration' => env('CLOCKWORK_STORAGE_EXPIRATION', 60 * 24 * 7), | ||||
|  | ||||
| 	/* | ||||
| 	|------------------------------------------------------------------------------------------------------------------ | ||||
| 	| Authentication | ||||
| 	|------------------------------------------------------------------------------------------------------------------ | ||||
| 	| | ||||
| 	| Clockwork can be configured to require authentication before allowing access to the collected data. This might be | ||||
| 	| useful when the application is publicly accessible. Setting to true will enable a simple authentication with a | ||||
| 	| pre-configured password. You can also pass a class name of a custom implementation. | ||||
| 	| | ||||
| 	*/ | ||||
|  | ||||
| 	'authentication' => env('CLOCKWORK_AUTHENTICATION', false), | ||||
|  | ||||
| 	// Password for the simple authentication | ||||
| 	'authentication_password' => env('CLOCKWORK_AUTHENTICATION_PASSWORD', 'VerySecretPassword'), | ||||
|  | ||||
| 	/* | ||||
| 	|------------------------------------------------------------------------------------------------------------------ | ||||
| 	| Stack traces collection | ||||
| 	|------------------------------------------------------------------------------------------------------------------ | ||||
| 	| | ||||
| 	| Clockwork can collect stack traces for log messages and certain data like database queries. Here you can set | ||||
| 	| whether to collect stack traces, limit the number of collected frames and set further configuration. Collecting | ||||
| 	| long stack traces considerably increases metadata size. | ||||
| 	| | ||||
| 	*/ | ||||
|  | ||||
| 	'stack_traces' => [ | ||||
| 		// Enable or disable collecting of stack traces | ||||
| 		'enabled' => env('CLOCKWORK_STACK_TRACES_ENABLED', true), | ||||
|  | ||||
| 		// Limit the number of frames to be collected | ||||
| 		'limit' => env('CLOCKWORK_STACK_TRACES_LIMIT', 10), | ||||
|  | ||||
| 		// List of vendor names to skip when determining caller, common vendors are automatically added | ||||
| 		'skip_vendors' => [ | ||||
| 			// 'phpunit' | ||||
| 		], | ||||
|  | ||||
| 		// List of namespaces to skip when determining caller | ||||
| 		'skip_namespaces' => [ | ||||
| 			// 'Laravel' | ||||
| 		], | ||||
|  | ||||
| 		// List of class names to skip when determining caller | ||||
| 		'skip_classes' => [ | ||||
| 			// App\CustomLog::class | ||||
| 		] | ||||
|  | ||||
| 	], | ||||
|  | ||||
| 	/* | ||||
| 	|------------------------------------------------------------------------------------------------------------------ | ||||
| 	| Serialization | ||||
| 	|------------------------------------------------------------------------------------------------------------------ | ||||
| 	| | ||||
| 	| Clockwork serializes the collected data to json for storage and transfer. Here you can configure certain aspects | ||||
| 	| of serialization. Serialization has a large effect on the cpu time and memory usage. | ||||
| 	| | ||||
| 	*/ | ||||
|  | ||||
| 	// Maximum depth of serialized multi-level arrays and objects | ||||
| 	'serialization_depth' => env('CLOCKWORK_SERIALIZATION_DEPTH', 10), | ||||
|  | ||||
| 	// A list of classes that will never be serialized (e.g. a common service container class) | ||||
| 	'serialization_blackbox' => [ | ||||
| 		\Illuminate\Container\Container::class, | ||||
| 		\Illuminate\Foundation\Application::class, | ||||
| 		\Laravel\Lumen\Application::class | ||||
| 	], | ||||
|  | ||||
| 	/* | ||||
| 	|------------------------------------------------------------------------------------------------------------------ | ||||
| 	| Register helpers | ||||
| 	|------------------------------------------------------------------------------------------------------------------ | ||||
| 	| | ||||
| 	| Clockwork comes with a "clock" global helper function. You can use this helper to quickly log something and to | ||||
| 	| access the Clockwork instance. | ||||
| 	| | ||||
| 	*/ | ||||
|  | ||||
| 	'register_helpers' => env('CLOCKWORK_REGISTER_HELPERS', true), | ||||
|  | ||||
| 	/* | ||||
| 	|------------------------------------------------------------------------------------------------------------------ | ||||
| 	| Send headers for AJAX request | ||||
| 	|------------------------------------------------------------------------------------------------------------------ | ||||
| 	| | ||||
| 	| When trying to collect data, the AJAX method can sometimes fail if it is missing required headers. For example, an | ||||
| 	| API might require a version number using Accept headers to route the HTTP request to the correct codebase. | ||||
| 	| | ||||
| 	*/ | ||||
|  | ||||
| 	'headers' => [ | ||||
| 		// 'Accept' => 'application/vnd.com.whatever.v1+json', | ||||
| 	], | ||||
|  | ||||
| 	/* | ||||
| 	|------------------------------------------------------------------------------------------------------------------ | ||||
| 	| Server timing | ||||
| 	|------------------------------------------------------------------------------------------------------------------ | ||||
| 	| | ||||
| 	| Clockwork supports the W3C Server Timing specification, which allows for collecting a simple performance metrics | ||||
| 	| in a cross-browser way. E.g. in Chrome, your app, database and timeline event timings will be shown in the Dev | ||||
| 	| Tools network tab. This setting specifies the max number of timeline events that will be sent. Setting to false | ||||
| 	| will disable the feature. | ||||
| 	| | ||||
| 	*/ | ||||
|  | ||||
| 	'server_timing' => env('CLOCKWORK_SERVER_TIMING', 10) | ||||
|  | ||||
| ]; | ||||
							
								
								
									
										17
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Laravel/helpers.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Laravel/helpers.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | ||||
| <?php | ||||
|  | ||||
| if (! function_exists('clock')) { | ||||
| 	// Log a message to Clockwork, returns Clockwork instance when called with no arguments, first argument otherwise | ||||
| 	function clock(...$arguments) | ||||
| 	{ | ||||
| 		if (empty($arguments)) { | ||||
| 			return app('clockwork'); | ||||
| 		} | ||||
|  | ||||
| 		foreach ($arguments as $argument) { | ||||
| 			app('clockwork')->debug($argument); | ||||
| 		} | ||||
|  | ||||
| 		return reset($arguments); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										38
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Lumen/ClockworkMiddleware.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Lumen/ClockworkMiddleware.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | ||||
| <?php namespace Clockwork\Support\Lumen; | ||||
|  | ||||
| use Illuminate\Contracts\Debug\ExceptionHandler; | ||||
| use Laravel\Lumen\Application; | ||||
|  | ||||
| // Clockwork Lumen middleware | ||||
| class ClockworkMiddleware | ||||
| { | ||||
| 	// Lumen application instance | ||||
| 	protected $app; | ||||
|  | ||||
| 	// Create a new middleware instance | ||||
| 	public function __construct(Application $app) | ||||
| 	{ | ||||
| 		$this->app = $app; | ||||
| 	} | ||||
|  | ||||
| 	// Handle an incoming request | ||||
| 	public function handle($request, \Closure $next) | ||||
| 	{ | ||||
| 		$this->app['clockwork']->event('Controller')->begin(); | ||||
|  | ||||
| 		try { | ||||
| 			$response = $next($request); | ||||
| 		} catch (\Exception $e) { | ||||
| 			$this->app[ExceptionHandler::class]->report($e); | ||||
| 			$response = $this->app[ExceptionHandler::class]->render($request, $e); | ||||
| 		} | ||||
|  | ||||
| 		return $this->app['clockwork.support']->processRequest($request, $response); | ||||
| 	} | ||||
|  | ||||
| 	// Record the current request after a response is sent | ||||
| 	public function terminate() | ||||
| 	{ | ||||
| 		$this->app['clockwork.support']->recordRequest(); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										95
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Lumen/ClockworkServiceProvider.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Lumen/ClockworkServiceProvider.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,95 @@ | ||||
| <?php namespace Clockwork\Support\Lumen; | ||||
|  | ||||
| use Clockwork\DataSource\LumenDataSource; | ||||
| use Clockwork\Support\Laravel\ClockworkServiceProvider as LaravelServiceProvider; | ||||
|  | ||||
| use Illuminate\Support\Facades\Facade; | ||||
|  | ||||
| // Clockwork Lumen service provider | ||||
| class ClockworkServiceProvider extends LaravelServiceProvider | ||||
| { | ||||
| 	// Register Clockwork configuration | ||||
| 	protected function registerConfiguration() | ||||
| 	{ | ||||
| 		$this->app->configure('clockwork'); | ||||
| 		$this->mergeConfigFrom(__DIR__ . '/../Laravel/config/clockwork.php', 'clockwork'); | ||||
| 	} | ||||
|  | ||||
| 	// Register Clockwork components | ||||
| 	protected function registerClockwork() | ||||
| 	{ | ||||
| 		parent::registerClockwork(); | ||||
|  | ||||
| 		$this->app->singleton('clockwork.support', function ($app) { | ||||
| 			return new ClockworkSupport($app); | ||||
| 		}); | ||||
|  | ||||
| 		if ($this->isRunningWithFacades() && ! class_exists('Clockwork')) { | ||||
| 			class_alias(\Clockwork\Support\Laravel\Facade::class, 'Clockwork'); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Register Clockwork data sources | ||||
| 	protected function registerDataSources() | ||||
| 	{ | ||||
| 		parent::registerDataSources(); | ||||
|  | ||||
| 		$this->app->singleton('clockwork.lumen', function ($app) { | ||||
| 			return (new LumenDataSource( | ||||
| 				$app, | ||||
| 				$app['clockwork.support']->isFeatureEnabled('log'), | ||||
| 				$app['clockwork.support']->isFeatureEnabled('views'), | ||||
| 				$app['clockwork.support']->isFeatureEnabled('routes') | ||||
| 			)); | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	// Register Clockwork components aliases for type hinting | ||||
| 	protected function registerAliases() | ||||
| 	{ | ||||
| 		parent::registerAliases(); | ||||
|  | ||||
| 		$this->app->alias('clockwork.lumen', LumenDataSource::class); | ||||
| 	} | ||||
|  | ||||
| 	// Register event listeners | ||||
| 	protected function registerEventListeners() | ||||
| 	{ | ||||
| 		$this->app['clockwork.support']->addDataSources()->listenToEvents(); | ||||
| 	} | ||||
|  | ||||
| 	// Register Clockwork middleware | ||||
| 	public function registerMiddleware() | ||||
| 	{ | ||||
| 		$this->app->middleware([ ClockworkMiddleware::class ]); | ||||
| 	} | ||||
|  | ||||
| 	// Register Clockwork REST api routes | ||||
| 	public function registerRoutes() | ||||
| 	{ | ||||
| 		$router = isset($this->app->router) ? $this->app->router : $this->app; | ||||
|  | ||||
| 		$router->get('/__clockwork/{id:(?:[0-9-]+|latest)}/extended', 'Clockwork\Support\Lumen\Controller@getExtendedData'); | ||||
| 		$router->get('/__clockwork/{id:(?:[0-9-]+|latest)}[/{direction:(?:next|previous)}[/{count:\d+}]]', 'Clockwork\Support\Lumen\Controller@getData'); | ||||
| 		$router->put('/__clockwork/{id}', 'Clockwork\Support\Lumen\Controller@updateData'); | ||||
| 		$router->post('/__clockwork/auth', 'Clockwork\Support\Lumen\Controller@authenticate'); | ||||
| 	} | ||||
|  | ||||
| 	// Register Clockwork app routes | ||||
| 	public function registerWebRoutes() | ||||
| 	{ | ||||
| 		$router = isset($this->app->router) ? $this->app->router : $this->app; | ||||
|  | ||||
| 		$this->app['clockwork.support']->webPaths()->each(function ($path) use ($router) { | ||||
| 			$router->get("{$path}", 'Clockwork\Support\Lumen\Controller@webRedirect'); | ||||
| 			$router->get("{$path}/app", 'Clockwork\Support\Lumen\Controller@webIndex'); | ||||
| 			$router->get("{$path}/{path:.+}", 'Clockwork\Support\Lumen\Controller@webAsset'); | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	// Check whether we are running with facades enabled | ||||
| 	protected function isRunningWithFacades() | ||||
| 	{ | ||||
| 		return Facade::getFacadeApplication() !== null; | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										65
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Lumen/ClockworkSupport.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Lumen/ClockworkSupport.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,65 @@ | ||||
| <?php namespace Clockwork\Support\Lumen; | ||||
|  | ||||
| use Clockwork\Support\Laravel\ClockworkSupport as LaravelSupport; | ||||
|  | ||||
| use Laravel\Lumen\Application; | ||||
| use Symfony\Component\HttpFoundation\Response; | ||||
|  | ||||
| // Support class for the Lumen integration | ||||
| class ClockworkSupport extends LaravelSupport | ||||
| { | ||||
| 	// Lumen application instance | ||||
| 	protected $app; | ||||
|  | ||||
| 	public function __construct(Application $app) | ||||
| 	{ | ||||
| 		$this->app = $app; | ||||
| 	} | ||||
|  | ||||
| 	// Resolves the framework data source from the container | ||||
| 	protected function frameworkDataSource() | ||||
| 	{ | ||||
| 		return $this->app['clockwork.lumen']; | ||||
| 	} | ||||
|  | ||||
| 	// Process an http request and response, resolves the request, sets Clockwork headers and cookies | ||||
| 	public function process($request, $response) | ||||
| 	{ | ||||
| 		if (! $response instanceof Response) { | ||||
| 			$response = new Response((string) $response); | ||||
| 		} | ||||
|  | ||||
| 		return parent::process($request, $response); | ||||
| 	} | ||||
|  | ||||
| 	// Set response on the framework data source | ||||
| 	protected function setResponse($response) | ||||
| 	{ | ||||
| 		$this->app['clockwork.lumen']->setResponse($response); | ||||
| 	} | ||||
|  | ||||
| 	// Check whether Clockwork is enabled | ||||
| 	public function isEnabled() | ||||
| 	{ | ||||
| 		return $this->getConfig('enable') | ||||
| 			|| $this->getConfig('enable') === null && env('APP_DEBUG', false); | ||||
| 	} | ||||
|  | ||||
| 	// Check whether a particular feature is available | ||||
| 	public function isFeatureAvailable($feature) | ||||
| 	{ | ||||
| 		if ($feature == 'database') { | ||||
| 			return $this->app->bound('db') && $this->app['config']->get('database.default'); | ||||
| 		} elseif ($feature == 'emails') { | ||||
| 			return $this->app->bound('mailer'); | ||||
| 		} elseif ($feature == 'redis') { | ||||
| 			return $this->app->bound('redis') && method_exists(\Illuminate\Redis\RedisManager::class, 'enableEvents'); | ||||
| 		} elseif ($feature == 'queue') { | ||||
| 			return $this->app->bound('queue') && method_exists(\Illuminate\Queue\Queue::class, 'createPayloadUsing'); | ||||
| 		} elseif ($feature == 'xdebug') { | ||||
| 			return in_array('xdebug', get_loaded_extensions()); | ||||
| 		} | ||||
|  | ||||
| 		return true; | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										96
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Lumen/Controller.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Lumen/Controller.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,96 @@ | ||||
| <?php namespace Clockwork\Support\Lumen; | ||||
|  | ||||
| use Clockwork\Clockwork; | ||||
| use Clockwork\Support\Lumen\ClockworkSupport; | ||||
|  | ||||
| use Illuminate\Http\JsonResponse; | ||||
| use Illuminate\Http\RedirectResponse; | ||||
| use Illuminate\Http\Request; | ||||
| use Laravel\Lumen\Routing\Controller as LumenController; | ||||
| use Laravel\Telescope\Telescope; | ||||
|  | ||||
| // Clockwork api and app controller | ||||
| class Controller extends LumenController | ||||
| { | ||||
| 	// Clockwork and support instances | ||||
| 	public $clockwork; | ||||
| 	public $clockworkSupport; | ||||
|  | ||||
| 	public function __construct(Clockwork $clockwork, ClockworkSupport $clockworkSupport) | ||||
| 	{ | ||||
| 		$this->clockwork = $clockwork; | ||||
| 		$this->clockworkSupport = $clockworkSupport; | ||||
| 	} | ||||
|  | ||||
| 	// Authantication endpoint | ||||
| 	public function authenticate(Request $request) | ||||
| 	{ | ||||
| 		$this->ensureClockworkIsEnabled(); | ||||
|  | ||||
| 		$token = $this->clockwork->authenticator()->attempt( | ||||
| 			$request->only([ 'username', 'password' ]) | ||||
| 		); | ||||
|  | ||||
| 		return new JsonResponse([ 'token' => $token ], $token ? 200 : 403); | ||||
| 	} | ||||
|  | ||||
| 	// Metadata retrieving endpoint | ||||
| 	public function getData(Request $request, $id = null, $direction = null, $count = null) | ||||
| 	{ | ||||
| 		$this->ensureClockworkIsEnabled(); | ||||
|  | ||||
| 		return $this->clockworkSupport->getData( | ||||
| 			$id, $direction, $count, $request->only([ 'only', 'except' ]) | ||||
| 		); | ||||
| 	} | ||||
|  | ||||
| 	// Extended metadata retrieving endpoint | ||||
| 	public function getExtendedData(Request $request, $id = null) | ||||
| 	{ | ||||
| 		$this->ensureClockworkIsEnabled(); | ||||
|  | ||||
| 		return $this->clockworkSupport->getExtendedData( | ||||
| 			$id, $request->only([ 'only', 'except' ]) | ||||
| 		); | ||||
| 	} | ||||
|  | ||||
| 	// Metadata updating endpoint | ||||
| 	public function updateData(Request $request, $id = null) | ||||
| 	{ | ||||
| 		$this->ensureClockworkIsEnabled(); | ||||
|  | ||||
| 		return $this->clockworkSupport->updateData($id, $request->json()->all()); | ||||
| 	} | ||||
|  | ||||
| 	// App index | ||||
| 	public function webIndex(Request $request) | ||||
| 	{ | ||||
| 		$this->ensureClockworkIsEnabled(); | ||||
|  | ||||
| 		return $this->clockworkSupport->getWebAsset('index.html'); | ||||
| 	} | ||||
|  | ||||
| 	// App assets serving | ||||
| 	public function webAsset($path) | ||||
| 	{ | ||||
| 		$this->ensureClockworkIsEnabled(); | ||||
|  | ||||
| 		return $this->clockworkSupport->getWebAsset($path); | ||||
| 	} | ||||
|  | ||||
| 	// App redirect (/clockwork -> /clockwork/app) | ||||
| 	public function webRedirect(Request $request) | ||||
| 	{ | ||||
| 		$this->ensureClockworkIsEnabled(); | ||||
|  | ||||
| 		return new RedirectResponse('/' . $request->path() . '/app'); | ||||
| 	} | ||||
|  | ||||
| 	// Ensure Clockwork is still enabled at this point and stop Telescope recording if present | ||||
| 	protected function ensureClockworkIsEnabled() | ||||
| 	{ | ||||
| 		if (class_exists(Telescope::class)) Telescope::stopRecording(); | ||||
|  | ||||
| 		if (! $this->clockworkSupport->isEnabled()) abort(404); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										25
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Monolog/Handler/ClockworkHandler.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Monolog/Handler/ClockworkHandler.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| <?php namespace Clockwork\Support\Monolog\Handler; | ||||
|  | ||||
| use Clockwork\Request\Log as ClockworkLog; | ||||
|  | ||||
| use Monolog\Logger; | ||||
| use Monolog\Handler\AbstractProcessingHandler; | ||||
|  | ||||
| // Stores messages in a Clockwork log instance | ||||
| // DEPRECATED Moved to Clockwork\Support\Monolog\Monolog\ClockworkHandler, will be removed in Clockwork 6 | ||||
| class ClockworkHandler extends AbstractProcessingHandler | ||||
| { | ||||
| 	protected $clockworkLog; | ||||
|  | ||||
| 	public function __construct(ClockworkLog $clockworkLog) | ||||
| 	{ | ||||
| 		parent::__construct(); | ||||
|  | ||||
| 		$this->clockworkLog = $clockworkLog; | ||||
| 	} | ||||
|  | ||||
| 	protected function write(array $record) | ||||
| 	{ | ||||
| 		$this->clockworkLog->log($record['level'], $record['message']); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										24
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Monolog/Monolog/ClockworkHandler.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Monolog/Monolog/ClockworkHandler.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | ||||
| <?php namespace Clockwork\Support\Monolog\Monolog; | ||||
|  | ||||
| use Clockwork\Request\Log as ClockworkLog; | ||||
|  | ||||
| use Monolog\Logger; | ||||
| use Monolog\Handler\AbstractProcessingHandler; | ||||
|  | ||||
| // Stores messages in a Clockwork log instance (compatible with Monolog 1.x) | ||||
| class ClockworkHandler extends AbstractProcessingHandler | ||||
| { | ||||
| 	protected $clockworkLog; | ||||
|  | ||||
| 	public function __construct(ClockworkLog $clockworkLog) | ||||
| 	{ | ||||
| 		parent::__construct(); | ||||
|  | ||||
| 		$this->clockworkLog = $clockworkLog; | ||||
| 	} | ||||
|  | ||||
| 	protected function write(array $record) | ||||
| 	{ | ||||
| 		$this->clockworkLog->log($record['level'], $record['message']); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										24
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Monolog/Monolog2/ClockworkHandler.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Monolog/Monolog2/ClockworkHandler.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | ||||
| <?php namespace Clockwork\Support\Monolog\Monolog2; | ||||
|  | ||||
| use Clockwork\Request\Log as ClockworkLog; | ||||
|  | ||||
| use Monolog\Logger; | ||||
| use Monolog\Handler\AbstractProcessingHandler; | ||||
|  | ||||
| // Stores messages in a Clockwork log instance (compatible with Monolog 2.x) | ||||
| class ClockworkHandler extends AbstractProcessingHandler | ||||
| { | ||||
| 	protected $clockworkLog; | ||||
|  | ||||
| 	public function __construct(ClockworkLog $clockworkLog) | ||||
| 	{ | ||||
| 		parent::__construct(); | ||||
|  | ||||
| 		$this->clockworkLog = $clockworkLog; | ||||
| 	} | ||||
|  | ||||
| 	protected function write(array $record): void | ||||
| 	{ | ||||
| 		$this->clockworkLog->log($record['level'], $record['message']); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										122
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Slim/ClockworkMiddleware.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										122
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Slim/ClockworkMiddleware.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,122 @@ | ||||
| <?php namespace Clockwork\Support\Slim; | ||||
|  | ||||
| use Clockwork\Clockwork; | ||||
| use Clockwork\Authentication\NullAuthenticator; | ||||
| use Clockwork\DataSource\PsrMessageDataSource; | ||||
| use Clockwork\Storage\FileStorage; | ||||
| use Clockwork\Helpers\ServerTiming; | ||||
|  | ||||
| use Psr\Http\Message\ServerRequestInterface as Request; | ||||
| use Psr\Http\Server\RequestHandlerInterface as RequestHandler; | ||||
|  | ||||
| // Slim 4 middleware | ||||
| class ClockworkMiddleware | ||||
| { | ||||
| 	protected $app; | ||||
| 	protected $clockwork; | ||||
| 	protected $startTime; | ||||
|  | ||||
| 	public function __construct($app, $storagePathOrClockwork, $startTime = null) | ||||
| 	{ | ||||
| 		$this->app = $app; | ||||
| 		$this->clockwork = $storagePathOrClockwork instanceof Clockwork | ||||
| 			? $storagePathOrClockwork : $this->createDefaultClockwork($storagePathOrClockwork); | ||||
| 		$this->startTime = $startTime ?: microtime(true); | ||||
| 	} | ||||
|  | ||||
| 	public function __invoke(Request $request, RequestHandler $handler) | ||||
| 	{ | ||||
| 		return $this->process($request, $handler); | ||||
| 	} | ||||
|  | ||||
| 	public function process(Request $request, RequestHandler $handler) | ||||
| 	{ | ||||
| 		$authUri = '#/__clockwork/auth#'; | ||||
| 		if (preg_match($authUri, $request->getUri()->getPath(), $matches)) { | ||||
| 			return $this->authenticate($request); | ||||
| 		} | ||||
|  | ||||
| 		$clockworkDataUri = '#/__clockwork(?:/(?<id>[0-9-]+))?(?:/(?<direction>(?:previous|next)))?(?:/(?<count>\d+))?#'; | ||||
| 		if (preg_match($clockworkDataUri, $request->getUri()->getPath(), $matches)) { | ||||
| 			$matches = array_merge([ 'id' => null, 'direction' => null, 'count' => null ], $matches); | ||||
| 			return $this->retrieveRequest($request, $matches['id'], $matches['direction'], $matches['count']); | ||||
| 		} | ||||
|  | ||||
| 		$response = $handler->handle($request); | ||||
|  | ||||
| 		return $this->logRequest($request, $response); | ||||
| 	} | ||||
|  | ||||
| 	protected function authenticate(Request $request) | ||||
| 	{ | ||||
| 		$token = $this->clockwork->authenticator()->attempt($request->getParsedBody()); | ||||
|  | ||||
| 		return $this->jsonResponse([ 'token' => $token ], $token ? 200 : 403); | ||||
| 	} | ||||
|  | ||||
| 	protected function retrieveRequest(Request $request, $id, $direction, $count) | ||||
| 	{ | ||||
| 		$authenticator = $this->clockwork->authenticator(); | ||||
| 		$storage = $this->clockwork->storage(); | ||||
|  | ||||
| 		$authenticated = $authenticator->check(current($request->getHeader('X-Clockwork-Auth'))); | ||||
|  | ||||
| 		if ($authenticated !== true) { | ||||
| 			return $this->jsonResponse([ 'message' => $authenticated, 'requires' => $authenticator->requires() ], 403); | ||||
| 		} | ||||
|  | ||||
| 		if ($direction == 'previous') { | ||||
| 			$data = $storage->previous($id, $count); | ||||
| 		} elseif ($direction == 'next') { | ||||
| 			$data = $storage->next($id, $count); | ||||
| 		} elseif ($id == 'latest') { | ||||
| 			$data = $storage->latest(); | ||||
| 		} else { | ||||
| 			$data = $storage->find($id); | ||||
| 		} | ||||
|  | ||||
| 		return $this->jsonResponse($data); | ||||
| 	} | ||||
|  | ||||
| 	protected function logRequest(Request $request, $response) | ||||
| 	{ | ||||
| 		$this->clockwork->timeline()->finalize($this->startTime); | ||||
| 		$this->clockwork->addDataSource(new PsrMessageDataSource($request, $response)); | ||||
|  | ||||
| 		$this->clockwork->resolveRequest(); | ||||
| 		$this->clockwork->storeRequest(); | ||||
|  | ||||
| 		$clockworkRequest = $this->clockwork->request(); | ||||
|  | ||||
| 		$response = $response | ||||
| 			->withHeader('X-Clockwork-Id', $clockworkRequest->id) | ||||
| 			->withHeader('X-Clockwork-Version', Clockwork::VERSION); | ||||
|  | ||||
| 		if ($basePath = $this->app->getBasePath()) { | ||||
| 			$response = $response->withHeader('X-Clockwork-Path', $basePath); | ||||
| 		} | ||||
|  | ||||
| 		return $response->withHeader('Server-Timing', ServerTiming::fromRequest($clockworkRequest)->value()); | ||||
| 	} | ||||
|  | ||||
| 	protected function createDefaultClockwork($storagePath) | ||||
| 	{ | ||||
| 		$clockwork = new Clockwork(); | ||||
|  | ||||
| 		$clockwork->storage(new FileStorage($storagePath)); | ||||
| 		$clockwork->authenticator(new NullAuthenticator); | ||||
|  | ||||
| 		return $clockwork; | ||||
| 	} | ||||
|  | ||||
| 	protected function jsonResponse($data, $status = 200) | ||||
| 	{ | ||||
| 		$response = $this->app->getResponseFactory() | ||||
| 			->createResponse($status) | ||||
| 			->withHeader('Content-Type', 'application/json'); | ||||
|  | ||||
| 		$response->getBody()->write(json_encode($data)); | ||||
|  | ||||
| 		return $response; | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										113
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Slim/Legacy/ClockworkMiddleware.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										113
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Slim/Legacy/ClockworkMiddleware.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,113 @@ | ||||
| <?php namespace Clockwork\Support\Slim\Legacy; | ||||
|  | ||||
| use Clockwork\Clockwork; | ||||
| use Clockwork\Authentication\NullAuthenticator; | ||||
| use Clockwork\DataSource\PsrMessageDataSource; | ||||
| use Clockwork\Storage\FileStorage; | ||||
| use Clockwork\Helpers\ServerTiming; | ||||
|  | ||||
| use Psr\Http\Message\ResponseInterface as Response; | ||||
| use Psr\Http\Message\ServerRequestInterface as Request; | ||||
|  | ||||
| // Slim 3 middleware | ||||
| class ClockworkMiddleware | ||||
| { | ||||
| 	protected $clockwork; | ||||
| 	protected $startTime; | ||||
|  | ||||
| 	public function __construct($storagePathOrClockwork, $startTime = null) | ||||
| 	{ | ||||
| 		$this->clockwork = $storagePathOrClockwork instanceof Clockwork | ||||
| 			? $storagePathOrClockwork : $this->createDefaultClockwork($storagePathOrClockwork); | ||||
| 		$this->startTime = $startTime ?: microtime(true); | ||||
| 	} | ||||
|  | ||||
| 	public function __invoke(Request $request, Response $response, callable $next) | ||||
| 	{ | ||||
| 		return $this->process($request, $response, $next); | ||||
| 	} | ||||
|  | ||||
| 	public function process(Request $request, Response $response, callable $next) | ||||
| 	{ | ||||
| 		$authUri = '#/__clockwork/auth#'; | ||||
| 		if (preg_match($authUri, $request->getUri()->getPath(), $matches)) { | ||||
| 			return $this->authenticate($response, $request); | ||||
| 		} | ||||
|  | ||||
| 		$clockworkDataUri = '#/__clockwork(?:/(?<id>[0-9-]+))?(?:/(?<direction>(?:previous|next)))?(?:/(?<count>\d+))?#'; | ||||
| 		if (preg_match($clockworkDataUri, $request->getUri()->getPath(), $matches)) { | ||||
| 			$matches = array_merge([ 'id' => null, 'direction' => null, 'count' => null ], $matches); | ||||
| 			return $this->retrieveRequest($response, $request, $matches['id'], $matches['direction'], $matches['count']); | ||||
| 		} | ||||
|  | ||||
| 		$response = $next($request, $response); | ||||
|  | ||||
| 		return $this->logRequest($request, $response); | ||||
| 	} | ||||
|  | ||||
| 	protected function authenticate(Response $response, Request $request) | ||||
| 	{ | ||||
| 		$token = $this->clockwork->authenticator()->attempt($request->getParsedBody()); | ||||
|  | ||||
| 		return $response->withJson([ 'token' => $token ])->withStatus($token ? 200 : 403); | ||||
| 	} | ||||
|  | ||||
| 	protected function retrieveRequest(Response $response, Request $request, $id, $direction, $count) | ||||
| 	{ | ||||
| 		$authenticator = $this->clockwork->authenticator(); | ||||
| 		$storage = $this->clockwork->storage(); | ||||
|  | ||||
| 		$authenticated = $authenticator->check(current($request->getHeader('X-Clockwork-Auth'))); | ||||
|  | ||||
| 		if ($authenticated !== true) { | ||||
| 			return $response | ||||
| 				->withJson([ 'message' => $authenticated, 'requires' => $authenticator->requires() ]) | ||||
| 				->withStatus(403); | ||||
| 		} | ||||
|  | ||||
| 		if ($direction == 'previous') { | ||||
| 			$data = $storage->previous($id, $count); | ||||
| 		} elseif ($direction == 'next') { | ||||
| 			$data = $storage->next($id, $count); | ||||
| 		} elseif ($id == 'latest') { | ||||
| 			$data = $storage->latest(); | ||||
| 		} else { | ||||
| 			$data = $storage->find($id); | ||||
| 		} | ||||
|  | ||||
| 		return $response | ||||
| 			->withHeader('Content-Type', 'application/json') | ||||
| 			->withJson($data); | ||||
| 	} | ||||
|  | ||||
| 	protected function logRequest(Request $request, Response $response) | ||||
| 	{ | ||||
| 		$this->clockwork->timeline()->finalize($this->startTime); | ||||
| 		$this->clockwork->addDataSource(new PsrMessageDataSource($request, $response)); | ||||
|  | ||||
| 		$this->clockwork->resolveRequest(); | ||||
| 		$this->clockwork->storeRequest(); | ||||
|  | ||||
| 		$clockworkRequest = $this->clockwork->request(); | ||||
|  | ||||
| 		$response = $response | ||||
| 			->withHeader('X-Clockwork-Id', $clockworkRequest->id) | ||||
| 			->withHeader('X-Clockwork-Version', Clockwork::VERSION); | ||||
|  | ||||
| 		if ($basePath = $request->getUri()->getBasePath()) { | ||||
| 			$response = $response->withHeader('X-Clockwork-Path', $basePath); | ||||
| 		} | ||||
|  | ||||
| 		return $response->withHeader('Server-Timing', ServerTiming::fromRequest($clockworkRequest)->value()); | ||||
| 	} | ||||
|  | ||||
| 	protected function createDefaultClockwork($storagePath) | ||||
| 	{ | ||||
| 		$clockwork = new Clockwork(); | ||||
|  | ||||
| 		$clockwork->storage(new FileStorage($storagePath)); | ||||
| 		$clockwork->authenticator(new NullAuthenticator); | ||||
|  | ||||
| 		return $clockwork; | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										42
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Slim/Old/ClockworkLogWriter.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Slim/Old/ClockworkLogWriter.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | ||||
| <?php namespace Clockwork\Support\Slim\Old; | ||||
|  | ||||
| use Clockwork\Clockwork; | ||||
|  | ||||
| use Slim\Middleware; | ||||
|  | ||||
| class ClockworkLogWriter | ||||
| { | ||||
| 	protected $clockwork; | ||||
| 	protected $originalLogWriter; | ||||
|  | ||||
| 	protected $logLevels = [ | ||||
| 		1 => 'emergency', | ||||
| 		2 => 'alert', | ||||
| 		3 => 'critical', | ||||
| 		4 => 'error', | ||||
| 		5 => 'warning', | ||||
| 		6 => 'notice', | ||||
| 		7 => 'info', | ||||
| 		8 => 'debug' | ||||
| 	]; | ||||
|  | ||||
| 	public function __construct(Clockwork $clockwork, $originalLogWriter) | ||||
| 	{ | ||||
| 		$this->clockwork = $clockwork; | ||||
| 		$this->originalLogWriter = $originalLogWriter; | ||||
| 	} | ||||
|  | ||||
| 	public function write($message, $level = null) | ||||
| 	{ | ||||
| 		$this->clockwork->log($this->getPsrLevel($level), $message); | ||||
|  | ||||
| 		if ($this->originalLogWriter) { | ||||
| 			return $this->originalLogWriter->write($message, $level); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	protected function getPsrLevel($level) | ||||
| 	{ | ||||
| 		return isset($this->logLevels[$level]) ? $this->logLevels[$level] : $level; | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										92
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Slim/Old/ClockworkMiddleware.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Slim/Old/ClockworkMiddleware.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,92 @@ | ||||
| <?php namespace Clockwork\Support\Slim\Old; | ||||
|  | ||||
| use Clockwork\Clockwork; | ||||
| use Clockwork\DataSource\PhpDataSource; | ||||
| use Clockwork\DataSource\SlimDataSource; | ||||
| use Clockwork\Helpers\ServerTiming; | ||||
| use Clockwork\Storage\FileStorage; | ||||
|  | ||||
| use Slim\Middleware; | ||||
|  | ||||
| // Slim 2 middleware | ||||
| class ClockworkMiddleware extends Middleware | ||||
| { | ||||
| 	private $storagePathOrClockwork; | ||||
|  | ||||
| 	public function __construct($storagePathOrClockwork) | ||||
| 	{ | ||||
| 		$this->storagePathOrClockwork = $storagePathOrClockwork; | ||||
| 	} | ||||
|  | ||||
| 	public function call() | ||||
| 	{ | ||||
| 		$this->app->container->singleton('clockwork', function () { | ||||
| 			if ($this->storagePathOrClockwork instanceof Clockwork) { | ||||
| 				return $this->storagePathOrClockwork; | ||||
| 			} | ||||
|  | ||||
| 			$clockwork = new Clockwork(); | ||||
|  | ||||
| 			$clockwork->addDataSource(new PhpDataSource()) | ||||
| 				->addDataSource(new SlimDataSource($this->app)) | ||||
| 				->storage(new FileStorage($this->storagePathOrClockwork)); | ||||
|  | ||||
| 			return $clockwork; | ||||
| 		}); | ||||
|  | ||||
| 		$originalLogWriter = $this->app->getLog()->getWriter(); | ||||
| 		$clockworkLogWriter = new ClockworkLogWriter($this->app->clockwork, $originalLogWriter); | ||||
|  | ||||
| 		$this->app->getLog()->setWriter($clockworkLogWriter); | ||||
|  | ||||
| 		$clockworkDataUri = '#/__clockwork(?:/(?<id>[0-9-]+))?(?:/(?<direction>(?:previous|next)))?(?:/(?<count>\d+))?#'; | ||||
| 		if ($this->app->config('debug') && preg_match($clockworkDataUri, $this->app->request()->getPathInfo(), $matches)) { | ||||
| 			$matches = array_merge([ 'direction' => null, 'count' => null ], $matches); | ||||
| 			return $this->retrieveRequest($matches['id'], $matches['direction'], $matches['count']); | ||||
| 		} | ||||
|  | ||||
| 		try { | ||||
| 			$this->next->call(); | ||||
| 			$this->logRequest(); | ||||
| 		} catch (Exception $e) { | ||||
| 			$this->logRequest(); | ||||
| 			throw $e; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	public function retrieveRequest($id = null, $direction = null, $count = null) | ||||
| 	{ | ||||
| 		$storage = $this->app->clockwork->storage(); | ||||
|  | ||||
| 		if ($direction == 'previous') { | ||||
| 			$data = $storage->previous($id, $count); | ||||
| 		} elseif ($direction == 'next') { | ||||
| 			$data = $storage->next($id, $count); | ||||
| 		} elseif ($id == 'latest') { | ||||
| 			$data = $storage->latest(); | ||||
| 		} else { | ||||
| 			$data = $storage->find($id); | ||||
| 		} | ||||
|  | ||||
| 		echo json_encode($data, \JSON_PARTIAL_OUTPUT_ON_ERROR); | ||||
| 	} | ||||
|  | ||||
| 	protected function logRequest() | ||||
| 	{ | ||||
| 		$this->app->clockwork->resolveRequest(); | ||||
| 		$this->app->clockwork->storeRequest(); | ||||
|  | ||||
| 		if ($this->app->config('debug')) { | ||||
| 			$this->app->response()->header('X-Clockwork-Id', $this->app->clockwork->request()->id); | ||||
| 			$this->app->response()->header('X-Clockwork-Version', Clockwork::VERSION); | ||||
|  | ||||
| 			$env = $this->app->environment(); | ||||
| 			if ($env['SCRIPT_NAME']) { | ||||
| 				$this->app->response()->header('X-Clockwork-Path', $env['SCRIPT_NAME'] . '/__clockwork/'); | ||||
| 			} | ||||
|  | ||||
| 			$request = $this->app->clockwork->request(); | ||||
| 			$this->app->response()->header('Server-Timing', ServerTiming::fromRequest($request)->value()); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										59
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Swift/SwiftPluginClockworkTimeline.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Swift/SwiftPluginClockworkTimeline.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,59 @@ | ||||
| <?php namespace Clockwork\Support\Swift; | ||||
|  | ||||
| use Clockwork\Request\Timeline\Timeline as ClockworkTimeline; | ||||
|  | ||||
| use Swift_Events_SendEvent; | ||||
| use Swift_Events_SendListener; | ||||
|  | ||||
| // Adds records of sent email to the Clockwork timeline | ||||
| class SwiftPluginClockworkTimeline implements Swift_Events_SendListener | ||||
| { | ||||
| 	// Clockwork timeline instance | ||||
| 	protected $timeline; | ||||
|  | ||||
| 	public function __construct(ClockworkTimeline $timeline) | ||||
| 	{ | ||||
| 		$this->timeline = $timeline; | ||||
| 	} | ||||
|  | ||||
| 	// Invoked immediately before a message is sent | ||||
| 	public function beforeSendPerformed(Swift_Events_SendEvent $evt) | ||||
| 	{ | ||||
| 		$message = $evt->getMessage(); | ||||
|  | ||||
| 		$headers = []; | ||||
| 		foreach ($message->getHeaders()->getAll() as $header) { | ||||
| 			$headers[$header->getFieldName()] = $header->getFieldBody(); | ||||
| 		} | ||||
|  | ||||
| 		$this->timeline->event('Sending an email message', [ | ||||
| 			'name'  => 'email ' . $message->getId(), | ||||
| 			'start' => $time = microtime(true), | ||||
| 			'data'  => [ | ||||
| 				'from'    => $this->addressToString($message->getFrom()), | ||||
| 				'to'      => $this->addressToString($message->getTo()), | ||||
| 				'subject' => $message->getSubject(), | ||||
| 				'headers' => $headers | ||||
| 			] | ||||
| 		]); | ||||
| 	} | ||||
|  | ||||
| 	// Invoked immediately after a message is sent | ||||
| 	public function sendPerformed(Swift_Events_SendEvent $evt) | ||||
| 	{ | ||||
| 		$message = $evt->getMessage(); | ||||
|  | ||||
| 		$this->timeline->event('email ' . $message->getId())->end(); | ||||
| 	} | ||||
|  | ||||
| 	protected function addressToString($address) | ||||
| 	{ | ||||
| 		if (! $address) return; | ||||
|  | ||||
| 		foreach ($address as $email => $name) { | ||||
| 			$address[$email] = $name ? "$name <$email>" : $email; | ||||
| 		} | ||||
|  | ||||
| 		return implode(', ', $address); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										11
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Symfony/ClockworkBundle.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Symfony/ClockworkBundle.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| <?php namespace Clockwork\Support\Symfony; | ||||
|  | ||||
| use Symfony\Component\HttpKernel\Bundle\Bundle; | ||||
|  | ||||
| class ClockworkBundle extends Bundle | ||||
| { | ||||
| 	protected function getContainerExtensionClass() | ||||
| 	{ | ||||
| 		return ClockworkExtension::class; | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										36
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Symfony/ClockworkConfiguration.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Symfony/ClockworkConfiguration.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,36 @@ | ||||
| <?php namespace Clockwork\Support\Symfony; | ||||
|  | ||||
| use Symfony\Component\Config\Definition\Builder\TreeBuilder; | ||||
| use Symfony\Component\Config\Definition\ConfigurationInterface; | ||||
| use Symfony\Component\HttpKernel\Kernel; | ||||
|  | ||||
| class ClockworkConfiguration implements ConfigurationInterface | ||||
| { | ||||
| 	protected $debug; | ||||
|  | ||||
| 	public function __construct($debug) | ||||
| 	{ | ||||
| 		$this->debug = $debug; | ||||
| 	} | ||||
|  | ||||
| 	public function getConfigTreeBuilder() | ||||
| 	{ | ||||
| 		return $this->getConfigRoot() | ||||
| 			->children() | ||||
| 				->booleanNode('enable')->defaultValue($this->debug)->end() | ||||
| 				->variableNode('web')->defaultValue(true)->end() | ||||
| 				->booleanNode('authentication')->defaultValue(false)->end() | ||||
| 				->scalarNode('authentication_password')->defaultValue('VerySecretPassword')->end() | ||||
| 				->end() | ||||
| 			->end(); | ||||
| 	} | ||||
|  | ||||
| 	protected function getConfigRoot() | ||||
| 	{ | ||||
| 		if (Kernel::VERSION_ID < 40300) { | ||||
| 			return (new TreeBuilder)->root('clockwork'); | ||||
| 		} | ||||
|  | ||||
| 		return (new TreeBuilder('clockwork'))->getRootNode(); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										69
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Symfony/ClockworkController.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Symfony/ClockworkController.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,69 @@ | ||||
| <?php namespace Clockwork\Support\Symfony; | ||||
|  | ||||
| use Clockwork\Clockwork; | ||||
|  | ||||
| use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; | ||||
| use Symfony\Component\HttpFoundation\JsonResponse; | ||||
| use Symfony\Component\HttpFoundation\Request; | ||||
|  | ||||
| class ClockworkController extends AbstractController | ||||
| { | ||||
| 	protected $clockwork; | ||||
| 	protected $support; | ||||
|  | ||||
| 	public function __construct(Clockwork $clockwork, ClockworkSupport $support) | ||||
| 	{ | ||||
| 		$this->clockwork = $clockwork; | ||||
| 		$this->support = $support; | ||||
| 	} | ||||
|  | ||||
| 	public function authenticate(Request $request) | ||||
| 	{ | ||||
| 		$this->ensureClockworkIsEnabled(); | ||||
|  | ||||
| 		$token = $this->clockwork->authenticator()->attempt($request->request->all()); | ||||
|  | ||||
| 		return new JsonResponse([ 'token' => $token ], $token ? 200 : 403); | ||||
| 	} | ||||
|  | ||||
| 	public function getData(Request $request, $id = null, $direction = null, $count = null) | ||||
| 	{ | ||||
| 		$this->ensureClockworkIsEnabled(); | ||||
|  | ||||
| 		return $this->support->getData($request, $id, $direction, $count); | ||||
| 	} | ||||
|  | ||||
| 	public function webIndex(Request $request) | ||||
| 	{ | ||||
| 		$this->ensureClockworkIsEnabled(); | ||||
| 		$this->ensureClockworkWebIsEnabled(); | ||||
|  | ||||
| 		return $this->support->getWebAsset('index.html'); | ||||
| 	} | ||||
|  | ||||
| 	public function webAsset($path) | ||||
| 	{ | ||||
| 		$this->ensureClockworkIsEnabled(); | ||||
| 		$this->ensureClockworkWebIsEnabled(); | ||||
|  | ||||
| 		return $this->support->getWebAsset($path); | ||||
| 	} | ||||
|  | ||||
| 	public function webRedirect(Request $request) | ||||
| 	{ | ||||
| 		$this->ensureClockworkIsEnabled(); | ||||
| 		$this->ensureClockworkWebIsEnabled(); | ||||
|  | ||||
| 		return $this->redirect('/' . $request->getPathInfo() . '/app'); | ||||
| 	} | ||||
|  | ||||
| 	protected function ensureClockworkIsEnabled() | ||||
| 	{ | ||||
| 		if (! $this->support->isEnabled()) throw $this->createNotFoundException(); | ||||
| 	} | ||||
|  | ||||
| 	protected function ensureClockworkWebIsEnabled() | ||||
| 	{ | ||||
| 		if (! $this->support->isWebEnabled()) throw $this->createNotFoundException(); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										22
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Symfony/ClockworkExtension.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Symfony/ClockworkExtension.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | ||||
| <?php namespace Clockwork\Support\Symfony; | ||||
|  | ||||
| use Symfony\Component\Config\FileLocator; | ||||
| use Symfony\Component\DependencyInjection\ContainerBuilder; | ||||
| use Symfony\Component\DependencyInjection\Loader\PhpFileLoader; | ||||
| use Symfony\Component\HttpKernel\DependencyInjection\ConfigurableExtension; | ||||
|  | ||||
| class ClockworkExtension extends ConfigurableExtension | ||||
| { | ||||
| 	public function loadInternal(array $config, ContainerBuilder $container) | ||||
| 	{ | ||||
| 		$loader = new PhpFileLoader($container, new FileLocator(__DIR__ . '/Resources/config')); | ||||
| 		$loader->load('clockwork.php'); | ||||
|  | ||||
| 		$container->getDefinition(ClockworkSupport::class)->replaceArgument('$config', $config); | ||||
| 	} | ||||
|  | ||||
| 	public function getConfiguration(array $config, ContainerBuilder $container) | ||||
| 	{ | ||||
| 		return new ClockworkConfiguration($container->getParameter('kernel.debug')); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										38
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Symfony/ClockworkFactory.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Symfony/ClockworkFactory.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | ||||
| <?php namespace Clockwork\Support\Symfony; | ||||
|  | ||||
| use Clockwork\Clockwork; | ||||
| use Clockwork\Storage\SymfonyStorage; | ||||
|  | ||||
| use Symfony\Component\DependencyInjection\ContainerInterface; | ||||
|  | ||||
| class ClockworkFactory | ||||
| { | ||||
| 	public function __construct(ContainerInterface $container) | ||||
| 	{ | ||||
| 		$this->container = $container; | ||||
| 	} | ||||
|  | ||||
| 	public function clockwork() | ||||
| 	{ | ||||
| 		return (new Clockwork) | ||||
| 			->authenticator($this->container->get('clockwork.authenticator')) | ||||
| 			->storage($this->container->get('clockwork.storage')); | ||||
| 	} | ||||
|  | ||||
| 	public function clockworkAuthenticator() | ||||
| 	{ | ||||
| 		return $this->container->get('clockwork.support')->makeAuthenticator(); | ||||
| 	} | ||||
|  | ||||
| 	public function clockworkStorage() | ||||
| 	{ | ||||
| 		return new SymfonyStorage( | ||||
| 			$this->container->get('profiler'), substr($this->container->getParameter('profiler.storage.dsn'), 5) | ||||
| 		); | ||||
| 	} | ||||
|  | ||||
| 	public function clockworkSupport($config) | ||||
| 	{ | ||||
| 		return new ClockworkSupport($this->container, $config); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										47
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Symfony/ClockworkListener.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Symfony/ClockworkListener.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,47 @@ | ||||
| <?php namespace Clockwork\Support\Symfony; | ||||
|  | ||||
| use Clockwork\Clockwork; | ||||
|  | ||||
| use Symfony\Component\HttpKernel\KernelEvents; | ||||
| use Symfony\Component\HttpKernel\Event\KernelEvent; | ||||
| use Symfony\Component\HttpKernel\Profiler\Profiler; | ||||
| use Symfony\Component\EventDispatcher\EventSubscriberInterface; | ||||
|  | ||||
| class ClockworkListener implements EventSubscriberInterface | ||||
| { | ||||
| 	protected $clockwork; | ||||
| 	protected $profiler; | ||||
|  | ||||
| 	public function __construct(ClockworkSupport $clockwork, Profiler $profiler) | ||||
| 	{ | ||||
| 		$this->clockwork = $clockwork; | ||||
| 		$this->profiler = $profiler; | ||||
| 	} | ||||
|  | ||||
| 	public function onKernelRequest(KernelEvent $event) | ||||
| 	{ | ||||
| 		if (preg_match('#/__clockwork(.*)#', $event->getRequest()->getPathInfo())) { | ||||
| 			$this->profiler->disable(); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	public function onKernelResponse(KernelEvent $event) | ||||
| 	{ | ||||
| 		if (! $this->clockwork->isEnabled()) return; | ||||
|  | ||||
| 		$response = $event->getResponse(); | ||||
|  | ||||
| 		if (! $response->headers->has('X-Debug-Token')) return; | ||||
|  | ||||
| 		$response->headers->set('X-Clockwork-Id', $response->headers->get('X-Debug-Token')); | ||||
| 		$response->headers->set('X-Clockwork-Version', Clockwork::VERSION); | ||||
| 	} | ||||
|  | ||||
| 	public static function getSubscribedEvents() | ||||
| 	{ | ||||
| 		return [ | ||||
| 			KernelEvents::REQUEST => [ 'onKernelRequest', 512 ], | ||||
| 			KernelEvents::RESPONSE => [ 'onKernelResponse', -128 ] | ||||
| 		]; | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										53
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Symfony/ClockworkLoader.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Symfony/ClockworkLoader.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,53 @@ | ||||
| <?php namespace Clockwork\Support\Symfony; | ||||
|  | ||||
| use Symfony\Component\Config\Loader\Loader; | ||||
| use Symfony\Component\Routing\Route; | ||||
| use Symfony\Component\Routing\RouteCollection; | ||||
|  | ||||
| class ClockworkLoader extends Loader | ||||
| { | ||||
| 	protected $support; | ||||
|  | ||||
| 	public function __construct(ClockworkSupport $support) | ||||
| 	{ | ||||
| 		$this->support = $support; | ||||
| 	} | ||||
|  | ||||
| 	public function load($resource, $type = null) | ||||
| 	{ | ||||
| 		$routes = new RouteCollection(); | ||||
|  | ||||
| 		$routes->add('clockwork', new Route('/__clockwork/{id}/{direction}/{count}', [ | ||||
| 			'_controller' => [ ClockworkController::class, 'getData' ], | ||||
| 			'direction' => null, | ||||
| 			'count' => null | ||||
| 		], [ 'id' => '(?!(app|auth))([a-z0-9-]+|latest)', 'direction' => '(next|previous)', 'count' => '\d+' ])); | ||||
|  | ||||
| 		$routes->add('clockwork.auth', new Route('/__clockwork/auth', [ | ||||
| 			'_controller' => [ ClockworkController::class, 'authenticate' ] | ||||
| 		])); | ||||
|  | ||||
| 		if (! $this->support->isWebEnabled()) return $routes; | ||||
|  | ||||
| 		foreach ($this->support->webPaths() as $path) { | ||||
| 			$routes->add("clockwork.webRedirect.{$path}", new Route("{$path}", [ | ||||
| 				'_controller' => [ ClockworkController::class, 'webRedirect' ] | ||||
| 			])); | ||||
|  | ||||
| 			$routes->add("clockwork.webIndex.{$path}", new Route("{$path}/app", [ | ||||
| 				'_controller' => [ ClockworkController::class, 'webIndex' ] | ||||
| 			])); | ||||
|  | ||||
| 			$routes->add("clockwork.webAsset.{$path}", new Route("{$path}/{path}", [ | ||||
| 				'_controller' => [ ClockworkController::class, 'webAsset' ] | ||||
| 			], [ 'path' => '.+' ])); | ||||
| 		} | ||||
|  | ||||
| 		return $routes; | ||||
| 	} | ||||
|  | ||||
| 	public function supports($resource, $type = null) | ||||
| 	{ | ||||
| 		return $type == 'clockwork'; | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										100
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Symfony/ClockworkSupport.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Symfony/ClockworkSupport.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,100 @@ | ||||
| <?php namespace Clockwork\Support\Symfony; | ||||
|  | ||||
| use Clockwork\Authentication\NullAuthenticator; | ||||
| use Clockwork\Authentication\SimpleAuthenticator; | ||||
| use Clockwork\Storage\Search; | ||||
| use Clockwork\Web\Web; | ||||
|  | ||||
| use Symfony\Component\DependencyInjection\ContainerInterface; | ||||
| use Symfony\Component\HttpFoundation\BinaryFileResponse; | ||||
| use Symfony\Component\HttpFoundation\JsonResponse; | ||||
| use Symfony\Component\HttpFoundation\Request; | ||||
| use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; | ||||
|  | ||||
| class ClockworkSupport | ||||
| { | ||||
| 	protected $container; | ||||
| 	protected $config; | ||||
|  | ||||
| 	public function __construct(ContainerInterface $container, $config) | ||||
| 	{ | ||||
| 		$this->container = $container; | ||||
| 		$this->config = $config; | ||||
| 	} | ||||
|  | ||||
| 	public function getConfig($key, $default = null) | ||||
| 	{ | ||||
| 		return isset($this->config[$key]) ? $this->config[$key] : $default; | ||||
| 	} | ||||
|  | ||||
| 	public function getData(Request $request, $id = null, $direction = null, $count = null) | ||||
| 	{ | ||||
| 		$authenticator = $this->container->get('clockwork')->authenticator(); | ||||
| 		$storage = $this->container->get('clockwork')->storage(); | ||||
|  | ||||
| 		$authenticated = $authenticator->check($request->headers->get('X-Clockwork-Auth')); | ||||
|  | ||||
| 		if ($authenticated !== true) { | ||||
| 			return new JsonResponse([ 'message' => $authenticated, 'requires' => $authenticator->requires() ], 403); | ||||
| 		} | ||||
|  | ||||
| 		if ($direction == 'previous') { | ||||
| 			$data = $storage->previous($id, $count, Search::fromRequest($request->query->all())); | ||||
| 		} elseif ($direction == 'next') { | ||||
| 			$data = $storage->next($id, $count, Search::fromRequest($request->query->all())); | ||||
| 		} elseif ($id == 'latest') { | ||||
| 			$data = $storage->latest(Search::fromRequest($request->query->all())); | ||||
| 		} else { | ||||
| 			$data = $storage->find($id); | ||||
| 		} | ||||
|  | ||||
| 		$data = is_array($data) | ||||
| 			? array_map(function ($request) { return $request->toArray(); }, $data) | ||||
| 			: $data->toArray(); | ||||
|  | ||||
| 		return new JsonResponse($data); | ||||
| 	} | ||||
|  | ||||
| 	public function getWebAsset($path) | ||||
| 	{ | ||||
| 		$web = new Web; | ||||
|  | ||||
| 		if ($asset = $web->asset($path)) { | ||||
| 			return new BinaryFileResponse($asset['path'], 200, [ 'Content-Type' => $asset['mime'] ]); | ||||
| 		} else { | ||||
| 			throw new NotFoundHttpException; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	public function makeAuthenticator() | ||||
| 	{ | ||||
| 		$authenticator = $this->getConfig('authentication'); | ||||
|  | ||||
| 		if (is_string($authenticator)) { | ||||
| 			return $this->container->get($authenticator); | ||||
| 		} elseif ($authenticator) { | ||||
| 			return new SimpleAuthenticator($this->getConfig('authentication_password')); | ||||
| 		} else { | ||||
| 			return new NullAuthenticator; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	public function isEnabled() | ||||
| 	{ | ||||
| 		return $this->getConfig('enable', false); | ||||
| 	} | ||||
|  | ||||
| 	public function isWebEnabled() | ||||
| 	{ | ||||
| 		return $this->getConfig('web', true); | ||||
| 	} | ||||
|  | ||||
| 	public function webPaths() | ||||
| 	{ | ||||
| 		$path = $this->getConfig('web', true); | ||||
|  | ||||
| 		if (is_string($path)) return [ trim($path, '/') ]; | ||||
|  | ||||
| 		return [ 'clockwork', '__clockwork' ]; | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										303
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Symfony/ProfileTransformer.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										303
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Symfony/ProfileTransformer.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,303 @@ | ||||
| <?php namespace Clockwork\Support\Symfony; | ||||
|  | ||||
| use Clockwork\Helpers\Serializer; | ||||
| use Clockwork\Request\Log; | ||||
| use Clockwork\Request\Request; | ||||
| use Clockwork\Request\Timeline\Timeline; | ||||
|  | ||||
| use Symfony\Component\HttpKernel\Profiler\Profile; | ||||
|  | ||||
| class ProfileTransformer | ||||
| { | ||||
| 	public function transform(Profile $profile) | ||||
| 	{ | ||||
| 		$request = new Request([ 'id' => $profile->getToken() ]); | ||||
|  | ||||
| 		$this->transformCacheData($profile, $request); | ||||
| 		$this->transformDoctrineData($profile, $request); | ||||
| 		$this->transformEventsData($profile, $request); | ||||
| 		$this->transformLoggerData($profile, $request); | ||||
| 		$this->transformRequestData($profile, $request); | ||||
| 		$this->transformTimeData($profile, $request); | ||||
| 		$this->transformTwigData($profile, $request); | ||||
|  | ||||
| 		$request->subrequests = $this->getSubrequests($profile); | ||||
|  | ||||
| 		return $request; | ||||
| 	} | ||||
|  | ||||
| 	// Cache collector | ||||
|  | ||||
| 	protected function transformCacheData(Profile $profile, Request $request) | ||||
| 	{ | ||||
| 		if (! $profile->hasCollector('cache')) return; | ||||
|  | ||||
| 		$data = $profile->getCollector('cache'); | ||||
|  | ||||
| 		$request->cacheQueries = $this->getCacheQueries($data); | ||||
| 		$request->cacheReads   = $data->getTotals()['reads']; | ||||
| 		$request->cacheHits    = $data->getTotals()['hits']; | ||||
| 		$request->cacheWrites  = $data->getTotals()['writes']; | ||||
| 		$request->cacheDeletes = $data->getTotals()['deletes']; | ||||
| 	} | ||||
|  | ||||
| 	protected function getCacheQueries($data) | ||||
| 	{ | ||||
| 		return array_reduce(array_map(function ($queries, $connection) { | ||||
| 			return array_filter(array_map(function ($query) use ($connection) { | ||||
| 				$value = $query['result']; | ||||
|  | ||||
| 				if (! is_array($value) || ! count($value)) return; | ||||
|  | ||||
| 				return [ | ||||
| 					'connection' => $connection, | ||||
| 					'time'       => $query['start'], | ||||
| 					'type'       => array_values($value)[0] ? 'hit' : 'miss', | ||||
| 					'key'        => array_keys($value)[0], | ||||
| 					'value'      => '', | ||||
| 					'duration'   => $query['end'] - $query['start'] | ||||
| 				]; | ||||
| 			}, $queries)); | ||||
| 		}, $this->unwrap($data->getCalls()), array_keys($this->unwrap($data->getCalls()))), function ($all, $queries) { | ||||
| 			return array_merge($all, $queries); | ||||
| 		}, []); | ||||
| 	} | ||||
|  | ||||
| 	// Doctrine collector | ||||
|  | ||||
| 	protected function transformDoctrineData(Profile $profile, Request $request) | ||||
| 	{ | ||||
| 		if (! $profile->hasCollector('db')) return; | ||||
|  | ||||
| 		$data = $profile->getCollector('db'); | ||||
|  | ||||
| 		$request->databaseDuration = $data->getTime(); | ||||
| 		$request->databaseQueries = $this->getQueries($data); | ||||
| 	} | ||||
|  | ||||
| 	protected function getQueries($data) | ||||
| 	{ | ||||
| 		return array_reduce(array_map(function ($queries, $connection) { | ||||
| 			return array_filter(array_map(function ($query) use ($connection) { | ||||
| 				return [ | ||||
| 					'query'      => $this->createRunnableQuery($query['sql'], $this->unwrap($query['params'])), | ||||
| 					'duration'   => $query['executionMS'] * 1000, | ||||
| 					'connection' => $connection | ||||
| 				]; | ||||
| 			}, $queries)); | ||||
| 		}, $data->getQueries(), array_keys($data->getQueries())), function ($all, $queries) { | ||||
| 			return array_merge($all, $queries); | ||||
| 		}, []); | ||||
| 	} | ||||
|  | ||||
| 	protected function createRunnableQuery($query, $bindings) | ||||
| 	{ | ||||
| 		foreach ($bindings as $binding) { | ||||
| 			$binding = \Doctrine\Bundle\DoctrineBundle\Twig\DoctrineExtension::escapeFunction($binding); | ||||
|  | ||||
| 			// escape backslashes in the binding (preg_replace requires to do so) | ||||
| 			$binding = str_replace('\\', '\\\\', $binding); | ||||
|  | ||||
| 			$query = preg_replace('/\?/', $binding, $query, 1); | ||||
| 		} | ||||
|  | ||||
| 		// highlight keywords | ||||
| 		$keywords = [ | ||||
| 			'select', 'insert', 'update', 'delete', 'where', 'from', 'limit', 'is', 'null', 'having', 'group by', | ||||
| 			'order by', 'asc', 'desc' | ||||
| 		]; | ||||
| 		$regexp = '/\b' . implode('\b|\b', $keywords) . '\b/i'; | ||||
|  | ||||
| 		$query = preg_replace_callback($regexp, function ($match) { return strtoupper($match[0]); }, $query); | ||||
|  | ||||
| 		return $query; | ||||
| 	} | ||||
|  | ||||
| 	// Events collector | ||||
|  | ||||
| 	protected function transformEventsData(Profile $profile, Request $request) | ||||
| 	{ | ||||
| 		if (! $profile->hasCollector('events')) return; | ||||
|  | ||||
| 		$data = $profile->getCollector('events'); | ||||
|  | ||||
| 		$request->events = $this->getEvents($data); | ||||
| 	} | ||||
|  | ||||
| 	protected function getEvents($data) | ||||
| 	{ | ||||
| 		$handledEvents = array_values(array_reduce($this->unwrap($data->getCalledListeners()), function ($events, $listener) { | ||||
| 			if (! isset($events[$listener['event']])) { | ||||
| 				$events[$listener['event']] = [ 'event' => $listener['event'], 'listeners' => [] ]; | ||||
| 			} | ||||
|  | ||||
| 			$events[$listener['event']]['listeners'][] = $listener['stub']; | ||||
|  | ||||
| 			return $events; | ||||
| 		}, [])); | ||||
|  | ||||
| 		$orphanedEvents = array_map(function ($event) { | ||||
| 			return [ 'event' => $event ]; | ||||
| 		}, $this->unwrap($data->getOrphanedEvents())); | ||||
|  | ||||
| 		return array_merge($handledEvents, $orphanedEvents); | ||||
| 	} | ||||
|  | ||||
| 	// Log collector | ||||
|  | ||||
| 	protected function transformLoggerData(Profile $profile, Request $request) | ||||
| 	{ | ||||
| 		if (! $profile->hasCollector('logger')) return; | ||||
|  | ||||
| 		$data = $profile->getCollector('logger'); | ||||
|  | ||||
| 		$request->log()->merge($this->getLog($data)); | ||||
| 	} | ||||
|  | ||||
| 	protected function getLog($data) | ||||
| 	{ | ||||
| 		$messages = array_map(function ($log) { | ||||
| 			$context = isset($log['context']) ? $log['context'] : []; | ||||
| 			$replacements = array_filter($context, function ($v) { return ! is_array($v) && ! is_object($v) && ! is_resource($v); }); | ||||
|  | ||||
| 			return [ | ||||
| 				'message' => str_replace( | ||||
| 					array_map(function ($v) { return "{{$v}}"; }, array_keys($replacements)), | ||||
| 					array_values($replacements), | ||||
| 					$log['message'] | ||||
| 				), | ||||
| 				'context' => (new Serializer)->normalize($log['context']), | ||||
| 				'level'   => strtolower($log['priorityName']), | ||||
| 				'time'    => $log['timestamp'] | ||||
| 			]; | ||||
| 		}, $this->unwrap($data->getLogs())); | ||||
|  | ||||
| 		return new Log($messages); | ||||
| 	} | ||||
|  | ||||
| 	// Request collector | ||||
|  | ||||
| 	protected function transformRequestData(Profile $profile, Request $request) | ||||
| 	{ | ||||
| 		if (! $profile->hasCollector('request')) return; | ||||
|  | ||||
| 		$data = $profile->getCollector('request'); | ||||
|  | ||||
| 		$request->method         = $data->getMethod(); | ||||
| 		$request->uri            = $data->getPathInfo(); | ||||
| 		$request->controller     = $this->getController($data); | ||||
| 		$request->responseStatus = $data->getStatusCode(); | ||||
| 		$request->headers        = $this->unwrap($data->getRequestHeaders()); | ||||
| 		$request->getData        = $this->unwrap($data->getRequestQuery()); | ||||
| 		$request->postData       = $this->unwrap($data->getRequestRequest()); | ||||
| 		$request->cookies        = $this->unwrap($data->getRequestCookies()); | ||||
| 		$request->sessionData    = (new Serializer)->normalizeEach($this->unwrap($data->getSessionAttributes())); | ||||
| 	} | ||||
|  | ||||
| 	protected function getController($data) | ||||
| 	{ | ||||
| 		$controller = $this->unwrap($data->getController()); | ||||
|  | ||||
| 		if (! is_array($controller)) return $controller; | ||||
|  | ||||
| 		return isset($controller['method']) | ||||
| 			? "{$controller['class']}@{$controller['method']}" | ||||
| 			: $controller['class']; | ||||
| 	} | ||||
|  | ||||
| 	// Time collector | ||||
|  | ||||
| 	protected function transformTimeData(Profile $profile, Request $request) | ||||
| 	{ | ||||
| 		if (! $profile->hasCollector('time')) return; | ||||
|  | ||||
| 		$data = $profile->getCollector('time'); | ||||
|  | ||||
| 		$request->time         = $data->getStartTime() / 1000; | ||||
| 		$request->responseTime = $this->getResponseTime($data); | ||||
|  | ||||
| 		$request->timeline()->merge($this->getTimeline($data)); | ||||
| 	} | ||||
|  | ||||
| 	protected function getResponseTime($data) | ||||
| 	{ | ||||
| 		$lastEvent = $data->getEvents()['__section__']; | ||||
|  | ||||
| 		return ($lastEvent->getOrigin() + $lastEvent->getDuration()) / 1000; | ||||
| 	} | ||||
|  | ||||
| 	protected function getTimeline($data) | ||||
| 	{ | ||||
| 		$events = array_map(function ($event, $name) { | ||||
| 			if ($name == '__section__') { | ||||
| 				$name = 'Application runtime'; | ||||
| 			} elseif ($name == '__section__.child') { | ||||
| 				$name = 'Subrequest'; | ||||
| 			} | ||||
|  | ||||
| 			return [ | ||||
| 				'start'       => ($event->getOrigin() + $event->getStartTime()) / 1000, | ||||
| 				'end'         => ($event->getOrigin() + $event->getEndTime()) / 1000, | ||||
| 				'duration'    => $event->getDuration(), | ||||
| 				'description' => $name, | ||||
| 				'data'        => [] | ||||
| 			]; | ||||
| 		}, $data->getEvents(), array_keys($data->getEvents())); | ||||
|  | ||||
| 		$topEvent = $data->getEvents()['__section__']; | ||||
| 		array_unshift($events, [ | ||||
| 			'start'       => $start = $data->getStartTime() / 1000, | ||||
| 			'end'         => $end = ($topEvent->getOrigin() + $topEvent->getStartTime()) / 1000, | ||||
| 			'duration'    => ($end - $start) * 1000, | ||||
| 			'description' => 'Symfony initialization', | ||||
| 			'data'        => [] | ||||
| 		]); | ||||
|  | ||||
| 		return new Timeline($events); | ||||
| 	} | ||||
|  | ||||
| 	// Twig collector | ||||
|  | ||||
| 	protected function transformTwigData(Profile $profile, Request $request) | ||||
| 	{ | ||||
| 		if (! $profile->hasCollector('twig')) return; | ||||
|  | ||||
| 		$data = $profile->getCollector('twig'); | ||||
|  | ||||
| 		$request->viewsData = $this->getViews($data); | ||||
| 	} | ||||
|  | ||||
| 	protected function getViews($data) | ||||
| 	{ | ||||
| 		return array_map(function ($template) { | ||||
| 			return [ | ||||
| 				'description' => 'Rendering a view', | ||||
| 				'data' => [ 'name' => $template, 'data' => [] ] | ||||
| 			]; | ||||
| 		}, array_keys($data->getTemplates())); | ||||
| 	} | ||||
|  | ||||
| 	protected function getSubrequests($profile) | ||||
| 	{ | ||||
| 		return array_map(function ($child) { | ||||
| 			return [ | ||||
| 				'url'  => urlencode($child->getCollector('request')->getPathInfo()), | ||||
| 				'id'   => $child->getToken(), | ||||
| 				'path' => null | ||||
| 			]; | ||||
| 		}, $profile->getChildren()); | ||||
| 	} | ||||
|  | ||||
| 	protected function unwrap($data) | ||||
| 	{ | ||||
| 		if ($data instanceof \Symfony\Component\VarDumper\Cloner\Data) { | ||||
| 			return $data->getValue(true); | ||||
| 		} elseif ($data instanceof \Symfony\Component\HttpFoundation\ParameterBag) { | ||||
| 			return array_map(function ($val) { return $val->getValue(); }, $data->all()); | ||||
| 		} elseif (is_array($data)) { | ||||
| 			return array_map(function ($item) { return $this->unwrap($item); }, $data); | ||||
| 		} | ||||
|  | ||||
| 		return $data; | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										39
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Symfony/Resources/config/clockwork.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Symfony/Resources/config/clockwork.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | ||||
| <?php | ||||
|  | ||||
| use Clockwork\Support\Symfony\ClockworkFactory; | ||||
|  | ||||
| use Symfony\Component\DependencyInjection\Reference; | ||||
|  | ||||
| $container->autowire(Clockwork\Support\Symfony\ClockworkFactory::class); | ||||
|  | ||||
| $container->register(Clockwork\Clockwork::class) | ||||
| 	->setFactory([ new Reference(ClockworkFactory::class), 'clockwork' ]) | ||||
| 	->setPublic(true); | ||||
|  | ||||
| $container->register(Clockwork\Authentication\AuthenticatorInterface::class) | ||||
| 	->setFactory([ new Reference(ClockworkFactory::class), 'clockworkAuthenticator' ]) | ||||
| 	->setPublic(true); | ||||
|  | ||||
| $container->register(Clockwork\Storage\StorageInterface::class) | ||||
| 	->setFactory([ new Reference(ClockworkFactory::class), 'clockworkStorage' ]) | ||||
| 	->setPublic(true); | ||||
|  | ||||
| $container->register(Clockwork\Support\Symfony\ClockworkSupport::class) | ||||
| 	->setArgument('$config', []) | ||||
| 	->setFactory([ new Reference(ClockworkFactory::class), 'clockworkSupport' ]) | ||||
| 	->setPublic(true); | ||||
|  | ||||
| $container->autowire(Clockwork\Support\Symfony\ClockworkController::class) | ||||
| 	->setAutoconfigured(true); | ||||
|  | ||||
| $container->autowire(Clockwork\Support\Symfony\ClockworkListener::class) | ||||
| 	->setArgument('$profiler', new Reference('profiler')) | ||||
| 	->addTag('kernel.event_subscriber'); | ||||
|  | ||||
| $container->autowire(Clockwork\Support\Symfony\ClockworkLoader::class) | ||||
| 	->addTag('routing.loader'); | ||||
|  | ||||
| $container->setAlias('clockwork', Clockwork\Clockwork::class)->setPublic('true'); | ||||
| $container->setAlias('clockwork.authenticator', Clockwork\Authentication\AuthenticatorInterface::class)->setPublic('true'); | ||||
| $container->setAlias('clockwork.storage', Clockwork\Storage\StorageInterface::class)->setPublic('true'); | ||||
| $container->setAlias('clockwork.support', Clockwork\Support\Symfony\ClockworkSupport::class)->setPublic('true'); | ||||
							
								
								
									
										7
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Symfony/Resources/config/routing/clockwork.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Symfony/Resources/config/routing/clockwork.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| <?php | ||||
|  | ||||
| use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; | ||||
|  | ||||
| return function (RoutingConfigurator $routes) { | ||||
| 	$routes->import('.', 'clockwork'); | ||||
| }; | ||||
							
								
								
									
										51
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Twig/ProfilerClockworkDumper.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Twig/ProfilerClockworkDumper.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,51 @@ | ||||
| <?php namespace Clockwork\Support\Twig; | ||||
|  | ||||
| use Clockwork\Request\Timeline\Timeline; | ||||
|  | ||||
| use Twig\Profiler\Profile; | ||||
|  | ||||
| // Converts Twig profiles to a Clockwork rendered views timelines | ||||
| class ProfilerClockworkDumper | ||||
| { | ||||
| 	protected $lastId = 1; | ||||
|  | ||||
| 	// Dumps a profile into a new rendered views timeline | ||||
| 	public function dump(Profile $profile) | ||||
| 	{ | ||||
| 		$timeline = new Timeline; | ||||
|  | ||||
| 		$this->dumpProfile($profile, $timeline); | ||||
|  | ||||
| 		return $timeline; | ||||
| 	} | ||||
|  | ||||
| 	public function dumpProfile(Profile $profile, Timeline $timeline, $parent = null) | ||||
| 	{ | ||||
| 		$id = $this->lastId++; | ||||
|  | ||||
| 		if ($profile->isRoot()) { | ||||
| 			$name = $profile->getName(); | ||||
| 		} elseif ($profile->isTemplate()) { | ||||
| 			$name = basename($profile->getTemplate()); | ||||
| 		} else { | ||||
| 			$name = basename($profile->getTemplate()) . '::' . $profile->getType() . '(' . $profile->getName() . ')'; | ||||
| 		} | ||||
|  | ||||
| 		foreach ($profile as $p) { | ||||
| 			$this->dumpProfile($p, $timeline, $id); | ||||
| 		} | ||||
|  | ||||
| 		$data = $profile->__serialize(); | ||||
|  | ||||
| 		$timeline->event($name, [ | ||||
| 			'name'  => $id, | ||||
| 			'start' => isset($data[3]['wt']) ? $data[3]['wt'] : null, | ||||
| 			'end'   => isset($data[4]['wt']) ? $data[4]['wt'] : null, | ||||
| 			'data'  => [ | ||||
| 				'data'        => [], | ||||
| 				'memoryUsage' => isset($data[4]['mu']) ? $data[4]['mu'] : null, | ||||
| 				'parent'      => $parent | ||||
| 			] | ||||
| 		]); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										478
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Vanilla/Clockwork.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										478
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Vanilla/Clockwork.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,478 @@ | ||||
| <?php namespace Clockwork\Support\Vanilla; | ||||
|  | ||||
| use Clockwork\Clockwork as BaseClockwork; | ||||
| use Clockwork\Authentication\NullAuthenticator; | ||||
| use Clockwork\Authentication\SimpleAuthenticator; | ||||
| use Clockwork\DataSource\PhpDataSource; | ||||
| use Clockwork\DataSource\PsrMessageDataSource; | ||||
| use Clockwork\Helpers\Serializer; | ||||
| use Clockwork\Helpers\ServerTiming; | ||||
| use Clockwork\Helpers\StackFilter; | ||||
| use Clockwork\Request\IncomingRequest; | ||||
| use Clockwork\Storage\FileStorage; | ||||
| use Clockwork\Storage\Search; | ||||
| use Clockwork\Storage\SqlStorage; | ||||
|  | ||||
| use Psr\Http\Message\ServerRequestInterface as PsrRequest; | ||||
| use Psr\Http\Message\ResponseInterface as PsrResponse; | ||||
|  | ||||
| // Clockwork integration for vanilla php and unsupported frameworks | ||||
| class Clockwork | ||||
| { | ||||
| 	// Clockwork config | ||||
| 	protected $config; | ||||
| 	// Clockwork instance | ||||
| 	protected $clockwork; | ||||
|  | ||||
| 	// PSR-7 request and response | ||||
| 	protected $psrRequest; | ||||
| 	protected $psrResponse; | ||||
|  | ||||
| 	// Whether the headers were already sent (header can be sent manually) | ||||
| 	protected $headersSent = false; | ||||
|  | ||||
| 	// Static instance when used as singleton | ||||
| 	protected static $defaultInstance; | ||||
|  | ||||
| 	// Create new instance, takes an additional config | ||||
| 	public function __construct($config = []) | ||||
| 	{ | ||||
| 		$this->config = array_merge(include __DIR__ . '/config.php', $config); | ||||
|  | ||||
| 		$this->clockwork = new BaseClockwork; | ||||
|  | ||||
| 		$this->clockwork->addDataSource(new PhpDataSource); | ||||
| 		$this->clockwork->storage($this->makeStorage()); | ||||
| 		$this->clockwork->authenticator($this->makeAuthenticator()); | ||||
|  | ||||
| 		$this->configureSerializer(); | ||||
| 		$this->configureShouldCollect(); | ||||
| 		$this->configureShouldRecord(); | ||||
|  | ||||
| 		if ($this->config['register_helpers']) include __DIR__ . '/helpers.php'; | ||||
| 	} | ||||
|  | ||||
| 	// Initialize a singleton instance, takes an additional config | ||||
| 	public static function init($config = []) | ||||
| 	{ | ||||
| 		return static::$defaultInstance = new static($config); | ||||
| 	} | ||||
|  | ||||
| 	// Return the singleton instance | ||||
| 	public static function instance() | ||||
| 	{ | ||||
| 		return static::$defaultInstance; | ||||
| 	} | ||||
|  | ||||
| 	// Resolves and records the current request and sends Clockwork headers, should be called at the end of app | ||||
| 	// execution, return PSR-7 response if one was set | ||||
| 	public function requestProcessed() | ||||
| 	{ | ||||
| 		if (! $this->config['enable'] && ! $this->config['collect_data_always']) return $this->psrResponse; | ||||
|  | ||||
| 		if (! $this->clockwork->shouldCollect()->filter($this->incomingRequest())) return $this->psrResponse; | ||||
| 		if (! $this->clockwork->shouldRecord()->filter($this->clockwork->request())) return $this->psrResponse; | ||||
|  | ||||
| 		$this->clockwork->resolveRequest()->storeRequest(); | ||||
|  | ||||
| 		if (! $this->config['enable']) return $this->psrResponse; | ||||
|  | ||||
| 		$this->sendHeaders(); | ||||
|  | ||||
| 		if (($eventsCount = $this->config['server_timing']) !== false) { | ||||
| 			$this->setHeader('Server-Timing', ServerTiming::fromRequest($this->clockwork->request(), $eventsCount)->value()); | ||||
| 		} | ||||
|  | ||||
| 		return $this->psrResponse; | ||||
| 	} | ||||
|  | ||||
| 	// Resolves and records the current request as a command, should be called at the end of app execution | ||||
| 	public function commandExecuted($name, $exitCode = null, $arguments = [], $options = [], $argumentsDefaults = [], $optionsDefaults = [], $output = null) | ||||
| 	{ | ||||
| 		if (! $this->config['enable'] && ! $this->config['collect_data_always']) return; | ||||
|  | ||||
| 		if (! $this->clockwork->shouldRecord()->filter($this->clockwork->request())) return; | ||||
|  | ||||
| 		$this->clockwork | ||||
| 			->resolveAsCommand($name, $exitCode, $arguments, $options, $argumentsDefaults, $optionsDefaults, $output) | ||||
| 			->storeRequest(); | ||||
| 	} | ||||
|  | ||||
| 	// Resolves and records the current request as a queue job, should be called at the end of app execution | ||||
| 	public function queueJobExecuted($name, $description = null, $status = 'processed', $payload = [], $queue = null, $connection = null, $options = []) | ||||
| 	{ | ||||
| 		if (! $this->config['enable'] && ! $this->config['collect_data_always']) return; | ||||
|  | ||||
| 		if (! $this->clockwork->shouldRecord()->filter($this->clockwork->request())) return; | ||||
|  | ||||
| 		$this->clockwork | ||||
| 			->resolveAsQueueJob($name, $description, $status, $payload, $queue, $connection, $options) | ||||
| 			->storeRequest(); | ||||
| 	} | ||||
|  | ||||
| 	// Manually send the Clockwork headers, this should be manually called only when the headers need to be sent early | ||||
| 	// in the request processing | ||||
| 	public function sendHeaders() | ||||
| 	{ | ||||
| 		if (! $this->config['enable'] || $this->headersSent) return; | ||||
|  | ||||
| 		$this->headersSent = true; | ||||
|  | ||||
| 		$clockworkRequest = $this->request(); | ||||
|  | ||||
| 		$this->setHeader('X-Clockwork-Id', $clockworkRequest->id); | ||||
| 		$this->setHeader('X-Clockwork-Version', BaseClockwork::VERSION); | ||||
|  | ||||
| 		if ($this->config['api'] != '/__clockwork/') { | ||||
| 			$this->setHeader('X-Clockwork-Path', $this->config['api']); | ||||
| 		} | ||||
|  | ||||
| 		foreach ($this->config['headers'] as $headerName => $headerValue) { | ||||
| 			$this->setHeader("X-Clockwork-Header-{$headerName}", $headerValue); | ||||
| 		} | ||||
|  | ||||
| 		if ($this->config['features']['performance']['client_metrics'] || $this->config['toolbar']) { | ||||
| 			$this->setCookie('x-clockwork', $this->getCookiePayload(), time() + 60); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Returns the x-clockwork cookie payload in case you need to set the cookie yourself (cookie can't be http only, | ||||
| 	// expiration time should be 60 seconds) | ||||
| 	public function getCookiePayload() | ||||
| 	{ | ||||
| 		$clockworkRequest = $this->request(); | ||||
|  | ||||
| 		return json_encode([ | ||||
| 			'requestId' => $clockworkRequest->id, | ||||
| 			'version'   => BaseClockwork::VERSION, | ||||
| 			'path'      => $this->config['api'], | ||||
| 			'webPath'   => $this->config['web']['enable'], | ||||
| 			'token'     => $clockworkRequest->updateToken, | ||||
| 			'metrics'   => $this->config['features']['performance']['client_metrics'], | ||||
| 			'toolbar'   => $this->config['toolbar'] | ||||
| 		]); | ||||
| 	} | ||||
|  | ||||
| 	// Handle Clockwork REST api request, retrieves or updates Clockwork metadata | ||||
| 	public function handleMetadata($request = null, $method = null) | ||||
| 	{ | ||||
| 		if (! $request) $request = isset($_GET['request']) ? $_GET['request'] : ''; | ||||
| 		if (! $method) $method = isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : 'GET'; | ||||
|  | ||||
| 		if ($method == 'POST' && $request == 'auth') return $this->authenticate(); | ||||
|  | ||||
| 		return $method == 'POST' ? $this->updateMetadata($request) : $this->returnMetadata($request); | ||||
| 	} | ||||
|  | ||||
| 	// Retrieve metadata based on the passed Clockwork REST api request and send HTTP response | ||||
| 	public function returnMetadata($request = null) | ||||
| 	{ | ||||
| 		if (! $this->config['enable']) return $this->response(null, 404); | ||||
|  | ||||
| 		$authenticator = $this->clockwork->authenticator(); | ||||
| 		$authenticated = $authenticator->check(isset($_SERVER['HTTP_X_CLOCKWORK_AUTH']) ? $_SERVER['HTTP_X_CLOCKWORK_AUTH'] : ''); | ||||
|  | ||||
| 		if ($authenticated !== true) { | ||||
| 			return $this->response([ 'message' => $authenticated, 'requires' => $authenticator->requires() ], 403); | ||||
| 		} | ||||
|  | ||||
| 		return $this->response($this->getMetadata($request)); | ||||
| 	} | ||||
|  | ||||
| 	// Returns metadata based on the passed Clockwork REST api request | ||||
| 	public function getMetadata($request = null) | ||||
| 	{ | ||||
| 		if (! $this->config['enable']) return; | ||||
|  | ||||
| 		$authenticator = $this->clockwork->authenticator(); | ||||
| 		$authenticated = $authenticator->check(isset($_SERVER['HTTP_X_CLOCKWORK_AUTH']) ? $_SERVER['HTTP_X_CLOCKWORK_AUTH'] : ''); | ||||
|  | ||||
| 		if ($authenticated !== true) return; | ||||
|  | ||||
| 		if (! $request) $request = isset($_GET['request']) ? $_GET['request'] : ''; | ||||
|  | ||||
| 		preg_match('#(?<id>[0-9-]+|latest)(?:/(?<direction>next|previous))?(?:/(?<count>\d+))?#', $request, $matches); | ||||
|  | ||||
| 		$id = isset($matches['id']) ? $matches['id'] : null; | ||||
| 		$direction = isset($matches['direction']) ? $matches['direction'] : null; | ||||
| 		$count = isset($matches['count']) ? $matches['count'] : null; | ||||
|  | ||||
| 		if ($direction == 'previous') { | ||||
| 			$data = $this->clockwork->storage()->previous($id, $count, Search::fromRequest($_GET)); | ||||
| 		} elseif ($direction == 'next') { | ||||
| 			$data = $this->clockwork->storage()->next($id, $count, Search::fromRequest($_GET)); | ||||
| 		} elseif ($id == 'latest') { | ||||
| 			$data = $this->clockwork->storage()->latest(Search::fromRequest($_GET)); | ||||
| 		} else { | ||||
| 			$data = $this->clockwork->storage()->find($id); | ||||
| 		} | ||||
|  | ||||
| 		if (preg_match('#(?<id>[0-9-]+|latest)/extended#', $request)) { | ||||
| 			$this->clockwork->extendRequest($data); | ||||
| 		} | ||||
|  | ||||
| 		if ($data) { | ||||
| 			$data = is_array($data) ? array_map(function ($item) { return $item->toArray(); }, $data) : $data->toArray(); | ||||
| 		} | ||||
|  | ||||
| 		return $data; | ||||
| 	} | ||||
|  | ||||
| 	// Update metadata based on the passed Clockwork REST api request and send HTTP response | ||||
| 	public function updateMetadata($request = null) | ||||
| 	{ | ||||
| 		if (! $this->config['enable'] || ! $this->config['features']['performance']['client_metrics']) { | ||||
| 			return $this->response(null, 404); | ||||
| 		} | ||||
|  | ||||
| 		if (! $request) $request = isset($_GET['request']) ? $_GET['request'] : ''; | ||||
|  | ||||
| 		$storage = $this->clockwork->storage(); | ||||
|  | ||||
| 		$request = $storage->find($request); | ||||
|  | ||||
| 		if (! $request) { | ||||
| 			return $this->response([ 'message' => 'Request not found.' ], 404); | ||||
| 		} | ||||
|  | ||||
| 		$input = json_decode(file_get_contents('php://input'), true); | ||||
|  | ||||
| 		$token = isset($input['_token']) ? $input['_token'] : ''; | ||||
|  | ||||
| 		if (! $request->updateToken || ! hash_equals($request->updateToken, $token)) { | ||||
| 			return $this->response([ 'message' => 'Invalid update token.' ], 403); | ||||
| 		} | ||||
|  | ||||
| 		foreach ($input as $key => $value) { | ||||
| 			if (in_array($key, [ 'clientMetrics', 'webVitals' ])) { | ||||
| 				$request->$key = $value; | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		$storage->update($request); | ||||
|  | ||||
| 		return $this->response(); | ||||
| 	} | ||||
|  | ||||
| 	// Authanticates access to Clockwork REST api | ||||
| 	public function authenticate($request = null) | ||||
| 	{ | ||||
| 		if (! $this->config['enable']) return; | ||||
|  | ||||
| 		if (! $request) $request = isset($_GET['request']) ? $_GET['request'] : ''; | ||||
|  | ||||
| 		$token = $this->clockwork->authenticator()->attempt([ | ||||
| 			'username' => isset($_POST['username']) ? $_POST['username'] : '', | ||||
| 			'password' => isset($_POST['password']) ? $_POST['password'] : '' | ||||
| 		]); | ||||
|  | ||||
| 		return $this->response([ 'token' => $token ], $token ? 200 : 403); | ||||
| 	} | ||||
|  | ||||
| 	// Returns the Clockwork Web UI as a HTTP response, installs the Web UI on the first run | ||||
| 	public function returnWeb() | ||||
| 	{ | ||||
| 		if (! $this->config['web']['enable']) return; | ||||
|  | ||||
| 		$this->installWeb(); | ||||
|  | ||||
| 		$asset = function ($uri) { return "{$this->config['web']['uri']}/{$uri}"; }; | ||||
| 		$metadataPath = $this->config['api']; | ||||
| 		$url = $this->config['web']['uri']; | ||||
|  | ||||
| 		if (! preg_match('#/index.html$#', $url)) { | ||||
| 			$url = rtrim($url, '/') . '/index.html'; | ||||
| 		} | ||||
|  | ||||
| 		ob_start(); | ||||
|  | ||||
| 		include __DIR__ . '/iframe.html.php'; | ||||
|  | ||||
| 		$html = ob_get_clean(); | ||||
|  | ||||
| 		return $this->response($html, null, false); | ||||
| 	} | ||||
|  | ||||
| 	// Installs the Web UI by copying the assets to the public directory, no-op if already installed | ||||
| 	public function installWeb() | ||||
| 	{ | ||||
| 		$path = $this->config['web']['path']; | ||||
| 		$source = __DIR__ . '/../../Web/public'; | ||||
|  | ||||
| 		if (file_exists("{$path}/index.html")) return; | ||||
|  | ||||
| 		@mkdir($path, 0755, true); | ||||
|  | ||||
| 		$iterator = new \RecursiveIteratorIterator( | ||||
| 			new \RecursiveDirectoryIterator($source, \RecursiveDirectoryIterator::SKIP_DOTS), | ||||
| 			\RecursiveIteratorIterator::SELF_FIRST | ||||
| 		); | ||||
|  | ||||
| 		foreach ($iterator as $item) { | ||||
| 			if ($item->isDir()) { | ||||
| 				mkdir("{$path}/" . $iterator->getSubPathName()); | ||||
| 			} else { | ||||
| 				copy($item, "{$path}/" . $iterator->getSubPathName()); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Use a PSR-7 request and response instances instead of vanilla php HTTP apis | ||||
| 	public function usePsrMessage(PsrRequest $request, PsrResponse $response = null) | ||||
| 	{ | ||||
| 		$this->psrRequest = $request; | ||||
| 		$this->psrResponse = $response; | ||||
|  | ||||
| 		$this->clockwork->addDataSource(new PsrMessageDataSource($request, $response)); | ||||
|  | ||||
| 		return $this; | ||||
| 	} | ||||
|  | ||||
| 	// Make a storage implementation based on user configuration | ||||
| 	protected function makeStorage() | ||||
| 	{ | ||||
| 		if ($this->config['storage'] == 'sql') { | ||||
| 			$database = $this->config['storage_sql_database']; | ||||
| 			$table = $this->config['storage_sql_table']; | ||||
|  | ||||
| 			$storage = new SqlStorage( | ||||
| 				$this->config['storage_sql_database'], | ||||
| 				$this->config['storage_sql_table'], | ||||
| 				$this->config['storage_sql_username'], | ||||
| 				$this->config['storage_sql_password'], | ||||
| 				$this->config['storage_expiration'] | ||||
| 			); | ||||
| 		} else { | ||||
| 			$storage = new FileStorage( | ||||
| 				$this->config['storage_files_path'], | ||||
| 				0700, | ||||
| 				$this->config['storage_expiration'], | ||||
| 				$this->config['storage_files_compress'] | ||||
| 			); | ||||
| 		} | ||||
|  | ||||
| 		return $storage; | ||||
| 	} | ||||
|  | ||||
| 	// Make an authenticator implementation based on user configuration | ||||
| 	protected function makeAuthenticator() | ||||
| 	{ | ||||
| 		$authenticator = $this->config['authentication']; | ||||
|  | ||||
| 		if (is_string($authenticator)) { | ||||
| 			return new $authenticator; | ||||
| 		} elseif ($authenticator) { | ||||
| 			return new SimpleAuthenticator($this->config['authentication_password']); | ||||
| 		} else { | ||||
| 			return new NullAuthenticator; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Configure serializer defaults based on user configuration | ||||
| 	protected function configureSerializer() | ||||
| 	{ | ||||
| 		Serializer::defaults([ | ||||
| 			'limit'       => $this->config['serialization_depth'], | ||||
| 			'blackbox'    => $this->config['serialization_blackbox'], | ||||
| 			'traces'      => $this->config['stack_traces']['enabled'], | ||||
| 			'tracesSkip'  => StackFilter::make() | ||||
| 				->isNotVendor(array_merge( | ||||
| 					$this->config['stack_traces']['skip_vendors'], | ||||
| 					[ 'itsgoingd', 'laravel', 'illuminate' ] | ||||
| 				)) | ||||
| 				->isNotNamespace($this->config['stack_traces']['skip_namespaces']) | ||||
| 				->isNotFunction([ 'call_user_func', 'call_user_func_array' ]) | ||||
| 				->isNotClass($this->config['stack_traces']['skip_classes']), | ||||
| 			'tracesLimit' => $this->config['stack_traces']['limit'] | ||||
| 		]); | ||||
| 	} | ||||
|  | ||||
| 	// Configure should collect rules based on user configuration | ||||
| 	public function configureShouldCollect() | ||||
| 	{ | ||||
| 		$this->clockwork->shouldCollect([ | ||||
| 			'onDemand'        => $this->config['requests']['on_demand'], | ||||
| 			'sample'          => $this->config['requests']['sample'], | ||||
| 			'except'          => $this->config['requests']['except'], | ||||
| 			'only'            => $this->config['requests']['only'], | ||||
| 			'exceptPreflight' => $this->config['requests']['except_preflight'] | ||||
| 		]); | ||||
|  | ||||
| 		// don't collect data for Clockwork requests | ||||
| 		$this->clockwork->shouldCollect()->except(preg_quote(rtrim($this->config['api'], '/'), '#')); | ||||
| 	} | ||||
|  | ||||
| 	// Configure should record rules based on user configuration | ||||
| 	public function configureShouldRecord() | ||||
| 	{ | ||||
| 		$this->clockwork->shouldRecord([ | ||||
| 			'errorsOnly' => $this->config['requests']['errors_only'], | ||||
| 			'slowOnly'   => $this->config['requests']['slow_only'] ? $this->config['requests']['slow_threshold'] : false | ||||
| 		]); | ||||
| 	} | ||||
|  | ||||
| 	// Set a cookie on PSR-7 response or using vanilla php | ||||
| 	protected function setCookie($name, $value, $expires) { | ||||
| 		if ($this->psrResponse) { | ||||
| 			$this->psrResponse = $this->psrResponse->withAddedHeader( | ||||
| 				'Set-Cookie', "{$name}=" . urlencode($value) . '; expires=' . gmdate('D, d M Y H:i:s T', $expires) | ||||
| 			); | ||||
| 		} else { | ||||
| 			setcookie($name, $value, $expires); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Set a header on PSR-7 response or using vanilla php | ||||
| 	protected function setHeader($header, $value) | ||||
| 	{ | ||||
| 		if ($this->psrResponse) { | ||||
| 			$this->psrResponse = $this->psrResponse->withHeader($header, $value); | ||||
| 		} else { | ||||
| 			header("{$header}: {$value}"); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Send a json response, uses the PSR-7 response if set | ||||
| 	protected function response($data = null, $status = null, $json = true) | ||||
| 	{ | ||||
| 		if ($json) $this->setHeader('Content-Type', 'application/json'); | ||||
|  | ||||
| 		if ($this->psrResponse) { | ||||
| 			if ($status) $this->psrResponse = $this->psrResponse->withStatus($status); | ||||
| 			$this->psrResponse->getBody()->write($json ? json_encode($data, \JSON_PARTIAL_OUTPUT_ON_ERROR) : $data); | ||||
| 			return $this->psrResponse; | ||||
| 		} else { | ||||
| 			if ($status) http_response_code($status); | ||||
| 			echo $json ? json_encode($data, \JSON_PARTIAL_OUTPUT_ON_ERROR) : $data; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Make a Clockwork incoming request instance | ||||
| 	protected function incomingRequest() | ||||
| 	{ | ||||
| 		return new IncomingRequest([ | ||||
| 			'method'  => $_SERVER['REQUEST_METHOD'], | ||||
| 			'uri'     => $_SERVER['REQUEST_URI'], | ||||
| 			'input'   => $_REQUEST, | ||||
| 			'cookies' => $_COOKIE | ||||
| 		]); | ||||
| 	} | ||||
|  | ||||
| 	// Return the underlying Clockwork instance | ||||
| 	public function getClockwork() | ||||
| 	{ | ||||
| 		return $this->clockwork; | ||||
| 	} | ||||
|  | ||||
| 	// Pass any method calls to the underlying Clockwork instance | ||||
| 	public function __call($method, $args = []) | ||||
| 	{ | ||||
| 		return $this->clockwork->$method(...$args); | ||||
| 	} | ||||
|  | ||||
| 	// Pass any static method calls to the underlying Clockwork instance | ||||
| 	public static function __callStatic($method, $args = []) | ||||
| 	{ | ||||
| 		return static::instance()->$method(...$args); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										274
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Vanilla/config.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										274
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Vanilla/config.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,274 @@ | ||||
| <?php | ||||
|  | ||||
| return [ | ||||
|  | ||||
| 	/* | ||||
| 	|------------------------------------------------------------------------------------------------------------------ | ||||
| 	| Enable Clockwork | ||||
| 	|------------------------------------------------------------------------------------------------------------------ | ||||
| 	| | ||||
| 	| You can explicitly enable or disable Clockwork here. When disabled, | ||||
| 	| the storeRequest and returnRequest methods will be no-ops. This provides | ||||
| 	| a convenient way to disable Clockwork in production. | ||||
| 	| | ||||
| 	*/ | ||||
|  | ||||
| 	'enable' => isset($_ENV['CLOCKWORK_ENABLE']) ? $_ENV['CLOCKWORK_ENABLE'] : true, | ||||
|  | ||||
| 	/* | ||||
| 	|------------------------------------------------------------------------------------------------------------------ | ||||
| 	| Features | ||||
| 	|------------------------------------------------------------------------------------------------------------------ | ||||
| 	| | ||||
| 	| You can enable or disable various Clockwork features here. Some features have additional settings (eg. slow query | ||||
| 	| threshold for database queries). | ||||
| 	| | ||||
| 	*/ | ||||
|  | ||||
| 	'features' => [ | ||||
|  | ||||
| 		// Performance metrics | ||||
| 		'performance' => [ | ||||
| 			// Allow collecting of client metrics. Requires separate clockwork-browser npm package. | ||||
| 			'client_metrics' => isset($_ENV['CLOCKWORK_PERFORMANCE_CLIENT_METRICS']) ? $_ENV['CLOCKWORK_PERFORMANCE_CLIENT_METRICS'] : true | ||||
| 		] | ||||
|  | ||||
| 	], | ||||
|  | ||||
| 	/* | ||||
| 	|------------------------------------------------------------------------------------------------------------------ | ||||
| 	| Enable toolbar | ||||
| 	|------------------------------------------------------------------------------------------------------------------ | ||||
| 	| | ||||
| 	| Clockwork can show a toolbar with basic metrics on all responses. Here you can enable or disable this feature. | ||||
| 	| Requires a separate clockwork-browser npm library. | ||||
| 	| | ||||
| 	*/ | ||||
|  | ||||
| 	'toolbar' => isset($_ENV['CLOCKWORK_TOOLBAR']) ? $_ENV['CLOCKWORK_TOOLBAR'] : true, | ||||
|  | ||||
| 	/* | ||||
| 	|------------------------------------------------------------------------------------------------------------------ | ||||
| 	| HTTP requests collection | ||||
| 	|------------------------------------------------------------------------------------------------------------------ | ||||
| 	| | ||||
| 	| Clockwork collects data about HTTP requests to your app. Here you can choose which requests should be collected. | ||||
| 	| | ||||
| 	*/ | ||||
|  | ||||
| 	'requests' => [ | ||||
| 		// With on-demand mode enabled, Clockwork will only profile requests when the browser extension is open or you | ||||
| 		// manually pass a "clockwork-profile" cookie or get/post data key. | ||||
| 		// Optionally you can specify a "secret" that has to be passed as the value to enable profiling. | ||||
| 		'on_demand' => isset($_ENV['CLOCKWORK_REQUESTS_ON_DEMAND']) ? $_ENV['CLOCKWORK_REQUESTS_ON_DEMAND'] : false, | ||||
|  | ||||
| 		// Collect only errors (requests with HTTP 4xx and 5xx responses) | ||||
| 		'errors_only' => isset($_ENV['CLOCKWORK_REQUESTS_ERRORS_ONLY']) ? $_ENV['CLOCKWORK_REQUESTS_ERRORS_ONLY'] : false, | ||||
|  | ||||
| 		// Response time threshold in milliseconds after which the request will be marked as slow | ||||
| 		'slow_threshold' => isset($_ENV['CLOCKWORK_REQUESTS_SLOW_THRESHOLD']) ? $_ENV['CLOCKWORK_REQUESTS_SLOW_THRESHOLD'] : null, | ||||
|  | ||||
| 		// Collect only slow requests | ||||
| 		'slow_only' => isset($_ENV['CLOCKWORK_REQUESTS_SLOW_ONLY']) ? $_ENV['CLOCKWORK_REQUESTS_SLOW_ONLY'] : false, | ||||
|  | ||||
| 		// Sample the collected requests (eg. set to 100 to collect only 1 in 100 requests) | ||||
| 		'sample' => isset($_ENV['CLOCKWORK_REQUESTS_SAMPLE']) ? $_ENV['CLOCKWORK_REQUESTS_SAMPLE'] : false, | ||||
|  | ||||
| 		// List of URIs that should not be collected | ||||
| 		'except' => [ | ||||
| 			// '/api/.*' | ||||
| 		], | ||||
|  | ||||
| 		// List of URIs that should be collected, any other URI will not be collected if not empty | ||||
| 		'only' => [ | ||||
| 			// '/api/.*' | ||||
| 		], | ||||
|  | ||||
| 		// Don't collect OPTIONS requests, mostly used in the CSRF pre-flight requests and are rarely of interest | ||||
| 		'except_preflight' => isset($_ENV['CLOCKWORK_REQUESTS_EXCEPT_PREFLIGHT']) ? $_ENV['CLOCKWORK_REQUESTS_EXCEPT_PREFLIGHT'] : true | ||||
| 	], | ||||
|  | ||||
| 	/* | ||||
| 	|------------------------------------------------------------------------------------------------------------------ | ||||
| 	| Enable data collection when Clockwork is disabled | ||||
| 	|------------------------------------------------------------------------------------------------------------------ | ||||
| 	| | ||||
| 	| You can enable this setting to collect data even when Clockwork is disabled. Eg. for future analysis. | ||||
| 	| | ||||
| 	*/ | ||||
|  | ||||
| 	'collect_data_always' => isset($_ENV['CLOCKWORK_COLLECT_DATA_ALWAYS']) ? $_ENV['CLOCKWORK_COLLECT_DATA_ALWAYS'] : false, | ||||
|  | ||||
| 	/* | ||||
| 	|------------------------------------------------------------------------------------------------------------------ | ||||
| 	| Clockwork API URI | ||||
| 	|------------------------------------------------------------------------------------------------------------------ | ||||
| 	| | ||||
| 	| Path of the script calling returnRequest to return Clockwork metadata to the client app. See installation | ||||
| 	| instructions for details. | ||||
| 	| | ||||
| 	*/ | ||||
|  | ||||
| 	'api' => isset($_ENV['CLOCKWORK_API']) ? $_ENV['CLOCKWORK_API'] : '/__clockwork/', | ||||
|  | ||||
| 	/* | ||||
| 	|------------------------------------------------------------------------------------------------------------------ | ||||
| 	| Clockwork web UI | ||||
| 	|------------------------------------------------------------------------------------------------------------------ | ||||
| 	| | ||||
| 	| Clockwork comes bundled with a full Clockwork App accessible as a Web UI. Here you can enable and configure this | ||||
| 	| feature. | ||||
| 	| Clockwork::returnWeb api is used to expose the Web UI in your vanilla app, see the installation instructions for | ||||
| 	| details. | ||||
| 	| | ||||
| 	*/ | ||||
|  | ||||
| 	'web' => [ | ||||
| 		// Enable or disable the Web UI, set to the public uri where Clockwork Web UI is accessible | ||||
| 		'enable' => isset($_ENV['CLOCKWORK_WEB_ENABLE']) ? $_ENV['CLOCKWORK_WEB_ENABLE'] : true, | ||||
|  | ||||
| 		// Path where to install the Web UI assets, should be publicly accessible | ||||
| 		'path' => isset($_ENV['CLOCKWORK_WEB_PATH']) ? $_ENV['CLOCKWORK_WEB_PATH'] : __DIR__ . '/../../../../../public/vendor/clockwork', | ||||
|  | ||||
| 		// Public URI where the installed Web UI assets will be accessible | ||||
| 		'uri' => isset($_ENV['CLOCKWORK_WEB_URI']) ? $_ENV['CLOCKWORK_WEB_URI'] : '/vendor/clockwork' | ||||
| 	], | ||||
|  | ||||
| 	/* | ||||
| 	|------------------------------------------------------------------------------------------------------------------ | ||||
| 	| Metadata storage | ||||
| 	|------------------------------------------------------------------------------------------------------------------ | ||||
| 	| | ||||
| 	| Configure how is the metadata collected by Clockwork stored. Two options are available: | ||||
| 	|   - files - A simple fast storage implementation storing data in one-per-request files. | ||||
| 	|   - sql - Stores requests in a sql database. Supports MySQL, Postgresql, Sqlite and requires PDO. | ||||
| 	| | ||||
| 	*/ | ||||
|  | ||||
| 	'storage' => isset($_ENV['CLOCKWORK_STORAGE']) ? $_ENV['CLOCKWORK_STORAGE'] : 'files', | ||||
|  | ||||
| 	// Path where the Clockwork metadata is stored | ||||
| 	'storage_files_path' => isset($_ENV['CLOCKWORK_STORAGE_FILES_PATH']) ? $_ENV['CLOCKWORK_STORAGE_FILES_PATH'] : __DIR__ . '/../../../../../../clockwork', | ||||
|  | ||||
| 	// Compress the metadata files using gzip, trading a little bit of performance for lower disk usage | ||||
| 	'storage_files_compress' => isset($_ENV['CLOCKWORK_STORAGE_FILES_COMPRESS']) ? $_ENV['CLOCKWORK_STORAGE_FILES_COMPRESS'] : false, | ||||
|  | ||||
| 	// SQL database to use, can be a PDO connection string or a path to a sqlite file | ||||
| 	'storage_sql_database' => isset($_ENV['CLOCKWORK_STORAGE_SQL_DATABASE']) ? $_ENV['CLOCKWORK_STORAGE_SQL_DATABASE'] : 'sqlite:' . __DIR__ . '/../../../../../clockwork.sqlite', | ||||
| 	'storage_sql_username' => isset($_ENV['CLOCKWORK_STORAGE_SQL_USERNAME']) ? $_ENV['CLOCKWORK_STORAGE_SQL_USERNAME'] : null, | ||||
| 	'storage_sql_password' => isset($_ENV['CLOCKWORK_STORAGE_SQL_PASSWORD']) ? $_ENV['CLOCKWORK_STORAGE_SQL_PASSWORD'] : null, | ||||
|  | ||||
| 	// SQL table name to use, the table is automatically created and updated when needed | ||||
| 	'storage_sql_table' => isset($_ENV['CLOCKWORK_STORAGE_SQL_TABLE']) ? $_ENV['CLOCKWORK_STORAGE_SQL_TABLE'] : 'clockwork', | ||||
|  | ||||
| 	// Maximum lifetime of collected metadata in minutes, older requests will automatically be deleted, false to disable | ||||
| 	'storage_expiration' => isset($_ENV['CLOCKWORK_STORAGE_EXPIRATION']) ? $_ENV['CLOCKWORK_STORAGE_EXPIRATION'] : 60 * 24 * 7, | ||||
|  | ||||
| 	/* | ||||
| 	|------------------------------------------------------------------------------------------------------------------ | ||||
| 	| Authentication | ||||
| 	|------------------------------------------------------------------------------------------------------------------ | ||||
| 	| | ||||
| 	| Clockwork can be configured to require authentication before allowing access to the collected data. This might be | ||||
| 	| useful when the application is publicly accessible. Setting to true will enable a simple authentication with a | ||||
| 	| pre-configured password. You can also pass a class name of a custom implementation. | ||||
| 	| | ||||
| 	*/ | ||||
|  | ||||
| 	'authentication' => isset($_ENV['CLOCKWORK_AUTHENTICATION']) ? $_ENV['CLOCKWORK_AUTHENTICATION'] : false, | ||||
|  | ||||
| 	// Password for the simple authentication | ||||
| 	'authentication_password' => isset($_ENV['CLOCKWORK_AUTHENTICATION_PASSWORD']) ? $_ENV['CLOCKWORK_AUTHENTICATION_PASSWORD'] : 'VerySecretPassword', | ||||
|  | ||||
| 	/* | ||||
| 	|------------------------------------------------------------------------------------------------------------------ | ||||
| 	| Stack traces collection | ||||
| 	|------------------------------------------------------------------------------------------------------------------ | ||||
| 	| | ||||
| 	| Clockwork can collect stack traces for log messages and certain data like database queries. Here you can set | ||||
| 	| whether to collect stack traces, limit the number of collected frames and set further configuration. Collecting | ||||
| 	| long stack traces considerably increases metadata size. | ||||
| 	| | ||||
| 	*/ | ||||
|  | ||||
| 	'stack_traces' => [ | ||||
| 		// Enable or disable collecting of stack traces | ||||
| 		'enabled' => isset($_ENV['CLOCKWORK_STACK_TRACES_ENABLED']) ? $_ENV['CLOCKWORK_STACK_TRACES_ENABLED'] : true, | ||||
|  | ||||
| 		// Limit the number of frames to be collected | ||||
| 		'limit' => isset($_ENV['CLOCKWORK_STACK_TRACES_LIMIT']) ? $_ENV['CLOCKWORK_STACK_TRACES_LIMIT'] : 10, | ||||
|  | ||||
| 		// List of vendor names to skip when determining caller, common vendor are automatically added | ||||
| 		'skip_vendors' => [ | ||||
| 			// 'phpunit' | ||||
| 		], | ||||
|  | ||||
| 		// List of namespaces to skip when determining caller | ||||
| 		'skip_namespaces' => [ | ||||
| 			// 'Vendor' | ||||
| 		], | ||||
|  | ||||
| 		// List of class names to skip when determining caller | ||||
| 		'skip_classes' => [ | ||||
| 			// App\CustomLog::class | ||||
| 		] | ||||
| 	], | ||||
|  | ||||
| 	/* | ||||
| 	|------------------------------------------------------------------------------------------------------------------ | ||||
| 	| Serialization | ||||
| 	|------------------------------------------------------------------------------------------------------------------ | ||||
| 	| | ||||
| 	| Clockwork serializes the collected data to json for storage and transfer. Here you can configure certain aspects | ||||
| 	| of serialization. Serialization has a large effect on the cpu time and memory usage. | ||||
| 	| | ||||
| 	*/ | ||||
|  | ||||
| 	// Maximum depth of serialized multi-level arrays and objects | ||||
| 	'serialization_depth' => isset($_ENV['CLOCKWORK_SERIALIZATION_DEPTH']) ? $_ENV['CLOCKWORK_SERIALIZATION_DEPTH'] : 10, | ||||
|  | ||||
| 	// A list of classes that will never be serialized (eg. a common service container class) | ||||
| 	'serialization_blackbox' => [ | ||||
| 		// \App\ServiceContainer::class | ||||
| 	], | ||||
|  | ||||
| 	/* | ||||
| 	|------------------------------------------------------------------------------------------------------------------ | ||||
| 	| Register helpers | ||||
| 	|------------------------------------------------------------------------------------------------------------------ | ||||
| 	| | ||||
| 	| Clockwork comes with a "clock" global helper function. You can use this helper to quickly log something and to | ||||
| 	| access the Clockwork instance. | ||||
| 	| | ||||
| 	*/ | ||||
|  | ||||
| 	'register_helpers' => isset($_ENV['CLOCKWORK_REGISTER_HELPERS']) ? $_ENV['CLOCKWORK_REGISTER_HELPERS'] : false, | ||||
|  | ||||
| 	/* | ||||
| 	|------------------------------------------------------------------------------------------------------------------ | ||||
| 	| Send Headers for AJAX request | ||||
| 	|------------------------------------------------------------------------------------------------------------------ | ||||
| 	| | ||||
| 	| When trying to collect data the AJAX method can sometimes fail if it is missing required headers. For example, an | ||||
| 	| API might require a version number using Accept headers to route the HTTP request to the correct codebase. | ||||
| 	| | ||||
| 	*/ | ||||
|  | ||||
| 	'headers' => [ | ||||
| 		// 'Accept' => 'application/vnd.com.whatever.v1+json', | ||||
| 	], | ||||
| 	/* | ||||
| 	|------------------------------------------------------------------------------------------------------------------ | ||||
| 	| Server-Timing | ||||
| 	|------------------------------------------------------------------------------------------------------------------ | ||||
| 	| | ||||
| 	| Clockwork supports the W3C Server Timing specification, which allows for collecting a simple performance metrics | ||||
| 	| in a cross-browser way. Eg. in Chrome, your app, database and timeline event timings will be shown in the Dev | ||||
| 	| Tools network tab. This setting specifies the max number of timeline events that will be sent. Setting to false | ||||
| 	| will disable the feature. | ||||
| 	| | ||||
| 	*/ | ||||
|  | ||||
| 	'server_timing' => isset($_ENV['CLOCKWORK_SERVER_TIMING']) ? $_ENV['CLOCKWORK_SERVER_TIMING'] : 10 | ||||
|  | ||||
| ]; | ||||
							
								
								
									
										19
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Vanilla/helpers.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Vanilla/helpers.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | ||||
| <?php | ||||
|  | ||||
| use Clockwork\Support\Vanilla\Clockwork; | ||||
|  | ||||
| if (! function_exists('clock')) { | ||||
| 	// Log a message to Clockwork, returns Clockwork instance when called with no arguments, first argument otherwise | ||||
| 	function clock(...$arguments) | ||||
| 	{ | ||||
| 		if (empty($arguments)) { | ||||
| 			return Clockwork::instance(); | ||||
| 		} | ||||
|  | ||||
| 		foreach ($arguments as $argument) { | ||||
| 			Clockwork::debug($argument); | ||||
| 		} | ||||
|  | ||||
| 		return reset($arguments); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										36
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Vanilla/iframe.html.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								vendor/itsgoingd/clockwork/Clockwork/Support/Vanilla/iframe.html.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,36 @@ | ||||
| <!doctype html> | ||||
| <html> | ||||
| <head> | ||||
| 	<meta charset="utf-8"> | ||||
| 	<meta name="viewport" content="width=device-width,initial-scale=1"> | ||||
| 	<title>Clockwork</title> | ||||
|  | ||||
| 	<link rel="icon" type="image/png" sizes="32x32" href="<?= $asset('img/icons/favicon-32x32.png') ?>"> | ||||
| 	<link rel="icon" type="image/png" sizes="16x16" href="<?= $asset('img/icons/favicon-16x16.png') ?>"> | ||||
| 	<link rel="manifest" href="<?= $asset('manifest.json') ?>"> | ||||
| 	<meta name="theme-color" content="#4DBA87"> | ||||
| 	<meta name="apple-mobile-web-app-capable" content="no"> | ||||
| 	<meta name="apple-mobile-web-app-status-bar-style" content="default"> | ||||
| 	<meta name="apple-mobile-web-app-title" content="Clockwork"> | ||||
| 	<link rel="apple-touch-icon" href="<?= $asset('img/icons/apple-touch-icon-152x152.png') ?>"> | ||||
| 	<link rel="mask-icon" href="<?= $asset('img/icons/safari-pinned-tab.svg') ?>" color="#4DBA87"> | ||||
| 	<meta name="msapplication-TileImage" content="<?= $asset('img/icons/msapplication-icon-144x144.png') ?>"> | ||||
| 	<meta name="msapplication-TileColor" content="#000000"> | ||||
|  | ||||
| 	<style> | ||||
| 		iframe { position: fixed; top: 0; left: 0; height: 100%; width: 100%; border: 0; } | ||||
| 	</style> | ||||
| </head> | ||||
| <body> | ||||
| 	<iframe src="<?= $url ?>"></iframe> | ||||
|  | ||||
| 	<script> | ||||
| 		let clockworkData = JSON.parse(localStorage.getItem('clockwork') || '{}') | ||||
|  | ||||
| 		clockworkData.settings = clockworkData.settings || {} | ||||
| 		clockworkData.settings.global = clockworkData.settings.global || {} | ||||
| 		clockworkData.settings.global.metadataPath = '<?= $metadataPath ?>' | ||||
|  | ||||
| 		localStorage.setItem('clockwork', JSON.stringify(clockworkData)) | ||||
| 	</script> | ||||
| </html> | ||||
							
								
								
									
										36
									
								
								vendor/itsgoingd/clockwork/Clockwork/Web/Web.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								vendor/itsgoingd/clockwork/Clockwork/Web/Web.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,36 @@ | ||||
| <?php namespace Clockwork\Web; | ||||
|  | ||||
| // Helper class for serving app assets | ||||
| class Web | ||||
| { | ||||
| 	// Return the absolute path and a mime type of an asset, protects from accessing files outside Clockwork public dir | ||||
| 	public function asset($path) | ||||
| 	{ | ||||
| 		$path = $this->resolveAssetPath($path); | ||||
|  | ||||
| 		if (! $path) return; | ||||
|  | ||||
| 		switch (pathinfo($path, PATHINFO_EXTENSION)) { | ||||
| 			case 'css': $mime = 'text/css'; break; | ||||
| 			case 'js': $mime = 'application/javascript'; break; | ||||
| 			case 'json': $mime = 'application/json'; break; | ||||
| 			case 'png': $mime = 'image/png'; break; | ||||
| 			default: $mime = 'text/html'; break; | ||||
| 		} | ||||
|  | ||||
| 		return [ | ||||
| 			'path' => $path, | ||||
| 			'mime' => $mime | ||||
| 		]; | ||||
| 	} | ||||
|  | ||||
| 	// Resolves absolute path of the asset, protects from accessing files outside Clockwork public dir | ||||
| 	protected function resolveAssetPath($path) | ||||
| 	{ | ||||
| 		$publicPath = realpath(__DIR__ . '/public'); | ||||
|  | ||||
| 		$path = realpath("$publicPath/{$path}"); | ||||
|  | ||||
| 		return strpos($path, $publicPath) === 0 ? $path : false; | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										1
									
								
								vendor/itsgoingd/clockwork/Clockwork/Web/public/css/app.515e4027.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								vendor/itsgoingd/clockwork/Clockwork/Web/public/css/app.515e4027.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								vendor/itsgoingd/clockwork/Clockwork/Web/public/img/appearance-auto-icon.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								vendor/itsgoingd/clockwork/Clockwork/Web/public/img/appearance-auto-icon.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 964 B | 
							
								
								
									
										
											BIN
										
									
								
								vendor/itsgoingd/clockwork/Clockwork/Web/public/img/appearance-dark-icon.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								vendor/itsgoingd/clockwork/Clockwork/Web/public/img/appearance-dark-icon.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 975 B | 
							
								
								
									
										
											BIN
										
									
								
								vendor/itsgoingd/clockwork/Clockwork/Web/public/img/appearance-light-icon.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								vendor/itsgoingd/clockwork/Clockwork/Web/public/img/appearance-light-icon.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 918 B | 
							
								
								
									
										
											BIN
										
									
								
								vendor/itsgoingd/clockwork/Clockwork/Web/public/img/icons/apple-touch-icon-120x120.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								vendor/itsgoingd/clockwork/Clockwork/Web/public/img/icons/apple-touch-icon-120x120.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 6.9 KiB | 
							
								
								
									
										
											BIN
										
									
								
								vendor/itsgoingd/clockwork/Clockwork/Web/public/img/icons/apple-touch-icon-152x152.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								vendor/itsgoingd/clockwork/Clockwork/Web/public/img/icons/apple-touch-icon-152x152.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 9.4 KiB | 
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user
	 noor
					noor