clock-work
This commit is contained in:
25
app/Providers/QueryListenProvider.php
Normal file
25
app/Providers/QueryListenProvider.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use Clockwork\Support\Laravel\ClockworkMiddleware;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
class QueryListenProvider extends ServiceProvider
|
||||
{
|
||||
|
||||
/**
|
||||
* Bootstrap services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
DB::listen(function ($query) {
|
||||
\Clockwork::info($query->sql, [$query->time]);
|
||||
});
|
||||
|
||||
$this->app['router']->aliasMiddleware('clockwork', ClockworkMiddleware::class);
|
||||
}
|
||||
}
|
@@ -35,6 +35,10 @@
|
||||
{
|
||||
"type": "vcs",
|
||||
"url": "https://github.com/sandesh556/pdf-laravel5.git"
|
||||
},
|
||||
{
|
||||
"type": "vcs",
|
||||
"url": "https://github.com/ladybirdweb/clockwork.git"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
@@ -70,7 +74,8 @@
|
||||
"unisharp/laravel-ckeditor": "dev-master",
|
||||
"thomaswelton/laravel-gravatar": "dev-master",
|
||||
"symfony/mailgun-mailer": "^6.2",
|
||||
"symfony/http-client": "^6.2"
|
||||
"symfony/http-client": "^6.2",
|
||||
"itsgoingd/clockwork": "dev-master"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^9.5.10",
|
||||
@@ -81,9 +86,10 @@
|
||||
"nunomaduro/collision": "^6.3",
|
||||
"mockery/mockery": "^1.4.4",
|
||||
"fakerphp/faker": "^1.9.1",
|
||||
"barryvdh/laravel-debugbar": "^3.7",
|
||||
"barryvdh/laravel-debugbar": "^3.8",
|
||||
"spatie/laravel-ignition": "^1.4",
|
||||
"laravel/pint": "^1.4"
|
||||
"laravel/pint": "^1.4",
|
||||
"beyondcode/laravel-query-detector": "^1.7"
|
||||
},
|
||||
"autoload": {
|
||||
"classmap": [
|
||||
|
158
composer.lock
generated
158
composer.lock
generated
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "de4b83b369c705c49ee38aa8f3984384",
|
||||
"content-hash": "ec936e44c27edb7acd9fdd54c2266a10",
|
||||
"packages": [
|
||||
{
|
||||
"name": "aws/aws-crt-php",
|
||||
@@ -2558,6 +2558,73 @@
|
||||
],
|
||||
"time": "2022-05-21T17:30:32+00:00"
|
||||
},
|
||||
{
|
||||
"name": "itsgoingd/clockwork",
|
||||
"version": "dev-master",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/ladybirdweb/clockwork.git",
|
||||
"reference": "e29be8125ce999947d60986dcd8e606c04c012ab"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/ladybirdweb/clockwork/zipball/e29be8125ce999947d60986dcd8e606c04c012ab",
|
||||
"reference": "e29be8125ce999947d60986dcd8e606c04c012ab",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"php": ">=5.6"
|
||||
},
|
||||
"default-branch": true,
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"Clockwork\\Support\\Laravel\\ClockworkServiceProvider"
|
||||
],
|
||||
"aliases": {
|
||||
"Clockwork": "Clockwork\\Support\\Laravel\\Facade"
|
||||
}
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Clockwork\\": "Clockwork/"
|
||||
}
|
||||
},
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "itsgoingd",
|
||||
"email": "itsgoingd@luzer.sk",
|
||||
"homepage": "https://twitter.com/itsgoingd"
|
||||
}
|
||||
],
|
||||
"description": "php dev tools in your browser",
|
||||
"homepage": "https://underground.works/clockwork",
|
||||
"keywords": [
|
||||
"debugging",
|
||||
"devtools",
|
||||
"laravel",
|
||||
"logging",
|
||||
"lumen",
|
||||
"profiling",
|
||||
"slim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/ladybirdweb/clockwork/tree/master"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/itsgoingd"
|
||||
}
|
||||
],
|
||||
"time": "2023-04-14T10:39:18+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/framework",
|
||||
"version": "v9.52.0",
|
||||
@@ -10063,30 +10130,30 @@
|
||||
"packages-dev": [
|
||||
{
|
||||
"name": "barryvdh/laravel-debugbar",
|
||||
"version": "v3.7.0",
|
||||
"version": "v3.8.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/barryvdh/laravel-debugbar.git",
|
||||
"reference": "3372ed65e6d2039d663ed19aa699956f9d346271"
|
||||
"reference": "eb01216141e62433178c52b0cbdb785b45bae871"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/3372ed65e6d2039d663ed19aa699956f9d346271",
|
||||
"reference": "3372ed65e6d2039d663ed19aa699956f9d346271",
|
||||
"url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/eb01216141e62433178c52b0cbdb785b45bae871",
|
||||
"reference": "eb01216141e62433178c52b0cbdb785b45bae871",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"illuminate/routing": "^7|^8|^9",
|
||||
"illuminate/session": "^7|^8|^9",
|
||||
"illuminate/support": "^7|^8|^9",
|
||||
"illuminate/routing": "^9|^10",
|
||||
"illuminate/session": "^9|^10",
|
||||
"illuminate/support": "^9|^10",
|
||||
"maximebf/debugbar": "^1.17.2",
|
||||
"php": ">=7.2.5",
|
||||
"symfony/finder": "^5|^6"
|
||||
"php": "^8.0",
|
||||
"symfony/finder": "^6"
|
||||
},
|
||||
"require-dev": {
|
||||
"mockery/mockery": "^1.3.3",
|
||||
"orchestra/testbench-dusk": "^5|^6|^7",
|
||||
"phpunit/phpunit": "^8.5|^9.0",
|
||||
"orchestra/testbench-dusk": "^5|^6|^7|^8",
|
||||
"phpunit/phpunit": "^8.5.30|^9.0",
|
||||
"squizlabs/php_codesniffer": "^3.5"
|
||||
},
|
||||
"type": "library",
|
||||
@@ -10131,7 +10198,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/barryvdh/laravel-debugbar/issues",
|
||||
"source": "https://github.com/barryvdh/laravel-debugbar/tree/v3.7.0"
|
||||
"source": "https://github.com/barryvdh/laravel-debugbar/tree/v3.8.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -10143,7 +10210,67 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2022-07-11T09:26:42+00:00"
|
||||
"time": "2023-02-04T15:47:28+00:00"
|
||||
},
|
||||
{
|
||||
"name": "beyondcode/laravel-query-detector",
|
||||
"version": "1.7.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/beyondcode/laravel-query-detector.git",
|
||||
"reference": "40c7e168fcf7eeb80d8e96f7922e05ab194269c8"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/beyondcode/laravel-query-detector/zipball/40c7e168fcf7eeb80d8e96f7922e05ab194269c8",
|
||||
"reference": "40c7e168fcf7eeb80d8e96f7922e05ab194269c8",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"illuminate/support": "^5.5 || ^6.0 || ^7.0 || ^8.0 || ^9.0|^10.0",
|
||||
"php": "^7.1 || ^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"laravel/legacy-factories": "^1.0",
|
||||
"orchestra/testbench": "^3.0 || ^4.0 || ^5.0 || ^6.0|^8.0",
|
||||
"phpunit/phpunit": "^7.0 || ^8.0 || ^9.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"BeyondCode\\QueryDetector\\QueryDetectorServiceProvider"
|
||||
]
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"BeyondCode\\QueryDetector\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Marcel Pociot",
|
||||
"email": "marcel@beyondco.de",
|
||||
"homepage": "https://beyondcode.de",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "Laravel N+1 Query Detector",
|
||||
"homepage": "https://github.com/beyondcode/laravel-query-detector",
|
||||
"keywords": [
|
||||
"beyondcode",
|
||||
"laravel-query-detector"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/beyondcode/laravel-query-detector/issues",
|
||||
"source": "https://github.com/beyondcode/laravel-query-detector/tree/1.7.0"
|
||||
},
|
||||
"time": "2023-02-15T10:37:22+00:00"
|
||||
},
|
||||
{
|
||||
"name": "doctrine/instantiator",
|
||||
@@ -13321,7 +13448,8 @@
|
||||
"chumper/zipper": 20,
|
||||
"brozot/laravel-fcm": 20,
|
||||
"unisharp/laravel-ckeditor": 20,
|
||||
"thomaswelton/laravel-gravatar": 20
|
||||
"thomaswelton/laravel-gravatar": 20,
|
||||
"itsgoingd/clockwork": 20
|
||||
},
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
|
@@ -186,6 +186,8 @@ return [
|
||||
\Yajra\DataTables\DataTablesServiceProvider::class,
|
||||
\Bugsnag\BugsnagLaravel\BugsnagServiceProvider::class,
|
||||
Maatwebsite\Excel\ExcelServiceProvider::class,
|
||||
App\Providers\QueryListenProvider::class
|
||||
|
||||
],
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
|
417
config/clockwork.php
Normal file
417
config/clockwork.php
Normal file
@@ -0,0 +1,417 @@
|
||||
<?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),
|
||||
'middlewares'=>['web','auth','roles'],
|
||||
|
||||
/*
|
||||
|------------------------------------------------------------------------------------------------------------------
|
||||
| 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)
|
||||
|
||||
];
|
69
config/querydetector.php
Normal file
69
config/querydetector.php
Normal file
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
/*
|
||||
* Enable or disable the query detection.
|
||||
* If this is set to "null", the app.debug config value will be used.
|
||||
*/
|
||||
'enabled' => env('QUERY_DETECTOR_ENABLED', null),
|
||||
|
||||
/*
|
||||
* Threshold level for the N+1 query detection. If a relation query will be
|
||||
* executed more then this amount, the detector will notify you about it.
|
||||
*/
|
||||
'threshold' => (int) env('QUERY_DETECTOR_THRESHOLD', 1),
|
||||
|
||||
/*
|
||||
* Here you can whitelist model relations.
|
||||
*
|
||||
* Right now, you need to define the model relation both as the class name and the attribute name on the model.
|
||||
* So if an "Author" model would have a "posts" relation that points to a "Post" class, you need to add both
|
||||
* the "posts" attribute and the "Post::class", since the relation can get resolved in multiple ways.
|
||||
*/
|
||||
'except' => [
|
||||
//Author::class => [
|
||||
// Post::class,
|
||||
// 'posts',
|
||||
//]
|
||||
],
|
||||
|
||||
/*
|
||||
* Here you can set a specific log channel to write to
|
||||
* in case you are trying to isolate queries or have a lot
|
||||
* going on in the laravel.log. Defaults to laravel.log though.
|
||||
*/
|
||||
'log_channel' => env('QUERY_DETECTOR_LOG_CHANNEL', 'daily'),
|
||||
|
||||
/*
|
||||
* Define the output format that you want to use. Multiple classes are supported.
|
||||
* Available options are:
|
||||
*
|
||||
* Alert:
|
||||
* Displays an alert on the website
|
||||
* \BeyondCode\QueryDetector\Outputs\Alert::class
|
||||
*
|
||||
* Console:
|
||||
* Writes the N+1 queries into your browsers console log
|
||||
* \BeyondCode\QueryDetector\Outputs\Console::class
|
||||
*
|
||||
* Clockwork: (make sure you have the itsgoingd/clockwork package installed)
|
||||
* Writes the N+1 queries warnings to Clockwork log
|
||||
* \BeyondCode\QueryDetector\Outputs\Clockwork::class
|
||||
*
|
||||
* Debugbar: (make sure you have the barryvdh/laravel-debugbar package installed)
|
||||
* Writes the N+1 queries into a custom messages collector of Debugbar
|
||||
* \BeyondCode\QueryDetector\Outputs\Debugbar::class
|
||||
*
|
||||
* JSON:
|
||||
* Writes the N+1 queries into the response body of your JSON responses
|
||||
* \BeyondCode\QueryDetector\Outputs\Json::class
|
||||
*
|
||||
* Log:
|
||||
* Writes the N+1 queries into the Laravel.log file
|
||||
* \BeyondCode\QueryDetector\Outputs\Log::class
|
||||
*/
|
||||
'output' => [
|
||||
\BeyondCode\QueryDetector\Outputs\Alert::class,
|
||||
\BeyondCode\QueryDetector\Outputs\Log::class,
|
||||
]
|
||||
];
|
@@ -1415,6 +1415,7 @@ return [
|
||||
*/
|
||||
'error-debug' => 'Error logs and debugging',
|
||||
'debug-options' => 'Debugging options',
|
||||
'clock-work' =>'Clock work',
|
||||
'view-logs' => 'View error logs',
|
||||
'not-authorised-error-debug' => 'You are not authorised to access the URL',
|
||||
'error-debug-settings' => 'Error and debugging settings',
|
||||
|
@@ -540,13 +540,27 @@
|
||||
</a>
|
||||
</div>
|
||||
<div class="text-center text-sm">{!! Lang::get('lang.debug-options') !!}</div>
|
||||
</div></div>
|
||||
@if(Config::get('app.debug'))
|
||||
<div class="col-md-2 col-sm-6">
|
||||
<div class="settingiconblue">
|
||||
<div class="settingdivblue">
|
||||
<a href="{{ url('clockwork/app') }}">
|
||||
<span class="fa-stack fa-2x">
|
||||
<i class="fa fa-server fa-stack-1x"></i>
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="text-center text-sm">{!!Lang::get('lang.clock-work')!!}</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.row -->
|
||||
</div>
|
||||
<!-- ./box-body -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="card card-light">
|
||||
|
18
vendor/barryvdh/laravel-debugbar/composer.json
vendored
18
vendor/barryvdh/laravel-debugbar/composer.json
vendored
@@ -10,17 +10,17 @@
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=7.2.5",
|
||||
"php": "^8.0",
|
||||
"maximebf/debugbar": "^1.17.2",
|
||||
"illuminate/routing": "^7|^8|^9",
|
||||
"illuminate/session": "^7|^8|^9",
|
||||
"illuminate/support": "^7|^8|^9",
|
||||
"symfony/finder": "^5|^6"
|
||||
"illuminate/routing": "^9|^10",
|
||||
"illuminate/session": "^9|^10",
|
||||
"illuminate/support": "^9|^10",
|
||||
"symfony/finder": "^6"
|
||||
},
|
||||
"require-dev": {
|
||||
"mockery/mockery": "^1.3.3",
|
||||
"orchestra/testbench-dusk": "^5|^6|^7",
|
||||
"phpunit/phpunit": "^8.5|^9.0",
|
||||
"orchestra/testbench-dusk": "^5|^6|^7|^8",
|
||||
"phpunit/phpunit": "^8.5.30|^9.0",
|
||||
"squizlabs/php_codesniffer": "^3.5"
|
||||
},
|
||||
"autoload": {
|
||||
@@ -52,8 +52,8 @@
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"check-style": "phpcs -p --standard=PSR12 config/ src/ tests/",
|
||||
"fix-style": "phpcbf -p --standard=PSR12 config/ src/ tests/",
|
||||
"check-style": "phpcs -p --standard=PSR12 config/ src/ tests/ --ignore=src/Resources/* ",
|
||||
"fix-style": "phpcbf -p --standard=PSR12 config/ src/ tests/ --ignore=src/Resources*",
|
||||
"test": "phpunit"
|
||||
}
|
||||
}
|
||||
|
@@ -92,7 +92,7 @@ return [
|
||||
| Vendor files are included by default, but can be set to false.
|
||||
| This can also be set to 'js' or 'css', to only include javascript or css vendor files.
|
||||
| Vendor files are for css: font-awesome (including fonts) and highlight.js (css files)
|
||||
| and for js: jquery and and highlight.js
|
||||
| and for js: jquery and highlight.js
|
||||
| So if you want syntax highlighting, set it to true.
|
||||
| jQuery is set to not conflict with existing jQuery scripts.
|
||||
|
|
||||
@@ -198,7 +198,8 @@ return [
|
||||
'types' => ['SELECT'], // Deprecated setting, is always only SELECT
|
||||
],
|
||||
'hints' => false, // Show hints for common mistakes
|
||||
'show_copy' => false, // Show copy button next to the query
|
||||
'show_copy' => false, // Show copy button next to the query,
|
||||
'slow_threshold' => false, // Only track queries that last longer than this time in ms
|
||||
],
|
||||
'mail' => [
|
||||
'full_log' => false,
|
||||
@@ -206,6 +207,7 @@ return [
|
||||
'views' => [
|
||||
'timeline' => false, // Add the views to the timeline (Experimental)
|
||||
'data' => false, //Note: Can slow down the application, because the data can be quite large..
|
||||
'exclude_paths' => [], // Add the paths which you don't want to appear in the views
|
||||
],
|
||||
'route' => [
|
||||
'label' => true, // show complete route on bar
|
||||
|
2
vendor/barryvdh/laravel-debugbar/readme.md
vendored
2
vendor/barryvdh/laravel-debugbar/readme.md
vendored
@@ -1,4 +1,4 @@
|
||||
## Laravel Debugbar
|
||||
## Debugbar for Laravel
|
||||

|
||||
[](http://choosealicense.com/licenses/mit/)
|
||||
[](https://packagist.org/packages/barryvdh/laravel-debugbar)
|
||||
|
@@ -134,7 +134,7 @@ class QueryCollector extends PDOCollector
|
||||
$pdo = null;
|
||||
try {
|
||||
$pdo = $connection->getPdo();
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
// ignore error for non-pdo laravel drivers
|
||||
}
|
||||
$bindings = $connection->prepareBindings($bindings);
|
||||
@@ -511,6 +511,41 @@ class QueryCollector extends PDOCollector
|
||||
'type' => 'explain',
|
||||
];
|
||||
}
|
||||
} elseif ($query['driver'] === 'sqlite') {
|
||||
$vmi = '<table style="margin:-5px -11px !important;width: 100% !important">';
|
||||
$vmi .= "<thead><tr>
|
||||
<td>Address</td>
|
||||
<td>Opcode</td>
|
||||
<td>P1</td>
|
||||
<td>P2</td>
|
||||
<td>P3</td>
|
||||
<td>P4</td>
|
||||
<td>P5</td>
|
||||
<td>Comment</td>
|
||||
</tr></thead>";
|
||||
|
||||
foreach ($query['explain'] as $explain) {
|
||||
$vmi .= "<tr>
|
||||
<td>{$explain->addr}</td>
|
||||
<td>{$explain->opcode}</td>
|
||||
<td>{$explain->p1}</td>
|
||||
<td>{$explain->p2}</td>
|
||||
<td>{$explain->p3}</td>
|
||||
<td>{$explain->p4}</td>
|
||||
<td>{$explain->p5}</td>
|
||||
<td>{$explain->comment}</td>
|
||||
</tr>";
|
||||
}
|
||||
|
||||
$vmi .= '</table>';
|
||||
|
||||
$statements[] = [
|
||||
'sql' => " - EXPLAIN:",
|
||||
'type' => 'explain',
|
||||
'params' => [
|
||||
'Virtual Machine Instructions' => $vmi,
|
||||
]
|
||||
];
|
||||
} else {
|
||||
foreach ($query['explain'] as $explain) {
|
||||
$statements[] = [
|
||||
|
@@ -5,24 +5,51 @@ namespace Barryvdh\Debugbar\DataCollector;
|
||||
use Barryvdh\Debugbar\DataFormatter\SimpleFormatter;
|
||||
use DebugBar\Bridge\Twig\TwigCollector;
|
||||
use Illuminate\View\View;
|
||||
use Symfony\Component\VarDumper\Cloner\VarCloner;
|
||||
use InvalidArgumentException;
|
||||
|
||||
class ViewCollector extends TwigCollector
|
||||
{
|
||||
protected $name;
|
||||
protected $templates = [];
|
||||
protected $collect_data;
|
||||
protected $exclude_paths;
|
||||
|
||||
/**
|
||||
* A list of known editor strings.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $editors = [
|
||||
'sublime' => 'subl://open?url=file://%file&line=%line',
|
||||
'textmate' => 'txmt://open?url=file://%file&line=%line',
|
||||
'emacs' => 'emacs://open?url=file://%file&line=%line',
|
||||
'macvim' => 'mvim://open/?url=file://%file&line=%line',
|
||||
'phpstorm' => 'phpstorm://open?file=%file&line=%line',
|
||||
'idea' => 'idea://open?file=%file&line=%line',
|
||||
'vscode' => 'vscode://file/%file:%line',
|
||||
'vscode-insiders' => 'vscode-insiders://file/%file:%line',
|
||||
'vscode-remote' => 'vscode://vscode-remote/%file:%line',
|
||||
'vscode-insiders-remote' => 'vscode-insiders://vscode-remote/%file:%line',
|
||||
'vscodium' => 'vscodium://file/%file:%line',
|
||||
'nova' => 'nova://core/open/file?filename=%file&line=%line',
|
||||
'xdebug' => 'xdebug://%file@%line',
|
||||
'atom' => 'atom://core/open/file?filename=%file&line=%line',
|
||||
'espresso' => 'x-espresso://open?filepath=%file&lines=%line',
|
||||
'netbeans' => 'netbeans://open/?f=%file:%line',
|
||||
];
|
||||
|
||||
/**
|
||||
* Create a ViewCollector
|
||||
*
|
||||
* @param bool $collectData Collects view data when tru
|
||||
* @param string[] $excludePaths Paths to exclude from collection
|
||||
*/
|
||||
public function __construct($collectData = true)
|
||||
public function __construct($collectData = true, $excludePaths = [])
|
||||
{
|
||||
$this->setDataFormatter(new SimpleFormatter());
|
||||
$this->collect_data = $collectData;
|
||||
$this->name = 'views';
|
||||
$this->templates = [];
|
||||
$this->exclude_paths = $excludePaths;
|
||||
}
|
||||
|
||||
public function getName()
|
||||
@@ -35,7 +62,7 @@ class ViewCollector extends TwigCollector
|
||||
return [
|
||||
'views' => [
|
||||
'icon' => 'leaf',
|
||||
'widget' => 'PhpDebugBar.Widgets.TemplatesWidget',
|
||||
'widget' => 'PhpDebugBar.Widgets.LaravelViewTemplatesWidget',
|
||||
'map' => 'views',
|
||||
'default' => '[]'
|
||||
],
|
||||
@@ -46,6 +73,36 @@ class ViewCollector extends TwigCollector
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the editor href for a given file and line, if available.
|
||||
*
|
||||
* @param string $filePath
|
||||
* @param int $line
|
||||
*
|
||||
* @throws InvalidArgumentException If editor resolver does not return a string
|
||||
*
|
||||
* @return null|string
|
||||
*/
|
||||
protected function getEditorHref($filePath, $line)
|
||||
{
|
||||
if (empty(config('debugbar.editor'))) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (empty($this->editors[config('debugbar.editor')])) {
|
||||
throw new InvalidArgumentException(
|
||||
'Unknown editor identifier: ' . config('debugbar.editor') . '. Known editors:' .
|
||||
implode(', ', array_keys($this->editors))
|
||||
);
|
||||
}
|
||||
|
||||
$filePath = $this->replaceSitesPath($filePath);
|
||||
|
||||
$url = str_replace(['%file', '%line'], [$filePath, $line], $this->editors[config('debugbar.editor')]);
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a View instance to the Collector
|
||||
*
|
||||
@@ -71,6 +128,12 @@ class ViewCollector extends TwigCollector
|
||||
$path = '';
|
||||
}
|
||||
|
||||
foreach ($this->exclude_paths as $excludePath) {
|
||||
if (strpos($path, $excludePath) !== false) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$this->collect_data) {
|
||||
$params = array_keys($view->getData());
|
||||
} else {
|
||||
@@ -86,6 +149,7 @@ class ViewCollector extends TwigCollector
|
||||
'param_count' => count($params),
|
||||
'params' => $params,
|
||||
'type' => $type,
|
||||
'editorLink' => $this->getEditorHref($view->getPath(), 0),
|
||||
];
|
||||
|
||||
if ($this->getXdebugLink($path)) {
|
||||
@@ -104,4 +168,16 @@ class ViewCollector extends TwigCollector
|
||||
'templates' => $templates,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace remote path
|
||||
*
|
||||
* @param string $filePath
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function replaceSitesPath($filePath)
|
||||
{
|
||||
return str_replace(config('debugbar.remote_sites_path'), config('debugbar.local_sites_path'), $filePath);
|
||||
}
|
||||
}
|
||||
|
@@ -4,6 +4,7 @@ namespace Barryvdh\Debugbar\DataFormatter;
|
||||
|
||||
use DebugBar\DataFormatter\DataFormatter;
|
||||
|
||||
#[\AllowDynamicProperties]
|
||||
class QueryFormatter extends DataFormatter
|
||||
{
|
||||
/**
|
||||
|
@@ -9,6 +9,7 @@ use DebugBar\DataFormatter\DataFormatter;
|
||||
*
|
||||
* @see https://github.com/symfony/symfony/blob/v3.4.4/src/Symfony/Component/HttpKernel/DataCollector/Util/ValueExporter.php
|
||||
*/
|
||||
#[\AllowDynamicProperties]
|
||||
class SimpleFormatter extends DataFormatter
|
||||
{
|
||||
/**
|
||||
|
@@ -23,6 +23,7 @@ class JavascriptRenderer extends BaseJavascriptRenderer
|
||||
$this->cssVendors['fontawesome'] = __DIR__ . '/Resources/vendor/font-awesome/style.css';
|
||||
$this->jsFiles['laravel-sql'] = __DIR__ . '/Resources/sqlqueries/widget.js';
|
||||
$this->jsFiles['laravel-cache'] = __DIR__ . '/Resources/cache/widget.js';
|
||||
$this->jsFiles['laravel-view'] = __DIR__ . '/Resources/templates/widget.js';
|
||||
|
||||
$theme = config('debugbar.theme', 'auto');
|
||||
switch ($theme) {
|
||||
|
@@ -34,6 +34,7 @@ use DebugBar\DebugBar;
|
||||
use DebugBar\Storage\PdoStorage;
|
||||
use DebugBar\Storage\RedisStorage;
|
||||
use Exception;
|
||||
use Throwable;
|
||||
use Illuminate\Contracts\Foundation\Application;
|
||||
use Illuminate\Session\SessionManager;
|
||||
use Illuminate\Support\Str;
|
||||
@@ -204,7 +205,8 @@ class LaravelDebugbar extends DebugBar
|
||||
if ($this->shouldCollect('views', true) && isset($this->app['events'])) {
|
||||
try {
|
||||
$collectData = $this->app['config']->get('debugbar.options.views.data', true);
|
||||
$this->addCollector(new ViewCollector($collectData));
|
||||
$excludePaths = $this->app['config']->get('debugbar.options.views.exclude_paths', []);
|
||||
$this->addCollector(new ViewCollector($collectData, $excludePaths));
|
||||
$this->app['events']->listen(
|
||||
'composing:*',
|
||||
function ($view, $data = []) use ($debugbar) {
|
||||
@@ -258,7 +260,7 @@ class LaravelDebugbar extends DebugBar
|
||||
try {
|
||||
$logMessage = (string) $message;
|
||||
if (mb_check_encoding($logMessage, 'UTF-8')) {
|
||||
$logMessage .= (!empty($context) ? ' ' . json_encode($context) : '');
|
||||
$logMessage .= (!empty($context) ? ' ' . json_encode($context, JSON_PRETTY_PRINT) : '');
|
||||
} else {
|
||||
$logMessage = "[INVALID UTF-8 DATA]";
|
||||
}
|
||||
@@ -635,7 +637,7 @@ class LaravelDebugbar extends DebugBar
|
||||
/**
|
||||
* Adds an exception to be profiled in the debug bar
|
||||
*
|
||||
* @param Exception $e
|
||||
* @param Throwable $e
|
||||
*/
|
||||
public function addThrowable($e)
|
||||
{
|
||||
|
@@ -24,6 +24,8 @@ div.phpdebugbar-openhandler,
|
||||
div.phpdebugbar div.phpdebugbar-header > div > *,
|
||||
div.phpdebugbar ul.phpdebugbar-widgets-timeline li span.phpdebugbar-widgets-label,
|
||||
div.phpdebugbar ul.phpdebugbar-widgets-timeline li span.phpdebugbar-widgets-collector,
|
||||
div.phpdebugbar ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item,
|
||||
div.phpdebugbar ul.phpdebugbar-widgets-list li span.phpdebugbar-widgets-label,
|
||||
div.phpdebugbar code.phpdebugbar-widgets-sql span.hljs-keyword,
|
||||
div.phpdebugbar-openhandler .phpdebugbar-openhandler-header,
|
||||
div.phpdebugbar-openhandler .phpdebugbar-openhandler-header a {
|
||||
|
@@ -13,6 +13,7 @@ div.phpdebugbar {
|
||||
|
||||
div.phpdebugbar * {
|
||||
direction: ltr;
|
||||
font-size: initial;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
|
98
vendor/barryvdh/laravel-debugbar/src/Resources/templates/widget.js
vendored
Normal file
98
vendor/barryvdh/laravel-debugbar/src/Resources/templates/widget.js
vendored
Normal file
@@ -0,0 +1,98 @@
|
||||
(function($) {
|
||||
|
||||
var csscls = PhpDebugBar.utils.makecsscls('phpdebugbar-widgets-');
|
||||
|
||||
/**
|
||||
* Widget for the displaying templates data
|
||||
*
|
||||
* Options:
|
||||
* - data
|
||||
*/
|
||||
var TemplatesWidget = PhpDebugBar.Widgets.LaravelViewTemplatesWidget = PhpDebugBar.Widget.extend({
|
||||
|
||||
className: csscls('templates'),
|
||||
|
||||
render: function() {
|
||||
this.$status = $('<div />').addClass(csscls('status')).appendTo(this.$el);
|
||||
|
||||
this.$list = new PhpDebugBar.Widgets.ListWidget({ itemRenderer: function(li, tpl) {
|
||||
$('<span />').addClass(csscls('name')).text(tpl.name).appendTo(li);
|
||||
|
||||
if (typeof tpl.editorLink !== 'undefined' && tpl.editorLink !== null) {
|
||||
$('<a href="' + tpl.editorLink + '"></a>')
|
||||
.addClass(csscls('editor-link'))
|
||||
.on('click', function (event) {
|
||||
event.stopPropagation();
|
||||
})
|
||||
.appendTo(li);
|
||||
}
|
||||
if (typeof tpl.xdebug_link !== 'undefined' && tpl.xdebug_link !== null) {
|
||||
if (tpl.xdebug_link.ajax) {
|
||||
$('<a title="' + tpl.xdebug_link.url + '"></a>').on('click', function () {
|
||||
$.ajax(tpl.xdebug_link.url);
|
||||
}).addClass(csscls('editor-link')).appendTo(li);
|
||||
} else {
|
||||
$('<a href="' + tpl.xdebug_link.url + '"></a>').addClass(csscls('editor-link')).appendTo(li);
|
||||
}
|
||||
}
|
||||
if (tpl.render_time_str) {
|
||||
$('<span title="Render time" />').addClass(csscls('render-time')).text(tpl.render_time_str).appendTo(li);
|
||||
}
|
||||
if (tpl.memory_str) {
|
||||
$('<span title="Memory usage" />').addClass(csscls('memory')).text(tpl.memory_str).appendTo(li);
|
||||
}
|
||||
if (typeof(tpl.param_count) != 'undefined') {
|
||||
$('<span title="Parameter count" />').addClass(csscls('param-count')).text(tpl.param_count).appendTo(li);
|
||||
}
|
||||
if (typeof(tpl.type) != 'undefined' && tpl.type) {
|
||||
$('<span title="Type" />').addClass(csscls('type')).text(tpl.type).appendTo(li);
|
||||
}
|
||||
if (tpl.params && !$.isEmptyObject(tpl.params)) {
|
||||
var table = $('<table><tr><th colspan="2">Params</th></tr></table>').addClass(csscls('params')).appendTo(li);
|
||||
for (var key in tpl.params) {
|
||||
if (typeof tpl.params[key] !== 'function') {
|
||||
table.append('<tr><td class="' + csscls('name') + '">' + key + '</td><td class="' + csscls('value') +
|
||||
'"><pre><code>' + tpl.params[key] + '</code></pre></td></tr>');
|
||||
}
|
||||
}
|
||||
li.css('cursor', 'pointer').click(function() {
|
||||
if (table.is(':visible')) {
|
||||
table.hide();
|
||||
} else {
|
||||
table.show();
|
||||
}
|
||||
});
|
||||
}
|
||||
}});
|
||||
this.$list.$el.appendTo(this.$el);
|
||||
this.$callgraph = $('<div />').addClass(csscls('callgraph')).appendTo(this.$el);
|
||||
|
||||
this.bindAttr('data', function(data) {
|
||||
this.$list.set('data', data.templates);
|
||||
this.$status.empty();
|
||||
this.$callgraph.empty();
|
||||
|
||||
var sentence = data.sentence || "templates were rendered";
|
||||
$('<span />').text(data.nb_templates + " " + sentence).appendTo(this.$status);
|
||||
|
||||
if (data.accumulated_render_time_str) {
|
||||
this.$status.append($('<span title="Accumulated render time" />').addClass(csscls('render-time')).text(data.accumulated_render_time_str));
|
||||
}
|
||||
if (data.memory_usage_str) {
|
||||
this.$status.append($('<span title="Memory usage" />').addClass(csscls('memory')).text(data.memory_usage_str));
|
||||
}
|
||||
if (data.nb_blocks > 0) {
|
||||
$('<div />').text(data.nb_blocks + " blocks were rendered").appendTo(this.$status);
|
||||
}
|
||||
if (data.nb_macros > 0) {
|
||||
$('<div />').text(data.nb_macros + " macros were rendered").appendTo(this.$status);
|
||||
}
|
||||
if (typeof data.callgraph !== 'undefined') {
|
||||
this.$callgraph.html(data.callgraph);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
})(PhpDebugBar.$);
|
@@ -10,7 +10,7 @@ use Twig_SimpleFunction;
|
||||
/**
|
||||
* Access Laravels auth class in your Twig templates.
|
||||
*/
|
||||
class Debug extends Twig_Extension
|
||||
class Debug extends Extension
|
||||
{
|
||||
/**
|
||||
* @var \Barryvdh\Debugbar\LaravelDebugbar
|
||||
@@ -44,8 +44,15 @@ class Debug extends Twig_Extension
|
||||
*/
|
||||
public function getFunctions()
|
||||
{
|
||||
// Maintain compatibility with Twig 2 and 3.
|
||||
$simpleFunction = 'Twig_SimpleFunction';
|
||||
|
||||
if (!class_exists($simpleFunction)) {
|
||||
$simpleFunction = '\Twig\TwigFunction';
|
||||
}
|
||||
|
||||
return [
|
||||
new Twig_SimpleFunction(
|
||||
new $simpleFunction(
|
||||
'debug',
|
||||
[$this, 'debug'],
|
||||
['needs_context' => true, 'needs_environment' => true]
|
||||
@@ -57,10 +64,10 @@ class Debug extends Twig_Extension
|
||||
* Based on Twig_Extension_Debug / twig_var_dump
|
||||
* (c) 2011 Fabien Potencier
|
||||
*
|
||||
* @param Twig_Environment $env
|
||||
* @param \Twig_Environment|\Twig\Environment $env
|
||||
* @param $context
|
||||
*/
|
||||
public function debug(Twig_Environment $env, $context)
|
||||
public function debug($env, $context)
|
||||
{
|
||||
if (!$env->isDebug() || !$this->debugbar) {
|
||||
return;
|
||||
|
@@ -3,14 +3,11 @@
|
||||
namespace Barryvdh\Debugbar\Twig\Extension;
|
||||
|
||||
use DebugBar\DataFormatter\DataFormatterInterface;
|
||||
use Twig_Environment;
|
||||
use Twig_Extension;
|
||||
use Twig_SimpleFunction;
|
||||
|
||||
/**
|
||||
* Dump variables using the DataFormatter
|
||||
*/
|
||||
class Dump extends Twig_Extension
|
||||
class Dump extends Extension
|
||||
{
|
||||
/**
|
||||
* @var \DebugBar\DataFormatter\DataFormatter
|
||||
@@ -40,8 +37,15 @@ class Dump extends Twig_Extension
|
||||
*/
|
||||
public function getFunctions()
|
||||
{
|
||||
// Maintain compatibility with Twig 2 and 3.
|
||||
$simpleFunction = '\Twig_SimpleFunction';
|
||||
|
||||
if (!class_exists($simpleFunction)) {
|
||||
$simpleFunction = '\Twig\TwigFunction';
|
||||
}
|
||||
|
||||
return [
|
||||
new Twig_SimpleFunction(
|
||||
new $simpleFunction(
|
||||
'dump',
|
||||
[$this, 'dump'],
|
||||
['is_safe' => ['html'], 'needs_context' => true, 'needs_environment' => true]
|
||||
@@ -53,12 +57,12 @@ class Dump extends Twig_Extension
|
||||
* Based on Twig_Extension_Debug / twig_var_dump
|
||||
* (c) 2011 Fabien Potencier
|
||||
*
|
||||
* @param Twig_Environment $env
|
||||
* @param \Twig_Environment|\Twig\Environment $env
|
||||
* @param $context
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function dump(Twig_Environment $env, $context)
|
||||
public function dump($env, $context)
|
||||
{
|
||||
$output = '';
|
||||
|
||||
|
14
vendor/barryvdh/laravel-debugbar/src/Twig/Extension/Extension.php
vendored
Normal file
14
vendor/barryvdh/laravel-debugbar/src/Twig/Extension/Extension.php
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace Barryvdh\Debugbar\Twig\Extension;
|
||||
|
||||
// Maintain compatibility with Twig 2 and 3.
|
||||
if (class_exists('\Twig_Extension')) {
|
||||
abstract class Extension extends \Twig_Extension
|
||||
{
|
||||
}
|
||||
} else {
|
||||
abstract class Extension extends \Twig\Extension\AbstractExtension
|
||||
{
|
||||
}
|
||||
}
|
@@ -4,13 +4,12 @@ namespace Barryvdh\Debugbar\Twig\Extension;
|
||||
|
||||
use Barryvdh\Debugbar\Twig\TokenParser\StopwatchTokenParser;
|
||||
use Illuminate\Foundation\Application;
|
||||
use Twig_Extension;
|
||||
|
||||
/**
|
||||
* Access Laravels auth class in your Twig templates.
|
||||
* Based on Symfony\Bridge\Twig\Extension\StopwatchExtension
|
||||
*/
|
||||
class Stopwatch extends Twig_Extension
|
||||
class Stopwatch extends Extension
|
||||
{
|
||||
/**
|
||||
* @var \Barryvdh\Debugbar\LaravelDebugbar
|
||||
|
14
vendor/barryvdh/laravel-debugbar/src/Twig/Node/Node.php
vendored
Normal file
14
vendor/barryvdh/laravel-debugbar/src/Twig/Node/Node.php
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace Barryvdh\Debugbar\Twig\Node;
|
||||
|
||||
// Maintain compatibility with Twig 2 and 3.
|
||||
if (class_exists('\Twig_Node')) {
|
||||
abstract class Node extends \Twig_Node
|
||||
{
|
||||
}
|
||||
} else {
|
||||
abstract class Node extends \Twig\Node\Node
|
||||
{
|
||||
}
|
||||
}
|
@@ -7,20 +7,37 @@ namespace Barryvdh\Debugbar\Twig\Node;
|
||||
*
|
||||
* @author Wouter J <wouter@wouterj.nl>
|
||||
*/
|
||||
class StopwatchNode extends \Twig_Node
|
||||
class StopwatchNode extends Node
|
||||
{
|
||||
/**
|
||||
* @param \Twig_NodeInterface|\Twig\Node\Node $name
|
||||
* @param $body
|
||||
* @param \Twig_Node_Expression_AssignName|\Twig\Node\Expression\AssignNameExpression $var
|
||||
* @param $lineno
|
||||
* @param $tag
|
||||
*/
|
||||
public function __construct(
|
||||
\Twig_NodeInterface $name,
|
||||
$name,
|
||||
$body,
|
||||
\Twig_Node_Expression_AssignName $var,
|
||||
$var,
|
||||
$lineno = 0,
|
||||
$tag = null
|
||||
) {
|
||||
parent::__construct(['body' => $body, 'name' => $name, 'var' => $var], [], $lineno, $tag);
|
||||
}
|
||||
|
||||
public function compile(\Twig_Compiler $compiler)
|
||||
/**
|
||||
* @param \Twig_Compiler|\Twig\Compiler $env
|
||||
* @return void
|
||||
*/
|
||||
public function compile($compiler)
|
||||
{
|
||||
// Maintain compatibility with Twig 2 and 3.
|
||||
$extension = \Barryvdh\Debugbar\Twig\Extension\Stopwatch::class;
|
||||
if (class_exists('\Twig_Node')) {
|
||||
$extension = 'stopwatch';
|
||||
}
|
||||
|
||||
$compiler
|
||||
->addDebugInfo($this)
|
||||
->write('')
|
||||
@@ -28,11 +45,11 @@ class StopwatchNode extends \Twig_Node
|
||||
->raw(' = ')
|
||||
->subcompile($this->getNode('name'))
|
||||
->write(";\n")
|
||||
->write("\$this->env->getExtension('stopwatch')->getDebugbar()->startMeasure(")
|
||||
->write(sprintf("\$this->env->getExtension('%s')->getDebugbar()->startMeasure(", $extension))
|
||||
->subcompile($this->getNode('var'))
|
||||
->raw(");\n")
|
||||
->subcompile($this->getNode('body'))
|
||||
->write("\$this->env->getExtension('stopwatch')->getDebugbar()->stopMeasure(")
|
||||
->write(sprintf("\$this->env->getExtension('%s')->getDebugbar()->stopMeasure(", $extension))
|
||||
->subcompile($this->getNode('var'))
|
||||
->raw(");\n");
|
||||
}
|
||||
|
@@ -9,7 +9,7 @@ use Barryvdh\Debugbar\Twig\Node\StopwatchNode;
|
||||
*
|
||||
* @author Wouter J <wouter@wouterj.nl>
|
||||
*/
|
||||
class StopwatchTokenParser extends \Twig_TokenParser
|
||||
class StopwatchTokenParser extends TokenParser
|
||||
{
|
||||
protected $debugbarAvailable;
|
||||
|
||||
@@ -18,7 +18,10 @@ class StopwatchTokenParser extends \Twig_TokenParser
|
||||
$this->debugbarAvailable = $debugbarAvailable;
|
||||
}
|
||||
|
||||
public function parse(\Twig_Token $token)
|
||||
/**
|
||||
* @param \Twig_Token|\Twig\Token $token
|
||||
*/
|
||||
public function parse($token)
|
||||
{
|
||||
$lineno = $token->getLine();
|
||||
$stream = $this->parser->getStream();
|
||||
@@ -26,17 +29,31 @@ class StopwatchTokenParser extends \Twig_TokenParser
|
||||
// {% stopwatch 'bar' %}
|
||||
$name = $this->parser->getExpressionParser()->parseExpression();
|
||||
|
||||
$stream->expect(\Twig_Token::BLOCK_END_TYPE);
|
||||
// Maintain compatibility with Twig 2 and 3.
|
||||
if (class_exists("\Twig_Token")) {
|
||||
$blockEndType = \Twig_Token::BLOCK_END_TYPE;
|
||||
} else {
|
||||
$blockEndType = \Twig\Token::BLOCK_END_TYPE;
|
||||
}
|
||||
|
||||
$stream->expect($blockEndType);
|
||||
|
||||
// {% endstopwatch %}
|
||||
$body = $this->parser->subparse([$this, 'decideStopwatchEnd'], true);
|
||||
$stream->expect(\Twig_Token::BLOCK_END_TYPE);
|
||||
$stream->expect($blockEndType);
|
||||
|
||||
// Maintain compatibility with Twig 2 and 3.
|
||||
if (class_exists("\Twig_Node_Expression_AssignName")) {
|
||||
$assignNameExpression = new \Twig_Node_Expression_AssignName($this->parser->getVarName(), $token->getLine());
|
||||
} else {
|
||||
$assignNameExpression = new \Twig\Node\Expression\AssignNameExpression($this->parser->getVarName(), $token->getLine());
|
||||
}
|
||||
|
||||
if ($this->debugbarAvailable) {
|
||||
return new StopwatchNode(
|
||||
$name,
|
||||
$body,
|
||||
new \Twig_Node_Expression_AssignName($this->parser->getVarName(), $token->getLine()),
|
||||
$assignNameExpression,
|
||||
$lineno,
|
||||
$this->getTag()
|
||||
);
|
||||
@@ -50,7 +67,10 @@ class StopwatchTokenParser extends \Twig_TokenParser
|
||||
return 'stopwatch';
|
||||
}
|
||||
|
||||
public function decideStopwatchEnd(\Twig_Token $token)
|
||||
/**
|
||||
* @param \Twig_Token|\Twig\Token $token
|
||||
*/
|
||||
public function decideStopwatchEnd($token)
|
||||
{
|
||||
return $token->test('endstopwatch');
|
||||
}
|
||||
|
14
vendor/barryvdh/laravel-debugbar/src/Twig/TokenParser/TokenParser.php
vendored
Normal file
14
vendor/barryvdh/laravel-debugbar/src/Twig/TokenParser/TokenParser.php
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace Barryvdh\Debugbar\Twig\TokenParser;
|
||||
|
||||
// Maintain compatibility with Twig 2 and 3.
|
||||
if (class_exists('\Twig_TokenParser')) {
|
||||
abstract class TokenParser extends \Twig_TokenParser
|
||||
{
|
||||
}
|
||||
} else {
|
||||
abstract class TokenParser extends \Twig\TokenParser\AbstractTokenParser
|
||||
{
|
||||
}
|
||||
}
|
4
vendor/beyondcode/laravel-query-detector/.styleci.yml
vendored
Normal file
4
vendor/beyondcode/laravel-query-detector/.styleci.yml
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
preset: laravel
|
||||
|
||||
disabled:
|
||||
- single_class_element_per_statement
|
7
vendor/beyondcode/laravel-query-detector/CHANGELOG.md
vendored
Normal file
7
vendor/beyondcode/laravel-query-detector/CHANGELOG.md
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to `laravel-query-detector` will be documented in this file
|
||||
|
||||
## 1.0.0 - 201X-XX-XX
|
||||
|
||||
- initial release
|
55
vendor/beyondcode/laravel-query-detector/CONTRIBUTING.md
vendored
Normal file
55
vendor/beyondcode/laravel-query-detector/CONTRIBUTING.md
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
# Contributing
|
||||
|
||||
Contributions are **welcome** and will be fully **credited**.
|
||||
|
||||
Please read and understand the contribution guide before creating an issue or pull request.
|
||||
|
||||
## Etiquette
|
||||
|
||||
This project is open source, and as such, the maintainers give their free time to build and maintain the source code
|
||||
held within. They make the code freely available in the hope that it will be of use to other developers. It would be
|
||||
extremely unfair for them to suffer abuse or anger for their hard work.
|
||||
|
||||
Please be considerate towards maintainers when raising issues or presenting pull requests. Let's show the
|
||||
world that developers are civilized and selfless people.
|
||||
|
||||
It's the duty of the maintainer to ensure that all submissions to the project are of sufficient
|
||||
quality to benefit the project. Many developers have different skillsets, strengths, and weaknesses. Respect the maintainer's decision, and do not be upset or abusive if your submission is not used.
|
||||
|
||||
## Viability
|
||||
|
||||
When requesting or submitting new features, first consider whether it might be useful to others. Open
|
||||
source projects are used by many developers, who may have entirely different needs to your own. Think about
|
||||
whether or not your feature is likely to be used by other users of the project.
|
||||
|
||||
## Procedure
|
||||
|
||||
Before filing an issue:
|
||||
|
||||
- Attempt to replicate the problem, to ensure that it wasn't a coincidental incident.
|
||||
- Check to make sure your feature suggestion isn't already present within the project.
|
||||
- Check the pull requests tab to ensure that the bug doesn't have a fix in progress.
|
||||
- Check the pull requests tab to ensure that the feature isn't already in progress.
|
||||
|
||||
Before submitting a pull request:
|
||||
|
||||
- Check the codebase to ensure that your feature doesn't already exist.
|
||||
- Check the pull requests to ensure that another person hasn't already submitted the feature or fix.
|
||||
|
||||
## Requirements
|
||||
|
||||
If the project maintainer has any additional requirements, you will find them listed here.
|
||||
|
||||
- **[PSR-2 Coding Standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)** - The easiest way to apply the conventions is to install [PHP Code Sniffer](https://pear.php.net/package/PHP_CodeSniffer).
|
||||
|
||||
- **Add tests!** - Your patch won't be accepted if it doesn't have tests.
|
||||
|
||||
- **Document any change in behaviour** - Make sure the `README.md` and any other relevant documentation are kept up-to-date.
|
||||
|
||||
- **Consider our release cycle** - We try to follow [SemVer v2.0.0](https://semver.org/). Randomly breaking public APIs is not an option.
|
||||
|
||||
- **One pull request per feature** - If you want to do more than one thing, send multiple pull requests.
|
||||
|
||||
- **Send coherent history** - Make sure each individual commit in your pull request is meaningful. If you had to make multiple intermediate commits while developing, please [squash them](https://www.git-scm.com/book/en/v2/Git-Tools-Rewriting-History#Changing-Multiple-Commit-Messages) before submitting.
|
||||
|
||||
**Happy coding**!
|
21
vendor/beyondcode/laravel-query-detector/LICENSE.md
vendored
Normal file
21
vendor/beyondcode/laravel-query-detector/LICENSE.md
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) Beyond Code GmbH <hello@beyondco.de>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
53
vendor/beyondcode/laravel-query-detector/README.md
vendored
Normal file
53
vendor/beyondcode/laravel-query-detector/README.md
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
# Laravel N+1 Query Detector
|
||||
|
||||
[](https://packagist.org/packages/beyondcode/laravel-query-detector)
|
||||
[](https://travis-ci.org/beyondcode/laravel-query-detector)
|
||||
[](https://scrutinizer-ci.com/g/beyondcode/laravel-query-detector)
|
||||
[](https://packagist.org/packages/beyondcode/laravel-query-detector)
|
||||
|
||||
The Laravel N+1 query detector helps you to increase your application's performance by reducing the number of queries it executes. This package monitors your queries in real-time, while you develop your application and notify you when you should add eager loading (N+1 queries).
|
||||
|
||||

|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
You can install the package via composer:
|
||||
|
||||
```bash
|
||||
composer require beyondcode/laravel-query-detector --dev
|
||||
```
|
||||
|
||||
The package will automatically register itself.
|
||||
|
||||
## Documentation
|
||||
|
||||
You can find the documentation on our [website](http://beyondco.de/docs/laravel-query-detector).
|
||||
|
||||
|
||||
### Testing
|
||||
|
||||
``` bash
|
||||
composer test
|
||||
```
|
||||
|
||||
### Changelog
|
||||
|
||||
Please see [CHANGELOG](CHANGELOG.md) for more information what has changed recently.
|
||||
|
||||
## Contributing
|
||||
|
||||
Please see [CONTRIBUTING](CONTRIBUTING.md) for details.
|
||||
|
||||
### Security
|
||||
|
||||
If you discover any security related issues, please email marcel@beyondco.de instead of using the issue tracker.
|
||||
|
||||
## Credits
|
||||
|
||||
- [Marcel Pociot](https://github.com/mpociot)
|
||||
- [All Contributors](../../contributors)
|
||||
|
||||
## License
|
||||
|
||||
The MIT License (MIT). Please see [License File](LICENSE.md) for more information.
|
51
vendor/beyondcode/laravel-query-detector/composer.json
vendored
Normal file
51
vendor/beyondcode/laravel-query-detector/composer.json
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
{
|
||||
"name": "beyondcode/laravel-query-detector",
|
||||
"description": "Laravel N+1 Query Detector",
|
||||
"keywords": [
|
||||
"beyondcode",
|
||||
"laravel-query-detector"
|
||||
],
|
||||
"homepage": "https://github.com/beyondcode/laravel-query-detector",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Marcel Pociot",
|
||||
"email": "marcel@beyondco.de",
|
||||
"homepage": "https://beyondcode.de",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "^7.1 || ^8.0",
|
||||
"illuminate/support": "^5.5 || ^6.0 || ^7.0 || ^8.0 || ^9.0|^10.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"laravel/legacy-factories": "^1.0",
|
||||
"orchestra/testbench": "^3.0 || ^4.0 || ^5.0 || ^6.0|^8.0",
|
||||
"phpunit/phpunit": "^7.0 || ^8.0 || ^9.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"BeyondCode\\QueryDetector\\": "src"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"BeyondCode\\QueryDetector\\Tests\\": "tests"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"test": "vendor/bin/phpunit",
|
||||
"test-coverage": "vendor/bin/phpunit --coverage-html coverage"
|
||||
},
|
||||
"config": {
|
||||
"sort-packages": true
|
||||
},
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"BeyondCode\\QueryDetector\\QueryDetectorServiceProvider"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
69
vendor/beyondcode/laravel-query-detector/config/config.php
vendored
Normal file
69
vendor/beyondcode/laravel-query-detector/config/config.php
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
/*
|
||||
* Enable or disable the query detection.
|
||||
* If this is set to "null", the app.debug config value will be used.
|
||||
*/
|
||||
'enabled' => env('QUERY_DETECTOR_ENABLED', null),
|
||||
|
||||
/*
|
||||
* Threshold level for the N+1 query detection. If a relation query will be
|
||||
* executed more then this amount, the detector will notify you about it.
|
||||
*/
|
||||
'threshold' => (int) env('QUERY_DETECTOR_THRESHOLD', 1),
|
||||
|
||||
/*
|
||||
* Here you can whitelist model relations.
|
||||
*
|
||||
* Right now, you need to define the model relation both as the class name and the attribute name on the model.
|
||||
* So if an "Author" model would have a "posts" relation that points to a "Post" class, you need to add both
|
||||
* the "posts" attribute and the "Post::class", since the relation can get resolved in multiple ways.
|
||||
*/
|
||||
'except' => [
|
||||
//Author::class => [
|
||||
// Post::class,
|
||||
// 'posts',
|
||||
//]
|
||||
],
|
||||
|
||||
/*
|
||||
* Here you can set a specific log channel to write to
|
||||
* in case you are trying to isolate queries or have a lot
|
||||
* going on in the laravel.log. Defaults to laravel.log though.
|
||||
*/
|
||||
'log_channel' => env('QUERY_DETECTOR_LOG_CHANNEL', 'daily'),
|
||||
|
||||
/*
|
||||
* Define the output format that you want to use. Multiple classes are supported.
|
||||
* Available options are:
|
||||
*
|
||||
* Alert:
|
||||
* Displays an alert on the website
|
||||
* \BeyondCode\QueryDetector\Outputs\Alert::class
|
||||
*
|
||||
* Console:
|
||||
* Writes the N+1 queries into your browsers console log
|
||||
* \BeyondCode\QueryDetector\Outputs\Console::class
|
||||
*
|
||||
* Clockwork: (make sure you have the itsgoingd/clockwork package installed)
|
||||
* Writes the N+1 queries warnings to Clockwork log
|
||||
* \BeyondCode\QueryDetector\Outputs\Clockwork::class
|
||||
*
|
||||
* Debugbar: (make sure you have the barryvdh/laravel-debugbar package installed)
|
||||
* Writes the N+1 queries into a custom messages collector of Debugbar
|
||||
* \BeyondCode\QueryDetector\Outputs\Debugbar::class
|
||||
*
|
||||
* JSON:
|
||||
* Writes the N+1 queries into the response body of your JSON responses
|
||||
* \BeyondCode\QueryDetector\Outputs\Json::class
|
||||
*
|
||||
* Log:
|
||||
* Writes the N+1 queries into the Laravel.log file
|
||||
* \BeyondCode\QueryDetector\Outputs\Log::class
|
||||
*/
|
||||
'output' => [
|
||||
\BeyondCode\QueryDetector\Outputs\Alert::class,
|
||||
\BeyondCode\QueryDetector\Outputs\Log::class,
|
||||
]
|
||||
];
|
4
vendor/beyondcode/laravel-query-detector/docs/_index.md
vendored
Normal file
4
vendor/beyondcode/laravel-query-detector/docs/_index.md
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
packageName: Laravel Query Detector
|
||||
githubUrl: https://github.com/beyondcode/laravel-query-detector
|
||||
---
|
17
vendor/beyondcode/laravel-query-detector/docs/installation.md
vendored
Normal file
17
vendor/beyondcode/laravel-query-detector/docs/installation.md
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
---
|
||||
title: Installation
|
||||
order: 1
|
||||
---
|
||||
# Laravel N+1 Query Detector
|
||||
|
||||
The Laravel N+1 query detector helps you to increase your application's performance by reducing the number of queries it executes. This package monitors your queries in real-time, while you develop your application and notify you when you should add eager loading (N+1 queries).
|
||||
|
||||
# Installation
|
||||
|
||||
You can install the package via composer:
|
||||
|
||||
```
|
||||
composer require beyondcode/laravel-query-detector --dev
|
||||
```
|
||||
|
||||
The package will automatically register itself.
|
92
vendor/beyondcode/laravel-query-detector/docs/usage.md
vendored
Normal file
92
vendor/beyondcode/laravel-query-detector/docs/usage.md
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
---
|
||||
title: Usage
|
||||
order: 2
|
||||
---
|
||||
|
||||
## Usage
|
||||
|
||||
If you run your application in the `debug` mode, the query monitor will be automatically active. So there is nothing you have to do.
|
||||
|
||||
By default, this package will display an `alert()` message to notify you about an N+1 query found in the current request.
|
||||
|
||||
If you rather want this information to be written to your `laravel.log` file, written to your browser's console log as a warning or listed in a new tab for the [Laravel Debugbar (barryvdh/laravel-debugbar)](https://github.com/barryvdh/laravel-debugbar), you can publish the configuration and change the output behaviour (see example below).
|
||||
|
||||
You can publish the package's configuration using this command:
|
||||
|
||||
```bash
|
||||
php artisan vendor:publish --provider="BeyondCode\QueryDetector\QueryDetectorServiceProvider"
|
||||
```
|
||||
|
||||
This will add the `querydetector.php` file in your config directory with the following contents:
|
||||
|
||||
```php
|
||||
return [
|
||||
/*
|
||||
* Enable or disable the query detection.
|
||||
* If this is set to "null", the app.debug config value will be used.
|
||||
*/
|
||||
'enabled' => env('QUERY_DETECTOR_ENABLED', null),
|
||||
|
||||
/*
|
||||
* Threshold level for the N+1 query detection. If a relation query will be
|
||||
* executed more then this amount, the detector will notify you about it.
|
||||
*/
|
||||
'threshold' => (int) env('QUERY_DETECTOR_THRESHOLD', 1),
|
||||
|
||||
/*
|
||||
* Here you can whitelist model relations.
|
||||
*
|
||||
* Right now, you need to define the model relation both as the class name and the attribute name on the model.
|
||||
* So if an "Author" model would have a "posts" relation that points to a "Post" class, you need to add both
|
||||
* the "posts" attribute and the "Post::class", since the relation can get resolved in multiple ways.
|
||||
*/
|
||||
'except' => [
|
||||
//Author::class => [
|
||||
// Post::class,
|
||||
// 'posts',
|
||||
//]
|
||||
],
|
||||
|
||||
/*
|
||||
* Define the output format that you want to use. Multiple classes are supported.
|
||||
* Available options are:
|
||||
*
|
||||
* Alert:
|
||||
* Displays an alert on the website
|
||||
* \BeyondCode\QueryDetector\Outputs\Alert::class
|
||||
*
|
||||
* Console:
|
||||
* Writes the N+1 queries into your browsers console log
|
||||
* \BeyondCode\QueryDetector\Outputs\Console::class
|
||||
*
|
||||
* Clockwork: (make sure you have the itsgoingd/clockwork package installed)
|
||||
* Writes the N+1 queries warnings to Clockwork log
|
||||
* \BeyondCode\QueryDetector\Outputs\Clockwork::class
|
||||
*
|
||||
* Debugbar: (make sure you have the barryvdh/laravel-debugbar package installed)
|
||||
* Writes the N+1 queries into a custom messages collector of Debugbar
|
||||
* \BeyondCode\QueryDetector\Outputs\Debugbar::class
|
||||
*
|
||||
* JSON:
|
||||
* Writes the N+1 queries into the response body of your JSON responses
|
||||
* \BeyondCode\QueryDetector\Outputs\Json::class
|
||||
*
|
||||
* Log:
|
||||
* Writes the N+1 queries into the Laravel.log file
|
||||
* \BeyondCode\QueryDetector\Outputs\Log::class
|
||||
*/
|
||||
'output' => [
|
||||
\BeyondCode\QueryDetector\Outputs\Log::class,
|
||||
\BeyondCode\QueryDetector\Outputs\Alert::class,
|
||||
]
|
||||
|
||||
];
|
||||
```
|
||||
|
||||
If you use **Lumen**, you need to copy the config file manually and register the Lumen Service Provider in `bootstrap/app.php` file
|
||||
|
||||
```php
|
||||
$app->register(\BeyondCode\QueryDetector\LumenQueryDetectorServiceProvider::class);
|
||||
```
|
||||
|
||||
If you need additional logic to run when the package detects unoptimized queries, you can listen to the `\BeyondCode\QueryDetector\Events\QueryDetected` event and write a listener to run your own handler. (e.g. send warning to Sentry/Bugsnag, send Slack notification, etc.)
|
26
vendor/beyondcode/laravel-query-detector/src/Events/QueryDetected.php
vendored
Normal file
26
vendor/beyondcode/laravel-query-detector/src/Events/QueryDetected.php
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace BeyondCode\QueryDetector\Events;
|
||||
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
class QueryDetected {
|
||||
use SerializesModels;
|
||||
|
||||
/** @var Collection */
|
||||
protected $queries;
|
||||
|
||||
public function __construct(Collection $queries)
|
||||
{
|
||||
$this->queries = $queries;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection
|
||||
*/
|
||||
public function getQueries()
|
||||
{
|
||||
return $this->queries;
|
||||
}
|
||||
}
|
21
vendor/beyondcode/laravel-query-detector/src/LumenQueryDetectorServiceProvider.php
vendored
Normal file
21
vendor/beyondcode/laravel-query-detector/src/LumenQueryDetectorServiceProvider.php
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
namespace BeyondCode\QueryDetector;
|
||||
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
class LumenQueryDetectorServiceProvider extends ServiceProvider
|
||||
{
|
||||
|
||||
public function register()
|
||||
{
|
||||
$this->app->configure('querydetector');
|
||||
$this->mergeConfigFrom(__DIR__ . '/../config/config.php', 'querydetector');
|
||||
|
||||
$this->app->middleware([
|
||||
QueryDetectorMiddleware::class
|
||||
]);
|
||||
|
||||
$this->app->singleton(QueryDetector::class);
|
||||
$this->app->alias(QueryDetector::class, 'querydetector');
|
||||
}
|
||||
}
|
53
vendor/beyondcode/laravel-query-detector/src/Outputs/Alert.php
vendored
Normal file
53
vendor/beyondcode/laravel-query-detector/src/Outputs/Alert.php
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace BeyondCode\QueryDetector\Outputs;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class Alert implements Output
|
||||
{
|
||||
public function boot()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
public function output(Collection $detectedQueries, Response $response)
|
||||
{
|
||||
if (stripos($response->headers->get('Content-Type'), 'text/html') !== 0 || $response->isRedirection()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$content = $response->getContent();
|
||||
|
||||
$outputContent = $this->getOutputContent($detectedQueries);
|
||||
|
||||
$pos = strripos($content, '</body>');
|
||||
|
||||
if (false !== $pos) {
|
||||
$content = substr($content, 0, $pos) . $outputContent . substr($content, $pos);
|
||||
} else {
|
||||
$content = $content . $outputContent;
|
||||
}
|
||||
|
||||
// Update the new content and reset the content length
|
||||
$response->setContent($content);
|
||||
|
||||
$response->headers->remove('Content-Length');
|
||||
}
|
||||
|
||||
protected function getOutputContent(Collection $detectedQueries)
|
||||
{
|
||||
$output = '<script type="text/javascript">';
|
||||
$output .= "alert('Found the following N+1 queries in this request:\\n\\n";
|
||||
foreach ($detectedQueries as $detectedQuery) {
|
||||
$output .= "Model: ".addslashes($detectedQuery['model'])." => Relation: ".addslashes($detectedQuery['relation']);
|
||||
$output .= " - You should add \"with(\'".addslashes($detectedQuery['relation'])."\')\" to eager-load this relation.";
|
||||
$output .= "\\n";
|
||||
}
|
||||
$output .= "')";
|
||||
$output .= '</script>';
|
||||
|
||||
return $output;
|
||||
}
|
||||
}
|
19
vendor/beyondcode/laravel-query-detector/src/Outputs/Clockwork.php
vendored
Normal file
19
vendor/beyondcode/laravel-query-detector/src/Outputs/Clockwork.php
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace BeyondCode\QueryDetector\Outputs;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class Clockwork implements Output
|
||||
{
|
||||
public function boot()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
public function output(Collection $detectedQueries, Response $response)
|
||||
{
|
||||
clock()->warning("{$detectedQueries->count()} N+1 queries detected:", $detectedQueries->toArray());
|
||||
}
|
||||
}
|
62
vendor/beyondcode/laravel-query-detector/src/Outputs/Console.php
vendored
Normal file
62
vendor/beyondcode/laravel-query-detector/src/Outputs/Console.php
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace BeyondCode\QueryDetector\Outputs;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class Console implements Output
|
||||
{
|
||||
public function boot()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
public function output(Collection $detectedQueries, Response $response)
|
||||
{
|
||||
if (stripos($response->headers->get('Content-Type'), 'text/html') !== 0 || $response->isRedirection()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$content = $response->getContent();
|
||||
|
||||
$outputContent = $this->getOutputContent($detectedQueries);
|
||||
|
||||
$pos = strripos($content, '</body>');
|
||||
|
||||
if (false !== $pos) {
|
||||
$content = substr($content, 0, $pos) . $outputContent . substr($content, $pos);
|
||||
} else {
|
||||
$content = $content . $outputContent;
|
||||
}
|
||||
|
||||
// Update the new content and reset the content length
|
||||
$response->setContent($content);
|
||||
|
||||
$response->headers->remove('Content-Length');
|
||||
}
|
||||
|
||||
protected function getOutputContent(Collection $detectedQueries)
|
||||
{
|
||||
$output = '<script type="text/javascript">';
|
||||
$output .= "console.warn('Found the following N+1 queries in this request:\\n\\n";
|
||||
foreach ($detectedQueries as $detectedQuery) {
|
||||
$output .= "Model: ".addslashes($detectedQuery['model'])." => Relation: ".addslashes($detectedQuery['relation']);
|
||||
$output .= " - You should add \"with(\'".$detectedQuery['relation']."\')\" to eager-load this relation.";
|
||||
$output .= "\\n\\n";
|
||||
$output .= "Model: ".addslashes($detectedQuery['model'])."\\n";
|
||||
$output .= "Relation: ".$detectedQuery['relation']."\\n";
|
||||
$output .= "Num-Called: ".$detectedQuery['count']."\\n";
|
||||
$output .= "\\n";
|
||||
$output .= 'Call-Stack:\\n';
|
||||
|
||||
foreach ($detectedQuery['sources'] as $source) {
|
||||
$output .= "#$source->index $source->name:$source->line\\n";
|
||||
}
|
||||
}
|
||||
$output .= "')";
|
||||
$output .= '</script>';
|
||||
|
||||
return $output;
|
||||
}
|
||||
}
|
34
vendor/beyondcode/laravel-query-detector/src/Outputs/Debugbar.php
vendored
Normal file
34
vendor/beyondcode/laravel-query-detector/src/Outputs/Debugbar.php
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace BeyondCode\QueryDetector\Outputs;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
use Barryvdh\Debugbar\Facade as LaravelDebugbar;
|
||||
use DebugBar\DataCollector\MessagesCollector;
|
||||
|
||||
class Debugbar implements Output
|
||||
{
|
||||
protected $collector;
|
||||
|
||||
public function boot()
|
||||
{
|
||||
$this->collector = new MessagesCollector('N+1 Queries');
|
||||
|
||||
if (!LaravelDebugbar::hasCollector($this->collector->getName())) {
|
||||
LaravelDebugbar::addCollector($this->collector);
|
||||
}
|
||||
}
|
||||
|
||||
public function output(Collection $detectedQueries, Response $response)
|
||||
{
|
||||
foreach ($detectedQueries as $detectedQuery) {
|
||||
$this->collector->addMessage(sprintf('Model: %s => Relation: %s - You should add `with(%s)` to eager-load this relation.',
|
||||
$detectedQuery['model'],
|
||||
$detectedQuery['relation'],
|
||||
$detectedQuery['relation']
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
27
vendor/beyondcode/laravel-query-detector/src/Outputs/Json.php
vendored
Normal file
27
vendor/beyondcode/laravel-query-detector/src/Outputs/Json.php
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
namespace BeyondCode\QueryDetector\Outputs;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
|
||||
class Json implements Output
|
||||
{
|
||||
public function boot()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
public function output(Collection $detectedQueries, Response $response)
|
||||
{
|
||||
if ($response instanceof JsonResponse) {
|
||||
$data = $response->getData(true);
|
||||
if (! is_array($data)){
|
||||
$data = [ $data ];
|
||||
}
|
||||
|
||||
$data['warning_queries'] = $detectedQueries;
|
||||
$response->setData($data);
|
||||
}
|
||||
}
|
||||
}
|
41
vendor/beyondcode/laravel-query-detector/src/Outputs/Log.php
vendored
Normal file
41
vendor/beyondcode/laravel-query-detector/src/Outputs/Log.php
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace BeyondCode\QueryDetector\Outputs;
|
||||
|
||||
use Illuminate\Support\Facades\Log as LaravelLog;
|
||||
use Illuminate\Support\Collection;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class Log implements Output
|
||||
{
|
||||
public function boot()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
public function output(Collection $detectedQueries, Response $response)
|
||||
{
|
||||
$this->log('Detected N+1 Query');
|
||||
|
||||
foreach ($detectedQueries as $detectedQuery) {
|
||||
$logOutput = 'Model: '.$detectedQuery['model'] . PHP_EOL;
|
||||
|
||||
$logOutput .= 'Relation: '.$detectedQuery['relation'] . PHP_EOL;
|
||||
|
||||
$logOutput .= 'Num-Called: '.$detectedQuery['count'] . PHP_EOL;
|
||||
|
||||
$logOutput .= 'Call-Stack:' . PHP_EOL;
|
||||
|
||||
foreach ($detectedQuery['sources'] as $source) {
|
||||
$logOutput .= '#'.$source->index.' '.$source->name.':'.$source->line . PHP_EOL;
|
||||
}
|
||||
|
||||
$this->log($logOutput);
|
||||
}
|
||||
}
|
||||
|
||||
private function log(string $message)
|
||||
{
|
||||
LaravelLog::channel(config('querydetector.log_channel'))->info($message);
|
||||
}
|
||||
}
|
13
vendor/beyondcode/laravel-query-detector/src/Outputs/Output.php
vendored
Normal file
13
vendor/beyondcode/laravel-query-detector/src/Outputs/Output.php
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace BeyondCode\QueryDetector\Outputs;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
interface Output
|
||||
{
|
||||
public function boot();
|
||||
|
||||
public function output(Collection $detectedQueries, Response $response);
|
||||
}
|
216
vendor/beyondcode/laravel-query-detector/src/QueryDetector.php
vendored
Executable file
216
vendor/beyondcode/laravel-query-detector/src/QueryDetector.php
vendored
Executable file
@@ -0,0 +1,216 @@
|
||||
<?php
|
||||
|
||||
namespace BeyondCode\QueryDetector;
|
||||
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Illuminate\Database\Eloquent\Relations\Relation;
|
||||
use BeyondCode\QueryDetector\Events\QueryDetected;
|
||||
|
||||
class QueryDetector
|
||||
{
|
||||
/** @var Collection */
|
||||
private $queries;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->queries = Collection::make();
|
||||
}
|
||||
|
||||
public function boot()
|
||||
{
|
||||
DB::listen(function($query) {
|
||||
$backtrace = collect(debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, 50));
|
||||
|
||||
$this->logQuery($query, $backtrace);
|
||||
});
|
||||
|
||||
foreach ($this->getOutputTypes() as $outputType) {
|
||||
app()->singleton($outputType);
|
||||
app($outputType)->boot();
|
||||
}
|
||||
}
|
||||
|
||||
public function isEnabled(): bool
|
||||
{
|
||||
$configEnabled = value(config('querydetector.enabled'));
|
||||
|
||||
if ($configEnabled === null) {
|
||||
$configEnabled = config('app.debug');
|
||||
}
|
||||
|
||||
return $configEnabled;
|
||||
}
|
||||
|
||||
public function logQuery($query, Collection $backtrace)
|
||||
{
|
||||
$modelTrace = $backtrace->first(function ($trace) {
|
||||
return Arr::get($trace, 'object') instanceof Builder;
|
||||
});
|
||||
|
||||
// The query is coming from an Eloquent model
|
||||
if (! is_null($modelTrace)) {
|
||||
/*
|
||||
* Relations get resolved by either calling the "getRelationValue" method on the model,
|
||||
* or if the class itself is a Relation.
|
||||
*/
|
||||
$relation = $backtrace->first(function ($trace) {
|
||||
return Arr::get($trace, 'function') === 'getRelationValue' || Arr::get($trace, 'class') === Relation::class ;
|
||||
});
|
||||
|
||||
// We try to access a relation
|
||||
if (is_array($relation) && isset($relation['object'])) {
|
||||
if ($relation['class'] === Relation::class) {
|
||||
$model = get_class($relation['object']->getParent());
|
||||
$relationName = get_class($relation['object']->getRelated());
|
||||
$relatedModel = $relationName;
|
||||
} else {
|
||||
$model = get_class($relation['object']);
|
||||
$relationName = $relation['args'][0];
|
||||
$relatedModel = $relationName;
|
||||
}
|
||||
|
||||
$sources = $this->findSource($backtrace);
|
||||
|
||||
$key = md5($query->sql . $model . $relationName . $sources[0]->name . $sources[0]->line);
|
||||
|
||||
$count = Arr::get($this->queries, $key.'.count', 0);
|
||||
$time = Arr::get($this->queries, $key.'.time', 0);
|
||||
|
||||
$this->queries[$key] = [
|
||||
'count' => ++$count,
|
||||
'time' => $time + $query->time,
|
||||
'query' => $query->sql,
|
||||
'model' => $model,
|
||||
'relatedModel' => $relatedModel,
|
||||
'relation' => $relationName,
|
||||
'sources' => $sources
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function findSource($stack)
|
||||
{
|
||||
$sources = [];
|
||||
|
||||
foreach ($stack as $index => $trace) {
|
||||
$sources[] = $this->parseTrace($index, $trace);
|
||||
}
|
||||
|
||||
return array_values(array_filter($sources));
|
||||
}
|
||||
|
||||
public function parseTrace($index, array $trace)
|
||||
{
|
||||
$frame = (object) [
|
||||
'index' => $index,
|
||||
'name' => null,
|
||||
'line' => isset($trace['line']) ? $trace['line'] : '?',
|
||||
];
|
||||
|
||||
if (isset($trace['class']) &&
|
||||
isset($trace['file']) &&
|
||||
!$this->fileIsInExcludedPath($trace['file'])
|
||||
) {
|
||||
$frame->name = $this->normalizeFilename($trace['file']);
|
||||
|
||||
return $frame;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given file is to be excluded from analysis
|
||||
*
|
||||
* @param string $file
|
||||
* @return bool
|
||||
*/
|
||||
protected function fileIsInExcludedPath($file)
|
||||
{
|
||||
$excludedPaths = [
|
||||
'/vendor/laravel/framework/src/Illuminate/Database',
|
||||
'/vendor/laravel/framework/src/Illuminate/Events',
|
||||
];
|
||||
|
||||
$normalizedPath = str_replace('\\', '/', $file);
|
||||
|
||||
foreach ($excludedPaths as $excludedPath) {
|
||||
if (strpos($normalizedPath, $excludedPath) !== false) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shorten the path by removing the relative links and base dir
|
||||
*
|
||||
* @param string $path
|
||||
* @return string
|
||||
*/
|
||||
protected function normalizeFilename($path): string
|
||||
{
|
||||
if (file_exists($path)) {
|
||||
$path = realpath($path);
|
||||
}
|
||||
|
||||
return str_replace(base_path(), '', $path);
|
||||
}
|
||||
|
||||
public function getDetectedQueries(): Collection
|
||||
{
|
||||
$exceptions = config('querydetector.except', []);
|
||||
|
||||
$queries = $this->queries
|
||||
->values();
|
||||
|
||||
foreach ($exceptions as $parentModel => $relations) {
|
||||
foreach ($relations as $relation) {
|
||||
$queries = $queries->reject(function ($query) use ($relation, $parentModel) {
|
||||
return $query['model'] === $parentModel && $query['relatedModel'] === $relation;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
$queries = $queries->where('count', '>', config('querydetector.threshold', 1))->values();
|
||||
|
||||
if ($queries->isNotEmpty()) {
|
||||
event(new QueryDetected($queries));
|
||||
}
|
||||
|
||||
return $queries;
|
||||
}
|
||||
|
||||
protected function getOutputTypes()
|
||||
{
|
||||
$outputTypes = config('querydetector.output');
|
||||
|
||||
if (! is_array($outputTypes)) {
|
||||
$outputTypes = [$outputTypes];
|
||||
}
|
||||
|
||||
return $outputTypes;
|
||||
}
|
||||
|
||||
protected function applyOutput(Response $response)
|
||||
{
|
||||
foreach ($this->getOutputTypes() as $type) {
|
||||
app($type)->output($this->getDetectedQueries(), $response);
|
||||
}
|
||||
}
|
||||
|
||||
public function output($request, $response)
|
||||
{
|
||||
if ($this->getDetectedQueries()->isNotEmpty()) {
|
||||
$this->applyOutput($response);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
40
vendor/beyondcode/laravel-query-detector/src/QueryDetectorMiddleware.php
vendored
Normal file
40
vendor/beyondcode/laravel-query-detector/src/QueryDetectorMiddleware.php
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace BeyondCode\QueryDetector;
|
||||
|
||||
use Closure;
|
||||
|
||||
class QueryDetectorMiddleware
|
||||
{
|
||||
/** @var QueryDetector */
|
||||
private $detector;
|
||||
|
||||
public function __construct(QueryDetector $detector)
|
||||
{
|
||||
$this->detector = $detector;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param Request $request
|
||||
* @param Closure $next
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle($request, Closure $next)
|
||||
{
|
||||
if (! $this->detector->isEnabled()) {
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
$this->detector->boot();
|
||||
|
||||
/** @var \Illuminate\Http\Response $response */
|
||||
$response = $next($request);
|
||||
|
||||
// Modify the response to add the Debugbar
|
||||
$this->detector->output($request, $response);
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
46
vendor/beyondcode/laravel-query-detector/src/QueryDetectorServiceProvider.php
vendored
Normal file
46
vendor/beyondcode/laravel-query-detector/src/QueryDetectorServiceProvider.php
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace BeyondCode\QueryDetector;
|
||||
|
||||
use Illuminate\Contracts\Http\Kernel;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
class QueryDetectorServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Bootstrap the application services.
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
if ($this->app->runningInConsole()) {
|
||||
$this->publishes([
|
||||
__DIR__.'/../config/config.php' => config_path('querydetector.php'),
|
||||
], 'config');
|
||||
}
|
||||
|
||||
$this->registerMiddleware(QueryDetectorMiddleware::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the application services.
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
$this->app->singleton(QueryDetector::class);
|
||||
|
||||
$this->app->alias(QueryDetector::class, 'querydetector');
|
||||
|
||||
$this->mergeConfigFrom(__DIR__.'/../config/config.php', 'querydetector');
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the middleware
|
||||
*
|
||||
* @param string $middleware
|
||||
*/
|
||||
protected function registerMiddleware($middleware)
|
||||
{
|
||||
$kernel = $this->app[Kernel::class];
|
||||
$kernel->pushMiddleware($middleware);
|
||||
}
|
||||
}
|
39
vendor/composer/ClassLoader.php
vendored
39
vendor/composer/ClassLoader.php
vendored
@@ -42,9 +42,6 @@ namespace Composer\Autoload;
|
||||
*/
|
||||
class ClassLoader
|
||||
{
|
||||
/** @var \Closure(string):void */
|
||||
private static $includeFile;
|
||||
|
||||
/** @var ?string */
|
||||
private $vendorDir;
|
||||
|
||||
@@ -109,7 +106,6 @@ class ClassLoader
|
||||
public function __construct($vendorDir = null)
|
||||
{
|
||||
$this->vendorDir = $vendorDir;
|
||||
self::initializeIncludeClosure();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -429,7 +425,7 @@ class ClassLoader
|
||||
public function loadClass($class)
|
||||
{
|
||||
if ($file = $this->findFile($class)) {
|
||||
(self::$includeFile)($file);
|
||||
includeFile($file);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -559,23 +555,18 @@ class ClassLoader
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static function initializeIncludeClosure(): void
|
||||
{
|
||||
if (self::$includeFile !== null) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope isolated include.
|
||||
*
|
||||
* Prevents access to $this/self from included files.
|
||||
*
|
||||
* @param string $file
|
||||
* @return void
|
||||
*/
|
||||
self::$includeFile = static function($file) {
|
||||
include $file;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope isolated include.
|
||||
*
|
||||
* Prevents access to $this/self from included files.
|
||||
*
|
||||
* @param string $file
|
||||
* @return void
|
||||
* @private
|
||||
*/
|
||||
function includeFile($file)
|
||||
{
|
||||
include $file;
|
||||
}
|
||||
|
2
vendor/composer/autoload_psr4.php
vendored
2
vendor/composer/autoload_psr4.php
vendored
@@ -146,12 +146,14 @@ return array(
|
||||
'Composer\\CaBundle\\' => array($vendorDir . '/composer/ca-bundle/src'),
|
||||
'Complex\\' => array($vendorDir . '/markbaker/complex/classes/src'),
|
||||
'Collective\\Html\\' => array($vendorDir . '/laravelcollective/html/src'),
|
||||
'Clockwork\\' => array($vendorDir . '/itsgoingd/clockwork/Clockwork'),
|
||||
'Chumper\\Zipper\\' => array($vendorDir . '/chumper/zipper/src/Chumper/Zipper'),
|
||||
'Carbon\\' => array($vendorDir . '/nesbot/carbon/src/Carbon'),
|
||||
'Bugsnag\\PsrLogger\\' => array($vendorDir . '/bugsnag/bugsnag-psr-logger/src'),
|
||||
'Bugsnag\\BugsnagLaravel\\' => array($vendorDir . '/bugsnag/bugsnag-laravel/src'),
|
||||
'Bugsnag\\' => array($vendorDir . '/bugsnag/bugsnag/src'),
|
||||
'Brick\\Math\\' => array($vendorDir . '/brick/math/src'),
|
||||
'BeyondCode\\QueryDetector\\' => array($vendorDir . '/beyondcode/laravel-query-detector/src'),
|
||||
'Barryvdh\\Debugbar\\' => array($vendorDir . '/barryvdh/laravel-debugbar/src'),
|
||||
'Aws\\' => array($vendorDir . '/aws/aws-sdk-php/src'),
|
||||
'App\\' => array($baseDir . '/app', $vendorDir . '/laravel/pint/app'),
|
||||
|
27
vendor/composer/autoload_real.php
vendored
27
vendor/composer/autoload_real.php
vendored
@@ -33,18 +33,25 @@ class ComposerAutoloaderInit10c1836cea18dd9470bc2e97275d9d56
|
||||
|
||||
$loader->register(true);
|
||||
|
||||
$filesToLoad = \Composer\Autoload\ComposerStaticInit10c1836cea18dd9470bc2e97275d9d56::$files;
|
||||
$requireFile = static function ($fileIdentifier, $file) {
|
||||
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
|
||||
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
|
||||
|
||||
require $file;
|
||||
}
|
||||
};
|
||||
foreach ($filesToLoad as $fileIdentifier => $file) {
|
||||
($requireFile)($fileIdentifier, $file);
|
||||
$includeFiles = \Composer\Autoload\ComposerStaticInit10c1836cea18dd9470bc2e97275d9d56::$files;
|
||||
foreach ($includeFiles as $fileIdentifier => $file) {
|
||||
composerRequire10c1836cea18dd9470bc2e97275d9d56($fileIdentifier, $file);
|
||||
}
|
||||
|
||||
return $loader;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $fileIdentifier
|
||||
* @param string $file
|
||||
* @return void
|
||||
*/
|
||||
function composerRequire10c1836cea18dd9470bc2e97275d9d56($fileIdentifier, $file)
|
||||
{
|
||||
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
|
||||
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
|
||||
|
||||
require $file;
|
||||
}
|
||||
}
|
||||
|
10
vendor/composer/autoload_static.php
vendored
10
vendor/composer/autoload_static.php
vendored
@@ -250,6 +250,7 @@ class ComposerStaticInit10c1836cea18dd9470bc2e97275d9d56
|
||||
'Composer\\CaBundle\\' => 18,
|
||||
'Complex\\' => 8,
|
||||
'Collective\\Html\\' => 16,
|
||||
'Clockwork\\' => 10,
|
||||
'Chumper\\Zipper\\' => 15,
|
||||
'Carbon\\' => 7,
|
||||
),
|
||||
@@ -259,6 +260,7 @@ class ComposerStaticInit10c1836cea18dd9470bc2e97275d9d56
|
||||
'Bugsnag\\BugsnagLaravel\\' => 23,
|
||||
'Bugsnag\\' => 8,
|
||||
'Brick\\Math\\' => 11,
|
||||
'BeyondCode\\QueryDetector\\' => 25,
|
||||
'Barryvdh\\Debugbar\\' => 18,
|
||||
),
|
||||
'A' =>
|
||||
@@ -836,6 +838,10 @@ class ComposerStaticInit10c1836cea18dd9470bc2e97275d9d56
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/laravelcollective/html/src',
|
||||
),
|
||||
'Clockwork\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/itsgoingd/clockwork/Clockwork',
|
||||
),
|
||||
'Chumper\\Zipper\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/chumper/zipper/src/Chumper/Zipper',
|
||||
@@ -860,6 +866,10 @@ class ComposerStaticInit10c1836cea18dd9470bc2e97275d9d56
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/brick/math/src',
|
||||
),
|
||||
'BeyondCode\\QueryDetector\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/beyondcode/laravel-query-detector/src',
|
||||
),
|
||||
'Barryvdh\\Debugbar\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/barryvdh/laravel-debugbar/src',
|
||||
|
162
vendor/composer/installed.json
vendored
162
vendor/composer/installed.json
vendored
@@ -152,34 +152,34 @@
|
||||
},
|
||||
{
|
||||
"name": "barryvdh/laravel-debugbar",
|
||||
"version": "v3.7.0",
|
||||
"version_normalized": "3.7.0.0",
|
||||
"version": "v3.8.0",
|
||||
"version_normalized": "3.8.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/barryvdh/laravel-debugbar.git",
|
||||
"reference": "3372ed65e6d2039d663ed19aa699956f9d346271"
|
||||
"reference": "eb01216141e62433178c52b0cbdb785b45bae871"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/3372ed65e6d2039d663ed19aa699956f9d346271",
|
||||
"reference": "3372ed65e6d2039d663ed19aa699956f9d346271",
|
||||
"url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/eb01216141e62433178c52b0cbdb785b45bae871",
|
||||
"reference": "eb01216141e62433178c52b0cbdb785b45bae871",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"illuminate/routing": "^7|^8|^9",
|
||||
"illuminate/session": "^7|^8|^9",
|
||||
"illuminate/support": "^7|^8|^9",
|
||||
"illuminate/routing": "^9|^10",
|
||||
"illuminate/session": "^9|^10",
|
||||
"illuminate/support": "^9|^10",
|
||||
"maximebf/debugbar": "^1.17.2",
|
||||
"php": ">=7.2.5",
|
||||
"symfony/finder": "^5|^6"
|
||||
"php": "^8.0",
|
||||
"symfony/finder": "^6"
|
||||
},
|
||||
"require-dev": {
|
||||
"mockery/mockery": "^1.3.3",
|
||||
"orchestra/testbench-dusk": "^5|^6|^7",
|
||||
"phpunit/phpunit": "^8.5|^9.0",
|
||||
"orchestra/testbench-dusk": "^5|^6|^7|^8",
|
||||
"phpunit/phpunit": "^8.5.30|^9.0",
|
||||
"squizlabs/php_codesniffer": "^3.5"
|
||||
},
|
||||
"time": "2022-07-11T09:26:42+00:00",
|
||||
"time": "2023-02-04T15:47:28+00:00",
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
@@ -223,7 +223,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/barryvdh/laravel-debugbar/issues",
|
||||
"source": "https://github.com/barryvdh/laravel-debugbar/tree/v3.7.0"
|
||||
"source": "https://github.com/barryvdh/laravel-debugbar/tree/v3.8.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -237,6 +237,69 @@
|
||||
],
|
||||
"install-path": "../barryvdh/laravel-debugbar"
|
||||
},
|
||||
{
|
||||
"name": "beyondcode/laravel-query-detector",
|
||||
"version": "1.7.0",
|
||||
"version_normalized": "1.7.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/beyondcode/laravel-query-detector.git",
|
||||
"reference": "40c7e168fcf7eeb80d8e96f7922e05ab194269c8"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/beyondcode/laravel-query-detector/zipball/40c7e168fcf7eeb80d8e96f7922e05ab194269c8",
|
||||
"reference": "40c7e168fcf7eeb80d8e96f7922e05ab194269c8",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"illuminate/support": "^5.5 || ^6.0 || ^7.0 || ^8.0 || ^9.0|^10.0",
|
||||
"php": "^7.1 || ^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"laravel/legacy-factories": "^1.0",
|
||||
"orchestra/testbench": "^3.0 || ^4.0 || ^5.0 || ^6.0|^8.0",
|
||||
"phpunit/phpunit": "^7.0 || ^8.0 || ^9.0"
|
||||
},
|
||||
"time": "2023-02-15T10:37:22+00:00",
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"BeyondCode\\QueryDetector\\QueryDetectorServiceProvider"
|
||||
]
|
||||
}
|
||||
},
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"BeyondCode\\QueryDetector\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Marcel Pociot",
|
||||
"email": "marcel@beyondco.de",
|
||||
"homepage": "https://beyondcode.de",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "Laravel N+1 Query Detector",
|
||||
"homepage": "https://github.com/beyondcode/laravel-query-detector",
|
||||
"keywords": [
|
||||
"beyondcode",
|
||||
"laravel-query-detector"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/beyondcode/laravel-query-detector/issues",
|
||||
"source": "https://github.com/beyondcode/laravel-query-detector/tree/1.7.0"
|
||||
},
|
||||
"install-path": "../beyondcode/laravel-query-detector"
|
||||
},
|
||||
{
|
||||
"name": "brick/math",
|
||||
"version": "0.10.2",
|
||||
@@ -3019,6 +3082,76 @@
|
||||
],
|
||||
"install-path": "../intervention/image"
|
||||
},
|
||||
{
|
||||
"name": "itsgoingd/clockwork",
|
||||
"version": "dev-master",
|
||||
"version_normalized": "dev-master",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/ladybirdweb/clockwork.git",
|
||||
"reference": "e29be8125ce999947d60986dcd8e606c04c012ab"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/ladybirdweb/clockwork/zipball/e29be8125ce999947d60986dcd8e606c04c012ab",
|
||||
"reference": "e29be8125ce999947d60986dcd8e606c04c012ab",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"php": ">=5.6"
|
||||
},
|
||||
"time": "2023-04-14T10:39:18+00:00",
|
||||
"default-branch": true,
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"Clockwork\\Support\\Laravel\\ClockworkServiceProvider"
|
||||
],
|
||||
"aliases": {
|
||||
"Clockwork": "Clockwork\\Support\\Laravel\\Facade"
|
||||
}
|
||||
}
|
||||
},
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Clockwork\\": "Clockwork/"
|
||||
}
|
||||
},
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "itsgoingd",
|
||||
"email": "itsgoingd@luzer.sk",
|
||||
"homepage": "https://twitter.com/itsgoingd"
|
||||
}
|
||||
],
|
||||
"description": "php dev tools in your browser",
|
||||
"homepage": "https://underground.works/clockwork",
|
||||
"keywords": [
|
||||
"debugging",
|
||||
"devtools",
|
||||
"laravel",
|
||||
"logging",
|
||||
"lumen",
|
||||
"profiling",
|
||||
"slim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/ladybirdweb/clockwork/tree/master"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/itsgoingd"
|
||||
}
|
||||
],
|
||||
"install-path": "../itsgoingd/clockwork"
|
||||
},
|
||||
{
|
||||
"name": "laravel/dusk",
|
||||
"version": "v6.25.2",
|
||||
@@ -13877,6 +14010,7 @@
|
||||
"dev": true,
|
||||
"dev-package-names": [
|
||||
"barryvdh/laravel-debugbar",
|
||||
"beyondcode/laravel-query-detector",
|
||||
"doctrine/instantiator",
|
||||
"fakerphp/faker",
|
||||
"filp/whoops",
|
||||
|
34
vendor/composer/installed.php
vendored
34
vendor/composer/installed.php
vendored
@@ -3,7 +3,7 @@
|
||||
'name' => 'laravel/laravel',
|
||||
'pretty_version' => 'dev-master',
|
||||
'version' => 'dev-master',
|
||||
'reference' => '2381fd7cf5b871b9db0df601f4fdd55c221c4f20',
|
||||
'reference' => 'c25c1d94b8fa38df50e8d839730bf32cb534b8cb',
|
||||
'type' => 'project',
|
||||
'install_path' => __DIR__ . '/../../',
|
||||
'aliases' => array(),
|
||||
@@ -29,14 +29,23 @@
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'barryvdh/laravel-debugbar' => array(
|
||||
'pretty_version' => 'v3.7.0',
|
||||
'version' => '3.7.0.0',
|
||||
'reference' => '3372ed65e6d2039d663ed19aa699956f9d346271',
|
||||
'pretty_version' => 'v3.8.0',
|
||||
'version' => '3.8.0.0',
|
||||
'reference' => 'eb01216141e62433178c52b0cbdb785b45bae871',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../barryvdh/laravel-debugbar',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'beyondcode/laravel-query-detector' => array(
|
||||
'pretty_version' => '1.7.0',
|
||||
'version' => '1.7.0.0',
|
||||
'reference' => '40c7e168fcf7eeb80d8e96f7922e05ab194269c8',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../beyondcode/laravel-query-detector',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'brick/math' => array(
|
||||
'pretty_version' => '0.10.2',
|
||||
'version' => '0.10.2.0',
|
||||
@@ -588,6 +597,17 @@
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'itsgoingd/clockwork' => array(
|
||||
'pretty_version' => 'dev-master',
|
||||
'version' => 'dev-master',
|
||||
'reference' => 'e29be8125ce999947d60986dcd8e606c04c012ab',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../itsgoingd/clockwork',
|
||||
'aliases' => array(
|
||||
0 => '9999999-dev',
|
||||
),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'kodova/hamcrest-php' => array(
|
||||
'dev_requirement' => true,
|
||||
'replaced' => array(
|
||||
@@ -615,7 +635,7 @@
|
||||
'laravel/laravel' => array(
|
||||
'pretty_version' => 'dev-master',
|
||||
'version' => 'dev-master',
|
||||
'reference' => '2381fd7cf5b871b9db0df601f4fdd55c221c4f20',
|
||||
'reference' => 'c25c1d94b8fa38df50e8d839730bf32cb534b8cb',
|
||||
'type' => 'project',
|
||||
'install_path' => __DIR__ . '/../../',
|
||||
'aliases' => array(),
|
||||
@@ -1247,8 +1267,8 @@
|
||||
'psr/log-implementation' => array(
|
||||
'dev_requirement' => false,
|
||||
'provided' => array(
|
||||
0 => '1.0|2.0|3.0',
|
||||
1 => '1.0.0 || 2.0.0 || 3.0.0',
|
||||
0 => '1.0.0 || 2.0.0 || 3.0.0',
|
||||
1 => '1.0|2.0|3.0',
|
||||
),
|
||||
),
|
||||
'psr/simple-cache' => array(
|
||||
|
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 ]) ]
|
||||
);
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user