vendor and env first commit
This commit is contained in:
+19
@@ -0,0 +1,19 @@
|
||||
Copyright (C) 2013-present Barry vd. Heuvel
|
||||
|
||||
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.
|
||||
@@ -0,0 +1,5 @@
|
||||
# Security Policy
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
Please report security issues to `barryvdh@gmail.com`
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
{
|
||||
"name": "barryvdh/laravel-debugbar",
|
||||
"description": "PHP Debugbar integration for Laravel",
|
||||
"keywords": ["laravel", "debugbar", "profiler", "debug", "webprofiler"],
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Barry vd. Heuvel",
|
||||
"email": "barryvdh@gmail.com"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "^8.0",
|
||||
"maximebf/debugbar": "~1.22.0",
|
||||
"illuminate/routing": "^9|^10|^11",
|
||||
"illuminate/session": "^9|^10|^11",
|
||||
"illuminate/support": "^9|^10|^11",
|
||||
"symfony/finder": "^6|^7"
|
||||
},
|
||||
"require-dev": {
|
||||
"mockery/mockery": "^1.3.3",
|
||||
"orchestra/testbench-dusk": "^5|^6|^7|^8|^9",
|
||||
"phpunit/phpunit": "^9.6|^10.5",
|
||||
"squizlabs/php_codesniffer": "^3.5"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Barryvdh\\Debugbar\\": "src/"
|
||||
},
|
||||
"files": [
|
||||
"src/helpers.php"
|
||||
]
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Barryvdh\\Debugbar\\Tests\\": "tests"
|
||||
}
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"prefer-stable": true,
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.13-dev"
|
||||
},
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"Barryvdh\\Debugbar\\ServiceProvider"
|
||||
],
|
||||
"aliases": {
|
||||
"Debugbar": "Barryvdh\\Debugbar\\Facades\\Debugbar"
|
||||
}
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"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"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,325 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Debugbar Settings
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Debugbar is enabled by default, when debug is set to true in app.php.
|
||||
| You can override the value by setting enable to true or false instead of null.
|
||||
|
|
||||
| You can provide an array of URI's that must be ignored (eg. 'api/*')
|
||||
|
|
||||
*/
|
||||
|
||||
'enabled' => env('DEBUGBAR_ENABLED', null),
|
||||
'except' => [
|
||||
'telescope*',
|
||||
'horizon*',
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Storage settings
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| DebugBar stores data for session/ajax requests.
|
||||
| You can disable this, so the debugbar stores data in headers/session,
|
||||
| but this can cause problems with large data collectors.
|
||||
| By default, file storage (in the storage folder) is used. Redis and PDO
|
||||
| can also be used. For PDO, run the package migrations first.
|
||||
|
|
||||
| Warning: Enabling storage.open will allow everyone to access previous
|
||||
| request, do not enable open storage in publicly available environments!
|
||||
| Specify a callback if you want to limit based on IP or authentication.
|
||||
| Leaving it to null will allow localhost only.
|
||||
*/
|
||||
'storage' => [
|
||||
'enabled' => true,
|
||||
'open' => env('DEBUGBAR_OPEN_STORAGE'), // bool/callback.
|
||||
'driver' => 'file', // redis, file, pdo, socket, custom
|
||||
'path' => storage_path('debugbar'), // For file driver
|
||||
'connection' => null, // Leave null for default connection (Redis/PDO)
|
||||
'provider' => '', // Instance of StorageInterface for custom driver
|
||||
'hostname' => '127.0.0.1', // Hostname to use with the "socket" driver
|
||||
'port' => 2304, // Port to use with the "socket" driver
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Editor
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Choose your preferred editor to use when clicking file name.
|
||||
|
|
||||
| Supported: "phpstorm", "vscode", "vscode-insiders", "vscode-remote",
|
||||
| "vscode-insiders-remote", "vscodium", "textmate", "emacs",
|
||||
| "sublime", "atom", "nova", "macvim", "idea", "netbeans",
|
||||
| "xdebug", "espresso"
|
||||
|
|
||||
*/
|
||||
|
||||
'editor' => env('DEBUGBAR_EDITOR') ?: env('IGNITION_EDITOR', 'phpstorm'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Remote Path Mapping
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| If you are using a remote dev server, like Laravel Homestead, Docker, or
|
||||
| even a remote VPS, it will be necessary to specify your path mapping.
|
||||
|
|
||||
| Leaving one, or both of these, empty or null will not trigger the remote
|
||||
| URL changes and Debugbar will treat your editor links as local files.
|
||||
|
|
||||
| "remote_sites_path" is an absolute base path for your sites or projects
|
||||
| in Homestead, Vagrant, Docker, or another remote development server.
|
||||
|
|
||||
| Example value: "/home/vagrant/Code"
|
||||
|
|
||||
| "local_sites_path" is an absolute base path for your sites or projects
|
||||
| on your local computer where your IDE or code editor is running on.
|
||||
|
|
||||
| Example values: "/Users/<name>/Code", "C:\Users\<name>\Documents\Code"
|
||||
|
|
||||
*/
|
||||
|
||||
'remote_sites_path' => env('DEBUGBAR_REMOTE_SITES_PATH'),
|
||||
'local_sites_path' => env('DEBUGBAR_LOCAL_SITES_PATH', env('IGNITION_LOCAL_SITES_PATH')),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Vendors
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| 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 highlight.js
|
||||
| So if you want syntax highlighting, set it to true.
|
||||
| jQuery is set to not conflict with existing jQuery scripts.
|
||||
|
|
||||
*/
|
||||
|
||||
'include_vendors' => true,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Capture Ajax Requests
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The Debugbar can capture Ajax requests and display them. If you don't want this (ie. because of errors),
|
||||
| you can use this option to disable sending the data through the headers.
|
||||
|
|
||||
| Optionally, you can also send ServerTiming headers on ajax requests for the Chrome DevTools.
|
||||
|
|
||||
| Note for your request to be identified as ajax requests they must either send the header
|
||||
| X-Requested-With with the value XMLHttpRequest (most JS libraries send this), or have application/json as a Accept header.
|
||||
|
|
||||
| By default `ajax_handler_auto_show` is set to true allowing ajax requests to be shown automatically in the Debugbar.
|
||||
| Changing `ajax_handler_auto_show` to false will prevent the Debugbar from reloading.
|
||||
*/
|
||||
|
||||
'capture_ajax' => true,
|
||||
'add_ajax_timing' => false,
|
||||
'ajax_handler_auto_show' => true,
|
||||
'ajax_handler_enable_tab' => true,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Custom Error Handler for Deprecated warnings
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When enabled, the Debugbar shows deprecated warnings for Symfony components
|
||||
| in the Messages tab.
|
||||
|
|
||||
*/
|
||||
'error_handler' => false,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Clockwork integration
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The Debugbar can emulate the Clockwork headers, so you can use the Chrome
|
||||
| Extension, without the server-side code. It uses Debugbar collectors instead.
|
||||
|
|
||||
*/
|
||||
'clockwork' => false,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| DataCollectors
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Enable/disable DataCollectors
|
||||
|
|
||||
*/
|
||||
|
||||
'collectors' => [
|
||||
'phpinfo' => true, // Php version
|
||||
'messages' => true, // Messages
|
||||
'time' => true, // Time Datalogger
|
||||
'memory' => true, // Memory usage
|
||||
'exceptions' => true, // Exception displayer
|
||||
'log' => true, // Logs from Monolog (merged in messages if enabled)
|
||||
'db' => true, // Show database (PDO) queries and bindings
|
||||
'views' => true, // Views with their data
|
||||
'route' => true, // Current route information
|
||||
'auth' => false, // Display Laravel authentication status
|
||||
'gate' => true, // Display Laravel Gate checks
|
||||
'session' => true, // Display session data
|
||||
'symfony_request' => true, // Only one can be enabled..
|
||||
'mail' => true, // Catch mail messages
|
||||
'laravel' => false, // Laravel version and environment
|
||||
'events' => false, // All events fired
|
||||
'default_request' => false, // Regular or special Symfony request logger
|
||||
'logs' => false, // Add the latest log messages
|
||||
'files' => false, // Show the included files
|
||||
'config' => false, // Display config settings
|
||||
'cache' => false, // Display cache events
|
||||
'models' => true, // Display models
|
||||
'livewire' => true, // Display Livewire (when available)
|
||||
'jobs' => false, // Display dispatched jobs
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Extra options
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Configure some DataCollectors
|
||||
|
|
||||
*/
|
||||
|
||||
'options' => [
|
||||
'time' => [
|
||||
'memory_usage' => false, // Calculated by subtracting memory start and end, it may be inaccurate
|
||||
],
|
||||
'messages' => [
|
||||
'trace' => true, // Trace the origin of the debug message
|
||||
],
|
||||
'memory' => [
|
||||
'reset_peak' => false, // run memory_reset_peak_usage before collecting
|
||||
'with_baseline' => false, // Set boot memory usage as memory peak baseline
|
||||
'precision' => 0, // Memory rounding precision
|
||||
],
|
||||
'auth' => [
|
||||
'show_name' => true, // Also show the users name/email in the debugbar
|
||||
'show_guards' => true, // Show the guards that are used
|
||||
],
|
||||
'db' => [
|
||||
'with_params' => true, // Render SQL with the parameters substituted
|
||||
'backtrace' => true, // Use a backtrace to find the origin of the query in your files.
|
||||
'backtrace_exclude_paths' => [], // Paths to exclude from backtrace. (in addition to defaults)
|
||||
'timeline' => false, // Add the queries to the timeline
|
||||
'duration_background' => true, // Show shaded background on each query relative to how long it took to execute.
|
||||
'explain' => [ // Show EXPLAIN output on queries
|
||||
'enabled' => false,
|
||||
'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,
|
||||
'slow_threshold' => false, // Only track queries that last longer than this time in ms
|
||||
'memory_usage' => false, // Show queries memory usage
|
||||
'soft_limit' => 100, // After the soft limit, no parameters/backtrace are captured
|
||||
'hard_limit' => 500, // After the hard limit, queries are ignored
|
||||
],
|
||||
'mail' => [
|
||||
'timeline' => false, // Add mails to the timeline
|
||||
'show_body' => true,
|
||||
],
|
||||
'views' => [
|
||||
'timeline' => false, // Add the views to the timeline (Experimental)
|
||||
'data' => false, //true for all data, 'keys' for only names, false for no parameters.
|
||||
'group' => 50, // Group duplicate views. Pass value to auto-group, or true/false to force
|
||||
'exclude_paths' => [ // Add the paths which you don't want to appear in the views
|
||||
'vendor/filament' // Exclude Filament components by default
|
||||
],
|
||||
],
|
||||
'route' => [
|
||||
'label' => true, // show complete route on bar
|
||||
],
|
||||
'session' => [
|
||||
'hiddens' => [], // hides sensitive values using array paths
|
||||
],
|
||||
'symfony_request' => [
|
||||
'hiddens' => [], // hides sensitive values using array paths, example: request_request.password
|
||||
],
|
||||
'events' => [
|
||||
'data' => false, // collect events data, listeners
|
||||
],
|
||||
'logs' => [
|
||||
'file' => null,
|
||||
],
|
||||
'cache' => [
|
||||
'values' => true, // collect cache values
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Inject Debugbar in Response
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Usually, the debugbar is added just before </body>, by listening to the
|
||||
| Response after the App is done. If you disable this, you have to add them
|
||||
| in your template yourself. See http://phpdebugbar.com/docs/rendering.html
|
||||
|
|
||||
*/
|
||||
|
||||
'inject' => true,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| DebugBar route prefix
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Sometimes you want to set route prefix to be used by DebugBar to load
|
||||
| its resources from. Usually the need comes from misconfigured web server or
|
||||
| from trying to overcome bugs like this: http://trac.nginx.org/nginx/ticket/97
|
||||
|
|
||||
*/
|
||||
'route_prefix' => '_debugbar',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| DebugBar route middleware
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Additional middleware to run on the Debugbar routes
|
||||
*/
|
||||
'route_middleware' => [],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| DebugBar route domain
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| By default DebugBar route served from the same domain that request served.
|
||||
| To override default domain, specify it as a non-empty value.
|
||||
*/
|
||||
'route_domain' => null,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| DebugBar theme
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Switches between light and dark theme. If set to auto it will respect system preferences
|
||||
| Possible values: auto, light, dark
|
||||
*/
|
||||
'theme' => env('DEBUGBAR_THEME', 'auto'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Backtrace stack limit
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| By default, the DebugBar limits the number of frames returned by the 'debug_backtrace()' function.
|
||||
| If you need larger stacktraces, you can increase this number. Setting it to 0 will result in no limit.
|
||||
*/
|
||||
'debug_backtrace_limit' => 50,
|
||||
];
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('phpdebugbar', function (Blueprint $table) {
|
||||
$table->string('id');
|
||||
$table->longText('data');
|
||||
$table->string('meta_utime');
|
||||
$table->dateTime('meta_datetime');
|
||||
$table->string('meta_uri');
|
||||
$table->string('meta_ip');
|
||||
$table->string('meta_method');
|
||||
|
||||
$table->primary('id');
|
||||
$table->index('meta_utime');
|
||||
$table->index('meta_datetime');
|
||||
$table->index('meta_uri');
|
||||
$table->index('meta_ip');
|
||||
$table->index('meta_method');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::drop('phpdebugbar');
|
||||
}
|
||||
};
|
||||
+219
@@ -0,0 +1,219 @@
|
||||
## Debugbar for Laravel
|
||||

|
||||
[](http://choosealicense.com/licenses/mit/)
|
||||
[](https://packagist.org/packages/barryvdh/laravel-debugbar)
|
||||
[](https://packagist.org/packages/barryvdh/laravel-debugbar)
|
||||
[](https://fruitcake.nl/)
|
||||
|
||||
This is a package to integrate [PHP Debug Bar](http://phpdebugbar.com/) with Laravel.
|
||||
It includes a ServiceProvider to register the debugbar and attach it to the output. You can publish assets and configure it through Laravel.
|
||||
It bootstraps some Collectors to work with Laravel and implements a couple custom DataCollectors, specific for Laravel.
|
||||
It is configured to display Redirects and (jQuery) Ajax Requests. (Shown in a dropdown)
|
||||
Read [the documentation](http://phpdebugbar.com/docs/) for more configuration options.
|
||||
|
||||

|
||||
|
||||
|
||||
### Note: Use the DebugBar only in development. Do not use Debugbar on publicly accessible websites, as it will leak information from stored requests (by design). It can also slow the application down (because it has to gather data). So when experiencing slowness, try disabling some of the collectors.
|
||||
|
||||
This package includes some custom collectors:
|
||||
- QueryCollector: Show all queries, including binding + timing
|
||||
- RouteCollector: Show information about the current Route.
|
||||
- ViewCollector: Show the currently loaded views. (Optionally: display the shared data)
|
||||
- EventsCollector: Show all events
|
||||
- LaravelCollector: Show the Laravel version and Environment. (disabled by default)
|
||||
- SymfonyRequestCollector: replaces the RequestCollector with more information about the request/response
|
||||
- LogsCollector: Show the latest log entries from the storage logs. (disabled by default)
|
||||
- FilesCollector: Show the files that are included/required by PHP. (disabled by default)
|
||||
- ConfigCollector: Display the values from the config files. (disabled by default)
|
||||
- CacheCollector: Display all cache events. (disabled by default)
|
||||
|
||||
Bootstraps the following collectors for Laravel:
|
||||
- LogCollector: Show all Log messages
|
||||
- SymfonyMailCollector for Mail
|
||||
|
||||
And the default collectors:
|
||||
- PhpInfoCollector
|
||||
- MessagesCollector
|
||||
- TimeDataCollector (With Booting and Application timing)
|
||||
- MemoryCollector
|
||||
- ExceptionsCollector
|
||||
|
||||
It also provides a facade interface (`Debugbar`) for easy logging Messages, Exceptions and Time
|
||||
|
||||
## Installation
|
||||
|
||||
Require this package with composer. It is recommended to only require the package for development.
|
||||
|
||||
```shell
|
||||
composer require barryvdh/laravel-debugbar --dev
|
||||
```
|
||||
|
||||
Laravel uses Package Auto-Discovery, so doesn't require you to manually add the ServiceProvider.
|
||||
|
||||
The Debugbar will be enabled when `APP_DEBUG` is `true`.
|
||||
|
||||
> If you use a catch-all/fallback route, make sure you load the Debugbar ServiceProvider before your own App ServiceProviders.
|
||||
|
||||
### Laravel without auto-discovery:
|
||||
|
||||
If you don't use auto-discovery, add the ServiceProvider to the providers array in config/app.php
|
||||
|
||||
```php
|
||||
Barryvdh\Debugbar\ServiceProvider::class,
|
||||
```
|
||||
|
||||
If you want to use the facade to log messages, add this to your facades in app.php:
|
||||
|
||||
```php
|
||||
'Debugbar' => Barryvdh\Debugbar\Facades\Debugbar::class,
|
||||
```
|
||||
|
||||
The profiler is enabled by default, if you have APP_DEBUG=true. You can override that in the config (`debugbar.enabled`) or by setting `DEBUGBAR_ENABLED` in your `.env`. See more options in `config/debugbar.php`
|
||||
You can also set in your config if you want to include/exclude the vendor files also (FontAwesome, Highlight.js and jQuery). If you already use them in your site, set it to false.
|
||||
You can also only display the js or css vendors, by setting it to 'js' or 'css'. (Highlight.js requires both css + js, so set to `true` for syntax highlighting)
|
||||
|
||||
#### Copy the package config to your local config with the publish command:
|
||||
|
||||
```shell
|
||||
php artisan vendor:publish --provider="Barryvdh\Debugbar\ServiceProvider"
|
||||
```
|
||||
|
||||
### Laravel with Octane:
|
||||
|
||||
Make sure to add LaravelDebugbar to your flush list in `config/octane.php`.
|
||||
|
||||
```php
|
||||
'flush' => [
|
||||
\Barryvdh\Debugbar\LaravelDebugbar::class,
|
||||
],
|
||||
```
|
||||
|
||||
### Lumen:
|
||||
|
||||
For Lumen, register a different Provider in `bootstrap/app.php`:
|
||||
|
||||
```php
|
||||
if (env('APP_DEBUG')) {
|
||||
$app->register(Barryvdh\Debugbar\LumenServiceProvider::class);
|
||||
}
|
||||
```
|
||||
|
||||
To change the configuration, copy the file to your config folder and enable it:
|
||||
|
||||
```php
|
||||
$app->configure('debugbar');
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
You can now add messages using the Facade (when added), using the PSR-3 levels (debug, info, notice, warning, error, critical, alert, emergency):
|
||||
|
||||
```php
|
||||
Debugbar::info($object);
|
||||
Debugbar::error('Error!');
|
||||
Debugbar::warning('Watch out…');
|
||||
Debugbar::addMessage('Another message', 'mylabel');
|
||||
```
|
||||
|
||||
And start/stop timing:
|
||||
|
||||
```php
|
||||
Debugbar::startMeasure('render','Time for rendering');
|
||||
Debugbar::stopMeasure('render');
|
||||
Debugbar::addMeasure('now', LARAVEL_START, microtime(true));
|
||||
Debugbar::measure('My long operation', function() {
|
||||
// Do something…
|
||||
});
|
||||
```
|
||||
|
||||
Or log exceptions:
|
||||
|
||||
```php
|
||||
try {
|
||||
throw new Exception('foobar');
|
||||
} catch (Exception $e) {
|
||||
Debugbar::addThrowable($e);
|
||||
}
|
||||
```
|
||||
|
||||
There are also helper functions available for the most common calls:
|
||||
|
||||
```php
|
||||
// All arguments will be dumped as a debug message
|
||||
debug($var1, $someString, $intValue, $object);
|
||||
|
||||
// `$collection->debug()` will return the collection and dump it as a debug message. Like `$collection->dump()`
|
||||
collect([$var1, $someString])->debug();
|
||||
|
||||
start_measure('render','Time for rendering');
|
||||
stop_measure('render');
|
||||
add_measure('now', LARAVEL_START, microtime(true));
|
||||
measure('My long operation', function() {
|
||||
// Do something…
|
||||
});
|
||||
```
|
||||
|
||||
If you want you can add your own DataCollectors, through the Container or the Facade:
|
||||
|
||||
```php
|
||||
Debugbar::addCollector(new DebugBar\DataCollector\MessagesCollector('my_messages'));
|
||||
//Or via the App container:
|
||||
$debugbar = App::make('debugbar');
|
||||
$debugbar->addCollector(new DebugBar\DataCollector\MessagesCollector('my_messages'));
|
||||
```
|
||||
|
||||
By default, the Debugbar is injected just before `</body>`. If you want to inject the Debugbar yourself,
|
||||
set the config option 'inject' to false and use the renderer yourself and follow http://phpdebugbar.com/docs/rendering.html
|
||||
|
||||
```php
|
||||
$renderer = Debugbar::getJavascriptRenderer();
|
||||
```
|
||||
|
||||
Note: Not using the auto-inject, will disable the Request information, because that is added After the response.
|
||||
You can add the default_request datacollector in the config as alternative.
|
||||
|
||||
## Enabling/Disabling on run time
|
||||
You can enable or disable the debugbar during run time.
|
||||
|
||||
```php
|
||||
\Debugbar::enable();
|
||||
\Debugbar::disable();
|
||||
```
|
||||
|
||||
NB. Once enabled, the collectors are added (and could produce extra overhead), so if you want to use the debugbar in production, disable in the config and only enable when needed.
|
||||
|
||||
## Storage
|
||||
|
||||
Debugbar remembers previous requests, which you can view using the Browse button on the right. This will only work if you enable `debugbar.storage.open` in the config.
|
||||
Make sure you only do this on local development, because otherwise other people will be able to view previous requests.
|
||||
In general, Debugbar should only be used locally or at least restricted by IP.
|
||||
It's possible to pass a callback, which will receive the Request object, so you can determine access to the OpenHandler storage.
|
||||
|
||||
## Twig Integration
|
||||
|
||||
Laravel Debugbar comes with two Twig Extensions. These are tested with [rcrowe/TwigBridge](https://github.com/rcrowe/TwigBridge) 0.6.x
|
||||
|
||||
Add the following extensions to your TwigBridge config/extensions.php (or register the extensions manually)
|
||||
|
||||
```php
|
||||
'Barryvdh\Debugbar\Twig\Extension\Debug',
|
||||
'Barryvdh\Debugbar\Twig\Extension\Dump',
|
||||
'Barryvdh\Debugbar\Twig\Extension\Stopwatch',
|
||||
```
|
||||
|
||||
The Dump extension will replace the [dump function](http://twig.sensiolabs.org/doc/functions/dump.html) to output variables using the DataFormatter. The Debug extension adds a `debug()` function which passes variables to the Message Collector,
|
||||
instead of showing it directly in the template. It dumps the arguments, or when empty; all context variables.
|
||||
|
||||
```twig
|
||||
{{ debug() }}
|
||||
{{ debug(user, categories) }}
|
||||
```
|
||||
|
||||
The Stopwatch extension adds a [stopwatch tag](http://symfony.com/blog/new-in-symfony-2-4-a-stopwatch-tag-for-twig) similar to the one in Symfony/Silex Twigbridge.
|
||||
|
||||
```twig
|
||||
{% stopwatch "foo" %}
|
||||
…some things that gets timed
|
||||
{% endstopwatch %}
|
||||
```
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace Barryvdh\Debugbar\Console;
|
||||
|
||||
use DebugBar\DebugBar;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class ClearCommand extends Command
|
||||
{
|
||||
protected $name = 'debugbar:clear';
|
||||
protected $description = 'Clear the Debugbar Storage';
|
||||
protected $debugbar;
|
||||
|
||||
public function __construct(DebugBar $debugbar)
|
||||
{
|
||||
$this->debugbar = $debugbar;
|
||||
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$this->debugbar->boot();
|
||||
|
||||
if ($storage = $this->debugbar->getStorage()) {
|
||||
try {
|
||||
$storage->clear();
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
// hide InvalidArgumentException if storage location does not exist
|
||||
if (strpos($e->getMessage(), 'does not exist') === false) {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
$this->info('Debugbar Storage cleared!');
|
||||
} else {
|
||||
$this->error('No Debugbar Storage found..');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace Barryvdh\Debugbar\Controllers;
|
||||
|
||||
use Illuminate\Http\Response;
|
||||
|
||||
class AssetController extends BaseController
|
||||
{
|
||||
/**
|
||||
* Return the javascript for the Debugbar
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
*/
|
||||
public function js()
|
||||
{
|
||||
$renderer = $this->debugbar->getJavascriptRenderer();
|
||||
|
||||
$content = $renderer->dumpAssetsToString('js');
|
||||
|
||||
$response = new Response(
|
||||
$content,
|
||||
200,
|
||||
[
|
||||
'Content-Type' => 'text/javascript',
|
||||
]
|
||||
);
|
||||
|
||||
return $this->cacheResponse($response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the stylesheets for the Debugbar
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
*/
|
||||
public function css()
|
||||
{
|
||||
$renderer = $this->debugbar->getJavascriptRenderer();
|
||||
|
||||
$content = $renderer->dumpAssetsToString('css');
|
||||
|
||||
$response = new Response(
|
||||
$content,
|
||||
200,
|
||||
[
|
||||
'Content-Type' => 'text/css',
|
||||
]
|
||||
);
|
||||
|
||||
return $this->cacheResponse($response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cache the response 1 year (31536000 sec)
|
||||
*/
|
||||
protected function cacheResponse(Response $response)
|
||||
{
|
||||
$response->setSharedMaxAge(31536000);
|
||||
$response->setMaxAge(31536000);
|
||||
$response->setExpires(new \DateTime('+1 year'));
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace Barryvdh\Debugbar\Controllers;
|
||||
|
||||
use Barryvdh\Debugbar\LaravelDebugbar;
|
||||
use Illuminate\Routing\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use Laravel\Telescope\Telescope;
|
||||
|
||||
// phpcs:ignoreFile
|
||||
if (class_exists('Illuminate\Routing\Controller')) {
|
||||
|
||||
class BaseController extends Controller
|
||||
{
|
||||
protected $debugbar;
|
||||
|
||||
public function __construct(Request $request, LaravelDebugbar $debugbar)
|
||||
{
|
||||
$this->debugbar = $debugbar;
|
||||
|
||||
if ($request->hasSession()) {
|
||||
$request->session()->reflash();
|
||||
}
|
||||
|
||||
$this->middleware(function ($request, $next) {
|
||||
if (class_exists(Telescope::class)) {
|
||||
Telescope::stopRecording();
|
||||
}
|
||||
return $next($request);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
class BaseController
|
||||
{
|
||||
protected $debugbar;
|
||||
|
||||
public function __construct(Request $request, LaravelDebugbar $debugbar)
|
||||
{
|
||||
$this->debugbar = $debugbar;
|
||||
|
||||
if ($request->hasSession()) {
|
||||
$request->session()->reflash();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace Barryvdh\Debugbar\Controllers;
|
||||
|
||||
use Illuminate\Http\Response;
|
||||
|
||||
class CacheController extends BaseController
|
||||
{
|
||||
/**
|
||||
* Forget a cache key
|
||||
*
|
||||
*/
|
||||
public function delete($key, $tags = '')
|
||||
{
|
||||
$cache = app('cache');
|
||||
|
||||
if (!empty($tags)) {
|
||||
$tags = json_decode($tags, true);
|
||||
$cache = $cache->tags($tags);
|
||||
} else {
|
||||
unset($tags);
|
||||
}
|
||||
|
||||
$success = $cache->forget($key);
|
||||
|
||||
return response()->json(compact('success'));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
namespace Barryvdh\Debugbar\Controllers;
|
||||
|
||||
use Barryvdh\Debugbar\Support\Clockwork\Converter;
|
||||
use DebugBar\DebugBarException;
|
||||
use DebugBar\OpenHandler;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response;
|
||||
|
||||
class OpenHandlerController extends BaseController
|
||||
{
|
||||
/**
|
||||
* Check if the storage is open for inspecting.
|
||||
*
|
||||
* @param Request $request
|
||||
* @return bool
|
||||
*/
|
||||
protected function isStorageOpen(Request $request)
|
||||
{
|
||||
$open = config('debugbar.storage.open');
|
||||
|
||||
if (is_callable($open)) {
|
||||
return call_user_func($open, [$request]);
|
||||
}
|
||||
|
||||
if (is_string($open) && class_exists($open)) {
|
||||
return method_exists($open, 'resolve') ? $open::resolve($request) : false;
|
||||
}
|
||||
|
||||
if (is_bool($open)) {
|
||||
return $open;
|
||||
}
|
||||
|
||||
// Allow localhost request when not explicitly allowed/disallowed
|
||||
if (in_array($request->ip(), ['127.0.0.1', '::1'], true)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function handle(Request $request)
|
||||
{
|
||||
if ($request->input('op') === 'get' || $this->isStorageOpen($request)) {
|
||||
$openHandler = new OpenHandler($this->debugbar);
|
||||
$data = $openHandler->handle($request->input(), false, false);
|
||||
} else {
|
||||
$data = [
|
||||
[
|
||||
'datetime' => date("Y-m-d H:i:s"),
|
||||
'id' => null,
|
||||
'ip' => $request->getClientIp(),
|
||||
'method' => 'ERROR',
|
||||
'uri' => '!! To enable public access to previous requests, set debugbar.storage.open to true in your config, or enable DEBUGBAR_OPEN_STORAGE if you did not publish the config. !!',
|
||||
'utime' => microtime(true),
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
return new Response(
|
||||
$data,
|
||||
200,
|
||||
[
|
||||
'Content-Type' => 'application/json'
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return Clockwork output
|
||||
*
|
||||
* @param $id
|
||||
* @return mixed
|
||||
* @throws \DebugBar\DebugBarException
|
||||
*/
|
||||
public function clockwork(Request $request, $id)
|
||||
{
|
||||
$request = [
|
||||
'op' => 'get',
|
||||
'id' => $id,
|
||||
];
|
||||
|
||||
$openHandler = new OpenHandler($this->debugbar);
|
||||
$data = $openHandler->handle($request, false, false);
|
||||
|
||||
// Convert to Clockwork
|
||||
$converter = new Converter();
|
||||
$output = $converter->convert(json_decode($data, true));
|
||||
|
||||
return response()->json($output);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace Barryvdh\Debugbar\Controllers;
|
||||
|
||||
use Barryvdh\Debugbar\Support\Clockwork\Converter;
|
||||
use DebugBar\OpenHandler;
|
||||
use Illuminate\Http\Response;
|
||||
use Laravel\Telescope\Contracts\EntriesRepository;
|
||||
use Laravel\Telescope\IncomingEntry;
|
||||
use Laravel\Telescope\Storage\EntryQueryOptions;
|
||||
use Laravel\Telescope\Telescope;
|
||||
|
||||
class TelescopeController extends BaseController
|
||||
{
|
||||
public function show(EntriesRepository $storage, $uuid)
|
||||
{
|
||||
|
||||
$entry = $storage->find($uuid);
|
||||
$result = $storage->get('request', (new EntryQueryOptions())->batchId($entry->batchId))->first();
|
||||
|
||||
return redirect(config('telescope.domain') . '/' . config('telescope.path') . '/requests/' . $result->id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
namespace Barryvdh\Debugbar\DataCollector;
|
||||
|
||||
use DebugBar\DataCollector\TimeDataCollector;
|
||||
use Illuminate\Cache\Events\CacheEvent;
|
||||
use Illuminate\Cache\Events\CacheHit;
|
||||
use Illuminate\Cache\Events\CacheMissed;
|
||||
use Illuminate\Cache\Events\KeyForgotten;
|
||||
use Illuminate\Cache\Events\KeyWritten;
|
||||
use Illuminate\Events\Dispatcher;
|
||||
|
||||
class CacheCollector extends TimeDataCollector
|
||||
{
|
||||
/** @var bool */
|
||||
protected $collectValues;
|
||||
|
||||
/** @var array */
|
||||
protected $classMap = [
|
||||
CacheHit::class => 'hit',
|
||||
CacheMissed::class => 'missed',
|
||||
KeyWritten::class => 'written',
|
||||
KeyForgotten::class => 'forgotten',
|
||||
];
|
||||
|
||||
public function __construct($requestStartTime, $collectValues)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->collectValues = $collectValues;
|
||||
}
|
||||
|
||||
public function onCacheEvent(CacheEvent $event)
|
||||
{
|
||||
$class = get_class($event);
|
||||
$params = get_object_vars($event);
|
||||
|
||||
$label = $this->classMap[$class];
|
||||
|
||||
if (isset($params['value'])) {
|
||||
if ($this->collectValues) {
|
||||
$params['value'] = htmlspecialchars($this->getDataFormatter()->formatVar($event->value));
|
||||
} else {
|
||||
unset($params['value']);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!empty($params['key']) && in_array($label, ['hit', 'written'])) {
|
||||
$params['delete'] = route('debugbar.cache.delete', [
|
||||
'key' => urlencode($params['key']),
|
||||
'tags' => !empty($params['tags']) ? json_encode($params['tags']) : '',
|
||||
]);
|
||||
}
|
||||
|
||||
$time = microtime(true);
|
||||
$this->addMeasure($label . "\t" . $event->key, $time, $time, $params);
|
||||
}
|
||||
|
||||
|
||||
public function subscribe(Dispatcher $dispatcher)
|
||||
{
|
||||
foreach ($this->classMap as $eventClass => $type) {
|
||||
$dispatcher->listen($eventClass, [$this, 'onCacheEvent']);
|
||||
}
|
||||
}
|
||||
|
||||
public function collect()
|
||||
{
|
||||
$data = parent::collect();
|
||||
$data['nb_measures'] = count($data['measures']);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return 'cache';
|
||||
}
|
||||
|
||||
public function getWidgets()
|
||||
{
|
||||
return [
|
||||
'cache' => [
|
||||
'icon' => 'clipboard',
|
||||
'widget' => 'PhpDebugBar.Widgets.LaravelCacheWidget',
|
||||
'map' => 'cache',
|
||||
'default' => '{}',
|
||||
],
|
||||
'cache:badge' => [
|
||||
'map' => 'cache.nb_measures',
|
||||
'default' => 'null',
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
<?php
|
||||
|
||||
namespace Barryvdh\Debugbar\DataCollector;
|
||||
|
||||
use Barryvdh\Debugbar\DataFormatter\SimpleFormatter;
|
||||
use DebugBar\DataCollector\TimeDataCollector;
|
||||
use Illuminate\Contracts\Events\Dispatcher;
|
||||
use Illuminate\Support\Str;
|
||||
use Symfony\Component\VarDumper\Cloner\VarCloner;
|
||||
|
||||
class EventCollector extends TimeDataCollector
|
||||
{
|
||||
/** @var Dispatcher */
|
||||
protected $events;
|
||||
|
||||
/** @var integer */
|
||||
protected $previousTime;
|
||||
|
||||
/** @var bool */
|
||||
protected $collectValues;
|
||||
|
||||
public function __construct($requestStartTime = null, $collectValues = false)
|
||||
{
|
||||
parent::__construct($requestStartTime);
|
||||
$this->previousTime = microtime(true);
|
||||
$this->collectValues = $collectValues;
|
||||
$this->setDataFormatter(new SimpleFormatter());
|
||||
}
|
||||
|
||||
public function onWildcardEvent($name = null, $data = [])
|
||||
{
|
||||
$currentTime = microtime(true);
|
||||
|
||||
if (! $this->collectValues) {
|
||||
$this->addMeasure($name, $this->previousTime, $currentTime);
|
||||
$this->previousTime = $currentTime;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$params = $this->prepareParams($data);
|
||||
|
||||
// Find all listeners for the current event
|
||||
foreach ($this->events->getListeners($name) as $i => $listener) {
|
||||
// Check if it's an object + method name
|
||||
if (is_array($listener) && count($listener) > 1 && is_object($listener[0])) {
|
||||
list($class, $method) = $listener;
|
||||
|
||||
// Skip this class itself
|
||||
if ($class instanceof static) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Format the listener to readable format
|
||||
$listener = get_class($class) . '@' . $method;
|
||||
|
||||
// Handle closures
|
||||
} elseif ($listener instanceof \Closure) {
|
||||
$reflector = new \ReflectionFunction($listener);
|
||||
|
||||
// Skip our own listeners
|
||||
if ($reflector->getNamespaceName() == 'Barryvdh\Debugbar') {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Format the closure to a readable format
|
||||
$filename = ltrim(str_replace(base_path(), '', $reflector->getFileName()), '/');
|
||||
$lines = $reflector->getStartLine() . '-' . $reflector->getEndLine();
|
||||
$listener = $reflector->getName() . ' (' . $filename . ':' . $lines . ')';
|
||||
} else {
|
||||
// Not sure if this is possible, but to prevent edge cases
|
||||
$listener = $this->getDataFormatter()->formatVar($listener);
|
||||
}
|
||||
|
||||
$params['listeners.' . $i] = $listener;
|
||||
}
|
||||
$this->addMeasure($name, $this->previousTime, $currentTime, $params);
|
||||
$this->previousTime = $currentTime;
|
||||
}
|
||||
|
||||
public function subscribe(Dispatcher $events)
|
||||
{
|
||||
$this->events = $events;
|
||||
$events->listen('*', [$this, 'onWildcardEvent']);
|
||||
}
|
||||
|
||||
protected function prepareParams($params)
|
||||
{
|
||||
$data = [];
|
||||
foreach ($params as $key => $value) {
|
||||
if (is_object($value) && Str::is('Illuminate\*\Events\*', get_class($value))) {
|
||||
$value = $this->prepareParams(get_object_vars($value));
|
||||
}
|
||||
$data[$key] = htmlentities($this->getDataFormatter()->formatVar($value), ENT_QUOTES, 'UTF-8', false);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function collect()
|
||||
{
|
||||
$data = parent::collect();
|
||||
$data['nb_measures'] = count($data['measures']);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return 'event';
|
||||
}
|
||||
|
||||
public function getWidgets()
|
||||
{
|
||||
return [
|
||||
"events" => [
|
||||
"icon" => "tasks",
|
||||
"widget" => "PhpDebugBar.Widgets.TimelineWidget",
|
||||
"map" => "event",
|
||||
"default" => "{}",
|
||||
],
|
||||
'events:badge' => [
|
||||
'map' => 'event.nb_measures',
|
||||
'default' => 0,
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
<?php
|
||||
|
||||
namespace Barryvdh\Debugbar\DataCollector;
|
||||
|
||||
use DebugBar\DataCollector\DataCollector;
|
||||
use DebugBar\DataCollector\Renderable;
|
||||
use Illuminate\Container\Container;
|
||||
|
||||
class FilesCollector extends DataCollector implements Renderable
|
||||
{
|
||||
/** @var \Illuminate\Container\Container */
|
||||
protected $app;
|
||||
protected $basePath;
|
||||
|
||||
/**
|
||||
* @param \Illuminate\Container\Container $app
|
||||
*/
|
||||
public function __construct(Container $app = null)
|
||||
{
|
||||
$this->app = $app;
|
||||
$this->basePath = base_path();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function collect()
|
||||
{
|
||||
$files = $this->getIncludedFiles();
|
||||
$compiled = $this->getCompiledFiles();
|
||||
|
||||
$included = [];
|
||||
$alreadyCompiled = [];
|
||||
foreach ($files as $file) {
|
||||
// Skip the files from Debugbar, they are only loaded for Debugging and confuse the output.
|
||||
// Of course some files are stil always loaded (ServiceProvider, Facade etc)
|
||||
if (
|
||||
strpos($file, 'vendor/maximebf/debugbar/src') !== false || strpos(
|
||||
$file,
|
||||
'vendor/barryvdh/laravel-debugbar/src'
|
||||
) !== false
|
||||
) {
|
||||
continue;
|
||||
} elseif (!in_array($file, $compiled)) {
|
||||
$included[] = [
|
||||
'message' => "'" . $this->stripBasePath($file) . "',",
|
||||
// Use PHP syntax so we can copy-paste to compile config file.
|
||||
'is_string' => true,
|
||||
];
|
||||
} else {
|
||||
$alreadyCompiled[] = [
|
||||
'message' => "* '" . $this->stripBasePath($file) . "',",
|
||||
// Mark with *, so know they are compiled anyway.
|
||||
'is_string' => true,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// First the included files, then those that are going to be compiled.
|
||||
$messages = array_merge($included, $alreadyCompiled);
|
||||
|
||||
return [
|
||||
'messages' => $messages,
|
||||
'count' => count($included),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the files included on load.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getIncludedFiles()
|
||||
{
|
||||
return get_included_files();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the files that are going to be compiled, so they aren't as important.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getCompiledFiles()
|
||||
{
|
||||
if ($this->app && class_exists('Illuminate\Foundation\Console\OptimizeCommand')) {
|
||||
$reflector = new \ReflectionClass('Illuminate\Foundation\Console\OptimizeCommand');
|
||||
$path = dirname($reflector->getFileName()) . '/Optimize/config.php';
|
||||
|
||||
if (file_exists($path)) {
|
||||
$app = $this->app;
|
||||
$core = require $path;
|
||||
return array_merge($core, $app['config']['compile']);
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the basePath from the paths, so they are relative to the base
|
||||
*
|
||||
* @param $path
|
||||
* @return string
|
||||
*/
|
||||
protected function stripBasePath($path)
|
||||
{
|
||||
return ltrim(str_replace($this->basePath, '', $path), '/');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getWidgets()
|
||||
{
|
||||
$name = $this->getName();
|
||||
return [
|
||||
"$name" => [
|
||||
"icon" => "files-o",
|
||||
"widget" => "PhpDebugBar.Widgets.MessagesWidget",
|
||||
"map" => "$name.messages",
|
||||
"default" => "{}"
|
||||
],
|
||||
"$name:badge" => [
|
||||
"map" => "$name.count",
|
||||
"default" => "null"
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'files';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace Barryvdh\Debugbar\DataCollector;
|
||||
|
||||
use Barryvdh\Debugbar\DataFormatter\SimpleFormatter;
|
||||
use DebugBar\DataCollector\MessagesCollector;
|
||||
use Illuminate\Auth\Access\Response;
|
||||
use Illuminate\Contracts\Auth\Access\Gate;
|
||||
use Illuminate\Contracts\Auth\Authenticatable;
|
||||
use Symfony\Component\VarDumper\Cloner\VarCloner;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* Collector for Laravel's Auth provider
|
||||
*/
|
||||
class GateCollector extends MessagesCollector
|
||||
{
|
||||
/**
|
||||
* @param Gate $gate
|
||||
*/
|
||||
public function __construct(Gate $gate)
|
||||
{
|
||||
parent::__construct('gate');
|
||||
$this->setDataFormatter(new SimpleFormatter());
|
||||
$gate->after(function ($user, $ability, $result, $arguments = []) {
|
||||
$this->addCheck($user, $ability, $result, $arguments);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function customizeMessageHtml($messageHtml, $message)
|
||||
{
|
||||
$pos = strpos((string) $messageHtml, 'array:4');
|
||||
if ($pos !== false) {
|
||||
$messageHtml = substr_replace($messageHtml, $message['ability'], $pos, 7);
|
||||
}
|
||||
|
||||
return parent::customizeMessageHtml($messageHtml, $message);
|
||||
}
|
||||
|
||||
public function addCheck($user, $ability, $result, $arguments = [])
|
||||
{
|
||||
$userKey = 'user';
|
||||
$userId = null;
|
||||
|
||||
if ($user) {
|
||||
$userKey = Str::snake(class_basename($user));
|
||||
$userId = $user instanceof Authenticatable ? $user->getAuthIdentifier() : $user->getKey();
|
||||
}
|
||||
|
||||
$label = $result ? 'success' : 'error';
|
||||
|
||||
if ($result instanceof Response) {
|
||||
$label = $result->allowed() ? 'success' : 'error';
|
||||
}
|
||||
|
||||
$this->addMessage([
|
||||
'ability' => $ability,
|
||||
'result' => $result,
|
||||
$userKey => $userId,
|
||||
'arguments' => $this->getDataFormatter()->formatVar($arguments),
|
||||
], $label, false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
namespace Barryvdh\Debugbar\DataCollector;
|
||||
|
||||
use DebugBar\DataCollector\DataCollector;
|
||||
use DebugBar\DataCollector\DataCollectorInterface;
|
||||
use DebugBar\DataCollector\Renderable;
|
||||
use Illuminate\Contracts\Events\Dispatcher;
|
||||
|
||||
/**
|
||||
* @deprecated in favor of \DebugBar\DataCollector\ObjectCountCollector
|
||||
*/
|
||||
class JobsCollector extends DataCollector implements DataCollectorInterface, Renderable
|
||||
{
|
||||
public $jobs = [];
|
||||
public $count = 0;
|
||||
|
||||
/**
|
||||
* @param Dispatcher $events
|
||||
*/
|
||||
public function __construct(Dispatcher $events)
|
||||
{
|
||||
$events->listen(\Illuminate\Queue\Events\JobQueued::class, function ($event) {
|
||||
$class = get_class($event->job);
|
||||
$this->jobs[$class] = ($this->jobs[$class] ?? 0) + 1;
|
||||
$this->count++;
|
||||
});
|
||||
}
|
||||
|
||||
public function collect()
|
||||
{
|
||||
ksort($this->jobs, SORT_NUMERIC);
|
||||
|
||||
return ['data' => array_reverse($this->jobs), 'count' => $this->count];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'jobs';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getWidgets()
|
||||
{
|
||||
return [
|
||||
"jobs" => [
|
||||
"icon" => "briefcase",
|
||||
"widget" => "PhpDebugBar.Widgets.HtmlVariableListWidget",
|
||||
"map" => "jobs.data",
|
||||
"default" => "{}"
|
||||
],
|
||||
'jobs:badge' => [
|
||||
'map' => 'jobs.count',
|
||||
'default' => 0
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
namespace Barryvdh\Debugbar\DataCollector;
|
||||
|
||||
use DebugBar\DataCollector\DataCollector;
|
||||
use DebugBar\DataCollector\Renderable;
|
||||
use Illuminate\Foundation\Application;
|
||||
|
||||
class LaravelCollector extends DataCollector implements Renderable
|
||||
{
|
||||
/** @var \Illuminate\Foundation\Application $app */
|
||||
protected $app;
|
||||
|
||||
/**
|
||||
* @param Application $app
|
||||
*/
|
||||
public function __construct(Application $app = null)
|
||||
{
|
||||
$this->app = $app;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function collect()
|
||||
{
|
||||
// Fallback if not injected
|
||||
$app = $this->app ?: app();
|
||||
|
||||
return [
|
||||
"version" => $app::VERSION,
|
||||
"environment" => $app->environment(),
|
||||
"locale" => $app->getLocale(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'laravel';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getWidgets()
|
||||
{
|
||||
return [
|
||||
"version" => [
|
||||
"icon" => "github",
|
||||
"tooltip" => "Laravel Version",
|
||||
"map" => "laravel.version",
|
||||
"default" => ""
|
||||
],
|
||||
"environment" => [
|
||||
"icon" => "desktop",
|
||||
"tooltip" => "Environment",
|
||||
"map" => "laravel.environment",
|
||||
"default" => ""
|
||||
],
|
||||
"locale" => [
|
||||
"icon" => "flag",
|
||||
"tooltip" => "Current locale",
|
||||
"map" => "laravel.locale",
|
||||
"default" => "",
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
namespace Barryvdh\Debugbar\DataCollector;
|
||||
|
||||
use DebugBar\DataCollector\DataCollector;
|
||||
use DebugBar\DataCollector\DataCollectorInterface;
|
||||
use DebugBar\DataCollector\Renderable;
|
||||
use Illuminate\Contracts\Events\Dispatcher;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Fluent;
|
||||
use Illuminate\Support\Str;
|
||||
use Livewire\Livewire;
|
||||
use Livewire\Component;
|
||||
|
||||
/**
|
||||
* Collector for Models.
|
||||
*/
|
||||
class LivewireCollector extends DataCollector implements DataCollectorInterface, Renderable
|
||||
{
|
||||
public $data = [];
|
||||
|
||||
public function __construct(Request $request)
|
||||
{
|
||||
// Listen to Livewire views
|
||||
Livewire::listen('view:render', function (View $view) use ($request) {
|
||||
/** @var \Livewire\Component $component */
|
||||
$component = $view->getData()['_instance'];
|
||||
|
||||
// Create a unique name for each component
|
||||
$key = $component->getName() . ' #' . $component->id;
|
||||
|
||||
$data = [
|
||||
'data' => $component->getPublicPropertiesDefinedBySubClass(),
|
||||
];
|
||||
|
||||
if ($request->request->get('id') == $component->id) {
|
||||
$data['oldData'] = $request->request->get('data');
|
||||
$data['actionQueue'] = $request->request->get('actionQueue');
|
||||
}
|
||||
|
||||
$data['name'] = $component->getName();
|
||||
$data['view'] = $view->name();
|
||||
$data['component'] = get_class($component);
|
||||
$data['id'] = $component->id;
|
||||
|
||||
$this->data[$key] = $this->formatVar($data);
|
||||
});
|
||||
|
||||
Livewire::listen('render', function (Component $component) use ($request) {
|
||||
// Create an unique name for each compoent
|
||||
$key = $component->getName() . ' #' . $component->getId();
|
||||
|
||||
$data = [
|
||||
'data' => $component->all(),
|
||||
];
|
||||
|
||||
if ($request->request->get('id') == $component->getId()) {
|
||||
$data['oldData'] = $request->request->get('data');
|
||||
$data['actionQueue'] = $request->request->get('actionQueue');
|
||||
}
|
||||
|
||||
$data['name'] = $component->getName();
|
||||
$data['component'] = get_class($component);
|
||||
$data['id'] = $component->getId();
|
||||
|
||||
$this->data[$key] = $this->formatVar($data);
|
||||
});
|
||||
}
|
||||
|
||||
public function collect()
|
||||
{
|
||||
return ['data' => $this->data, 'count' => count($this->data)];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'livewire';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getWidgets()
|
||||
{
|
||||
return [
|
||||
"livewire" => [
|
||||
"icon" => "bolt",
|
||||
"widget" => "PhpDebugBar.Widgets.VariableListWidget",
|
||||
"map" => "livewire.data",
|
||||
"default" => "{}"
|
||||
],
|
||||
'livewire:badge' => [
|
||||
'map' => 'livewire.count',
|
||||
'default' => 0
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
<?php
|
||||
|
||||
namespace Barryvdh\Debugbar\DataCollector;
|
||||
|
||||
use DebugBar\DataCollector\MessagesCollector;
|
||||
use Illuminate\Support\Arr;
|
||||
use Psr\Log\LogLevel;
|
||||
use ReflectionClass;
|
||||
|
||||
class LogsCollector extends MessagesCollector
|
||||
{
|
||||
protected $lines = 124;
|
||||
|
||||
public function __construct($path = null, $name = 'logs')
|
||||
{
|
||||
parent::__construct($name);
|
||||
|
||||
$paths = Arr::wrap($path ?: [
|
||||
storage_path('logs/laravel.log'),
|
||||
storage_path('logs/laravel-' . date('Y-m-d') . '.log'), // for daily driver
|
||||
]);
|
||||
|
||||
foreach ($paths as $path) {
|
||||
$this->getStorageLogs($path);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* get logs apache in app/storage/logs
|
||||
* only 24 last of current day
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getStorageLogs($path)
|
||||
{
|
||||
if (!file_exists($path)) {
|
||||
return;
|
||||
}
|
||||
|
||||
//Load the latest lines, guessing about 15x the number of log entries (for stack traces etc)
|
||||
$file = implode("", $this->tailFile($path, $this->lines));
|
||||
$basename = basename($path);
|
||||
|
||||
foreach ($this->getLogs($file) as $log) {
|
||||
$this->messages[] = [
|
||||
'message' => $log['header'] . $log['stack'],
|
||||
'label' => $log['level'],
|
||||
'time' => substr($log['header'], 1, 19),
|
||||
'collector' => $basename,
|
||||
'is_string' => false,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* By Ain Tohvri (ain)
|
||||
* http://tekkie.flashbit.net/php/tail-functionality-in-php
|
||||
* @param string $file
|
||||
* @param int $lines
|
||||
* @return array
|
||||
*/
|
||||
protected function tailFile($file, $lines)
|
||||
{
|
||||
$handle = fopen($file, "r");
|
||||
$linecounter = $lines;
|
||||
$pos = -2;
|
||||
$beginning = false;
|
||||
$text = [];
|
||||
while ($linecounter > 0) {
|
||||
$t = " ";
|
||||
while ($t != "\n") {
|
||||
if (fseek($handle, $pos, SEEK_END) == -1) {
|
||||
$beginning = true;
|
||||
break;
|
||||
}
|
||||
$t = fgetc($handle);
|
||||
$pos--;
|
||||
}
|
||||
$linecounter--;
|
||||
if ($beginning) {
|
||||
rewind($handle);
|
||||
}
|
||||
$text[$lines - $linecounter - 1] = fgets($handle);
|
||||
if ($beginning) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
fclose($handle);
|
||||
return array_reverse($text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search a string for log entries
|
||||
* Based on https://github.com/mikemand/logviewer/blob/master/src/Kmd/Logviewer/Logviewer.php by mikemand
|
||||
*
|
||||
* @param $file
|
||||
* @return array
|
||||
*/
|
||||
public function getLogs($file)
|
||||
{
|
||||
$pattern = "/\[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\].*/";
|
||||
|
||||
$log_levels = $this->getLevels();
|
||||
|
||||
// There has GOT to be a better way of doing this...
|
||||
preg_match_all($pattern, $file, $headings);
|
||||
$log_data = preg_split($pattern, $file);
|
||||
|
||||
$log = [];
|
||||
foreach ($headings as $h) {
|
||||
for ($i = 0, $j = count($h); $i < $j; $i++) {
|
||||
foreach ($log_levels as $ll) {
|
||||
if (strpos(strtolower($h[$i]), strtolower('.' . $ll))) {
|
||||
$log[] = ['level' => $ll, 'header' => $h[$i], 'stack' => $log_data[$i]];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $log;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getMessages()
|
||||
{
|
||||
return array_reverse(parent::getMessages());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the log levels from psr/log.
|
||||
* Based on https://github.com/mikemand/logviewer/blob/master/src/Kmd/Logviewer/Logviewer.php by mikemand
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getLevels()
|
||||
{
|
||||
$class = new ReflectionClass(new LogLevel());
|
||||
return $class->getConstants();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace Barryvdh\Debugbar\DataCollector;
|
||||
|
||||
use DebugBar\DataCollector\DataCollector;
|
||||
use DebugBar\DataCollector\DataCollectorInterface;
|
||||
use DebugBar\DataCollector\Renderable;
|
||||
use Illuminate\Contracts\Events\Dispatcher;
|
||||
|
||||
/**
|
||||
* Collector for Models.
|
||||
* @deprecated in favor of \DebugBar\DataCollector\ObjectCountCollector
|
||||
*/
|
||||
class ModelsCollector extends DataCollector implements DataCollectorInterface, Renderable
|
||||
{
|
||||
public $models = [];
|
||||
public $count = 0;
|
||||
|
||||
/**
|
||||
* @param Dispatcher $events
|
||||
*/
|
||||
public function __construct(Dispatcher $events)
|
||||
{
|
||||
$events->listen('eloquent.retrieved:*', function ($event, $models) {
|
||||
foreach (array_filter($models) as $model) {
|
||||
$class = get_class($model);
|
||||
$this->models[$class] = ($this->models[$class] ?? 0) + 1;
|
||||
$this->count++;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public function collect()
|
||||
{
|
||||
ksort($this->models, SORT_NUMERIC);
|
||||
|
||||
return ['data' => array_reverse($this->models), 'count' => $this->count];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'models';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getWidgets()
|
||||
{
|
||||
return [
|
||||
"models" => [
|
||||
"icon" => "cubes",
|
||||
"widget" => "PhpDebugBar.Widgets.HtmlVariableListWidget",
|
||||
"map" => "models.data",
|
||||
"default" => "{}"
|
||||
],
|
||||
'models:badge' => [
|
||||
'map' => 'models.count',
|
||||
'default' => 0
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
<?php
|
||||
|
||||
namespace Barryvdh\Debugbar\DataCollector;
|
||||
|
||||
use DebugBar\DataCollector\DataCollector;
|
||||
use DebugBar\DataCollector\Renderable;
|
||||
use Illuminate\Auth\Recaller;
|
||||
use Illuminate\Auth\SessionGuard;
|
||||
use Illuminate\Contracts\Auth\Authenticatable;
|
||||
use Illuminate\Contracts\Auth\Guard;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Contracts\Support\Arrayable;
|
||||
|
||||
/**
|
||||
* Collector for Laravel's Auth provider
|
||||
*/
|
||||
class MultiAuthCollector extends DataCollector implements Renderable
|
||||
{
|
||||
/** @var array $guards */
|
||||
protected $guards;
|
||||
|
||||
/** @var \Illuminate\Auth\AuthManager */
|
||||
protected $auth;
|
||||
|
||||
/** @var bool */
|
||||
protected $showName = false;
|
||||
|
||||
/** @var bool */
|
||||
protected $showGuardsData = true;
|
||||
|
||||
/**
|
||||
* @param \Illuminate\Auth\AuthManager $auth
|
||||
* @param array $guards
|
||||
*/
|
||||
public function __construct($auth, $guards)
|
||||
{
|
||||
$this->auth = $auth;
|
||||
$this->guards = $guards;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to show the users name/email
|
||||
* @param bool $showName
|
||||
*/
|
||||
public function setShowName($showName)
|
||||
{
|
||||
$this->showName = (bool) $showName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to hide the guards tab, and show only name
|
||||
* @param bool $showGuardsData
|
||||
*/
|
||||
public function setShowGuardsData($showGuardsData)
|
||||
{
|
||||
$this->showGuardsData = (bool) $showGuardsData;
|
||||
}
|
||||
|
||||
/**
|
||||
* @{inheritDoc}
|
||||
*/
|
||||
public function collect()
|
||||
{
|
||||
$data = [
|
||||
'guards' => [],
|
||||
];
|
||||
$names = '';
|
||||
|
||||
foreach ($this->guards as $guardName => $config) {
|
||||
try {
|
||||
$guard = $this->auth->guard($guardName);
|
||||
if ($this->hasUser($guard)) {
|
||||
$user = $guard->user();
|
||||
|
||||
if (!is_null($user)) {
|
||||
$data['guards'][$guardName] = $this->getUserInformation($user);
|
||||
$names .= $guardName . ": " . $data['guards'][$guardName]['name'] . ', ';
|
||||
}
|
||||
} else {
|
||||
$data['guards'][$guardName] = null;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($data['guards'] as $key => $var) {
|
||||
if (!is_string($data['guards'][$key])) {
|
||||
$data['guards'][$key] = $this->formatVar($var);
|
||||
}
|
||||
}
|
||||
|
||||
$data['names'] = rtrim($names, ', ');
|
||||
if (!$this->showGuardsData) {
|
||||
unset($data['guards']);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
private function hasUser(Guard $guard)
|
||||
{
|
||||
if (method_exists($guard, 'hasUser')) {
|
||||
return $guard->hasUser();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get displayed user information
|
||||
* @param \Illuminate\Auth\UserInterface $user
|
||||
* @return array
|
||||
*/
|
||||
protected function getUserInformation($user = null)
|
||||
{
|
||||
// Defaults
|
||||
if (is_null($user)) {
|
||||
return [
|
||||
'name' => 'Guest',
|
||||
'user' => ['guest' => true],
|
||||
];
|
||||
}
|
||||
|
||||
// The default auth identifer is the ID number, which isn't all that
|
||||
// useful. Try username, email and name.
|
||||
$identifier = $user instanceof Authenticatable ? $user->getAuthIdentifier() : $user->getKey();
|
||||
if (is_numeric($identifier) || Str::isUuid($identifier) || Str::isUlid($identifier)) {
|
||||
try {
|
||||
if (isset($user->username)) {
|
||||
$identifier = $user->username;
|
||||
} elseif (isset($user->email)) {
|
||||
$identifier = $user->email;
|
||||
} elseif (isset($user->name)) {
|
||||
$identifier = Str::limit($user->name, 24);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'name' => $identifier,
|
||||
'user' => $user instanceof Arrayable ? $user->toArray() : $user,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @{inheritDoc}
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'auth';
|
||||
}
|
||||
|
||||
/**
|
||||
* @{inheritDoc}
|
||||
*/
|
||||
public function getWidgets()
|
||||
{
|
||||
$widgets = [];
|
||||
|
||||
if ($this->showGuardsData) {
|
||||
$widgets["auth"] = [
|
||||
"icon" => "lock",
|
||||
"widget" => "PhpDebugBar.Widgets.VariableListWidget",
|
||||
"map" => "auth.guards",
|
||||
"default" => "{}",
|
||||
];
|
||||
}
|
||||
|
||||
if ($this->showName) {
|
||||
$widgets['auth.name'] = [
|
||||
'icon' => 'user',
|
||||
'tooltip' => 'Auth status',
|
||||
'map' => 'auth.names',
|
||||
'default' => '',
|
||||
];
|
||||
}
|
||||
|
||||
return $widgets;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,685 @@
|
||||
<?php
|
||||
|
||||
namespace Barryvdh\Debugbar\DataCollector;
|
||||
|
||||
use DebugBar\DataCollector\PDO\PDOCollector;
|
||||
use DebugBar\DataCollector\TimeDataCollector;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* Collects data about SQL statements executed with PDO
|
||||
*/
|
||||
class QueryCollector extends PDOCollector
|
||||
{
|
||||
protected $timeCollector;
|
||||
protected $queries = [];
|
||||
protected $queryCount = 0;
|
||||
protected $softLimit = null;
|
||||
protected $hardLimit = null;
|
||||
protected $lastMemoryUsage;
|
||||
protected $renderSqlWithParams = false;
|
||||
protected $findSource = false;
|
||||
protected $middleware = [];
|
||||
protected $durationBackground = true;
|
||||
protected $explainQuery = false;
|
||||
protected $explainTypes = ['SELECT']; // ['SELECT', 'INSERT', 'UPDATE', 'DELETE']; for MySQL 5.6.3+
|
||||
protected $showHints = false;
|
||||
protected $showCopyButton = false;
|
||||
protected $reflection = [];
|
||||
protected $backtraceExcludePaths = [
|
||||
'/vendor/laravel/framework/src/Illuminate/Support',
|
||||
'/vendor/laravel/framework/src/Illuminate/Database',
|
||||
'/vendor/laravel/framework/src/Illuminate/Events',
|
||||
'/vendor/laravel/framework/src/Illuminate/Collections',
|
||||
'/vendor/october/rain',
|
||||
'/vendor/barryvdh/laravel-debugbar',
|
||||
];
|
||||
|
||||
/**
|
||||
* @param TimeDataCollector $timeCollector
|
||||
*/
|
||||
public function __construct(TimeDataCollector $timeCollector = null)
|
||||
{
|
||||
$this->timeCollector = $timeCollector;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int|null $softLimit After the soft limit, no parameters/backtrace are captured
|
||||
* @param int|null $hardLimit After the hard limit, queries are ignored
|
||||
* @return void
|
||||
*/
|
||||
public function setLimits(?int $softLimit, ?int $hardLimit): void
|
||||
{
|
||||
$this->softLimit = $softLimit;
|
||||
$this->hardLimit = $hardLimit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the SQL of traced statements with params embedded
|
||||
*
|
||||
* @param boolean $enabled
|
||||
* @param string $quotationChar NOT USED
|
||||
*/
|
||||
public function setRenderSqlWithParams($enabled = true, $quotationChar = "'")
|
||||
{
|
||||
$this->renderSqlWithParams = $enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show or hide the hints in the parameters
|
||||
*
|
||||
* @param boolean $enabled
|
||||
*/
|
||||
public function setShowHints($enabled = true)
|
||||
{
|
||||
$this->showHints = $enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show or hide copy button next to the queries
|
||||
*
|
||||
* @param boolean $enabled
|
||||
*/
|
||||
public function setShowCopyButton($enabled = true)
|
||||
{
|
||||
$this->showCopyButton = $enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable/disable finding the source
|
||||
*
|
||||
* @param bool|int $value
|
||||
* @param array $middleware
|
||||
*/
|
||||
public function setFindSource($value, array $middleware)
|
||||
{
|
||||
$this->findSource = $value;
|
||||
$this->middleware = $middleware;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set additional paths to exclude from the backtrace
|
||||
*
|
||||
* @param array $excludePaths Array of file paths to exclude from backtrace
|
||||
*/
|
||||
public function mergeBacktraceExcludePaths(array $excludePaths)
|
||||
{
|
||||
$this->backtraceExcludePaths = array_merge($this->backtraceExcludePaths, $excludePaths);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable/disable the shaded duration background on queries
|
||||
*
|
||||
* @param bool $enabled
|
||||
*/
|
||||
public function setDurationBackground($enabled = true)
|
||||
{
|
||||
$this->durationBackground = $enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable/disable the EXPLAIN queries
|
||||
*
|
||||
* @param bool $enabled
|
||||
* @param array|null $types Array of types to explain queries (select/insert/update/delete)
|
||||
*/
|
||||
public function setExplainSource($enabled, $types)
|
||||
{
|
||||
$this->explainQuery = $enabled;
|
||||
// workaround ['SELECT'] only. https://github.com/barryvdh/laravel-debugbar/issues/888
|
||||
// if($types){
|
||||
// $this->explainTypes = $types;
|
||||
// }
|
||||
}
|
||||
|
||||
public function startMemoryUsage()
|
||||
{
|
||||
$this->lastMemoryUsage = memory_get_usage(false);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param \Illuminate\Database\Events\QueryExecuted $query
|
||||
*/
|
||||
public function addQuery($query)
|
||||
{
|
||||
$this->queryCount++;
|
||||
|
||||
if ($this->hardLimit && $this->queryCount > $this->hardLimit) {
|
||||
return;
|
||||
}
|
||||
|
||||
$limited = $this->softLimit && $this->queryCount > $this->softLimit;
|
||||
|
||||
$sql = (string) $query->sql;
|
||||
$explainResults = [];
|
||||
$time = $query->time / 1000;
|
||||
$endTime = microtime(true);
|
||||
$startTime = $endTime - $time;
|
||||
$hints = $this->performQueryAnalysis($sql);
|
||||
|
||||
$pdo = null;
|
||||
try {
|
||||
$pdo = $query->connection->getPdo();
|
||||
} catch (\Throwable $e) {
|
||||
// ignore error for non-pdo laravel drivers
|
||||
}
|
||||
$bindings = $query->connection->prepareBindings($query->bindings);
|
||||
|
||||
// Run EXPLAIN on this query (if needed)
|
||||
if (!$limited && $this->explainQuery && $pdo && preg_match('/^\s*(' . implode('|', $this->explainTypes) . ') /i', $sql)) {
|
||||
$statement = $pdo->prepare('EXPLAIN ' . $sql);
|
||||
$statement->execute($bindings);
|
||||
$explainResults = $statement->fetchAll(\PDO::FETCH_CLASS);
|
||||
}
|
||||
|
||||
$bindings = $this->getDataFormatter()->checkBindings($bindings);
|
||||
if (!empty($bindings) && $this->renderSqlWithParams) {
|
||||
foreach ($bindings as $key => $binding) {
|
||||
// This regex matches placeholders only, not the question marks,
|
||||
// nested in quotes, while we iterate through the bindings
|
||||
// and substitute placeholders by suitable values.
|
||||
$regex = is_numeric($key)
|
||||
? "/(?<!\?)\?(?=(?:[^'\\\']*'[^'\\']*')*[^'\\\']*$)(?!\?)/"
|
||||
: "/:{$key}(?=(?:[^'\\\']*'[^'\\\']*')*[^'\\\']*$)/";
|
||||
|
||||
// Mimic bindValue and only quote non-integer and non-float data types
|
||||
if (!is_int($binding) && !is_float($binding)) {
|
||||
if ($pdo) {
|
||||
try {
|
||||
$binding = $pdo->quote((string) $binding);
|
||||
} catch (\Exception $e) {
|
||||
$binding = $this->emulateQuote($binding);
|
||||
}
|
||||
} else {
|
||||
$binding = $this->emulateQuote($binding);
|
||||
}
|
||||
}
|
||||
|
||||
$sql = preg_replace($regex, addcslashes($binding, '$'), $sql, 1);
|
||||
}
|
||||
}
|
||||
|
||||
$source = [];
|
||||
|
||||
if (!$limited && $this->findSource) {
|
||||
try {
|
||||
$source = $this->findSource();
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
}
|
||||
|
||||
$this->queries[] = [
|
||||
'query' => $sql,
|
||||
'type' => 'query',
|
||||
'bindings' => !$limited ? $this->getDataFormatter()->escapeBindings($bindings) : null,
|
||||
'start' => $startTime,
|
||||
'time' => $time,
|
||||
'memory' => $this->lastMemoryUsage ? memory_get_usage(false) - $this->lastMemoryUsage : 0,
|
||||
'source' => $source,
|
||||
'explain' => $explainResults,
|
||||
'connection' => $query->connection->getDatabaseName(),
|
||||
'driver' => $query->connection->getConfig('driver'),
|
||||
'hints' => ($this->showHints && !$limited) ? $hints : null,
|
||||
'show_copy' => $this->showCopyButton,
|
||||
];
|
||||
|
||||
if ($this->timeCollector !== null) {
|
||||
$this->timeCollector->addMeasure(Str::limit($sql, 100), $startTime, $endTime, [], 'db');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mimic mysql_real_escape_string
|
||||
*
|
||||
* @param string $value
|
||||
* @return string
|
||||
*/
|
||||
protected function emulateQuote($value)
|
||||
{
|
||||
$search = ["\\", "\x00", "\n", "\r", "'", '"', "\x1a"];
|
||||
$replace = ["\\\\","\\0","\\n", "\\r", "\'", '\"', "\\Z"];
|
||||
|
||||
return "'" . str_replace($search, $replace, (string) $value) . "'";
|
||||
}
|
||||
|
||||
/**
|
||||
* Explainer::performQueryAnalysis()
|
||||
*
|
||||
* Perform simple regex analysis on the code
|
||||
*
|
||||
* @package xplain (https://github.com/rap2hpoutre/mysql-xplain-xplain)
|
||||
* @author e-doceo
|
||||
* @copyright 2014
|
||||
* @version $Id$
|
||||
* @access public
|
||||
* @param string $query
|
||||
* @return string[]
|
||||
*/
|
||||
protected function performQueryAnalysis($query)
|
||||
{
|
||||
// @codingStandardsIgnoreStart
|
||||
$hints = [];
|
||||
if (preg_match('/^\\s*SELECT\\s*`?[a-zA-Z0-9]*`?\\.?\\*/i', $query)) {
|
||||
$hints[] = 'Use <code>SELECT *</code> only if you need all columns from table';
|
||||
}
|
||||
if (preg_match('/ORDER BY RAND()/i', $query)) {
|
||||
$hints[] = '<code>ORDER BY RAND()</code> is slow, try to avoid if you can.
|
||||
You can <a href="https://stackoverflow.com/questions/2663710/how-does-mysqls-order-by-rand-work" target="_blank">read this</a>
|
||||
or <a href="https://stackoverflow.com/questions/1244555/how-can-i-optimize-mysqls-order-by-rand-function" target="_blank">this</a>';
|
||||
}
|
||||
if (strpos($query, '!=') !== false) {
|
||||
$hints[] = 'The <code>!=</code> operator is not standard. Use the <code><></code> operator to test for inequality instead.';
|
||||
}
|
||||
if (stripos($query, 'WHERE') === false && preg_match('/^(SELECT) /i', $query)) {
|
||||
$hints[] = 'The <code>SELECT</code> statement has no <code>WHERE</code> clause and could examine many more rows than intended';
|
||||
}
|
||||
if (preg_match('/LIMIT\\s/i', $query) && stripos($query, 'ORDER BY') === false) {
|
||||
$hints[] = '<code>LIMIT</code> without <code>ORDER BY</code> causes non-deterministic results, depending on the query execution plan';
|
||||
}
|
||||
if (preg_match('/LIKE\\s[\'"](%.*?)[\'"]/i', $query, $matches)) {
|
||||
$hints[] = 'An argument has a leading wildcard character: <code>' . $matches[1] . '</code>.
|
||||
The predicate with this argument is not sargable and cannot use an index if one exists.';
|
||||
}
|
||||
return $hints;
|
||||
|
||||
// @codingStandardsIgnoreEnd
|
||||
}
|
||||
|
||||
/**
|
||||
* Use a backtrace to search for the origins of the query.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function findSource()
|
||||
{
|
||||
$stack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS | DEBUG_BACKTRACE_PROVIDE_OBJECT, app('config')->get('debugbar.debug_backtrace_limit', 50));
|
||||
|
||||
$sources = [];
|
||||
|
||||
foreach ($stack as $index => $trace) {
|
||||
$sources[] = $this->parseTrace($index, $trace);
|
||||
}
|
||||
|
||||
return array_slice(array_filter($sources), 0, is_int($this->findSource) ? $this->findSource : 5);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a trace element from the backtrace stack.
|
||||
*
|
||||
* @param int $index
|
||||
* @param array $trace
|
||||
* @return object|bool
|
||||
*/
|
||||
protected function parseTrace($index, array $trace)
|
||||
{
|
||||
$frame = (object) [
|
||||
'index' => $index,
|
||||
'namespace' => null,
|
||||
'name' => null,
|
||||
'file' => null,
|
||||
'line' => $trace['line'] ?? '1',
|
||||
];
|
||||
|
||||
if (isset($trace['function']) && $trace['function'] == 'substituteBindings') {
|
||||
$frame->name = 'Route binding';
|
||||
|
||||
return $frame;
|
||||
}
|
||||
|
||||
if (
|
||||
isset($trace['class']) &&
|
||||
isset($trace['file']) &&
|
||||
!$this->fileIsInExcludedPath($trace['file'])
|
||||
) {
|
||||
$frame->file = $trace['file'];
|
||||
|
||||
if (isset($trace['object']) && is_a($trace['object'], '\Twig\Template')) {
|
||||
list($frame->file, $frame->line) = $this->getTwigInfo($trace);
|
||||
} elseif (strpos($frame->file, storage_path()) !== false) {
|
||||
$hash = pathinfo($frame->file, PATHINFO_FILENAME);
|
||||
|
||||
if ($frame->name = $this->findViewFromHash($hash)) {
|
||||
$frame->file = $frame->name[1];
|
||||
$frame->name = $frame->name[0];
|
||||
} else {
|
||||
$frame->name = $hash;
|
||||
}
|
||||
|
||||
$frame->namespace = 'view';
|
||||
|
||||
return $frame;
|
||||
} elseif (strpos($frame->file, 'Middleware') !== false) {
|
||||
$frame->name = $this->findMiddlewareFromFile($frame->file);
|
||||
|
||||
if ($frame->name) {
|
||||
$frame->namespace = 'middleware';
|
||||
} else {
|
||||
$frame->name = $this->normalizeFilePath($frame->file);
|
||||
}
|
||||
|
||||
return $frame;
|
||||
}
|
||||
|
||||
$frame->name = $this->normalizeFilePath($frame->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)
|
||||
{
|
||||
$normalizedPath = str_replace('\\', '/', $file);
|
||||
|
||||
foreach ($this->backtraceExcludePaths as $excludedPath) {
|
||||
if (strpos($normalizedPath, $excludedPath) !== false) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the middleware alias from the file.
|
||||
*
|
||||
* @param string $file
|
||||
* @return string|null
|
||||
*/
|
||||
protected function findMiddlewareFromFile($file)
|
||||
{
|
||||
$filename = pathinfo($file, PATHINFO_FILENAME);
|
||||
|
||||
foreach ($this->middleware as $alias => $class) {
|
||||
if (strpos($class, $filename) !== false) {
|
||||
return $alias;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the template name from the hash.
|
||||
*
|
||||
* @param string $hash
|
||||
* @return null|array
|
||||
*/
|
||||
protected function findViewFromHash($hash)
|
||||
{
|
||||
$finder = app('view')->getFinder();
|
||||
|
||||
if (isset($this->reflection['viewfinderViews'])) {
|
||||
$property = $this->reflection['viewfinderViews'];
|
||||
} else {
|
||||
$reflection = new \ReflectionClass($finder);
|
||||
$property = $reflection->getProperty('views');
|
||||
$property->setAccessible(true);
|
||||
$this->reflection['viewfinderViews'] = $property;
|
||||
}
|
||||
|
||||
$xxh128Exists = in_array('xxh128', hash_algos());
|
||||
|
||||
foreach ($property->getValue($finder) as $name => $path) {
|
||||
if (($xxh128Exists && hash('xxh128', 'v2' . $path) == $hash) || sha1('v2' . $path) == $hash) {
|
||||
return [$name, $path];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the filename/line from a Twig template trace
|
||||
*
|
||||
* @param array $trace
|
||||
* @return array The file and line
|
||||
*/
|
||||
protected function getTwigInfo($trace)
|
||||
{
|
||||
$file = $trace['object']->getTemplateName();
|
||||
|
||||
if (isset($trace['line'])) {
|
||||
foreach ($trace['object']->getDebugInfo() as $codeLine => $templateLine) {
|
||||
if ($codeLine <= $trace['line']) {
|
||||
return [$file, $templateLine];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [$file, -1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect a database transaction event.
|
||||
* @param string $event
|
||||
* @param \Illuminate\Database\Connection $connection
|
||||
* @return array
|
||||
*/
|
||||
public function collectTransactionEvent($event, $connection)
|
||||
{
|
||||
$source = [];
|
||||
|
||||
if ($this->findSource) {
|
||||
try {
|
||||
$source = $this->findSource();
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
}
|
||||
|
||||
$this->queries[] = [
|
||||
'query' => $event,
|
||||
'type' => 'transaction',
|
||||
'bindings' => [],
|
||||
'start' => microtime(true),
|
||||
'time' => 0,
|
||||
'memory' => 0,
|
||||
'source' => $source,
|
||||
'explain' => [],
|
||||
'connection' => $connection->getDatabaseName(),
|
||||
'driver' => $connection->getConfig('driver'),
|
||||
'hints' => null,
|
||||
'show_copy' => false,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the queries.
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
$this->queries = [];
|
||||
$this->queryCount = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function collect()
|
||||
{
|
||||
$totalTime = 0;
|
||||
$totalMemory = 0;
|
||||
$queries = $this->queries;
|
||||
|
||||
$statements = [];
|
||||
foreach ($queries as $query) {
|
||||
$source = reset($query['source']);
|
||||
$totalTime += $query['time'];
|
||||
$totalMemory += $query['memory'];
|
||||
|
||||
if (str_ends_with($query['connection'], '.sqlite')) {
|
||||
$query['connection'] = $this->normalizeFilePath($query['connection']);
|
||||
}
|
||||
|
||||
$statements[] = [
|
||||
'sql' => $this->getDataFormatter()->formatSql($query['query']),
|
||||
'type' => $query['type'],
|
||||
'params' => [],
|
||||
'bindings' => $query['bindings'],
|
||||
'hints' => $query['hints'],
|
||||
'show_copy' => $query['show_copy'],
|
||||
'backtrace' => array_values($query['source']),
|
||||
'start' => $query['start'] ?? null,
|
||||
'duration' => $query['time'],
|
||||
'duration_str' => ($query['type'] == 'transaction') ? '' : $this->formatDuration($query['time']),
|
||||
'memory' => $query['memory'],
|
||||
'memory_str' => $query['memory'] ? $this->getDataFormatter()->formatBytes($query['memory']) : null,
|
||||
'filename' => $this->getDataFormatter()->formatSource($source, true),
|
||||
'source' => $this->getDataFormatter()->formatSource($source),
|
||||
'xdebug_link' => is_object($source) ? $this->getXdebugLink($source->file ?: '', $source->line) : null,
|
||||
'connection' => $query['connection'],
|
||||
];
|
||||
|
||||
if ($query['explain']) {
|
||||
// Add the results from the EXPLAIN as new rows
|
||||
if ($query['driver'] === 'pgsql') {
|
||||
$explainer = trim(implode("\n", array_map(function ($explain) {
|
||||
return $explain->{'QUERY PLAN'};
|
||||
}, $query['explain'])));
|
||||
|
||||
if ($explainer) {
|
||||
$statements[] = [
|
||||
'sql' => " - EXPLAIN: {$explainer}",
|
||||
'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[] = [
|
||||
'sql' => " - EXPLAIN # {$explain->id}: `{$explain->table}` ({$explain->select_type})",
|
||||
'type' => 'explain',
|
||||
'params' => $explain,
|
||||
'row_count' => $explain->rows,
|
||||
'stmt_id' => $explain->id,
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->durationBackground) {
|
||||
if ($totalTime > 0) {
|
||||
// For showing background measure on Queries tab
|
||||
$start_percent = 0;
|
||||
|
||||
foreach ($statements as $i => $statement) {
|
||||
if (!isset($statement['duration'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$width_percent = $statement['duration'] / $totalTime * 100;
|
||||
|
||||
$statements[$i] = array_merge($statement, [
|
||||
'start_percent' => round($start_percent, 3),
|
||||
'width_percent' => round($width_percent, 3),
|
||||
]);
|
||||
|
||||
$start_percent += $width_percent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->softLimit && $this->hardLimit && ($this->queryCount > $this->softLimit && $this->queryCount > $this->hardLimit)) {
|
||||
array_unshift($statements, [
|
||||
'sql' => '# Query soft and hard limit for Debugbar are reached. Only the first ' . $this->softLimit . ' queries show details. Queries after the first ' . $this->hardLimit . ' are ignored. Limits can be raised in the config (debugbar.options.db.soft/hard_limit).',
|
||||
'type' => 'info',
|
||||
]);
|
||||
$statements[] = [
|
||||
'sql' => '... ' . ($this->queryCount - $this->hardLimit) . ' additional queries are executed but now shown because of Debugbar query limits. Limits can be raised in the config (debugbar.options.db.soft/hard_limit)',
|
||||
'type' => 'info',
|
||||
];
|
||||
} elseif ($this->hardLimit && $this->queryCount > $this->hardLimit) {
|
||||
array_unshift($statements, [
|
||||
'sql' => '# Query hard limit for Debugbar is reached after ' . $this->hardLimit . ' queries, additional ' . ($this->queryCount - $this->hardLimit) . ' queries are not shown.. Limits can be raised in the config (debugbar.options.db.hard_limit)',
|
||||
'type' => 'info',
|
||||
]);
|
||||
$statements[] = [
|
||||
'sql' => '... ' . ($this->queryCount - $this->hardLimit) . ' additional queries are executed but now shown because of Debugbar query limits. Limits can be raised in the config (debugbar.options.db.hard_limit)',
|
||||
'type' => 'info',
|
||||
];
|
||||
} elseif ($this->softLimit && $this->queryCount > $this->softLimit) {
|
||||
array_unshift($statements, [
|
||||
'sql' => '# Query soft limit for Debugbar is reached after ' . $this->softLimit . ' queries, additional ' . ($this->queryCount - $this->softLimit) . ' queries only show the query. Limit can be raised in the config. Limits can be raised in the config (debugbar.options.db.soft_limit)',
|
||||
'type' => 'info',
|
||||
]);
|
||||
}
|
||||
|
||||
$data = [
|
||||
'nb_statements' => $this->queryCount,
|
||||
'nb_failed_statements' => 0,
|
||||
'accumulated_duration' => $totalTime,
|
||||
'accumulated_duration_str' => $this->formatDuration($totalTime),
|
||||
'memory_usage' => $totalMemory,
|
||||
'memory_usage_str' => $totalMemory ? $this->getDataFormatter()->formatBytes($totalMemory) : null,
|
||||
'statements' => $statements
|
||||
];
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'queries';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getWidgets()
|
||||
{
|
||||
return [
|
||||
"queries" => [
|
||||
"icon" => "database",
|
||||
"widget" => "PhpDebugBar.Widgets.SQLQueriesWidget",
|
||||
"map" => "queries",
|
||||
"default" => "[]"
|
||||
],
|
||||
"queries:badge" => [
|
||||
"map" => "queries.nb_statements",
|
||||
"default" => 0
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
<?php
|
||||
|
||||
namespace Barryvdh\Debugbar\DataCollector;
|
||||
|
||||
use DebugBar\DataCollector\DataCollector;
|
||||
use DebugBar\DataCollector\DataCollectorInterface;
|
||||
use DebugBar\DataCollector\Renderable;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Str;
|
||||
use Laravel\Telescope\IncomingEntry;
|
||||
use Laravel\Telescope\Telescope;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
*
|
||||
* Based on \Symfony\Component\HttpKernel\DataCollector\RequestDataCollector by Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
*/
|
||||
class RequestCollector extends DataCollector implements DataCollectorInterface, Renderable
|
||||
{
|
||||
/** @var \Symfony\Component\HttpFoundation\Request $request */
|
||||
protected $request;
|
||||
/** @var \Symfony\Component\HttpFoundation\Request $response */
|
||||
protected $response;
|
||||
/** @var \Symfony\Component\HttpFoundation\Session\SessionInterface $session */
|
||||
protected $session;
|
||||
/** @var string|null */
|
||||
protected $currentRequestId;
|
||||
/** @var array */
|
||||
protected $hiddens;
|
||||
|
||||
/**
|
||||
* Create a new SymfonyRequestCollector
|
||||
*
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* @param \Symfony\Component\HttpFoundation\Response $response
|
||||
* @param \Symfony\Component\HttpFoundation\Session\SessionInterface $session
|
||||
* @param string|null $currentRequestId
|
||||
* @param array $hiddens
|
||||
*/
|
||||
public function __construct($request, $response, $session = null, $currentRequestId = null, $hiddens = [])
|
||||
{
|
||||
$this->request = $request;
|
||||
$this->response = $response;
|
||||
$this->session = $session;
|
||||
$this->currentRequestId = $currentRequestId;
|
||||
$this->hiddens = array_merge($hiddens, [
|
||||
'request_request.password',
|
||||
'request_headers.php-auth-pw.0',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'request';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getWidgets()
|
||||
{
|
||||
return [
|
||||
"request" => [
|
||||
"icon" => "tags",
|
||||
"widget" => "PhpDebugBar.Widgets.HtmlVariableListWidget",
|
||||
"map" => "request",
|
||||
"default" => "{}"
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function collect()
|
||||
{
|
||||
$request = $this->request;
|
||||
$response = $this->response;
|
||||
|
||||
$responseHeaders = $response->headers->all();
|
||||
$cookies = [];
|
||||
foreach ($response->headers->getCookies() as $cookie) {
|
||||
$cookies[] = $this->getCookieHeader(
|
||||
$cookie->getName(),
|
||||
$cookie->getValue(),
|
||||
$cookie->getExpiresTime(),
|
||||
$cookie->getPath(),
|
||||
$cookie->getDomain(),
|
||||
$cookie->isSecure(),
|
||||
$cookie->isHttpOnly()
|
||||
);
|
||||
}
|
||||
if (count($cookies) > 0) {
|
||||
$responseHeaders['Set-Cookie'] = $cookies;
|
||||
}
|
||||
|
||||
$statusCode = $response->getStatusCode();
|
||||
|
||||
$data = [
|
||||
'path_info' => $request->getPathInfo(),
|
||||
'status_code' => $statusCode,
|
||||
'status_text' => isset(Response::$statusTexts[$statusCode]) ? Response::$statusTexts[$statusCode] : '',
|
||||
'format' => $request->getRequestFormat(),
|
||||
'content_type' => $response->headers->get('Content-Type') ? $response->headers->get(
|
||||
'Content-Type'
|
||||
) : 'text/html',
|
||||
'request_query' => $request->query->all(),
|
||||
'request_request' => $request->request->all(),
|
||||
'request_headers' => $request->headers->all(),
|
||||
'request_cookies' => $request->cookies->all(),
|
||||
'response_headers' => $responseHeaders,
|
||||
];
|
||||
|
||||
if ($this->session) {
|
||||
$data['session_attributes'] = $this->session->all();
|
||||
}
|
||||
|
||||
if (isset($data['request_headers']['authorization'][0])) {
|
||||
$data['request_headers']['authorization'][0] = substr($data['request_headers']['authorization'][0], 0, 12) . '******';
|
||||
}
|
||||
|
||||
foreach ($this->hiddens as $key) {
|
||||
if (Arr::has($data, $key)) {
|
||||
Arr::set($data, $key, '******');
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($data as $key => $var) {
|
||||
if (!is_string($data[$key])) {
|
||||
$data[$key] = DataCollector::getDefaultVarDumper()->renderVar($var);
|
||||
} else {
|
||||
$data[$key] = e($data[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
$htmlData = [];
|
||||
if (class_exists(Telescope::class)) {
|
||||
$entry = IncomingEntry::make([
|
||||
'requestId' => $this->currentRequestId,
|
||||
])->type('debugbar');
|
||||
Telescope::$entriesQueue[] = $entry;
|
||||
$url = route('debugbar.telescope', [$entry->uuid]);
|
||||
$htmlData['telescope'] = '<a href="' . $url . '" target="_blank">View in Telescope</a>';
|
||||
}
|
||||
|
||||
return $htmlData + $data;
|
||||
}
|
||||
|
||||
private function getCookieHeader($name, $value, $expires, $path, $domain, $secure, $httponly)
|
||||
{
|
||||
$cookie = sprintf('%s=%s', $name, urlencode($value));
|
||||
|
||||
if (0 !== $expires) {
|
||||
if (is_numeric($expires)) {
|
||||
$expires = (int) $expires;
|
||||
} elseif ($expires instanceof \DateTime) {
|
||||
$expires = $expires->getTimestamp();
|
||||
} else {
|
||||
$expires = strtotime($expires);
|
||||
if (false === $expires || -1 == $expires) {
|
||||
throw new \InvalidArgumentException(
|
||||
sprintf('The "expires" cookie parameter is not valid.', $expires)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$cookie .= '; expires=' . substr(
|
||||
\DateTime::createFromFormat('U', $expires, new \DateTimeZone('UTC'))->format('D, d-M-Y H:i:s T'),
|
||||
0,
|
||||
-5
|
||||
);
|
||||
}
|
||||
|
||||
if ($domain) {
|
||||
$cookie .= '; domain=' . $domain;
|
||||
}
|
||||
|
||||
$cookie .= '; path=' . $path;
|
||||
|
||||
if ($secure) {
|
||||
$cookie .= '; secure';
|
||||
}
|
||||
|
||||
if ($httponly) {
|
||||
$cookie .= '; httponly';
|
||||
}
|
||||
|
||||
return $cookie;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
<?php
|
||||
|
||||
namespace Barryvdh\Debugbar\DataCollector;
|
||||
|
||||
use Closure;
|
||||
use DebugBar\DataCollector\DataCollector;
|
||||
use DebugBar\DataCollector\Renderable;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Routing\Route;
|
||||
use Illuminate\Routing\Router;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
|
||||
/**
|
||||
* Based on Illuminate\Foundation\Console\RoutesCommand for Taylor Otwell
|
||||
* https://github.com/laravel/framework/blob/master/src/Illuminate/Foundation/Console/RoutesCommand.php
|
||||
*
|
||||
*/
|
||||
class RouteCollector extends DataCollector implements Renderable
|
||||
{
|
||||
/**
|
||||
* The router instance.
|
||||
*
|
||||
* @var \Illuminate\Routing\Router
|
||||
*/
|
||||
protected $router;
|
||||
|
||||
public function __construct(Router $router)
|
||||
{
|
||||
$this->router = $router;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function collect()
|
||||
{
|
||||
$route = $this->router->current();
|
||||
return $this->getRouteInformation($route);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the route information for a given route.
|
||||
*
|
||||
* @param \Illuminate\Routing\Route $route
|
||||
* @return array
|
||||
*/
|
||||
protected function getRouteInformation($route)
|
||||
{
|
||||
if (!is_a($route, 'Illuminate\Routing\Route')) {
|
||||
return [];
|
||||
}
|
||||
$uri = head($route->methods()) . ' ' . $route->uri();
|
||||
$action = $route->getAction();
|
||||
|
||||
$result = [
|
||||
'uri' => $uri ?: '-',
|
||||
];
|
||||
|
||||
$result = array_merge($result, $action);
|
||||
$uses = $action['uses'] ?? null;
|
||||
$controller = is_string($action['controller'] ?? null) ? $action['controller'] : '';
|
||||
|
||||
if (request()->hasHeader('X-Livewire')) {
|
||||
try {
|
||||
$component = request('components')[0];
|
||||
$name = json_decode($component['snapshot'], true)['memo']['name'];
|
||||
$method = $component['calls'][0]['method'];
|
||||
$class = app(\Livewire\Mechanisms\ComponentRegistry::class)->getClass($name);
|
||||
if (class_exists($class) && method_exists($class, $method)) {
|
||||
$controller = $class . '@' . $method;
|
||||
$result['controller'] = ltrim($controller, '\\');
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
//
|
||||
}
|
||||
}
|
||||
|
||||
if (str_contains($controller, '@')) {
|
||||
list($controller, $method) = explode('@', $controller);
|
||||
if (class_exists($controller) && method_exists($controller, $method)) {
|
||||
$reflector = new \ReflectionMethod($controller, $method);
|
||||
}
|
||||
unset($result['uses']);
|
||||
} elseif ($uses instanceof \Closure) {
|
||||
$reflector = new \ReflectionFunction($uses);
|
||||
$result['uses'] = $this->formatVar($uses);
|
||||
} elseif (is_string($uses) && str_contains($uses, '@__invoke')) {
|
||||
if (class_exists($controller) && method_exists($controller, 'render')) {
|
||||
$reflector = new \ReflectionMethod($controller, 'render');
|
||||
$result['controller'] = $controller . '@render';
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($reflector)) {
|
||||
$filename = $this->normalizeFilePath($reflector->getFileName());
|
||||
|
||||
if ($link = $this->getXdebugLink($reflector->getFileName(), $reflector->getStartLine())) {
|
||||
$result['file'] = sprintf(
|
||||
'<a href="%s" onclick="%s">%s:%s-%s</a>',
|
||||
$link['url'],
|
||||
$link['ajax'] ? 'event.preventDefault();$.ajax(this.href);' : '',
|
||||
$filename,
|
||||
$reflector->getStartLine(),
|
||||
$reflector->getEndLine()
|
||||
);
|
||||
} else {
|
||||
$result['file'] = sprintf('%s:%s-%s', $filename, $reflector->getStartLine(), $reflector->getEndLine());
|
||||
}
|
||||
}
|
||||
|
||||
if ($middleware = $this->getMiddleware($route)) {
|
||||
$result['middleware'] = $middleware;
|
||||
}
|
||||
|
||||
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get middleware
|
||||
*
|
||||
* @param \Illuminate\Routing\Route $route
|
||||
* @return string
|
||||
*/
|
||||
protected function getMiddleware($route)
|
||||
{
|
||||
return implode(', ', array_map(function ($middleware) {
|
||||
return $middleware instanceof Closure ? 'Closure' : $middleware;
|
||||
}, $route->gatherMiddleware()));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'route';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getWidgets()
|
||||
{
|
||||
$widgets = [
|
||||
"route" => [
|
||||
"icon" => "share",
|
||||
"widget" => "PhpDebugBar.Widgets.HtmlVariableListWidget",
|
||||
"map" => "route",
|
||||
"default" => "{}"
|
||||
]
|
||||
];
|
||||
if (Config::get('debugbar.options.route.label', true)) {
|
||||
$widgets['currentroute'] = [
|
||||
"icon" => "share",
|
||||
"tooltip" => "Route",
|
||||
"map" => "route.uri",
|
||||
"default" => ""
|
||||
];
|
||||
}
|
||||
return $widgets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the route information on the console.
|
||||
*
|
||||
* @param array $routes
|
||||
* @return void
|
||||
*/
|
||||
protected function displayRoutes(array $routes)
|
||||
{
|
||||
$this->table->setHeaders($this->headers)->setRows($routes);
|
||||
|
||||
$this->table->render($this->getOutput());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
namespace Barryvdh\Debugbar\DataCollector;
|
||||
|
||||
use DebugBar\DataCollector\DataCollector;
|
||||
use DebugBar\DataCollector\DataCollectorInterface;
|
||||
use DebugBar\DataCollector\Renderable;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
class SessionCollector extends DataCollector implements DataCollectorInterface, Renderable
|
||||
{
|
||||
/** @var \Symfony\Component\HttpFoundation\Session\SessionInterface|\Illuminate\Contracts\Session\Session $session */
|
||||
protected $session;
|
||||
/** @var array */
|
||||
protected $hiddens;
|
||||
|
||||
/**
|
||||
* Create a new SessionCollector
|
||||
*
|
||||
* @param \Symfony\Component\HttpFoundation\Session\SessionInterface|\Illuminate\Contracts\Session\Session $session
|
||||
* @param array $hiddens
|
||||
*/
|
||||
public function __construct($session, $hiddens = [])
|
||||
{
|
||||
$this->session = $session;
|
||||
$this->hiddens = $hiddens;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function collect()
|
||||
{
|
||||
$data = $this->session->all();
|
||||
|
||||
foreach ($this->hiddens as $key) {
|
||||
if (Arr::has($data, $key)) {
|
||||
Arr::set($data, $key, '******');
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($data as $key => $value) {
|
||||
$data[$key] = is_string($value) ? $value : $this->formatVar($value);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'session';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getWidgets()
|
||||
{
|
||||
return [
|
||||
"session" => [
|
||||
"icon" => "archive",
|
||||
"widget" => "PhpDebugBar.Widgets.VariableListWidget",
|
||||
"map" => "session",
|
||||
"default" => "{}"
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
<?php
|
||||
|
||||
namespace Barryvdh\Debugbar\DataCollector;
|
||||
|
||||
use Barryvdh\Debugbar\DataFormatter\SimpleFormatter;
|
||||
use DebugBar\DataCollector\AssetProvider;
|
||||
use DebugBar\DataCollector\DataCollector;
|
||||
use DebugBar\DataCollector\Renderable;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class ViewCollector extends DataCollector implements Renderable, AssetProvider
|
||||
{
|
||||
protected $name;
|
||||
protected $templates = [];
|
||||
protected $collect_data;
|
||||
protected $exclude_paths;
|
||||
protected $group;
|
||||
|
||||
/**
|
||||
* Create a ViewCollector
|
||||
*
|
||||
* @param bool|string $collectData Collects view data when true
|
||||
* @param string[] $excludePaths Paths to exclude from collection
|
||||
* @param int|bool $group Group the same templates together
|
||||
* */
|
||||
public function __construct($collectData = true, $excludePaths = [], $group = true)
|
||||
{
|
||||
$this->setDataFormatter(new SimpleFormatter());
|
||||
$this->collect_data = $collectData;
|
||||
$this->templates = [];
|
||||
$this->exclude_paths = $excludePaths;
|
||||
$this->group = $group;
|
||||
}
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return 'views';
|
||||
}
|
||||
|
||||
public function getWidgets()
|
||||
{
|
||||
return [
|
||||
'views' => [
|
||||
'icon' => 'leaf',
|
||||
'widget' => 'PhpDebugBar.Widgets.TemplatesWidget',
|
||||
'map' => 'views',
|
||||
'default' => '[]'
|
||||
],
|
||||
'views:badge' => [
|
||||
'map' => 'views.nb_templates',
|
||||
'default' => 0
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getAssets()
|
||||
{
|
||||
return [
|
||||
'css' => 'widgets/templates/widget.css',
|
||||
'js' => 'widgets/templates/widget.js',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a View instance to the Collector
|
||||
*
|
||||
* @param \Illuminate\View\View $view
|
||||
*/
|
||||
public function addView(View $view)
|
||||
{
|
||||
$name = $view->getName();
|
||||
$type = null;
|
||||
$data = $view->getData();
|
||||
$path = $view->getPath();
|
||||
|
||||
if (class_exists('\Inertia\Inertia')) {
|
||||
list($name, $type, $data, $path) = $this->getInertiaView($name, $data, $path);
|
||||
}
|
||||
|
||||
if (is_object($path)) {
|
||||
$type = get_class($view);
|
||||
$path = null;
|
||||
}
|
||||
|
||||
if ($path) {
|
||||
if (!$type) {
|
||||
if (substr($path, -10) == '.blade.php') {
|
||||
$type = 'blade';
|
||||
} else {
|
||||
$type = pathinfo($path, PATHINFO_EXTENSION);
|
||||
}
|
||||
}
|
||||
|
||||
$shortPath = $this->normalizeFilePath($path);
|
||||
foreach ($this->exclude_paths as $excludePath) {
|
||||
if (str_starts_with($shortPath, $excludePath)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->addTemplate($name, $data, $type, $path);
|
||||
}
|
||||
|
||||
private function getInertiaView(string $name, array $data, ?string $path)
|
||||
{
|
||||
if (isset($data['page']) && is_array($data['page'])) {
|
||||
$data = $data['page'];
|
||||
}
|
||||
|
||||
if (isset($data['props'], $data['component'])) {
|
||||
$name = $data['component'];
|
||||
$data = $data['props'];
|
||||
|
||||
if ($files = glob(resource_path('js/Pages/' . $name . '.*'))) {
|
||||
$path = $files[0];
|
||||
$type = pathinfo($path, PATHINFO_EXTENSION);
|
||||
|
||||
if (in_array($type, ['js', 'jsx'])) {
|
||||
$type = 'react';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [$name, $type ?? '', $data, $path];
|
||||
}
|
||||
|
||||
public function addInertiaAjaxView(array $data)
|
||||
{
|
||||
list($name, $type, $data, $path) = $this->getInertiaView('', $data, '');
|
||||
|
||||
if (! $name) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->addTemplate($name, $data, $type, $path);
|
||||
}
|
||||
|
||||
private function addTemplate(string $name, array $data, ?string $type, ?string $path)
|
||||
{
|
||||
// Prevent duplicates
|
||||
$hash = $type . $path . $name . ($this->collect_data ? implode(array_keys($data)) : '');
|
||||
|
||||
if ($this->collect_data === 'keys') {
|
||||
$params = array_keys($data);
|
||||
} elseif ($this->collect_data) {
|
||||
$params = array_map(
|
||||
fn ($value) => $this->getDataFormatter()->formatVar($value),
|
||||
$data
|
||||
);
|
||||
} else {
|
||||
$params = [];
|
||||
}
|
||||
|
||||
$template = [
|
||||
'name' => $name,
|
||||
'param_count' => $this->collect_data ? count($params) : null,
|
||||
'params' => $params,
|
||||
'start' => microtime(true),
|
||||
'type' => $type,
|
||||
'hash' => $hash,
|
||||
];
|
||||
|
||||
if ($path && $this->getXdebugLinkTemplate()) {
|
||||
$template['xdebug_link'] = $this->getXdebugLink($path);
|
||||
}
|
||||
|
||||
$this->templates[] = $template;
|
||||
}
|
||||
|
||||
public function collect()
|
||||
{
|
||||
if ($this->group === true || count($this->templates) > $this->group) {
|
||||
$templates = [];
|
||||
foreach ($this->templates as $template) {
|
||||
$hash = $template['hash'];
|
||||
if (!isset($templates[$hash])) {
|
||||
$template['render_count'] = 0;
|
||||
$template['name_original'] = $template['name'];
|
||||
$templates[$hash] = $template;
|
||||
}
|
||||
|
||||
$templates[$hash]['render_count']++;
|
||||
$templates[$hash]['name'] = $templates[$hash]['render_count'] . 'x ' . $templates[$hash]['name_original'];
|
||||
}
|
||||
$templates = array_values($templates);
|
||||
} else {
|
||||
$templates = $this->templates;
|
||||
}
|
||||
|
||||
return [
|
||||
'nb_templates' => count($this->templates),
|
||||
'templates' => $templates,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
namespace Barryvdh\Debugbar\DataFormatter;
|
||||
|
||||
use DebugBar\DataFormatter\DataFormatter;
|
||||
|
||||
#[\AllowDynamicProperties]
|
||||
class QueryFormatter extends DataFormatter
|
||||
{
|
||||
/**
|
||||
* Removes extra spaces at the beginning and end of the SQL query and its lines.
|
||||
*
|
||||
* @param string $sql
|
||||
* @return string
|
||||
*/
|
||||
public function formatSql($sql)
|
||||
{
|
||||
$sql = preg_replace("/\?(?=(?:[^'\\\']*'[^'\\']*')*[^'\\\']*$)(?:\?)/", '?', $sql);
|
||||
$sql = trim(preg_replace("/\s*\n\s*/", "\n", $sql));
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check bindings for illegal (non UTF-8) strings, like Binary data.
|
||||
*
|
||||
* @param $bindings
|
||||
* @return mixed
|
||||
*/
|
||||
public function checkBindings($bindings)
|
||||
{
|
||||
foreach ($bindings as &$binding) {
|
||||
if (is_string($binding) && !mb_check_encoding($binding, 'UTF-8')) {
|
||||
$binding = '[BINARY DATA]';
|
||||
}
|
||||
|
||||
if (is_array($binding)) {
|
||||
$binding = $this->checkBindings($binding);
|
||||
$binding = '[' . implode(',', $binding) . ']';
|
||||
}
|
||||
|
||||
if (is_object($binding)) {
|
||||
$binding = json_encode($binding);
|
||||
}
|
||||
}
|
||||
|
||||
return $bindings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make the bindings safe for outputting.
|
||||
*
|
||||
* @param array $bindings
|
||||
* @return array
|
||||
*/
|
||||
public function escapeBindings($bindings)
|
||||
{
|
||||
foreach ($bindings as &$binding) {
|
||||
$binding = htmlentities((string) $binding, ENT_QUOTES, 'UTF-8', false);
|
||||
}
|
||||
|
||||
return $bindings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a source object.
|
||||
*
|
||||
* @param object|null $source If the backtrace is disabled, the $source will be null.
|
||||
* @return string
|
||||
*/
|
||||
public function formatSource($source, $short = false)
|
||||
{
|
||||
if (! is_object($source)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$parts = [];
|
||||
|
||||
if (!$short && $source->namespace) {
|
||||
$parts['namespace'] = $source->namespace . '::';
|
||||
}
|
||||
|
||||
$parts['name'] = $short ? basename($source->name) : $source->name;
|
||||
$parts['line'] = ':' . $source->line;
|
||||
|
||||
return implode($parts);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
|
||||
namespace Barryvdh\Debugbar\DataFormatter;
|
||||
|
||||
use DebugBar\DataFormatter\DataFormatter;
|
||||
|
||||
/**
|
||||
* Simple DataFormatter based on the deprecated Symfony ValueExporter
|
||||
*
|
||||
* @see https://github.com/symfony/symfony/blob/v3.4.4/src/Symfony/Component/HttpKernel/DataCollector/Util/ValueExporter.php
|
||||
*/
|
||||
#[\AllowDynamicProperties]
|
||||
class SimpleFormatter extends DataFormatter
|
||||
{
|
||||
/**
|
||||
* @param $data
|
||||
* @return string
|
||||
*/
|
||||
public function formatVar($data)
|
||||
{
|
||||
return $this->exportValue($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a PHP value to a string.
|
||||
*
|
||||
* @param mixed $value The PHP value
|
||||
* @param int $depth Only for internal usage
|
||||
* @param bool $deep Only for internal usage
|
||||
*
|
||||
* @return string The string representation of the given value
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
private function exportValue($value, $depth = 1, $deep = false)
|
||||
{
|
||||
if ($value instanceof \__PHP_Incomplete_Class) {
|
||||
return sprintf('__PHP_Incomplete_Class(%s)', $this->getClassNameFromIncomplete($value));
|
||||
}
|
||||
|
||||
if (is_object($value)) {
|
||||
if ($value instanceof \DateTimeInterface) {
|
||||
return sprintf('Object(%s) - %s', get_class($value), $value->format(\DateTime::ATOM));
|
||||
}
|
||||
|
||||
return sprintf('Object(%s)', get_class($value));
|
||||
}
|
||||
|
||||
if (is_array($value)) {
|
||||
if (empty($value)) {
|
||||
return '[]';
|
||||
}
|
||||
|
||||
$indent = str_repeat(' ', $depth);
|
||||
|
||||
$a = [];
|
||||
foreach ($value as $k => $v) {
|
||||
if (is_array($v)) {
|
||||
$deep = true;
|
||||
}
|
||||
$a[] = sprintf('%s => %s', $k, $this->exportValue($v, $depth + 1, $deep));
|
||||
}
|
||||
|
||||
if ($deep) {
|
||||
$args = [$indent, implode(sprintf(", \n%s", $indent), $a), str_repeat(' ', $depth - 1)];
|
||||
return sprintf("[\n%s%s\n%s]", ...$args);
|
||||
}
|
||||
|
||||
$s = sprintf('[%s]', implode(', ', $a));
|
||||
|
||||
if (80 > strlen($s)) {
|
||||
return $s;
|
||||
}
|
||||
|
||||
return sprintf("[\n%s%s\n]", $indent, implode(sprintf(",\n%s", $indent), $a));
|
||||
}
|
||||
|
||||
if (is_resource($value)) {
|
||||
return sprintf('Resource(%s#%d)', get_resource_type($value), $value);
|
||||
}
|
||||
|
||||
if (null === $value) {
|
||||
return 'null';
|
||||
}
|
||||
|
||||
if (false === $value) {
|
||||
return 'false';
|
||||
}
|
||||
|
||||
if (true === $value) {
|
||||
return 'true';
|
||||
}
|
||||
|
||||
return (string) $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \__PHP_Incomplete_Class $value
|
||||
* @return mixed
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
private function getClassNameFromIncomplete(\__PHP_Incomplete_Class $value)
|
||||
{
|
||||
$array = new \ArrayObject($value);
|
||||
|
||||
return $array['__PHP_Incomplete_Class_Name'];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Barryvdh\Debugbar;
|
||||
|
||||
use Illuminate\Contracts\View\Engine;
|
||||
|
||||
class DebugbarViewEngine implements Engine
|
||||
{
|
||||
/**
|
||||
* @var Engine
|
||||
*/
|
||||
protected $engine;
|
||||
|
||||
/**
|
||||
* @var LaravelDebugbar
|
||||
*/
|
||||
protected $laravelDebugbar;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $exclude_paths;
|
||||
|
||||
/**
|
||||
* @param Engine $engine
|
||||
* @param LaravelDebugbar $laravelDebugbar
|
||||
*/
|
||||
public function __construct(Engine $engine, LaravelDebugbar $laravelDebugbar)
|
||||
{
|
||||
$this->engine = $engine;
|
||||
$this->laravelDebugbar = $laravelDebugbar;
|
||||
$this->exclude_paths = app('config')->get('debugbar.options.views.exclude_paths', []);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
* @param array $data
|
||||
* @return string
|
||||
*/
|
||||
public function get($path, array $data = [])
|
||||
{
|
||||
$basePath = base_path();
|
||||
$shortPath = @file_exists((string) $path) ? realpath($path) : $path;
|
||||
|
||||
if (str_starts_with($shortPath, $basePath)) {
|
||||
$shortPath = ltrim(
|
||||
str_replace('\\', '/', substr($shortPath, strlen($basePath))),
|
||||
'/'
|
||||
);
|
||||
}
|
||||
|
||||
foreach ($this->exclude_paths as $excludePath) {
|
||||
if (str_starts_with($shortPath, $excludePath)) {
|
||||
return $this->engine->get($path, $data);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->laravelDebugbar->measure($shortPath, function () use ($path, $data) {
|
||||
return $this->engine->get($path, $data);
|
||||
}, 'views');
|
||||
}
|
||||
|
||||
/**
|
||||
* NOTE: This is done to support other Engine swap (example: Livewire).
|
||||
* @param $name
|
||||
* @param $arguments
|
||||
* @return mixed
|
||||
*/
|
||||
public function __call($name, $arguments)
|
||||
{
|
||||
return $this->engine->$name(...$arguments);
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace Barryvdh\Debugbar;
|
||||
|
||||
use DebugBar\DataCollector\DataCollectorInterface;
|
||||
|
||||
/**
|
||||
* @method static LaravelDebugbar addCollector(DataCollectorInterface $collector)
|
||||
* @method static void addMessage(mixed $message, string $label = 'info')
|
||||
* @method static void alert(mixed $message)
|
||||
* @method static void critical(mixed $message)
|
||||
* @method static void debug(mixed $message)
|
||||
* @method static void emergency(mixed $message)
|
||||
* @method static void error(mixed $message)
|
||||
* @method static LaravelDebugbar getCollector(string $name)
|
||||
* @method static bool hasCollector(string $name)
|
||||
* @method static void info(mixed $message)
|
||||
* @method static void log(mixed $message)
|
||||
* @method static void notice(mixed $message)
|
||||
* @method static void warning(mixed $message)
|
||||
* @method static mixed measure(string $label, \Closure $closure)
|
||||
*
|
||||
* @deprecated Renamed to \Barryvdh\Debugbar\Facades\Debugbar
|
||||
* @see \Barryvdh\Debugbar\Facades\Debugbar
|
||||
*
|
||||
* @see \Barryvdh\Debugbar\LaravelDebugbar
|
||||
*/
|
||||
class Facade extends \Barryvdh\Debugbar\Facades\Debugbar
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace Barryvdh\Debugbar\Facades;
|
||||
|
||||
use DebugBar\DataCollector\DataCollectorInterface;
|
||||
|
||||
/**
|
||||
* @method static LaravelDebugbar addCollector(DataCollectorInterface $collector)
|
||||
* @method static void addMessage(mixed $message, string $label = 'info')
|
||||
* @method static void alert(mixed $message)
|
||||
* @method static void critical(mixed $message)
|
||||
* @method static void debug(mixed $message)
|
||||
* @method static void emergency(mixed $message)
|
||||
* @method static void error(mixed $message)
|
||||
* @method static LaravelDebugbar getCollector(string $name)
|
||||
* @method static bool hasCollector(string $name)
|
||||
* @method static void info(mixed $message)
|
||||
* @method static void log(mixed $message)
|
||||
* @method static void notice(mixed $message)
|
||||
* @method static void warning(mixed $message)
|
||||
*
|
||||
* @see \Barryvdh\Debugbar\LaravelDebugbar
|
||||
*/
|
||||
class Debugbar extends \Illuminate\Support\Facades\Facade
|
||||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected static function getFacadeAccessor()
|
||||
{
|
||||
return \Barryvdh\Debugbar\LaravelDebugbar::class;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
<?php
|
||||
|
||||
namespace Barryvdh\Debugbar;
|
||||
|
||||
use DebugBar\DebugBar;
|
||||
use DebugBar\JavascriptRenderer as BaseJavascriptRenderer;
|
||||
use Illuminate\Routing\UrlGenerator;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
class JavascriptRenderer extends BaseJavascriptRenderer
|
||||
{
|
||||
// Use XHR handler by default, instead of jQuery
|
||||
protected $ajaxHandlerBindToJquery = false;
|
||||
protected $ajaxHandlerBindToXHR = true;
|
||||
|
||||
public function __construct(DebugBar $debugBar, $baseUrl = null, $basePath = null)
|
||||
{
|
||||
parent::__construct($debugBar, $baseUrl, $basePath);
|
||||
|
||||
$this->cssFiles['laravel'] = __DIR__ . '/Resources/laravel-debugbar.css';
|
||||
$this->jsFiles['laravel-cache'] = __DIR__ . '/Resources/cache/widget.js';
|
||||
|
||||
$theme = config('debugbar.theme', 'auto');
|
||||
switch ($theme) {
|
||||
case 'dark':
|
||||
$this->cssFiles['laravel-dark'] = __DIR__ . '/Resources/laravel-debugbar-dark-mode.css';
|
||||
break;
|
||||
case 'auto':
|
||||
$this->cssFiles['laravel-dark-0'] = __DIR__ . '/Resources/laravel-debugbar-dark-mode-media-start.css';
|
||||
$this->cssFiles['laravel-dark-1'] = __DIR__ . '/Resources/laravel-debugbar-dark-mode.css';
|
||||
$this->cssFiles['laravel-dark-2'] = __DIR__ . '/Resources/laravel-debugbar-dark-mode-media-end.css';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the URL Generator
|
||||
*
|
||||
* @param \Illuminate\Routing\UrlGenerator $url
|
||||
* @deprecated
|
||||
*/
|
||||
public function setUrlGenerator($url)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function renderHead()
|
||||
{
|
||||
$cssRoute = preg_replace('/\Ahttps?:\/\/[^\/]+/', '', route('debugbar.assets.css', [
|
||||
'v' => $this->getModifiedTime('css'),
|
||||
'theme' => config('debugbar.theme', 'auto'),
|
||||
]));
|
||||
|
||||
$jsRoute = preg_replace('/\Ahttps?:\/\/[^\/]+/', '', route('debugbar.assets.js', [
|
||||
'v' => $this->getModifiedTime('js')
|
||||
]));
|
||||
|
||||
$nonce = $this->getNonceAttribute();
|
||||
|
||||
$html = "<link rel='stylesheet' type='text/css' property='stylesheet' href='{$cssRoute}' data-turbolinks-eval='false' data-turbo-eval='false'>";
|
||||
$html .= "<script{$nonce} src='{$jsRoute}' data-turbolinks-eval='false' data-turbo-eval='false'></script>";
|
||||
|
||||
if ($this->isJqueryNoConflictEnabled()) {
|
||||
$html .= "<script{$nonce} data-turbo-eval='false'>jQuery.noConflict(true);</script>" . "\n";
|
||||
}
|
||||
|
||||
$inlineHtml = $this->getInlineHtml();
|
||||
if ($nonce != '') {
|
||||
$inlineHtml = preg_replace("/<(script|style)>/", "<$1{$nonce}>", $inlineHtml);
|
||||
}
|
||||
$html .= $inlineHtml;
|
||||
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
protected function getInlineHtml()
|
||||
{
|
||||
$html = '';
|
||||
|
||||
foreach (['head', 'css', 'js'] as $asset) {
|
||||
foreach ($this->getAssets('inline_' . $asset) as $item) {
|
||||
$html .= $item . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
return $html;
|
||||
}
|
||||
/**
|
||||
* Get the last modified time of any assets.
|
||||
*
|
||||
* @param string $type 'js' or 'css'
|
||||
* @return int
|
||||
*/
|
||||
protected function getModifiedTime($type)
|
||||
{
|
||||
$files = $this->getAssets($type);
|
||||
|
||||
$latest = 0;
|
||||
foreach ($files as $file) {
|
||||
$mtime = filemtime($file);
|
||||
if ($mtime > $latest) {
|
||||
$latest = $mtime;
|
||||
}
|
||||
}
|
||||
return $latest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return assets as a string
|
||||
*
|
||||
* @param string $type 'js' or 'css'
|
||||
* @return string
|
||||
*/
|
||||
public function dumpAssetsToString($type)
|
||||
{
|
||||
$files = $this->getAssets($type);
|
||||
|
||||
$content = '';
|
||||
foreach ($files as $file) {
|
||||
$content .= file_get_contents($file) . "\n";
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a URI relative to another
|
||||
*
|
||||
* @param string|array $uri
|
||||
* @param string $root
|
||||
* @return string
|
||||
*/
|
||||
protected function makeUriRelativeTo($uri, $root)
|
||||
{
|
||||
if (!$root) {
|
||||
return $uri;
|
||||
}
|
||||
|
||||
if (is_array($uri)) {
|
||||
$uris = [];
|
||||
foreach ($uri as $u) {
|
||||
$uris[] = $this->makeUriRelativeTo($u, $root);
|
||||
}
|
||||
return $uris;
|
||||
}
|
||||
|
||||
if (substr($uri ?? '', 0, 1) === '/' || preg_match('/^([a-zA-Z]+:\/\/|[a-zA-Z]:\/|[a-zA-Z]:\\\)/', $uri ?? '')) {
|
||||
return $uri;
|
||||
}
|
||||
return rtrim($root, '/') . "/$uri";
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace Barryvdh\Debugbar;
|
||||
|
||||
use Laravel\Lumen\Application;
|
||||
|
||||
class LumenServiceProvider extends ServiceProvider
|
||||
{
|
||||
/** @var Application */
|
||||
protected $app;
|
||||
|
||||
/**
|
||||
* Get the active router.
|
||||
*
|
||||
* @return Application
|
||||
*/
|
||||
protected function getRouter()
|
||||
{
|
||||
return $this->app->router;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the config path
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getConfigPath()
|
||||
{
|
||||
return base_path('config/debugbar.php');
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the Debugbar Middleware
|
||||
*
|
||||
* @param string $middleware
|
||||
*/
|
||||
protected function registerMiddleware($middleware)
|
||||
{
|
||||
$this->app->middleware([$middleware]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the services provided by the provider.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function provides()
|
||||
{
|
||||
return ['debugbar', 'command.debugbar.clear'];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace Barryvdh\Debugbar\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Barryvdh\Debugbar\LaravelDebugbar;
|
||||
|
||||
class DebugbarEnabled
|
||||
{
|
||||
/**
|
||||
* The DebugBar instance
|
||||
*
|
||||
* @var LaravelDebugbar
|
||||
*/
|
||||
protected $debugbar;
|
||||
|
||||
/**
|
||||
* Create a new middleware instance.
|
||||
*
|
||||
* @param LaravelDebugbar $debugbar
|
||||
*/
|
||||
public function __construct(LaravelDebugbar $debugbar)
|
||||
{
|
||||
$this->debugbar = $debugbar;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param Request $request
|
||||
* @param Closure $next
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle($request, Closure $next)
|
||||
{
|
||||
if (!$this->debugbar->isEnabled()) {
|
||||
abort(404);
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
<?php
|
||||
|
||||
namespace Barryvdh\Debugbar\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Exception;
|
||||
use Illuminate\Http\Request;
|
||||
use Barryvdh\Debugbar\LaravelDebugbar;
|
||||
use Illuminate\Contracts\Container\Container;
|
||||
use Illuminate\Contracts\Debug\ExceptionHandler;
|
||||
use Throwable;
|
||||
|
||||
class InjectDebugbar
|
||||
{
|
||||
/**
|
||||
* The App container
|
||||
*
|
||||
* @var Container
|
||||
*/
|
||||
protected $container;
|
||||
|
||||
/**
|
||||
* The DebugBar instance
|
||||
*
|
||||
* @var LaravelDebugbar
|
||||
*/
|
||||
protected $debugbar;
|
||||
|
||||
/**
|
||||
* The URIs that should be excluded.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $except = [];
|
||||
|
||||
/**
|
||||
* Create a new middleware instance.
|
||||
*
|
||||
* @param Container $container
|
||||
* @param LaravelDebugbar $debugbar
|
||||
*/
|
||||
public function __construct(Container $container, LaravelDebugbar $debugbar)
|
||||
{
|
||||
$this->container = $container;
|
||||
$this->debugbar = $debugbar;
|
||||
$this->except = config('debugbar.except') ?: [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param Request $request
|
||||
* @param Closure $next
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle($request, Closure $next)
|
||||
{
|
||||
if (!$this->debugbar->isEnabled() || $this->inExceptArray($request)) {
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
$this->debugbar->boot();
|
||||
|
||||
try {
|
||||
/** @var \Illuminate\Http\Response $response */
|
||||
$response = $next($request);
|
||||
} catch (Throwable $e) {
|
||||
$response = $this->handleException($request, $e);
|
||||
}
|
||||
|
||||
// Modify the response to add the Debugbar
|
||||
$this->debugbar->modifyResponse($request, $response);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the given exception.
|
||||
*
|
||||
* (Copy from Illuminate\Routing\Pipeline by Taylor Otwell)
|
||||
*
|
||||
* @param $passable
|
||||
* @param Throwable $e
|
||||
* @return mixed
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function handleException($passable, $e)
|
||||
{
|
||||
if (! $this->container->bound(ExceptionHandler::class) || ! $passable instanceof Request) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
$handler = $this->container->make(ExceptionHandler::class);
|
||||
|
||||
$handler->report($e);
|
||||
|
||||
return $handler->render($passable, $e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the request has a URI that should be ignored.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return bool
|
||||
*/
|
||||
protected function inExceptArray($request)
|
||||
{
|
||||
foreach ($this->except as $except) {
|
||||
if ($except !== '/') {
|
||||
$except = trim($except, '/');
|
||||
}
|
||||
|
||||
if ($request->is($except)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
(function ($) {
|
||||
|
||||
var csscls = PhpDebugBar.utils.makecsscls('phpdebugbar-widgets-');
|
||||
|
||||
/**
|
||||
* Widget for the displaying cache events
|
||||
*
|
||||
* Options:
|
||||
* - data
|
||||
*/
|
||||
var LaravelCacheWidget = PhpDebugBar.Widgets.LaravelCacheWidget = PhpDebugBar.Widgets.TimelineWidget.extend({
|
||||
|
||||
tagName: 'ul',
|
||||
|
||||
className: csscls('timeline cache'),
|
||||
|
||||
onForgetClick: function (e, el) {
|
||||
e.stopPropagation();
|
||||
|
||||
$.ajax({
|
||||
url: $(el).attr("data-url"),
|
||||
type: 'DELETE',
|
||||
success: function (result) {
|
||||
$(el).fadeOut(200);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
render: function () {
|
||||
LaravelCacheWidget.__super__.render.apply(this);
|
||||
|
||||
this.bindAttr('data', function (data) {
|
||||
|
||||
if (data.measures) {
|
||||
var self = this;
|
||||
var lines = this.$el.find('.' + csscls('measure'));
|
||||
|
||||
for (var i = 0; i < data.measures.length; i++) {
|
||||
var measure = data.measures[i];
|
||||
var m = lines[i];
|
||||
|
||||
if (measure.params && !$.isEmptyObject(measure.params)) {
|
||||
if (measure.params.delete && measure.params.key) {
|
||||
$('<a />')
|
||||
.addClass(csscls('forget'))
|
||||
.text('forget')
|
||||
.attr('data-url', measure.params.delete)
|
||||
.one('click', function (e) {
|
||||
self.onForgetClick(e, this); })
|
||||
.appendTo(m);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
})(PhpDebugBar.$);
|
||||
+1
@@ -0,0 +1 @@
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
@media (prefers-color-scheme: dark) {
|
||||
+355
@@ -0,0 +1,355 @@
|
||||
/* Dark mode */
|
||||
|
||||
div.phpdebugbar,
|
||||
div.phpdebugbar-openhandler {
|
||||
--color-gray-100: #F7FAFC;
|
||||
--color-gray-200: #EDF2F7;
|
||||
--color-gray-300: #E2E8F0;
|
||||
--color-gray-400: #CBD5E0;
|
||||
--color-gray-500: #A0AEC0;
|
||||
--color-gray-600: #718096;
|
||||
--color-gray-700: #4A5568;
|
||||
--color-gray-800: #252a37;
|
||||
--color-gray-900: #18181b;
|
||||
--color-red-vivid: #eb4432;
|
||||
}
|
||||
|
||||
div.phpdebugbar,
|
||||
div.phpdebugbar-widgets-dataset-history .phpdebugbar-widgets-dataset-actions,
|
||||
div.phpdebugbar-openhandler {
|
||||
background: var(--color-gray-800);
|
||||
}
|
||||
|
||||
div.phpdebugbar,
|
||||
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,
|
||||
div.phpdebugbar-panel .phpdebugbar-widgets-dataset-actions select {
|
||||
color: var(--color-gray-200);
|
||||
}
|
||||
|
||||
div.phpdebugbar ul.phpdebugbar-widgets-timeline li span.phpdebugbar-widgets-label {
|
||||
text-shadow: 1px 1px #000;
|
||||
}
|
||||
|
||||
div.phpdebugbar-openhandler,
|
||||
div.phpdebugbar div.phpdebugbar-widgets-messages div.phpdebugbar-widgets-toolbar,
|
||||
div.phpdebugbar div.phpdebugbar-widgets-exceptions li.phpdebugbar-widgets-list-item pre.phpdebugbar-widgets-file,
|
||||
div.phpdebugbar ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item table.phpdebugbar-widgets-params,
|
||||
div.phpdebugbar-openhandler .phpdebugbar-openhandler-actions > a,
|
||||
div.phpdebugbar-openhandler .phpdebugbar-openhandler-actions button,
|
||||
div.phpdebugbar-openhandler .phpdebugbar-openhandler-actions > form input,
|
||||
div.phpdebugbar-openhandler .phpdebugbar-openhandler-actions > form select,
|
||||
div.phpdebugbar-panel .phpdebugbar-widgets-dataset-actions select {
|
||||
border-color: var(--color-gray-600);
|
||||
}
|
||||
|
||||
div.phpdebugbar div.phpdebugbar-header,
|
||||
div.phpdebugbar div.phpdebugbar-panel div.phpdebugbar-widgets-status > span:first-child:before,
|
||||
div.phpdebugbar-openhandler table th,
|
||||
div.phpdebugbar-openhandler .phpdebugbar-openhandler-actions > a,
|
||||
div.phpdebugbar-openhandler .phpdebugbar-openhandler-actions button,
|
||||
div.phpdebugbar-openhandler .phpdebugbar-openhandler-actions > form input,
|
||||
div.phpdebugbar-openhandler .phpdebugbar-openhandler-actions > form select {
|
||||
text-shadow: 1px 1px var(--color-gray-700);
|
||||
}
|
||||
|
||||
div.phpdebugbar div.phpdebugbar-header > div > select,
|
||||
div.phpdebugbar ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item table.phpdebugbar-widgets-params,
|
||||
div.phpdebugbar-openhandler .phpdebugbar-openhandler-actions > form input,
|
||||
div.phpdebugbar-openhandler .phpdebugbar-openhandler-actions > form select,
|
||||
div.phpdebugbar-panel .phpdebugbar-widgets-dataset-actions select,
|
||||
div.phpdebugbar input[type='text'],
|
||||
div.phpdebugbar input[type='password'] {
|
||||
background-color: var(--color-gray-800);
|
||||
}
|
||||
|
||||
div.phpdebugbar div.phpdebugbar-header,
|
||||
div.phpdebugbar a.phpdebugbar-restore-btn,
|
||||
div.phpdebugbar-openhandler .phpdebugbar-openhandler-header,
|
||||
div.phpdebugbar dl.phpdebugbar-widgets-kvlist > :nth-child(4n-1),
|
||||
div.phpdebugbar dl.phpdebugbar-widgets-kvlist > :nth-child(4n),
|
||||
div.phpdebugbar ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item:nth-child(even),
|
||||
div.phpdebugbar .hljs,
|
||||
div.phpdebugbar div.phpdebugbar-widgets-sqlqueries table.phpdebugbar-widgets-params th,
|
||||
div.phpdebugbar-openhandler .phpdebugbar-openhandler-actions > a,
|
||||
div.phpdebugbar-openhandler .phpdebugbar-openhandler-actions button,
|
||||
div.phpdebugbar div.phpdebugbar-widgets-templates table.phpdebugbar-widgets-params th {
|
||||
background-color: var(--color-gray-900);
|
||||
}
|
||||
|
||||
div.phpdebugbar .phpdebugbar-widgets-mails ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item .phpdebugbar-widgets-headers,
|
||||
div.phpdebugbar ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item table.phpdebugbar-widgets-params {
|
||||
border-left-color: var(--color-gray-600);
|
||||
}
|
||||
|
||||
div.phpdebugbar a.phpdebugbar-tab:hover,
|
||||
div.phpdebugbar span.phpdebugbar-indicator:hover,
|
||||
div.phpdebugbar a.phpdebugbar-indicator:hover,
|
||||
div.phpdebugbar a.phpdebugbar-close-btn:hover,
|
||||
div.phpdebugbar a.phpdebugbar-minimize-btn:hover,
|
||||
div.phpdebugbar a.phpdebugbar-maximize-btn:hover,
|
||||
div.phpdebugbar a.phpdebugbar-open-btn:hover,
|
||||
div.phpdebugbar-openhandler table th,
|
||||
div.phpdebugbar-openhandler table tr:nth-child(2n),
|
||||
div.phpdebugbar div.phpdebugbar-widgets-messages div.phpdebugbar-widgets-toolbar {
|
||||
background-color: var(--color-gray-700);
|
||||
}
|
||||
|
||||
div.phpdebugbar .phpdebugbar-indicator span.phpdebugbar-tooltip,
|
||||
div.phpdebugbar div.phpdebugbar-mini-design a.phpdebugbar-tab:hover span.phpdebugbar-text,
|
||||
div.phpdebugbar pre.sf-dump,
|
||||
div.phpdebugbar .hljs,
|
||||
div.phpdebugbar code.phpdebugbar-widgets-sql span.hljs-operator {
|
||||
color: var(--color-gray-100);
|
||||
}
|
||||
|
||||
div.phpdebugbar pre.sf-dump .sf-dump-public,
|
||||
div.phpdebugbar pre.sf-dump .sf-dump-protected,
|
||||
div.phpdebugbar pre.sf-dump .sf-dump-private {
|
||||
color: var(--color-gray-100) !important;
|
||||
}
|
||||
|
||||
div.phpdebugbar div.phpdebugbar-panel div.phpdebugbar-widgets-status > span:first-child:before,
|
||||
div.phpdebugbar-openhandler a {
|
||||
color: var(--color-gray-500);
|
||||
}
|
||||
|
||||
div.phpdebugbar .phpdebugbar-indicator span.phpdebugbar-tooltip,
|
||||
div.phpdebugbar div.phpdebugbar-mini-design a.phpdebugbar-tab:hover span.phpdebugbar-text {
|
||||
background: var(--color-gray-900);
|
||||
}
|
||||
|
||||
div.phpdebugbar .hljs-tag .hljs-value,
|
||||
div.phpdebugbar .hljs-phpdoc,
|
||||
div.phpdebugbar .tex .hljs-formula,
|
||||
div.phpdebugbar div.phpdebugbar-widgets-exceptions li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-message {
|
||||
color: var(--color-red-vivid);
|
||||
}
|
||||
|
||||
div.phpdebugbar div.phpdebugbar-widgets-sqlqueries li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-database,
|
||||
div.phpdebugbar div.phpdebugbar-widgets-sqlqueries li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-duration,
|
||||
div.phpdebugbar div.phpdebugbar-widgets-sqlqueries li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-memory,
|
||||
div.phpdebugbar div.phpdebugbar-widgets-sqlqueries li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-row-count,
|
||||
div.phpdebugbar div.phpdebugbar-widgets-sqlqueries li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-copy-clipboard,
|
||||
div.phpdebugbar div.phpdebugbar-widgets-sqlqueries li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-stmt-id,
|
||||
div.phpdebugbar .phpdebugbar-widgets-callgraph pre,
|
||||
div.phpdebugbar .phpdebugbar-text-muted,
|
||||
div.phpdebugbar-openhandler .phpdebugbar-text-muted
|
||||
{
|
||||
color: var(--color-gray-600);
|
||||
}
|
||||
|
||||
li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-filename,
|
||||
li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-filename a.phpdebugbar-widgets-editor-link
|
||||
{
|
||||
color: #ddd;
|
||||
}
|
||||
div.phpdebugbar div.phpdebugbar-widgets-sqlqueries li.phpdebugbar-widgets-list-item.phpdebugbar-widgets-sql-duplicate {
|
||||
background-color: #473e00;
|
||||
}
|
||||
|
||||
div.phpdebugbar-widgets-messages li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-value:before {
|
||||
color: #7B7B7B;
|
||||
}
|
||||
|
||||
div.phpdebugbar pre.sf-dump .sf-dump-note {
|
||||
color: #8be9fd;
|
||||
}
|
||||
|
||||
div.phpdebugbar pre.sf-dump .sf-dump-num,
|
||||
div.phpdebugbar pre.sf-dump .sf-dump-const,
|
||||
div.phpdebugbar pre.sf-dump .sf-dump-index {
|
||||
color: #bd93f9;
|
||||
}
|
||||
|
||||
div.phpdebugbar pre.sf-dump .sf-dump-key,
|
||||
div.phpdebugbar pre.sf-dump .sf-dump-str {
|
||||
color: #f1fa8c;
|
||||
}
|
||||
|
||||
div.phpdebugbar-openhandler {
|
||||
border-top-color: var(--color-red-vivid);
|
||||
}
|
||||
|
||||
div.phpdebugbar div.phpdebugbar-header .phpdebugbar-tab {
|
||||
border-left-color: var(--color-gray-800);
|
||||
}
|
||||
|
||||
div.phpdebugbar div.phpdebugbar-body {
|
||||
border-top-color: var(--color-gray-800);
|
||||
}
|
||||
|
||||
div.phpdebugbar a.phpdebugbar-restore-btn {
|
||||
border-right-color: var(--color-gray-800) !important;
|
||||
}
|
||||
|
||||
div.phpdebugbar span.phpdebugbar-indicator,
|
||||
div.phpdebugbar a.phpdebugbar-indicator,
|
||||
div.phpdebugbar a.phpdebugbar-close-btn {
|
||||
border-right-color: var(--color-gray-800);
|
||||
}
|
||||
|
||||
div.phpdebugbar div.phpdebugbar-panel div.phpdebugbar-widgets-status {
|
||||
background-color: var(--color-gray-900) !important;
|
||||
border-bottom-color: var(--color-gray-800) !important;
|
||||
}
|
||||
|
||||
div.phpdebugbar div.phpdebugbar-widgets-templates div.phpdebugbar-widgets-status {
|
||||
background: var(--color-gray-900) !important;
|
||||
}
|
||||
|
||||
div.phpdebugbar div.phpdebugbar-panel div.phpdebugbar-widgets-status > * {
|
||||
color: var(--color-gray-200) !important;
|
||||
}
|
||||
|
||||
div.phpdebugbar div.phpdebugbar-widgets-templates span.phpdebugbar-widgets-render-time,
|
||||
div.phpdebugbar div.phpdebugbar-widgets-templates span.phpdebugbar-widgets-memory,
|
||||
div.phpdebugbar div.phpdebugbar-widgets-templates span.phpdebugbar-widgets-param-count,
|
||||
div.phpdebugbar div.phpdebugbar-widgets-templates span.phpdebugbar-widgets-type {
|
||||
color: var(--color-gray-600) !important;
|
||||
}
|
||||
|
||||
div.phpdebugbar div.phpdebugbar-widgets-sqlqueries table.phpdebugbar-widgets-params td {
|
||||
border-color: var(--color-gray-600) !important;
|
||||
}
|
||||
|
||||
div.phpdebugbar code,
|
||||
div.phpdebugbar pre {
|
||||
color: #f1fa8c;
|
||||
}
|
||||
|
||||
div.phpdebugbar-openhandler .phpdebugbar-openhandler-actions > a,
|
||||
div.phpdebugbar-openhandler .phpdebugbar-openhandler-actions button,
|
||||
div.phpdebugbar-openhandler .phpdebugbar-openhandler-actions > form input,
|
||||
div.phpdebugbar-openhandler .phpdebugbar-openhandler-actions > form select,
|
||||
div.phpdebugbar input[type='text'],
|
||||
div.phpdebugbar input[type='password'] {
|
||||
color: var(--color-gray-300);
|
||||
}
|
||||
|
||||
div.phpdebugbar div.phpdebugbar-widgets-exceptions li.phpdebugbar-widgets-list-item.phpdebugbar-widgets-sql-duplicate span.phpdebugbar-widgets-filename,
|
||||
div.phpdebugbar div.phpdebugbar-widgets-sqlqueries li.phpdebugbar-widgets-list-item.phpdebugbar-widgets-sql-duplicate span.phpdebugbar-widgets-database,
|
||||
div.phpdebugbar div.phpdebugbar-widgets-sqlqueries li.phpdebugbar-widgets-list-item.phpdebugbar-widgets-sql-duplicate span.phpdebugbar-widgets-duration,
|
||||
div.phpdebugbar div.phpdebugbar-widgets-sqlqueries li.phpdebugbar-widgets-list-item.phpdebugbar-widgets-sql-duplicate span.phpdebugbar-widgets-memory,
|
||||
div.phpdebugbar div.phpdebugbar-widgets-sqlqueries li.phpdebugbar-widgets-list-item.phpdebugbar-widgets-sql-duplicate span.phpdebugbar-widgets-row-count,
|
||||
div.phpdebugbar div.phpdebugbar-widgets-sqlqueries li.phpdebugbar-widgets-list-item.phpdebugbar-widgets-sql-duplicate span.phpdebugbar-widgets-copy-clipboard,
|
||||
div.phpdebugbar div.phpdebugbar-widgets-sqlqueries li.phpdebugbar-widgets-list-item.phpdebugbar-widgets-sql-duplicate span.phpdebugbar-widgets-stmt-id {
|
||||
color: var(--color-gray-500);
|
||||
}
|
||||
|
||||
div.phpdebugbar dl.phpdebugbar-widgets-kvlist > :nth-child(4n)::before {
|
||||
background-color: var(--color-gray-900);
|
||||
}
|
||||
|
||||
dt.phpdebugbar-widgets-key {
|
||||
background: var(--color-gray-800);
|
||||
}
|
||||
|
||||
a.phpdebugbar-minimize-btn {
|
||||
background:url(data:image/svg+xml,%3Csvg%20viewBox=%220%200%201792%201792%22%20fill=%22none%22%20xmlns=%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d=%22m1683%20653.5-742%20741c-12.667%2012.67-27.667%2019-45%2019s-32.333-6.33-45-19l-742-741c-12.667-12.667-19-27.833-19-45.5s6.333-32.833%2019-45.5l166-165c12.667-12.667%2027.667-19%2045-19s32.333%206.333%2045%2019l531%20531%20531-531c12.67-12.667%2027.67-19%2045-19s32.33%206.333%2045%2019l166%20165c12.67%2012.667%2019%2027.833%2019%2045.5s-6.33%2032.833-19%2045.5Z%22%20fill=%22%23EDF2F7%22%2F%3E%3C%2Fsvg%3E) no-repeat center / 14px 14px;
|
||||
}
|
||||
|
||||
a.phpdebugbar-maximize-btn {
|
||||
background: url(data:image/svg+xml,%3Csvg%20viewBox=%220%200%201792%201792%22%20fill=%22none%22%20xmlns=%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d=%22m1683%201229.5-166%20165c-12.67%2012.67-27.67%2019-45%2019s-32.33-6.33-45-19l-531-531-531%20531c-12.667%2012.67-27.667%2019-45%2019s-32.333-6.33-45-19l-166-165c-12.667-12.67-19-27.83-19-45.5s6.333-32.83%2019-45.5l742-741c12.667-12.667%2027.667-19%2045-19s32.333%206.333%2045%2019l742%20741c12.67%2012.67%2019%2027.83%2019%2045.5s-6.33%2032.83-19%2045.5Z%22%20fill=%22%23EDF2F7%22%2F%3E%3C%2Fsvg%3E) no-repeat center / 14px 14px;
|
||||
}
|
||||
|
||||
a.phpdebugbar-close-btn {
|
||||
background: url(data:image/svg+xml,%3Csvg%20viewBox=%220%200%201792%201792%22%20fill=%22none%22%20xmlns=%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d=%22M1490%201258c0%2026.67-9.33%2049.33-28%2068l-136%20136c-18.67%2018.67-41.33%2028-68%2028s-49.33-9.33-68-28l-294-294-294%20294c-18.667%2018.67-41.333%2028-68%2028s-49.333-9.33-68-28l-136-136c-18.667-18.67-28-41.33-28-68s9.333-49.33%2028-68l294-294-294-294c-18.667-18.667-28-41.333-28-68s9.333-49.333%2028-68l136-136c18.667-18.667%2041.333-28%2068-28s49.333%209.333%2068%2028l294%20294%20294-294c18.67-18.667%2041.33-28%2068-28s49.33%209.333%2068%2028l136%20136c18.67%2018.667%2028%2041.333%2028%2068s-9.33%2049.333-28%2068l-294%20294%20294%20294c18.67%2018.67%2028%2041.33%2028%2068Z%22%20fill=%22%23EDF2F7%22%2F%3E%3C%2Fsvg%3E) no-repeat center / 14px 14px;
|
||||
}
|
||||
|
||||
a.phpdebugbar-open-btn {
|
||||
background: url(data:image/svg+xml,%3Csvg%20width%3D%221792%22%20height%3D%221792%22%20fill%3D%22none%22%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%3E%3Cpath%20d%3D%22M1646%20991.797c0%2016.493-8.25%2034.063-24.75%2052.683l-268.22%20316.13c-22.89%2027.14-54.95%2050.16-96.2%2069.05S1177.4%201458%201142.27%201458H273.728c-18.095%200-34.194-3.46-48.297-10.38-14.104-6.92-21.155-18.36-21.155-34.32%200-16.5%208.249-34.06%2024.747-52.69l268.228-316.13c22.884-27.14%2054.949-50.155%2096.194-69.048%2041.246-18.893%2079.431-28.34%20114.556-28.34h868.549c18.09%200%2034.19%203.458%2048.3%2010.377%2014.1%206.918%2021.15%2018.362%2021.15%2034.328Zm-273.82-274.615V844.91H708.001c-50.027%200-102.448%2012.639-157.264%2037.918-54.817%2025.28-98.457%2057.078-130.921%2095.397L150.79%201294.35l-3.992%204.79c0-2.13-.133-5.46-.399-9.98-.266-4.52-.399-7.85-.399-9.98V512.818c0-48.962%2017.563-91.005%2052.688-126.13C233.813%20351.562%20275.856%20334%20324.819%20334h255.455c48.962%200%2091.005%2017.562%20126.13%2052.688%2035.126%2035.125%2052.688%2077.168%2052.688%20126.13v25.545h434.278c48.96%200%2091%2017.564%20126.13%2052.689%2035.12%2035.125%2052.68%2077.168%2052.68%20126.13Z%22%20fill%3D%22%23EDF2F7%22/%3E%3C/svg%3E) no-repeat center / 14px 14px;
|
||||
}
|
||||
|
||||
div.phpdebugbar code.language-php,
|
||||
div.phpdebugbar pre.language-php {
|
||||
color: #FFF;
|
||||
}
|
||||
|
||||
pre.phpdebugbar-widgets-code-block ul {
|
||||
background: none;
|
||||
}
|
||||
|
||||
/* Dracula Theme v1.2.5
|
||||
*
|
||||
* https://github.com/dracula/highlightjs
|
||||
*
|
||||
* Copyright 2016-present, All rights reserved
|
||||
*
|
||||
* Code licensed under the MIT license
|
||||
*
|
||||
* @author Denis Ciccale <dciccale@gmail.com>
|
||||
* @author Zeno Rocha <hi@zenorocha.com>
|
||||
*/
|
||||
|
||||
div.phpdebugbar .hljs-built_in,
|
||||
div.phpdebugbar .hljs-selector-tag,
|
||||
div.phpdebugbar .hljs-section,
|
||||
div.phpdebugbar .hljs-link {
|
||||
color: #8be9fd;
|
||||
}
|
||||
|
||||
div.phpdebugbar .hljs-keyword {
|
||||
color: #ff79c6;
|
||||
}
|
||||
|
||||
div.phpdebugbar .hljs,
|
||||
div.phpdebugbar .hljs-subst {
|
||||
color: #f8f8f2;
|
||||
}
|
||||
|
||||
div.phpdebugbar .hljs-title,
|
||||
div.phpdebugbar .hljs-meta-keyword {
|
||||
color: #50fa7b;
|
||||
}
|
||||
|
||||
div.phpdebugbar .hljs-string,
|
||||
div.phpdebugbar .hljs-meta,
|
||||
div.phpdebugbar .hljs-name,
|
||||
div.phpdebugbar .hljs-type,
|
||||
div.phpdebugbar .hljs-attr,
|
||||
div.phpdebugbar .hljs-symbol,
|
||||
div.phpdebugbar .hljs-bullet,
|
||||
div.phpdebugbar .hljs-addition,
|
||||
div.phpdebugbar .hljs-variable,
|
||||
div.phpdebugbar .hljs-template-tag,
|
||||
div.phpdebugbar .hljs-template-variable {
|
||||
color: #f1fa8c;
|
||||
}
|
||||
|
||||
div.phpdebugbar .hljs-comment,
|
||||
div.phpdebugbar .hljs-quote,
|
||||
div.phpdebugbar .hljs-deletion {
|
||||
color: #6272a4;
|
||||
}
|
||||
|
||||
div.phpdebugbar .hljs-strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
div.phpdebugbar .hljs-literal,
|
||||
div.phpdebugbar .hljs-number {
|
||||
color: #bd93f9;
|
||||
}
|
||||
|
||||
.phpdebugbar-widgets-dataset-history table tr.phpdebugbar-widgets-active {
|
||||
background-color: var(--color-gray-700);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.phpdebugbar-widgets-dataset-history table th,
|
||||
.phpdebugbar-widgets-dataset-history table td {
|
||||
border-color: var(--color-gray-600);
|
||||
}
|
||||
|
||||
ul.phpdebugbar-widgets-timeline table.phpdebugbar-widgets-params td {
|
||||
border-color: var(--color-gray-700);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,921 @@
|
||||
/* Force Laravel Whoops exception handler to be displayed under the debug bar */
|
||||
.Whoops.container {
|
||||
z-index: 5999999;
|
||||
}
|
||||
div.phpdebugbar,
|
||||
div.phpdebugbar-openhandler {
|
||||
--color-red-vivid: #eb4432;
|
||||
}
|
||||
|
||||
div.phpdebugbar {
|
||||
font-size: 13px;
|
||||
font-family: "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", Geneva, Verdana, sans-serif;
|
||||
direction: ltr;
|
||||
text-align: left;
|
||||
z-index: 6000000;
|
||||
}
|
||||
|
||||
div.phpdebugbar * {
|
||||
direction: ltr;
|
||||
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
div.phpdebugbar-openhandler-overlay {
|
||||
z-index: 6000001;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
div.phpdebugbar-openhandler {
|
||||
border: 1px solid #aaa;
|
||||
border-top: 3px solid var(--color-red-vivid);
|
||||
width: 80%;
|
||||
height: 70%;
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
overflow-y: scroll;
|
||||
z-index: 6000002;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
div.phpdebugbar-openhandler .phpdebugbar-openhandler-actions > a,
|
||||
div.phpdebugbar-openhandler .phpdebugbar-openhandler-actions button {
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
margin: 5px;
|
||||
padding: 0px 12px 2px;
|
||||
border-radius: 3px;
|
||||
border: 1px solid #ddd;
|
||||
background-color: #f5f5f5;
|
||||
color: #000;
|
||||
text-shadow: 1px 1px #fff;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
div.phpdebugbar-openhandler .phpdebugbar-openhandler-actions > form {
|
||||
margin: 15px 0px 5px;
|
||||
text-transform: uppercase;
|
||||
font-size: 13px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
div.phpdebugbar-openhandler .phpdebugbar-openhandler-actions > form br {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div.phpdebugbar-openhandler .phpdebugbar-openhandler-actions > form > b {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div.phpdebugbar-openhandler .phpdebugbar-openhandler-actions > form input,
|
||||
div.phpdebugbar-openhandler .phpdebugbar-openhandler-actions > form select {
|
||||
margin: 0px 10px 10px 2px;
|
||||
border: 1px solid #aaa;
|
||||
border-radius: 3px;
|
||||
padding: 3px 6px 2px;
|
||||
line-height: 16px;
|
||||
}
|
||||
|
||||
@media (max-width: 720px) {
|
||||
div.phpdebugbar-openhandler .phpdebugbar-openhandler-actions > form select + br,
|
||||
div.phpdebugbar-openhandler .phpdebugbar-openhandler-actions > form input[name="uri"] + br {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
div.phpdebugbar-openhandler .phpdebugbar-openhandler-actions > form select {
|
||||
padding: 2px 5px 1px;
|
||||
}
|
||||
|
||||
div.phpdebugbar-openhandler .phpdebugbar-openhandler-actions input[name="uri"] {
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
div.phpdebugbar-openhandler .phpdebugbar-openhandler-actions input[name="ip"] {
|
||||
width: 90px;
|
||||
}
|
||||
|
||||
div.phpdebugbar-openhandler .phpdebugbar-openhandler-actions button {
|
||||
outline: none;
|
||||
margin: 0px 0px 10px 2px;
|
||||
padding: 5px 15px 4px;
|
||||
line-height: 12px;
|
||||
}
|
||||
|
||||
div.phpdebugbar-openhandler table {
|
||||
margin: 15px 0px 10px;
|
||||
table-layout: auto;
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
div.phpdebugbar-openhandler table td,
|
||||
div.phpdebugbar-openhandler table th {
|
||||
width: auto!important;
|
||||
text-align: left;
|
||||
border: 0px solid #bbb;
|
||||
padding: 2px 8px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
div.phpdebugbar-openhandler table th {
|
||||
text-shadow: 1px 1px #fff;
|
||||
font-size: 12px;
|
||||
text-transform: uppercase;
|
||||
padding: 5px 8px;
|
||||
}
|
||||
|
||||
div.phpdebugbar-openhandler table th,
|
||||
div.phpdebugbar-openhandler table tr:nth-child(2n) {
|
||||
background-color: #efefef;
|
||||
}
|
||||
|
||||
div.phpdebugbar-openhandler table th:nth-child(1), div.phpdebugbar-openhandler table td:nth-child(1), /* Date */
|
||||
div.phpdebugbar-openhandler table th:nth-child(2), div.phpdebugbar-openhandler table td:nth-child(2), /* Method */
|
||||
div.phpdebugbar-openhandler table th:nth-child(4), div.phpdebugbar-openhandler table td:nth-child(4), /* IP */
|
||||
div.phpdebugbar-openhandler table th:nth-child(5), div.phpdebugbar-openhandler table td:nth-child(5) { /* Filter */
|
||||
width: 5%!important;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
div.phpdebugbar-openhandler table th:nth-child(2), div.phpdebugbar-openhandler table td:nth-child(2), /* Method */
|
||||
div.phpdebugbar-openhandler table th:nth-child(4), div.phpdebugbar-openhandler table td:nth-child(4), /* IP */
|
||||
div.phpdebugbar-openhandler table th:nth-child(5), div.phpdebugbar-openhandler table td:nth-child(5) { /* Filter */
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
div.phpdebugbar-openhandler table th:nth-child(3) { /* URL */
|
||||
width: calc(100vw - 100% - 196px)!important;
|
||||
}
|
||||
|
||||
div.phpdebugbar-openhandler table td a {
|
||||
display: block;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
div.phpdebugbar-resize-handle {
|
||||
display: block!important;
|
||||
height: 3px;
|
||||
margin-top: -3px;
|
||||
width: 100%;
|
||||
background: none;
|
||||
cursor: ns-resize;
|
||||
border-top: none;
|
||||
border-bottom: 0px;
|
||||
background-color: var(--color-red-vivid);
|
||||
}
|
||||
|
||||
.phpdebugbar.phpdebugbar-minimized div.phpdebugbar-resize-handle {
|
||||
cursor: default!important;
|
||||
}
|
||||
|
||||
div.phpdebugbar-closed,
|
||||
div.phpdebugbar-minimized {
|
||||
border-top-color: #ddd;
|
||||
}
|
||||
|
||||
div.phpdebugbar code, div.phpdebugbar pre, div.phpdebugbar samp {
|
||||
background: none !important;
|
||||
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
|
||||
font-size: 1em;
|
||||
border: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
div.phpdebugbar .hljs {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
div.phpdebugbar .phpdebugbar-widgets-messages .hljs > code {
|
||||
padding-bottom: 3px;
|
||||
}
|
||||
|
||||
div.phpdebugbar code, div.phpdebugbar pre {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
div.phpdebugbar-widgets-exceptions .phpdebugbar-widgets-filename {
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
div.phpdebugbar-widgets-exceptions li.phpdebugbar-widgets-list-item pre.phpdebugbar-widgets-file {
|
||||
border: 1px solid #d2d2d2;
|
||||
border-left: 2px solid #d2d2d2;
|
||||
}
|
||||
|
||||
div.phpdebugbar-widgets-exceptions li.phpdebugbar-widgets-list-item pre.phpdebugbar-widgets-file[style="display: block;"] ~ div {
|
||||
display: block;
|
||||
}
|
||||
|
||||
div.phpdebugbar pre.sf-dump {
|
||||
color: #000;
|
||||
outline: none;
|
||||
padding-left: 0px;
|
||||
}
|
||||
|
||||
div.phpdebugbar-body {
|
||||
border-top: 1px solid #ddd;
|
||||
}
|
||||
|
||||
div.phpdebugbar-header {
|
||||
min-height: 30px;
|
||||
line-height: 20px;
|
||||
text-shadow: 1px 1px #FFF;
|
||||
}
|
||||
|
||||
div.phpdebugbar-header span.phpdebugbar-text, div.phpdebugbar-header > div > span > span {
|
||||
transform: translateY(-1px) !important;
|
||||
}
|
||||
|
||||
a.phpdebugbar-restore-btn {
|
||||
background: url(data:image/svg+xml,%3Csvg%20viewBox%3D%220%200%2048%2048%22%20fill%3D%22none%22%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%3E%3Cpath%20fill-rule%3D%22evenodd%22%20clip-rule%3D%22evenodd%22%20d%3D%22M47.973%2010.859a.74.74%200%200%201%20.027.196v10.303a.735.735%200%200%201-.104.377.763.763%200%200%201-.285.275l-8.902%204.979v9.868a.75.75%200%200%201-.387.652L19.74%2047.9c-.043.023-.09.038-.135.054-.018.006-.034.016-.053.021a.801.801%200%200%201-.396%200c-.021-.006-.04-.017-.061-.024-.043-.015-.087-.029-.128-.051L.39%2037.509a.763.763%200%200%201-.285-.276.736.736%200%200%201-.104-.376V5.947c0-.067.01-.133.027-.196.006-.022.02-.042.027-.063.015-.04.028-.08.05-.117.014-.024.035-.044.053-.067.022-.03.042-.06.068-.087.022-.021.051-.037.077-.056.028-.023.053-.047.085-.065L9.677.1a.793.793%200%200%201%20.774%200l9.29%205.196h.002c.03.019.057.042.085.064.025.019.053.036.075.056.027.027.047.058.07.088.016.023.038.043.052.067.022.038.035.077.05.117.008.021.021.04.027.063a.74.74%200%200%201%20.027.197v19.305l7.742-4.33v-9.869c0-.066.01-.132.027-.195.006-.023.019-.042.027-.064.015-.04.029-.08.05-.116.014-.025.036-.045.052-.067.023-.03.043-.061.07-.087.022-.022.05-.038.075-.057.03-.022.054-.047.085-.065l9.292-5.195a.792.792%200%200%201%20.773%200l9.29%205.195c.033.02.058.043.087.064.025.02.053.036.075.057.027.027.046.058.07.088.017.022.038.042.052.067.022.036.034.077.05.116.009.022.021.041.027.064ZM46.45%2020.923v-8.567l-3.25%201.818-4.492%202.512v8.567l7.743-4.33Zm-9.29%2015.5v-8.574l-4.417%202.45-12.616%206.995v8.654l17.033-9.526ZM1.55%207.247v29.174l17.03%209.525v-8.653l-8.897-4.89-.003-.003-.003-.002c-.03-.017-.056-.041-.084-.062-.024-.018-.052-.033-.073-.054l-.002-.003c-.025-.023-.042-.053-.064-.079-.02-.025-.042-.047-.058-.073v-.003c-.018-.028-.029-.062-.041-.094-.013-.028-.03-.054-.037-.084-.01-.036-.012-.075-.016-.111-.003-.028-.011-.056-.011-.085V11.58L4.8%209.064%201.549%207.248Zm8.516-5.628-7.74%204.328%207.738%204.328%207.74-4.33-7.74-4.326h.002Zm4.026%2027.01%204.49-2.51V7.247L15.33%209.066l-4.492%202.512V30.45l3.253-1.819ZM37.935%206.727l-7.74%204.328%207.74%204.328%207.738-4.329-7.738-4.327Zm-.775%209.959-4.49-2.512-3.252-1.818v8.567l4.49%202.511%203.252%201.82v-8.568ZM19.353%2035.993l11.352-6.295%205.674-3.146-7.733-4.325-8.904%204.98-8.116%204.538%207.727%204.248Z%22%20fill%3D%22%23FF2D20%22/%3E%3C/svg%3E) no-repeat 11px center / 20px 20px !important;
|
||||
}
|
||||
|
||||
div.phpdebugbar-openhandler .phpdebugbar-openhandler-header {
|
||||
background: url(data:image/svg+xml,%3Csvg%20viewBox%3D%220%200%2048%2048%22%20fill%3D%22none%22%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%3E%3Cpath%20fill-rule%3D%22evenodd%22%20clip-rule%3D%22evenodd%22%20d%3D%22M47.973%2010.859a.74.74%200%200%201%20.027.196v10.303a.735.735%200%200%201-.104.377.763.763%200%200%201-.285.275l-8.902%204.979v9.868a.75.75%200%200%201-.387.652L19.74%2047.9c-.043.023-.09.038-.135.054-.018.006-.034.016-.053.021a.801.801%200%200%201-.396%200c-.021-.006-.04-.017-.061-.024-.043-.015-.087-.029-.128-.051L.39%2037.509a.763.763%200%200%201-.285-.276.736.736%200%200%201-.104-.376V5.947c0-.067.01-.133.027-.196.006-.022.02-.042.027-.063.015-.04.028-.08.05-.117.014-.024.035-.044.053-.067.022-.03.042-.06.068-.087.022-.021.051-.037.077-.056.028-.023.053-.047.085-.065L9.677.1a.793.793%200%200%201%20.774%200l9.29%205.196h.002c.03.019.057.042.085.064.025.019.053.036.075.056.027.027.047.058.07.088.016.023.038.043.052.067.022.038.035.077.05.117.008.021.021.04.027.063a.74.74%200%200%201%20.027.197v19.305l7.742-4.33v-9.869c0-.066.01-.132.027-.195.006-.023.019-.042.027-.064.015-.04.029-.08.05-.116.014-.025.036-.045.052-.067.023-.03.043-.061.07-.087.022-.022.05-.038.075-.057.03-.022.054-.047.085-.065l9.292-5.195a.792.792%200%200%201%20.773%200l9.29%205.195c.033.02.058.043.087.064.025.02.053.036.075.057.027.027.046.058.07.088.017.022.038.042.052.067.022.036.034.077.05.116.009.022.021.041.027.064ZM46.45%2020.923v-8.567l-3.25%201.818-4.492%202.512v8.567l7.743-4.33Zm-9.29%2015.5v-8.574l-4.417%202.45-12.616%206.995v8.654l17.033-9.526ZM1.55%207.247v29.174l17.03%209.525v-8.653l-8.897-4.89-.003-.003-.003-.002c-.03-.017-.056-.041-.084-.062-.024-.018-.052-.033-.073-.054l-.002-.003c-.025-.023-.042-.053-.064-.079-.02-.025-.042-.047-.058-.073v-.003c-.018-.028-.029-.062-.041-.094-.013-.028-.03-.054-.037-.084-.01-.036-.012-.075-.016-.111-.003-.028-.011-.056-.011-.085V11.58L4.8%209.064%201.549%207.248Zm8.516-5.628-7.74%204.328%207.738%204.328%207.74-4.33-7.74-4.326h.002Zm4.026%2027.01%204.49-2.51V7.247L15.33%209.066l-4.492%202.512V30.45l3.253-1.819ZM37.935%206.727l-7.74%204.328%207.74%204.328%207.738-4.329-7.738-4.327Zm-.775%209.959-4.49-2.512-3.252-1.818v8.567l4.49%202.511%203.252%201.82v-8.568ZM19.353%2035.993l11.352-6.295%205.674-3.146-7.733-4.325-8.904%204.98-8.116%204.538%207.727%204.248Z%22%20fill%3D%22%23FF2D20%22/%3E%3C/svg%3E) no-repeat 11px center / 20px 20px !important;
|
||||
padding: 4px 4px 6px 38px;
|
||||
margin: 0px !important;
|
||||
}
|
||||
|
||||
div.phpdebugbar-openhandler .phpdebugbar-openhandler-header a {
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
div.phpdebugbar-header,
|
||||
div.phpdebugbar-openhandler-header {
|
||||
background-size: 21px auto;
|
||||
background-position: 9px center;
|
||||
}
|
||||
|
||||
a.phpdebugbar-restore-btn {
|
||||
border-right-color: #ddd!important;
|
||||
height: 20px;
|
||||
width: 24px;
|
||||
background-position: center;
|
||||
background-size: 21px;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.phpdebugbar:not(.phpdebugbar-closed) a.phpdebugbar-restore-btn {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
div.phpdebugbar-header > div > * {
|
||||
font-size: 13px;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
div.phpdebugbar-header .phpdebugbar-tab {
|
||||
padding: 5px 8px;
|
||||
border-left: 1px solid #ddd;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
a.phpdebugbar-tab.phpdebugbar-tab-history {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
div.phpdebugbar-header .phpdebugbar-header-left {
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
div.phpdebugbar .phpdebugbar-header select {
|
||||
margin: 0;
|
||||
padding: 2px 3px 3px 3px;
|
||||
border-radius: 3px;
|
||||
width: auto;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
dl.phpdebugbar-widgets-kvlist dt,
|
||||
dl.phpdebugbar-widgets-kvlist dd {
|
||||
min-height: 20px;
|
||||
line-height: 20px;
|
||||
padding: 4px 5px 5px;
|
||||
border-top: 0px;
|
||||
}
|
||||
|
||||
dl.phpdebugbar-widgets-kvlist dd.phpdebugbar-widgets-value.phpdebugbar-widgets-pretty .phpdebugbar-widgets-code-block {
|
||||
padding: 0px 0px;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
dl.phpdebugbar-widgets-kvlist dt {
|
||||
width: 25%;
|
||||
}
|
||||
|
||||
dl.phpdebugbar-widgets-kvlist dd {
|
||||
margin-left: 25%;
|
||||
}
|
||||
|
||||
ul.phpdebugbar-widgets-timeline li .phpdebugbar-widgets-measure {
|
||||
margin: 0 6px !important;
|
||||
height: 28px;
|
||||
line-height: 28px;
|
||||
border: none;
|
||||
}
|
||||
|
||||
ul.phpdebugbar-widgets-timeline li > .phpdebugbar-widgets-measure {
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
ul.phpdebugbar-widgets-timeline li span.phpdebugbar-widgets-value {
|
||||
height: 16px;
|
||||
background-color: #63abca;
|
||||
border-bottom: 2px solid #477e96;
|
||||
}
|
||||
|
||||
ul.phpdebugbar-widgets-timeline li span.phpdebugbar-widgets-label,
|
||||
ul.phpdebugbar-widgets-timeline li span.phpdebugbar-widgets-collector {
|
||||
top: 0px;
|
||||
color: #000;
|
||||
font-size: 11px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 90%;
|
||||
}
|
||||
|
||||
ul.phpdebugbar-widgets-timeline li .phpdebugbar-widgets-value span.phpdebugbar-widgets-label {
|
||||
color: #fff;
|
||||
text-shadow: 1px 1px #000;
|
||||
}
|
||||
|
||||
ul.phpdebugbar-widgets-timeline table.phpdebugbar-widgets-params {
|
||||
font-size: 11px;
|
||||
margin-top: 20px !important;
|
||||
}
|
||||
|
||||
ul.phpdebugbar-widgets-timeline table.phpdebugbar-widgets-params td {
|
||||
border-left: none ;
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
ul.phpdebugbar-widgets-timeline table.phpdebugbar-widgets-params td:first-child {
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
div.phpdebugbar-widgets-messages div.phpdebugbar-widgets-toolbar {
|
||||
width: calc(100% - 20px);
|
||||
margin-left: 10px;
|
||||
padding: 4px 0px 4px;
|
||||
height: 20px;
|
||||
border-top: 1px solid #ddd;
|
||||
border-bottom: 0px;
|
||||
background-color: #e8e8e8;
|
||||
border-radius: 5px 5px 0px 0px;
|
||||
}
|
||||
|
||||
div.phpdebugbar-widgets-messages div.phpdebugbar-widgets-toolbar a.phpdebugbar-widgets-filter, div.phpdebugbar-widgets-messages div.phpdebugbar-widgets-toolbar a.phpdebugbar-widgets-filter.phpdebugbar-widgets-excluded {
|
||||
right: 3px;
|
||||
}
|
||||
|
||||
div.phpdebugbar-widgets-messages div.phpdebugbar-widgets-toolbar input {
|
||||
width: calc(100% - 48px);
|
||||
margin-left: 0px;
|
||||
border-radius: 3px;
|
||||
padding: 2px 6px;
|
||||
height: 15px;
|
||||
}
|
||||
|
||||
div.phpdebugbar-widgets-messages li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-label,
|
||||
div.phpdebugbar-widgets-messages li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-collector {
|
||||
padding: 1px 0px 0px 10px;
|
||||
margin: 0px;
|
||||
text-transform: uppercase;
|
||||
font-style: normal;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
div.phpdebugbar-widgets-messages li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-collector {
|
||||
text-transform: none;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
.phpdebugbar-widgets-toolbar i.phpdebugbar-fa.phpdebugbar-fa-search {
|
||||
position: relative;
|
||||
top: -1px;
|
||||
padding: 0px 10px;
|
||||
}
|
||||
|
||||
div.phpdebugbar-widgets-messages div.phpdebugbar-widgets-toolbar a.phpdebugbar-widgets-filter,
|
||||
div.phpdebugbar-widgets-messages div.phpdebugbar-widgets-toolbar a.phpdebugbar-widgets-filter.phpdebugbar-widgets-excluded {
|
||||
position: relative;
|
||||
top: -48px;
|
||||
display: inline-block;
|
||||
background-color: #6d6d6d;
|
||||
margin-left: 3px;
|
||||
border-radius: 3px;
|
||||
padding: 5px 8px 4px;
|
||||
text-transform: uppercase;
|
||||
font-size: 10px;
|
||||
text-shadow: 1px 1px #585858;
|
||||
transition: background-color .25s linear 0s, color .25s linear 0s;
|
||||
color: #FFF;
|
||||
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
div.phpdebugbar-widgets-messages div.phpdebugbar-widgets-toolbar a.phpdebugbar-widgets-filter[rel="alert"],
|
||||
div.phpdebugbar-widgets-messages div.phpdebugbar-widgets-toolbar a.phpdebugbar-widgets-filter.phpdebugbar-widgets-excluded[rel="alert"],
|
||||
div.phpdebugbar-widgets-messages div.phpdebugbar-widgets-toolbar a.phpdebugbar-widgets-filter[rel="info"],
|
||||
div.phpdebugbar-widgets-messages div.phpdebugbar-widgets-toolbar a.phpdebugbar-widgets-filter.phpdebugbar-widgets-excluded[rel="info"] {
|
||||
background-color: #5896e2;
|
||||
}
|
||||
|
||||
div.phpdebugbar-widgets-messages div.phpdebugbar-widgets-toolbar a.phpdebugbar-widgets-filter[rel="debug"],
|
||||
div.phpdebugbar-widgets-messages div.phpdebugbar-widgets-toolbar a.phpdebugbar-widgets-filter.phpdebugbar-widgets-excluded[rel="debug"],
|
||||
div.phpdebugbar-widgets-messages div.phpdebugbar-widgets-toolbar a.phpdebugbar-widgets-filter[rel="success"],
|
||||
div.phpdebugbar-widgets-messages div.phpdebugbar-widgets-toolbar a.phpdebugbar-widgets-filter.phpdebugbar-widgets-excluded[rel="success"] {
|
||||
background-color: #45ab45;
|
||||
}
|
||||
|
||||
div.phpdebugbar-widgets-messages div.phpdebugbar-widgets-toolbar a.phpdebugbar-widgets-filter[rel="critical"],
|
||||
div.phpdebugbar-widgets-messages div.phpdebugbar-widgets-toolbar a.phpdebugbar-widgets-filter.phpdebugbar-widgets-excluded[rel="critical"],
|
||||
div.phpdebugbar-widgets-messages div.phpdebugbar-widgets-toolbar a.phpdebugbar-widgets-filter[rel="error"],
|
||||
div.phpdebugbar-widgets-messages div.phpdebugbar-widgets-toolbar a.phpdebugbar-widgets-filter.phpdebugbar-widgets-excluded[rel="error"] {
|
||||
background-color: var(--color-red-vivid);
|
||||
}
|
||||
|
||||
div.phpdebugbar-widgets-messages div.phpdebugbar-widgets-toolbar a.phpdebugbar-widgets-filter[rel="notice"],
|
||||
div.phpdebugbar-widgets-messages div.phpdebugbar-widgets-toolbar a.phpdebugbar-widgets-filter.phpdebugbar-widgets-excluded[rel="notice"],
|
||||
div.phpdebugbar-widgets-messages div.phpdebugbar-widgets-toolbar a.phpdebugbar-widgets-filter[rel="warning"],
|
||||
div.phpdebugbar-widgets-messages div.phpdebugbar-widgets-toolbar a.phpdebugbar-widgets-filter.phpdebugbar-widgets-excluded[rel="warning"] {
|
||||
background-color: #f99400;
|
||||
}
|
||||
|
||||
div.phpdebugbar-widgets-messages div.phpdebugbar-widgets-toolbar a.phpdebugbar-widgets-filter:hover {
|
||||
color: #FFF;
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
div.phpdebugbar-widgets-messages div.phpdebugbar-widgets-toolbar a.phpdebugbar-widgets-filter.phpdebugbar-widgets-excluded {
|
||||
opacity: 0.45;
|
||||
}
|
||||
|
||||
a.phpdebugbar-tab:hover,
|
||||
span.phpdebugbar-indicator:hover,
|
||||
a.phpdebugbar-indicator:hover,
|
||||
a.phpdebugbar-close-btn:hover,
|
||||
a.phpdebugbar-restore-btn:hover,
|
||||
a.phpdebugbar-minimize-btn:hover,
|
||||
a.phpdebugbar-maximize-btn:hover,
|
||||
a.phpdebugbar-open-btn:hover {
|
||||
background-color: #ebebeb;
|
||||
/* transition: background-color .25s linear 0s, color .25s linear 0s; */
|
||||
}
|
||||
|
||||
a.phpdebugbar-minimize-btn,
|
||||
a.phpdebugbar-maximize-btn {
|
||||
width: 28px!important;
|
||||
}
|
||||
|
||||
a.phpdebugbar-tab.phpdebugbar-active {
|
||||
background: var(--color-red-vivid);
|
||||
background-image: none;
|
||||
color: #fff !important;
|
||||
text-shadow: 1px 1px #bf3039;
|
||||
}
|
||||
|
||||
a.phpdebugbar-tab.phpdebugbar-active span.phpdebugbar-badge {
|
||||
background-color: white;
|
||||
color: var(--color-red-vivid);
|
||||
text-shadow: 1px 1px #ebebeb;
|
||||
}
|
||||
|
||||
a.phpdebugbar-tab span.phpdebugbar-badge {
|
||||
vertical-align: 0px;
|
||||
padding: 2px 8px;
|
||||
text-align: center;
|
||||
background: var(--color-red-vivid);
|
||||
font-size: 11px;
|
||||
font-family: monospace;
|
||||
color: #fff;
|
||||
text-shadow: 1px 1px #bf3039;
|
||||
border-radius: 10px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.phpdebugbar-indicator {
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
.phpdebugbar-indicator span.phpdebugbar-tooltip,
|
||||
div.phpdebugbar-mini-design a.phpdebugbar-tab:hover span.phpdebugbar-text {
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
background: #f5f5f5;
|
||||
font-size: 12px;
|
||||
width: auto;
|
||||
white-space: nowrap;
|
||||
padding: 2px 18px;
|
||||
text-shadow: none;
|
||||
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
div.phpdebugbar-mini-design a.phpdebugbar-tab:hover span.phpdebugbar-text {
|
||||
left: 0px;
|
||||
right: auto;
|
||||
}
|
||||
|
||||
.phpdebugbar-widgets-toolbar > .fa {
|
||||
width: 25px;
|
||||
font-size: 15px;
|
||||
color: #555;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item {
|
||||
padding: 7px 10px;
|
||||
border: none;
|
||||
font-family: inherit;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item .phpdebugbar-widgets-sql {
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item:hover,
|
||||
ul.phpdebugbar-widgets-timeline li:hover {
|
||||
background-color: initial;
|
||||
}
|
||||
|
||||
.phpdebugbar-widgets-sqlqueries ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.phpdebugbar-widgets-templates ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.phpdebugbar-widgets-templates ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item,
|
||||
.phpdebugbar-widgets-mails ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item {
|
||||
line-height: 15px;
|
||||
}
|
||||
|
||||
.phpdebugbar-widgets-mails ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item {
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.phpdebugbar-widgets-mails ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item .phpdebugbar-widgets-subject {
|
||||
display: inline-block;
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
.phpdebugbar-widgets-mails ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item .phpdebugbar-widgets-headers {
|
||||
margin: 10px 0px;
|
||||
padding: 7px 10px;
|
||||
border-left: 2px solid #ddd;
|
||||
line-height: 17px;
|
||||
}
|
||||
|
||||
.phpdebugbar-widgets-sql.phpdebugbar-widgets-name {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item .phpdebugbar-widgets-sql {
|
||||
flex: 1;
|
||||
margin-right: 5px;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item .phpdebugbar-widgets-duration,
|
||||
ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item .phpdebugbar-widgets-stmt-id,
|
||||
ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item .phpdebugbar-widgets-memory {
|
||||
margin-left: auto;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item .phpdebugbar-widgets-database {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item .phpdebugbar-widgets-stmt-id a {
|
||||
color: #888;
|
||||
}
|
||||
|
||||
ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item .phpdebugbar-widgets-stmt-id a:hover {
|
||||
color: #aaa;
|
||||
}
|
||||
|
||||
ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item table.phpdebugbar-widgets-params {
|
||||
background-color: #fdfdfd;
|
||||
margin: 10px 0px;
|
||||
font-size: 12px;
|
||||
border-left: 2px solid #cecece;
|
||||
}
|
||||
|
||||
div.phpdebugbar-widgets-templates table.phpdebugbar-widgets-params th,
|
||||
div.phpdebugbar-widgets-templates table.phpdebugbar-widgets-params td {
|
||||
padding: 1px 10px!important;
|
||||
}
|
||||
|
||||
div.phpdebugbar-widgets-templates table.phpdebugbar-widgets-params th {
|
||||
padding: 2px 10px!important;
|
||||
background-color: #efefef;
|
||||
}
|
||||
|
||||
div.phpdebugbar-widgets-sqlqueries table.phpdebugbar-widgets-params td.phpdebugbar-widgets-name {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
div.phpdebugbar-widgets-sqlqueries table.phpdebugbar-widgets-params td.phpdebugbar-widgets-name .phpdebugbar-fa {
|
||||
position: relative;
|
||||
top: 1px;
|
||||
margin-left: 3px;
|
||||
}
|
||||
|
||||
ul.phpdebugbar-widgets-list li.phpdebugbar-widgets-list-item:nth-child(even) {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
div.phpdebugbar-widgets-messages li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-value {
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
div.phpdebugbar-widgets-messages li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-value:before {
|
||||
font-family: PhpDebugbarFontAwesome;
|
||||
content: "\f005";
|
||||
color: #333;
|
||||
font-size: 15px !important;
|
||||
margin-right: 8px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
div.phpdebugbar-widgets-messages li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-value.phpdebugbar-widgets-info {
|
||||
color: #1299DA;
|
||||
}
|
||||
|
||||
div.phpdebugbar-widgets-messages li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-value.phpdebugbar-widgets-info:before {
|
||||
content: "\f05a";
|
||||
color: #5896e2;
|
||||
}
|
||||
|
||||
div.phpdebugbar-widgets-messages li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-value.phpdebugbar-widgets-success:before {
|
||||
content: "\f058";
|
||||
color: #45ab45;
|
||||
}
|
||||
|
||||
div.phpdebugbar-widgets-messages li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-value.phpdebugbar-widgets-error {
|
||||
color: #e74c3c;
|
||||
}
|
||||
|
||||
div.phpdebugbar-widgets-messages li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-value.phpdebugbar-widgets-error:before {
|
||||
color: var(--color-red-vivid);
|
||||
}
|
||||
|
||||
div.phpdebugbar-widgets-messages li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-value.phpdebugbar-widgets-warning:before,
|
||||
div.phpdebugbar-widgets-messages .phpdebugbar-widgets-value.phpdebugbar-widgets-warning {
|
||||
color: #FF9800;
|
||||
}
|
||||
|
||||
div.phpdebugbar-widgets-messages li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-value.phpdebugbar-widgets-deprecation:before {
|
||||
content: "\f1f6";
|
||||
color: #FF9800;
|
||||
}
|
||||
|
||||
div.phpdebugbar-widgets-messages li.phpdebugbar-widgets-list-item pre.sf-dump {
|
||||
display: inline-block !important;
|
||||
position: relative;
|
||||
top: -1px;
|
||||
}
|
||||
|
||||
div.phpdebugbar-panel div.phpdebugbar-widgets-status {
|
||||
padding: 9px 10px !important;
|
||||
width: calc(100% - 20px);
|
||||
margin-top: 0px !important;
|
||||
line-height: 11px!important;
|
||||
font-weight: bold!important;
|
||||
background: #f5f5f5!important;
|
||||
border-bottom: 1px solid #cecece!important;
|
||||
}
|
||||
|
||||
div.phpdebugbar-panel div.phpdebugbar-widgets-status > * {
|
||||
color: #383838!important;
|
||||
}
|
||||
|
||||
div.phpdebugbar-panel div.phpdebugbar-widgets-status > span:first-child:before {
|
||||
font-family: PhpDebugbarFontAwesome;
|
||||
content: "\f05a";
|
||||
color: #737373;
|
||||
text-shadow: 1px 1px #fff;
|
||||
font-size: 14px;
|
||||
position: relative;
|
||||
top: 1px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
div.phpdebugbar-widgets-sqlqueries table.phpdebugbar-widgets-params th,
|
||||
div.phpdebugbar-widgets-sqlqueries table.phpdebugbar-widgets-params td {
|
||||
padding: 4px 10px;
|
||||
}
|
||||
|
||||
div.phpdebugbar-widgets-sqlqueries table.phpdebugbar-widgets-params th {
|
||||
background-color: #efefef;
|
||||
}
|
||||
|
||||
div.phpdebugbar-widgets-sqlqueries table.phpdebugbar-widgets-params td.phpdebugbar-widgets-name {
|
||||
text-align: right;
|
||||
vertical-align: top;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
div.phpdebugbar-widgets-sqlqueries table.phpdebugbar-widgets-params td.phpdebugbar-widgets-value {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
div.phpdebugbar-widgets-templates .phpdebugbar-widgets-list-item table.phpdebugbar-widgets-params {
|
||||
width: auto!important;
|
||||
}
|
||||
|
||||
ul.phpdebugbar-widgets-list ul.phpdebugbar-widgets-table-list {
|
||||
text-align: left;
|
||||
line-height: 150%;
|
||||
}
|
||||
|
||||
.phpdebugbar-text-muted {
|
||||
color: #888;
|
||||
}
|
||||
|
||||
ul.phpdebugbar-widgets-cache a.phpdebugbar-widgets-forget {
|
||||
float: right;
|
||||
font-size: 12px;
|
||||
padding: 0 4px;
|
||||
background: var(--color-red-vivid);
|
||||
margin: 0 2px;
|
||||
border-radius: 4px;
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
line-height: 1.5rem;
|
||||
}
|
||||
|
||||
a.phpdebugbar-tab i {
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
div.phpdebugbar-mini-design a.phpdebugbar-tab {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
div.phpdebugbar-header-right {
|
||||
display:flex;
|
||||
flex-direction: row-reverse;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
div.phpdebugbar-header-right > a {
|
||||
height: 20px;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
div.phpdebugbar-header-right .phpdebugbar-indicator > i.phpdebugbar-fa {
|
||||
vertical-align: baseline;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
div.phpdebugbar-panel[data-collector="__datasets"] {
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
div.phpdebugbar-panel table {
|
||||
margin: 10px 0px!important;
|
||||
width: 100%!important;
|
||||
}
|
||||
|
||||
div.phpdebugbar-panel table .phpdebugbar-widgets-name {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
dl.phpdebugbar-widgets-kvlist > :nth-child(4n-1),
|
||||
dl.phpdebugbar-widgets-kvlist > :nth-child(4n) {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.phpdebugbar pre.sf-dump:after {
|
||||
clear: none!important;
|
||||
}
|
||||
|
||||
div.phpdebugbar-widgets-exceptions li.phpdebugbar-widgets-list-item span.phpdebugbar-widgets-message {
|
||||
color: #dd1044;
|
||||
}
|
||||
|
||||
div.phpdebugbar-widgets-exceptions li.phpdebugbar-widgets-list-item > div {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
div.phpdebugbar-widgets-sqlqueries span.phpdebugbar-widgets-database:before,
|
||||
div.phpdebugbar-widgets-sqlqueries span.phpdebugbar-widgets-duration:before,
|
||||
div.phpdebugbar-widgets-sqlqueries span.phpdebugbar-widgets-memory:before,
|
||||
div.phpdebugbar-widgets-sqlqueries span.phpdebugbar-widgets-row-count:before,
|
||||
div.phpdebugbar-widgets-sqlqueries span.phpdebugbar-widgets-copy-clipboard:before,
|
||||
div.phpdebugbar-widgets-sqlqueries span.phpdebugbar-widgets-stmt-id:before,
|
||||
div.phpdebugbar-widgets-templates span.phpdebugbar-widgets-param-count:before {
|
||||
margin-right: 6px!important;
|
||||
}
|
||||
|
||||
.phpdebugbar-widgets-list-item .phpdebugbar-widgets-bg-measure {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.phpdebugbar-widgets-bg-measure .phpdebugbar-widgets-value {
|
||||
position: absolute;
|
||||
height: 1px !important;
|
||||
opacity: 1 !important;
|
||||
bottom: 0;
|
||||
background: #63abca !important;
|
||||
}
|
||||
|
||||
div.phpdebugbar dl.phpdebugbar-widgets-kvlist > :nth-child(4n)::before {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
dt.phpdebugbar-widgets-key {
|
||||
padding-left: 10px !important;
|
||||
}
|
||||
|
||||
dt.phpdebugbar-widgets-key {
|
||||
position: relative;
|
||||
background: white;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
dd.phpdebugbar-widgets-value {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
dd.phpdebugbar-widgets-value::before {
|
||||
content: " ";
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 33.33%;
|
||||
margin-left: -33.33%;
|
||||
}
|
||||
|
||||
dd.phpdebugbar-widgets-value pre.sf-dump {
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
ul.phpdebugbar-widgets-table-list {
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
ul.phpdebugbar-widgets-table-list li {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
ul.phpdebugbar-widgets-table-list li:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
|
||||
pre.phpdebugbar-widgets-code-block ul.phpdebugbar-widgets-numbered-code li {
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
div.phpdebugbar .phpdebugbar-indicator span.phpdebugbar-tooltip,
|
||||
div.phpdebugbar div.phpdebugbar-mini-design a.phpdebugbar-tab:hover span.phpdebugbar-text,
|
||||
div.phpdebugbar pre.sf-dump,
|
||||
div.phpdebugbar .hljs,
|
||||
div.phpdebugbar code.phpdebugbar-widgets-sql span.hljs-operator {
|
||||
color: var(--color-gray-100) !important;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
<?php
|
||||
|
||||
namespace Barryvdh\Debugbar;
|
||||
|
||||
use Barryvdh\Debugbar\Middleware\DebugbarEnabled;
|
||||
use Barryvdh\Debugbar\Middleware\InjectDebugbar;
|
||||
use DebugBar\DataFormatter\DataFormatter;
|
||||
use DebugBar\DataFormatter\DataFormatterInterface;
|
||||
use Illuminate\Container\Container;
|
||||
use Illuminate\Contracts\View\Factory;
|
||||
use Illuminate\Contracts\Http\Kernel;
|
||||
use Illuminate\Routing\Router;
|
||||
use Illuminate\Session\SessionManager;
|
||||
use Illuminate\Support\Collection;
|
||||
use Barryvdh\Debugbar\Facade as DebugBar;
|
||||
|
||||
class ServiceProvider extends \Illuminate\Support\ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Register the service provider.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
$configPath = __DIR__ . '/../config/debugbar.php';
|
||||
$this->mergeConfigFrom($configPath, 'debugbar');
|
||||
|
||||
$this->app->alias(
|
||||
DataFormatter::class,
|
||||
DataFormatterInterface::class
|
||||
);
|
||||
|
||||
$this->app->singleton(LaravelDebugbar::class, function ($app) {
|
||||
$debugbar = new LaravelDebugbar($app);
|
||||
|
||||
if ($app->bound(SessionManager::class)) {
|
||||
$sessionManager = $app->make(SessionManager::class);
|
||||
$httpDriver = new SymfonyHttpDriver($sessionManager);
|
||||
$debugbar->setHttpDriver($httpDriver);
|
||||
}
|
||||
|
||||
return $debugbar;
|
||||
});
|
||||
|
||||
$this->app->alias(LaravelDebugbar::class, 'debugbar');
|
||||
|
||||
$this->app->singleton(
|
||||
'command.debugbar.clear',
|
||||
function ($app) {
|
||||
return new Console\ClearCommand($app['debugbar']);
|
||||
}
|
||||
);
|
||||
|
||||
$this->app->extend(
|
||||
'view',
|
||||
function (Factory $factory, Container $application): Factory {
|
||||
$laravelDebugbar = $application->make(LaravelDebugbar::class);
|
||||
|
||||
$shouldTrackViewTime = $laravelDebugbar->isEnabled() &&
|
||||
$laravelDebugbar->shouldCollect('time', true) &&
|
||||
$laravelDebugbar->shouldCollect('views', true) &&
|
||||
$application['config']->get('debugbar.options.views.timeline', false);
|
||||
|
||||
if (! $shouldTrackViewTime) {
|
||||
/* Do not swap the engine to save performance */
|
||||
return $factory;
|
||||
}
|
||||
|
||||
$extensions = array_reverse($factory->getExtensions());
|
||||
$engines = array_flip($extensions);
|
||||
$enginesResolver = $application->make('view.engine.resolver');
|
||||
|
||||
foreach ($engines as $engine => $extension) {
|
||||
$resolved = $enginesResolver->resolve($engine);
|
||||
|
||||
$factory->addExtension($extension, $engine, function () use ($resolved, $laravelDebugbar) {
|
||||
return new DebugbarViewEngine($resolved, $laravelDebugbar);
|
||||
});
|
||||
}
|
||||
|
||||
// returns original order of extensions
|
||||
foreach ($extensions as $extension => $engine) {
|
||||
$factory->addExtension($extension, $engine);
|
||||
}
|
||||
|
||||
return $factory;
|
||||
}
|
||||
);
|
||||
|
||||
Collection::macro('debug', function () {
|
||||
debug($this);
|
||||
return $this;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Bootstrap the application events.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
$configPath = __DIR__ . '/../config/debugbar.php';
|
||||
$this->publishes([$configPath => $this->getConfigPath()], 'config');
|
||||
|
||||
$this->loadRoutesFrom(realpath(__DIR__ . '/debugbar-routes.php'));
|
||||
|
||||
$this->registerMiddleware(InjectDebugbar::class);
|
||||
|
||||
$this->commands(['command.debugbar.clear']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the active router.
|
||||
*
|
||||
* @return Router
|
||||
*/
|
||||
protected function getRouter()
|
||||
{
|
||||
return $this->app['router'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the config path
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getConfigPath()
|
||||
{
|
||||
return config_path('debugbar.php');
|
||||
}
|
||||
|
||||
/**
|
||||
* Publish the config file
|
||||
*
|
||||
* @param string $configPath
|
||||
*/
|
||||
protected function publishConfig($configPath)
|
||||
{
|
||||
$this->publishes([$configPath => config_path('debugbar.php')], 'config');
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the Debugbar Middleware
|
||||
*
|
||||
* @param string $middleware
|
||||
*/
|
||||
protected function registerMiddleware($middleware)
|
||||
{
|
||||
$kernel = $this->app[Kernel::class];
|
||||
$kernel->pushMiddleware($middleware);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
<?php
|
||||
|
||||
namespace Barryvdh\Debugbar\Storage;
|
||||
|
||||
use DebugBar\Storage\StorageInterface;
|
||||
use Illuminate\Filesystem\Filesystem;
|
||||
use Symfony\Component\Finder\Finder;
|
||||
|
||||
/**
|
||||
* Stores collected data into files
|
||||
*/
|
||||
class FilesystemStorage implements StorageInterface
|
||||
{
|
||||
protected $dirname;
|
||||
protected $files;
|
||||
protected $gc_lifetime = 24; // Hours to keep collected data;
|
||||
protected $gc_probability = 5; // Probability of GC being run on a save request. (5/100)
|
||||
|
||||
/**
|
||||
* @param \Illuminate\Filesystem\Filesystem $files The filesystem
|
||||
* @param string $dirname Directories where to store files
|
||||
*/
|
||||
public function __construct($files, $dirname)
|
||||
{
|
||||
$this->files = $files;
|
||||
$this->dirname = rtrim($dirname, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function save($id, $data)
|
||||
{
|
||||
if (!$this->files->isDirectory($this->dirname)) {
|
||||
if ($this->files->makeDirectory($this->dirname, 0777, true)) {
|
||||
$this->files->put($this->dirname . '.gitignore', "*\n!.gitignore\n");
|
||||
} else {
|
||||
throw new \Exception("Cannot create directory '$this->dirname'..");
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$this->files->put($this->makeFilename($id), json_encode($data));
|
||||
} catch (\Exception $e) {
|
||||
//TODO; error handling
|
||||
}
|
||||
|
||||
// Randomly check if we should collect old files
|
||||
if (rand(1, 100) <= $this->gc_probability) {
|
||||
$this->garbageCollect();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the filename for the data, based on the id.
|
||||
*
|
||||
* @param $id
|
||||
* @return string
|
||||
*/
|
||||
public function makeFilename($id)
|
||||
{
|
||||
return $this->dirname . basename($id) . ".json";
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete files older than a certain age (gc_lifetime)
|
||||
*/
|
||||
protected function garbageCollect()
|
||||
{
|
||||
foreach (
|
||||
Finder::create()->files()->name('*.json')->date('< ' . $this->gc_lifetime . ' hour ago')->in(
|
||||
$this->dirname
|
||||
) as $file
|
||||
) {
|
||||
$this->files->delete($file->getRealPath());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function get($id)
|
||||
{
|
||||
return json_decode($this->files->get($this->makeFilename($id)), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function find(array $filters = [], $max = 20, $offset = 0)
|
||||
{
|
||||
// Sort by modified time, newest first
|
||||
$sort = function (\SplFileInfo $a, \SplFileInfo $b) {
|
||||
return strcmp($b->getMTime(), $a->getMTime());
|
||||
};
|
||||
|
||||
// Loop through .json files, filter the metadata and stop when max is found.
|
||||
$i = 0;
|
||||
$results = [];
|
||||
foreach (Finder::create()->files()->name('*.json')->in($this->dirname)->sort($sort) as $file) {
|
||||
if ($i++ < $offset && empty($filters)) {
|
||||
$results[] = null;
|
||||
continue;
|
||||
}
|
||||
$data = json_decode($file->getContents(), true);
|
||||
$meta = $data['__meta'];
|
||||
unset($data);
|
||||
if ($this->filter($meta, $filters)) {
|
||||
$results[] = $meta;
|
||||
}
|
||||
if (count($results) >= ($max + $offset)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return array_slice($results, $offset, $max);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the metadata for matches.
|
||||
*
|
||||
* @param $meta
|
||||
* @param $filters
|
||||
* @return bool
|
||||
*/
|
||||
protected function filter($meta, $filters)
|
||||
{
|
||||
foreach ($filters as $key => $value) {
|
||||
if (!isset($meta[$key]) || fnmatch($value, $meta[$key]) === false) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function clear()
|
||||
{
|
||||
foreach (Finder::create()->files()->name('*.json')->in($this->dirname) as $file) {
|
||||
$this->files->delete($file->getRealPath());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
namespace Barryvdh\Debugbar\Storage;
|
||||
|
||||
use DebugBar\Storage\StorageInterface;
|
||||
|
||||
class SocketStorage implements StorageInterface
|
||||
{
|
||||
protected $hostname;
|
||||
protected $port;
|
||||
protected $socket;
|
||||
|
||||
/**
|
||||
* @param string $hostname The hostname to use for the socket
|
||||
* @param int $port The port to use for the socket
|
||||
*/
|
||||
public function __construct($hostname, $port)
|
||||
{
|
||||
$this->hostname = $hostname;
|
||||
$this->port = $port;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
function save($id, $data)
|
||||
{
|
||||
$socketIsFresh = !$this->socket;
|
||||
|
||||
if (!$this->socket = $this->socket ?: $this->createSocket()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$encodedPayload = json_encode([
|
||||
'id' => $id,
|
||||
'base_path' => base_path(),
|
||||
'app' => config('app.name'),
|
||||
'data' => $data,
|
||||
]);
|
||||
|
||||
$encodedPayload = strlen($encodedPayload) . '#' . $encodedPayload;
|
||||
|
||||
set_error_handler([self::class, 'nullErrorHandler']);
|
||||
try {
|
||||
if (-1 !== stream_socket_sendto($this->socket, $encodedPayload)) {
|
||||
return true;
|
||||
}
|
||||
if (!$socketIsFresh) {
|
||||
stream_socket_shutdown($this->socket, \STREAM_SHUT_RDWR);
|
||||
fclose($this->socket);
|
||||
$this->socket = $this->createSocket();
|
||||
}
|
||||
if (-1 !== stream_socket_sendto($this->socket, $encodedPayload)) {
|
||||
return true;
|
||||
}
|
||||
} finally {
|
||||
restore_error_handler();
|
||||
}
|
||||
}
|
||||
|
||||
private static function nullErrorHandler($t, $m)
|
||||
{
|
||||
// no-op
|
||||
}
|
||||
|
||||
protected function createSocket()
|
||||
{
|
||||
set_error_handler([self::class, 'nullErrorHandler']);
|
||||
try {
|
||||
return stream_socket_client("tcp://{$this->hostname}:{$this->port}");
|
||||
} finally {
|
||||
restore_error_handler();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
function get($id)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
function find(array $filters = [], $max = 20, $offset = 0)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
function clear()
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
namespace Barryvdh\Debugbar\Support\Clockwork;
|
||||
|
||||
use DebugBar\DataCollector\DataCollector;
|
||||
use DebugBar\DataCollector\DataCollectorInterface;
|
||||
use DebugBar\DataCollector\Renderable;
|
||||
use Illuminate\Support\Arr;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
*
|
||||
* Based on \Symfony\Component\HttpKernel\DataCollector\RequestDataCollector by Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
*/
|
||||
class ClockworkCollector extends DataCollector implements DataCollectorInterface, Renderable
|
||||
{
|
||||
/** @var \Symfony\Component\HttpFoundation\Request $request */
|
||||
protected $request;
|
||||
/** @var \Symfony\Component\HttpFoundation\Request $response */
|
||||
protected $response;
|
||||
/** @var \Symfony\Component\HttpFoundation\Session\SessionInterface $session */
|
||||
protected $session;
|
||||
/** @var array */
|
||||
protected $hiddens;
|
||||
|
||||
/**
|
||||
* Create a new SymfonyRequestCollector
|
||||
*
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* @param \Symfony\Component\HttpFoundation\Request $response
|
||||
* @param \Symfony\Component\HttpFoundation\Session\SessionInterface $session
|
||||
* @param array $hiddens
|
||||
*/
|
||||
public function __construct($request, $response, $session = null, $hiddens = [])
|
||||
{
|
||||
$this->request = $request;
|
||||
$this->response = $response;
|
||||
$this->session = $session;
|
||||
$this->hiddens = array_merge($hiddens, [
|
||||
'request_request.password',
|
||||
'request_request.PHP_AUTH_PW',
|
||||
'request_request.php-auth-pw',
|
||||
'request_headers.php-auth-pw.0',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'clockwork';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getWidgets()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function collect()
|
||||
{
|
||||
$request = $this->request;
|
||||
$response = $this->response;
|
||||
|
||||
$data = [
|
||||
'getData' => $request->query->all(),
|
||||
'postData' => $request->request->all(),
|
||||
'headers' => $request->headers->all(),
|
||||
'cookies' => $request->cookies->all(),
|
||||
'uri' => $request->getRequestUri(),
|
||||
'method' => $request->getMethod(),
|
||||
'responseStatus' => $response->getStatusCode(),
|
||||
];
|
||||
|
||||
if ($this->session) {
|
||||
$data['sessionData'] = $this->session->all();
|
||||
}
|
||||
|
||||
if (isset($data['headers']['authorization'][0])) {
|
||||
$data['headers']['authorization'][0] = substr($data['headers']['authorization'][0], 0, 12) . '******';
|
||||
}
|
||||
|
||||
$keyAlias = [
|
||||
'request_query' => 'getData',
|
||||
'request_request' => 'postData',
|
||||
'request_headers' => 'headers',
|
||||
'request_cookies' => 'cookies',
|
||||
'session_attributes' => 'sessionData',
|
||||
];
|
||||
foreach ($this->hiddens as $key) {
|
||||
$key = explode('.', $key);
|
||||
$key[0] = $keyAlias[$key[0]] ?? $key[0];
|
||||
$key = implode('.', $key);
|
||||
if (Arr::has($data, $key)) {
|
||||
Arr::set($data, $key, '******');
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
<?php
|
||||
|
||||
namespace Barryvdh\Debugbar\Support\Clockwork;
|
||||
|
||||
class Converter
|
||||
{
|
||||
/**
|
||||
* Convert the phpdebugbar data to Clockwork format.
|
||||
*
|
||||
* @param array $data
|
||||
* @return array
|
||||
*/
|
||||
public function convert($data)
|
||||
{
|
||||
$meta = $data['__meta'];
|
||||
|
||||
// Default output
|
||||
$output = [
|
||||
'id' => $meta['id'],
|
||||
'method' => $meta['method'],
|
||||
'uri' => $meta['uri'],
|
||||
'time' => $meta['utime'],
|
||||
'headers' => [],
|
||||
'cookies' => [],
|
||||
'emailsData' => [],
|
||||
'getData' => [],
|
||||
'log' => [],
|
||||
'postData' => [],
|
||||
'sessionData' => [],
|
||||
'timelineData' => [],
|
||||
'viewsData' => [],
|
||||
'controller' => null,
|
||||
'responseTime' => null,
|
||||
'responseStatus' => null,
|
||||
'responseDuration' => 0,
|
||||
];
|
||||
|
||||
if (isset($data['clockwork'])) {
|
||||
$output = array_merge($output, $data['clockwork']);
|
||||
}
|
||||
|
||||
if (isset($data['memory'])) {
|
||||
$output['memoryUsage'] = $data['memory']['peak_usage'];
|
||||
}
|
||||
|
||||
if (isset($data['time'])) {
|
||||
$time = $data['time'];
|
||||
$output['time'] = $time['start'];
|
||||
$output['responseTime'] = $time['end'];
|
||||
$output['responseDuration'] = $time['duration'] * 1000;
|
||||
foreach ($time['measures'] as $measure) {
|
||||
$output['timelineData'][] = [
|
||||
'data' => [],
|
||||
'description' => $measure['label'],
|
||||
'duration' => $measure['duration'] * 1000,
|
||||
'end' => $measure['end'],
|
||||
'start' => $measure['start'],
|
||||
'relative_start' => $measure['start'] - $time['start'],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($data['route'])) {
|
||||
$route = $data['route'];
|
||||
|
||||
$controller = null;
|
||||
if (isset($route['controller'])) {
|
||||
$controller = $route['controller'];
|
||||
} elseif (isset($route['uses'])) {
|
||||
$controller = $route['uses'];
|
||||
}
|
||||
|
||||
$output['controller'] = $controller;
|
||||
|
||||
list($method, $uri) = explode(' ', $route['uri'], 2);
|
||||
|
||||
$output['routes'][] = [
|
||||
'action' => $controller,
|
||||
'after' => isset($route['after']) ? $route['after'] : null,
|
||||
'before' => isset($route['before']) ? $route['before'] : null,
|
||||
'method' => $method,
|
||||
'name' => isset($route['as']) ? $route['as'] : null,
|
||||
'uri' => $uri,
|
||||
];
|
||||
}
|
||||
|
||||
if (isset($data['messages'])) {
|
||||
foreach ($data['messages']['messages'] as $message) {
|
||||
$output['log'][] = [
|
||||
'message' => $message['message'],
|
||||
'time' => $message['time'],
|
||||
'level' => $message['label'],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($data['queries'])) {
|
||||
$queries = $data['queries'];
|
||||
foreach ($queries['statements'] as $statement) {
|
||||
if ($statement['type'] === 'explain') {
|
||||
continue;
|
||||
}
|
||||
$output['databaseQueries'][] = [
|
||||
'query' => $statement['sql'],
|
||||
'bindings' => $statement['params'],
|
||||
'duration' => $statement['duration'] * 1000,
|
||||
'time' => $statement['start'] ?? null,
|
||||
'connection' => $statement['connection']
|
||||
];
|
||||
}
|
||||
|
||||
$output['databaseDuration'] = $queries['accumulated_duration'] * 1000;
|
||||
}
|
||||
|
||||
if (isset($data['models'])) {
|
||||
$output['modelsActions'] = [];
|
||||
$output['modelsCreated'] = [];
|
||||
$output['modelsUpdated'] = [];
|
||||
$output['modelsDeleted'] = [];
|
||||
$output['modelsRetrieved'] = $data['models']['data'];
|
||||
}
|
||||
|
||||
if (isset($data['views'])) {
|
||||
foreach ($data['views']['templates'] as $view) {
|
||||
$output['viewsData'][] = [
|
||||
'description' => 'Rendering a view',
|
||||
'duration' => 0,
|
||||
'end' => 0,
|
||||
'start' => $view['start'] ?? 0,
|
||||
'data' => [
|
||||
'name' => $view['name'],
|
||||
'data' => $view['params'],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($data['event'])) {
|
||||
foreach ($data['event']['measures'] as $event) {
|
||||
$event['data'] = [];
|
||||
$event['listeners'] = [];
|
||||
foreach ($event['params'] ?? [] as $key => $param) {
|
||||
$event[is_numeric($key) ? 'data' : 'listeners'] = $param;
|
||||
}
|
||||
$output['events'][] = [
|
||||
'event' => ['event' => $event['label']],
|
||||
'data' => $event['data'],
|
||||
'time' => $event['start'],
|
||||
'duration' => $event['duration'] * 1000,
|
||||
'listeners' => $event['listeners'],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($data['symfonymailer_mails'])) {
|
||||
foreach ($data['symfonymailer_mails']['mails'] as $mail) {
|
||||
$output['emailsData'][] = [
|
||||
'data' => [
|
||||
'to' => implode(', ', $mail['to']),
|
||||
'subject' => $mail['subject'],
|
||||
'headers' => isset($mail['headers']) ? explode("\n", $mail['headers']) : null,
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
namespace Barryvdh\Debugbar;
|
||||
|
||||
use DebugBar\HttpDriverInterface;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpFoundation\Session\Session;
|
||||
|
||||
/**
|
||||
* HTTP driver for Symfony Request/Session
|
||||
*/
|
||||
class SymfonyHttpDriver implements HttpDriverInterface
|
||||
{
|
||||
/** @var \Illuminate\Contracts\Session\Session|\Illuminate\Session\SessionManager */
|
||||
protected $session;
|
||||
|
||||
/** @var \Symfony\Component\HttpFoundation\Response */
|
||||
protected $response;
|
||||
|
||||
public function __construct($session, $response = null)
|
||||
{
|
||||
$this->session = $session;
|
||||
$this->response = $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function setHeaders(array $headers)
|
||||
{
|
||||
if (!is_null($this->response)) {
|
||||
$this->response->headers->add($headers);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function isSessionStarted()
|
||||
{
|
||||
if (!$this->session->isStarted()) {
|
||||
$this->session->start();
|
||||
}
|
||||
|
||||
return $this->session->isStarted();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function setSessionValue($name, $value)
|
||||
{
|
||||
$this->session->put($name, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function hasSessionValue($name)
|
||||
{
|
||||
return $this->session->has($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getSessionValue($name)
|
||||
{
|
||||
return $this->session->get($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function deleteSessionValue($name)
|
||||
{
|
||||
$this->session->remove($name);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace Barryvdh\Debugbar\Twig\Extension;
|
||||
|
||||
use DebugBar\Bridge\Twig\DebugTwigExtension;
|
||||
use Illuminate\Foundation\Application;
|
||||
use Twig\Environment;
|
||||
|
||||
/**
|
||||
* Access debugbar debug in your Twig templates.
|
||||
*/
|
||||
class Debug extends DebugTwigExtension
|
||||
{
|
||||
protected $app;
|
||||
|
||||
/**
|
||||
* Create a new debug extension.
|
||||
*
|
||||
* @param \Illuminate\Foundation\Application $app
|
||||
*/
|
||||
public function __construct(Application $app)
|
||||
{
|
||||
$this->app = $app;
|
||||
parent::__construct(null);
|
||||
}
|
||||
|
||||
public function debug(Environment $env, $context)
|
||||
{
|
||||
if ($this->app->bound('debugbar') && $this->app['debugbar']->hasCollector('messages')) {
|
||||
$this->messagesCollector = $this->app['debugbar']['messages'];
|
||||
}
|
||||
|
||||
return parent::debug($env, $context);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace Barryvdh\Debugbar\Twig\Extension;
|
||||
|
||||
use DebugBar\Bridge\Twig\DumpTwigExtension;
|
||||
|
||||
/**
|
||||
* Dump variables using the DataFormatter
|
||||
*/
|
||||
class Dump extends DumpTwigExtension
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
namespace Barryvdh\Debugbar\Twig\Extension;
|
||||
|
||||
use DebugBar\Bridge\Twig\MeasureTwigExtension;
|
||||
use DebugBar\Bridge\Twig\MeasureTwigTokenParser;
|
||||
use Illuminate\Foundation\Application;
|
||||
|
||||
/**
|
||||
* Access debugbar time measures in your Twig templates.
|
||||
* Based on Symfony\Bridge\Twig\Extension\StopwatchExtension
|
||||
*/
|
||||
class Stopwatch extends MeasureTwigExtension
|
||||
{
|
||||
/**
|
||||
* @var \Barryvdh\Debugbar\LaravelDebugbar
|
||||
*/
|
||||
protected $debugbar;
|
||||
|
||||
/**
|
||||
* Create a new time measure extension.
|
||||
*
|
||||
* @param \Illuminate\Foundation\Application $app
|
||||
*/
|
||||
public function __construct(Application $app)
|
||||
{
|
||||
if ($app->bound('debugbar')) {
|
||||
$this->debugbar = $app['debugbar'];
|
||||
}
|
||||
|
||||
parent::__construct(null, 'stopwatch');
|
||||
}
|
||||
|
||||
|
||||
public function getDebugbar()
|
||||
{
|
||||
return $this->debugbar;
|
||||
}
|
||||
|
||||
public function getTokenParsers()
|
||||
{
|
||||
return [
|
||||
/*
|
||||
* {% measure foo %}
|
||||
* Some stuff which will be recorded on the timeline
|
||||
* {% endmeasure %}
|
||||
*/
|
||||
new MeasureTwigTokenParser(!is_null($this->debugbar), $this->tagName, $this->getName()),
|
||||
];
|
||||
}
|
||||
|
||||
public function startMeasure(...$arg)
|
||||
{
|
||||
if (!$this->debugbar || !$this->debugbar->hasCollector('time')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->debugbar->getCollector('time')->startMeasure(...$arg);
|
||||
}
|
||||
|
||||
public function stopMeasure(...$arg)
|
||||
{
|
||||
if (!$this->debugbar || !$this->debugbar->hasCollector('time')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->debugbar->getCollector('time')->stopMeasure(...$arg);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
$routeConfig = [
|
||||
'namespace' => 'Barryvdh\Debugbar\Controllers',
|
||||
'prefix' => app('config')->get('debugbar.route_prefix'),
|
||||
'domain' => app('config')->get('debugbar.route_domain'),
|
||||
'middleware' => array_merge(app('config')->get('debugbar.route_middleware', []), [\Barryvdh\Debugbar\Middleware\DebugbarEnabled::class]),
|
||||
];
|
||||
|
||||
app('router')->group($routeConfig, function ($router) {
|
||||
$router->get('open', [
|
||||
'uses' => 'OpenHandlerController@handle',
|
||||
'as' => 'debugbar.openhandler',
|
||||
]);
|
||||
|
||||
$router->get('clockwork/{id}', [
|
||||
'uses' => 'OpenHandlerController@clockwork',
|
||||
'as' => 'debugbar.clockwork',
|
||||
]);
|
||||
|
||||
if (class_exists(\Laravel\Telescope\Telescope::class)) {
|
||||
$router->get('telescope/{id}', [
|
||||
'uses' => 'TelescopeController@show',
|
||||
'as' => 'debugbar.telescope',
|
||||
]);
|
||||
}
|
||||
|
||||
$router->get('assets/stylesheets', [
|
||||
'uses' => 'AssetController@css',
|
||||
'as' => 'debugbar.assets.css',
|
||||
]);
|
||||
|
||||
$router->get('assets/javascript', [
|
||||
'uses' => 'AssetController@js',
|
||||
'as' => 'debugbar.assets.js',
|
||||
]);
|
||||
|
||||
$router->delete('cache/{key}/{tags?}', [
|
||||
'uses' => 'CacheController@delete',
|
||||
'as' => 'debugbar.cache.delete',
|
||||
]);
|
||||
});
|
||||
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
if (!function_exists('debugbar')) {
|
||||
/**
|
||||
* Get the Debugbar instance
|
||||
*
|
||||
* @return \Barryvdh\Debugbar\LaravelDebugbar
|
||||
*/
|
||||
function debugbar()
|
||||
{
|
||||
return app(\Barryvdh\Debugbar\LaravelDebugbar::class);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('debug')) {
|
||||
/**
|
||||
* Adds one or more messages to the MessagesCollector
|
||||
*
|
||||
* @param mixed ...$value
|
||||
* @return string
|
||||
*/
|
||||
function debug($value)
|
||||
{
|
||||
$debugbar = debugbar();
|
||||
foreach (func_get_args() as $value) {
|
||||
$debugbar->addMessage($value, 'debug');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('start_measure')) {
|
||||
/**
|
||||
* Starts a measure
|
||||
*
|
||||
* @param string $name Internal name, used to stop the measure
|
||||
* @param string $label Public name
|
||||
*/
|
||||
function start_measure($name, $label = null)
|
||||
{
|
||||
debugbar()->startMeasure($name, $label);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('stop_measure')) {
|
||||
/**
|
||||
* Stop a measure
|
||||
*
|
||||
* @param string $name Internal name, used to stop the measure
|
||||
*/
|
||||
function stop_measure($name)
|
||||
{
|
||||
debugbar()->stopMeasure($name);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('add_measure')) {
|
||||
/**
|
||||
* Adds a measure
|
||||
*
|
||||
* @param string $label
|
||||
* @param float $start
|
||||
* @param float $end
|
||||
*/
|
||||
function add_measure($label, $start, $end)
|
||||
{
|
||||
debugbar()->addMeasure($label, $start, $end);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('measure')) {
|
||||
/**
|
||||
* Utility function to measure the execution of a Closure
|
||||
*
|
||||
* @param string $label
|
||||
* @param \Closure $closure
|
||||
* @return mixed
|
||||
*/
|
||||
function measure($label, \Closure $closure)
|
||||
{
|
||||
return debugbar()->measure($label, $closure);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
// Share common rules between non-test and test files
|
||||
return [
|
||||
'@PSR12' => true,
|
||||
'blank_line_after_opening_tag' => true,
|
||||
'braces' => [
|
||||
'allow_single_line_anonymous_class_with_empty_body' => true,
|
||||
],
|
||||
'compact_nullable_typehint' => true,
|
||||
'declare_equal_normalize' => true,
|
||||
'lowercase_cast' => true,
|
||||
'lowercase_static_reference' => true,
|
||||
'new_with_braces' => true,
|
||||
'no_blank_lines_after_class_opening' => true,
|
||||
'no_leading_import_slash' => true,
|
||||
'no_whitespace_in_blank_line' => true,
|
||||
'ordered_class_elements' => [
|
||||
'order' => [
|
||||
'use_trait',
|
||||
],
|
||||
],
|
||||
'ordered_imports' => [
|
||||
'imports_order' => [
|
||||
'class',
|
||||
'function',
|
||||
'const',
|
||||
],
|
||||
'sort_algorithm' => 'alpha',
|
||||
],
|
||||
'return_type_declaration' => true,
|
||||
'short_scalar_cast' => true,
|
||||
'single_trait_insert_per_statement' => true,
|
||||
'ternary_operator_spaces' => true,
|
||||
'visibility_required' => [
|
||||
'elements' => [
|
||||
'const',
|
||||
'method',
|
||||
'property',
|
||||
],
|
||||
],
|
||||
|
||||
// Further quality-of-life improvements
|
||||
'array_syntax' => [
|
||||
'syntax' => 'short',
|
||||
],
|
||||
'concat_space' => [
|
||||
'spacing' => 'one',
|
||||
],
|
||||
'fully_qualified_strict_types' => true,
|
||||
'native_function_invocation' => [
|
||||
'include' => [],
|
||||
'strict' => true,
|
||||
],
|
||||
'no_unused_imports' => true,
|
||||
'single_quote' => true,
|
||||
'space_after_semicolon' => true,
|
||||
'trailing_comma_in_multiline' => true,
|
||||
'trim_array_spaces' => true,
|
||||
'unary_operator_spaces' => true,
|
||||
'whitespace_after_comma_in_array' => true,
|
||||
];
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
require __DIR__ . '/vendor/autoload.php';
|
||||
|
||||
$finder = PhpCsFixer\Finder::create()
|
||||
->in(__DIR__)
|
||||
->exclude('tests');
|
||||
|
||||
$config = require __DIR__ . '/.php-cs-fixer.common.php';
|
||||
|
||||
return (new PhpCsFixer\Config())
|
||||
->setFinder($finder)
|
||||
->setRules($config)
|
||||
->setRiskyAllowed(true)
|
||||
->setCacheFile(__DIR__ . '/.php-cs-fixer.cache');
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
require __DIR__ . '/vendor/autoload.php';
|
||||
|
||||
$finder = PhpCsFixer\Finder::create()
|
||||
->in(__DIR__ . '/tests')
|
||||
->exclude('__snapshots__');
|
||||
|
||||
$config = require __DIR__ . '/.php-cs-fixer.common.php';
|
||||
|
||||
// Additional rules for tests
|
||||
$config = array_merge(
|
||||
$config,
|
||||
[
|
||||
'declare_strict_types' => true,
|
||||
]
|
||||
);
|
||||
|
||||
return (new PhpCsFixer\Config())
|
||||
->setFinder($finder)
|
||||
->setRules($config)
|
||||
->setRiskyAllowed(true)
|
||||
->setCacheFile(__DIR__ . '/.php-cs-fixer.tests.cache');
|
||||
+279
@@ -0,0 +1,279 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
[Next release](https://github.com/barryvdh/laravel-ide-helper/compare/v3.1.0...master)
|
||||
--------------
|
||||
|
||||
### Fixed
|
||||
|
||||
### Changed
|
||||
|
||||
### Added
|
||||
|
||||
2024-07-12, 3.1.0
|
||||
------------------
|
||||
|
||||
### Fixed
|
||||
- Fix return value of query scopes from parent class [#1366 / sforward](https://github.com/barryvdh/laravel-ide-helper/pull/1366)
|
||||
- Add static to isBuiltin() check in ide-helper:models [#1541 / bram-pkg](https://github.com/barryvdh/laravel-ide-helper/pull/1541)
|
||||
- Fix for getSomethingAttribute functions which return a collection with type templating in the phpDoc. [#1567 / stefanScrumble](https://github.com/barryvdh/laravel-ide-helper/pull/1567)
|
||||
|
||||
### Added
|
||||
- Add type to pivot when using a custom pivot class [#1518 / d3v2a](https://github.com/barryvdh/laravel-ide-helper/pull/1518)
|
||||
- Add support in morphTo relationship for null values [#1547 / matysekmichal](https://github.com/barryvdh/laravel-ide-helper/pull/1547)
|
||||
- Add support for AsEnumCollection casts [#1557 / Braunson](https://github.com/barryvdh/laravel-ide-helper/pull/1557)
|
||||
- Support for Attribute class in attributes [#1567 / stefanScrumble](https://github.com/barryvdh/laravel-ide-helper/pull/1567)
|
||||
|
||||
2024-03-01, 3.0.0
|
||||
------------------
|
||||
|
||||
### Added
|
||||
- Support for Laravel 11 [#1520 / KentarouTakeda](https://github.com/barryvdh/laravel-ide-helper/pull/1520)
|
||||
|
||||
### Changed
|
||||
- Make `--reset` always keep the text and remove `--smart-reset`. Always skip the classname [#1523 / barryvdh](https://github.com/barryvdh/laravel-ide-helper/pull/1523) & [#1525 / mfn](https://github.com/barryvdh/laravel-ide-helper/pull/1525)
|
||||
- Use short types (`int` and `bool` instead of `integer` and `boolean`) [#1524 / barryvdh](https://github.com/barryvdh/laravel-ide-helper/pull/1524)
|
||||
|
||||
### Removed
|
||||
- Support for Laravel 9 and use of doctrine/dbal [#1512 / barryvdh](https://github.com/barryvdh/laravel-ide-helper/pull/1512)
|
||||
With this functionality gone, a few changes have been made:
|
||||
- support for custom datatypes has been dropped (config `custom_db_types`) unknown data types default to `string` now and to fix the type, add a proper cast in Eloquent
|
||||
- You _might_ have top-level dependency on doctrine/dbal. This may have been in the past due to ide-helper, we suggest to check if you still need it and remove it otherwise
|
||||
- Minimum PHP version, due to Laravel 10, is now PHP 8.1
|
||||
|
||||
2024-02-15, 2.15.1
|
||||
------------------
|
||||
|
||||
### Fixed
|
||||
- Fix final class keyword in wrong position [#1517 / barryvdh](https://github.com/barryvdh/laravel-ide-helper/pull/1517)
|
||||
|
||||
### Changed
|
||||
|
||||
### Added
|
||||
|
||||
2024-02-14, 2.15.0
|
||||
------------------
|
||||
### Fixed
|
||||
- Fix case issue in `ModelsCommand::unsetMethod()` [#1453 / leo108](https://github.com/barryvdh/laravel-ide-helper/pull/1453)
|
||||
- Fix non-facade classes will result in no autocomplete [#841 / netpok](https://github.com/barryvdh/laravel-ide-helper/pull/841)
|
||||
- Skip swoole, otherwise fatal error [#1477 / TimoFrenzel](https://github.com/barryvdh/laravel-ide-helper/pull/1477)
|
||||
- Fix vulnerability CVE-2021-43608 [#1392 / allanlaal](https://github.com/barryvdh/laravel-ide-helper/pull/1392)
|
||||
- Reset foreignKeyConstraintsColumns on model loop start [#1461 / snmatsui](https://github.com/barryvdh/laravel-ide-helper/pull/1461)
|
||||
- Accept scope & scopes as relation [#1452 / Muetze42](https://github.com/barryvdh/laravel-ide-helper/pull/1452)
|
||||
- Fix #1300 relation_return_type must take precedence if it is defined [#1394 / menthol](https://github.com/barryvdh/laravel-ide-helper/pull/1394)
|
||||
|
||||
### Changed
|
||||
- Disable inspections of helper files [#1486 / eidng8](https://github.com/barryvdh/laravel-ide-helper/pull/1486)
|
||||
- Removed support for Laravel 8 and therefore for PHP < 8.0 [#1504 / mfn](https://github.com/barryvdh/laravel-ide-helper/pull/1504)
|
||||
|
||||
### Added
|
||||
- Add support for enum default arguments using enum cases. [#1464 / d8vjork](https://github.com/barryvdh/laravel-ide-helper/pull/1464)
|
||||
- Add support for real-time facades in the helper file. [#1455 / filipac](https://github.com/barryvdh/laravel-ide-helper/pull/1455)
|
||||
- Add support for relations with composite keys. [#1479 / calebdw](https://github.com/barryvdh/laravel-ide-helper/pull/1479)
|
||||
- Add support for attribute accessors with no backing field or type hinting [#1411 / pindab0ter](https://github.com/barryvdh/laravel-ide-helper/pull/1411).
|
||||
- Add support for AsCollection and AsArrayObject casts [#1393 / pataar](https://github.com/barryvdh/laravel-ide-helper/pull/1393)
|
||||
- Reintroduce support for multi-db setups [#1426 / benpoulson](https://github.com/barryvdh/laravel-ide-helper/pull/1426)
|
||||
- Support the BINARY(...) database field type [#1434 / Sfonxs](https://github.com/barryvdh/laravel-ide-helper/pull/1434)
|
||||
- Add AllowDynamicProperties Attribute to cooperate with php8.2 deprecation [#1428 / GeoSot](https://github.com/barryvdh/laravel-ide-helper/pull/1428)
|
||||
|
||||
2024-02-05, 2.14.0
|
||||
------------------
|
||||
|
||||
### Changed
|
||||
- Official support for Lumen has been dropped [#1425 / mfn](https://github.com/barryvdh/laravel-ide-helper/pull/1425)
|
||||
- Refactor resolving of null information for custom casted attribute types [#1330 / wimski](https://github.com/barryvdh/laravel-ide-helper/pull/1330)
|
||||
|
||||
### Fixed
|
||||
- Catch exceptions when loading aliases [#1465 / dongm2ez](https://github.com/barryvdh/laravel-ide-helper/pull/1465)
|
||||
|
||||
### Added
|
||||
- Add support for nikic/php-parser 5 (next to 4) [#1502 / mfn](https://github.com/barryvdh/laravel-ide-helper/pull/1502)
|
||||
- Add support for `immutable_date:*` and `immutable_datetime:*` casts. [#1380 / thekonz](https://github.com/barryvdh/laravel-ide-helper/pull/1380)
|
||||
- Add support for attribute accessors marked as protected. [#1339 / pindab0ter](https://github.com/barryvdh/laravel-ide-helper/pull/1339)
|
||||
|
||||
2023-02-04, 2.13.0
|
||||
------------------
|
||||
|
||||
### Fixed
|
||||
- Fix return type of methods provided by `SoftDeletes` [#1345 / KentarouTakeda](https://github.com/barryvdh/laravel-ide-helper/pull/1345)
|
||||
- Handle PHP 8.1 deprecation warnings when passing `null` to `new \ReflectionClass` [#1351 / mfn](https://github.com/barryvdh/laravel-ide-helper/pull/1351)
|
||||
- Fix issue where \Eloquent is not included when using write_mixin [#1352 / Jefemy](https://github.com/barryvdh/laravel-ide-helper/pull/1352)
|
||||
- Fix model factory method arguments for Laravel >= 9 [#1361 / wimski](https://github.com/barryvdh/laravel-ide-helper/pull/1361)
|
||||
- Improve return type of mock helper methods in tests [#1405 / bentleyo](https://github.com/barryvdh/laravel-ide-helper/pull/1405)
|
||||
- Fix Castable class if failed to detect it from return types [#1388 / kwarcu](https://github.com/barryvdh/laravel-ide-helper/pull/1388)
|
||||
|
||||
### Added
|
||||
- Added Laravel 10 support [#1407 / lptn](https://github.com/barryvdh/laravel-ide-helper/pull/1407)
|
||||
- Add support for custom casts that implement `CastsInboundAttributes` [#1329 / sforward](https://github.com/barryvdh/laravel-ide-helper/pull/1329)
|
||||
- Add option `use_generics_annotations` for collection type hints [#1298 / tanerkay](https://github.com/barryvdh/laravel-ide-helper/pull/1298)
|
||||
|
||||
2022-03-06, 2.12.3
|
||||
------------------
|
||||
|
||||
### Fixed
|
||||
- Fix date and datetime handling for attributes that set a serialization format option for the Carbon instance [#1324 / FLeudts](https://github.com/barryvdh/laravel-ide-helper/pull/1324)
|
||||
- Fix composer conflict with composer/pcre version 2/3. [#1327 / barryvdh](https://github.com/barryvdh/laravel-ide-helper/pull/1327)
|
||||
|
||||
2022-02-08, 2.12.2
|
||||
------------------
|
||||
### Fixed
|
||||
- Remove composer dependecy, use copy of ClassMapGenerator [#1313 / barryvdh](https://github.com/barryvdh/laravel-ide-helper/pull/1313)
|
||||
|
||||
2022-01-24, 2.12.1
|
||||
------------------
|
||||
### Fixed
|
||||
- Properly handle `Castable`s without return type. [#1306 / binotaliu](https://github.com/barryvdh/laravel-ide-helper/pull/1306)
|
||||
|
||||
2022-01-23, 2.12.0
|
||||
------------------
|
||||
### Added
|
||||
- Add support for custom casts that using `Castable` [#1287 / binotaliu](https://github.com/barryvdh/laravel-ide-helper/pull/1287)
|
||||
- Added Laravel 9 support [#1297 / rcerljenko](https://github.com/barryvdh/laravel-ide-helper/pull/1297)
|
||||
- Added option `additional_relation_return_types` for custom relations that don't fit the typical naming scheme
|
||||
|
||||
2022-01-03, 2.11.0
|
||||
------------------
|
||||
### Added
|
||||
- Add support for Laravel 8.77 Attributes [\#1289 / SimonJnsson](https://github.com/barryvdh/laravel-ide-helper/pull/1289)
|
||||
- Add support for cast types `decimal:*`, `encrypted:*`, `immutable_date`, `immutable_datetime`, `custom_datetime`, and `immutable_custom_datetime` [#1262 / miken32](https://github.com/barryvdh/laravel-ide-helper/pull/1262)
|
||||
- Add support of variadic parameters in `ide-helper:models` [\#1234 / shaffe-fr](https://github.com/barryvdh/laravel-ide-helper/pull/1234)
|
||||
- Add support of custom casts without properties [\#1267 / sparclex](https://github.com/barryvdh/laravel-ide-helper/pull/1267)
|
||||
|
||||
### Fixed
|
||||
- Fix recursively searching for `HasFactory` and `Macroable` traits [\#1216 / daniel-de-wit](https://github.com/barryvdh/laravel-ide-helper/pull/1216)
|
||||
- Use platformName to determine db type when casting boolean types [\#1212 / stockalexander](https://github.com/barryvdh/laravel-ide-helper/pull/1212)
|
||||
|
||||
### Changed
|
||||
- Move default models helper filename to config [\#1241 / wimski](https://github.com/barryvdh/laravel-ide-helper/pull/1241)
|
||||
|
||||
2021-06-18, 2.10.1
|
||||
------------------
|
||||
### Added
|
||||
- Added Type registration according to [Custom Mapping Types documentation](https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/types.html#custom-mapping-types) [\#1228 / wimski](https://github.com/barryvdh/laravel-ide-helper/pull/1241)
|
||||
|
||||
### Fixed
|
||||
- Fixing issue where configured custom_db_types could cause a DBAL exception to be thrown while running `ide-helper:models` [\#1228 / wimski](https://github.com/barryvdh/laravel-ide-helper/pull/1241)
|
||||
|
||||
2021-04-09, 2.10.0
|
||||
------------------
|
||||
### Added
|
||||
- Allowing Methods to be set or unset in ModelHooks [\#1198 / jenga201](https://github.com/barryvdh/laravel-ide-helper/pull/1198)\
|
||||
Note: the visibility of `\Barryvdh\LaravelIdeHelper\Console\ModelsCommand::setMethod` has been changed to **public**!
|
||||
|
||||
### Fixed
|
||||
- Fixing issue where incorrect autoloader unregistered [\#1210 / tezhm](https://github.com/barryvdh/laravel-ide-helper/pull/1210)
|
||||
|
||||
2021-04-02, 2.9.3
|
||||
-----------------
|
||||
### Fixed
|
||||
- Support both customized namespace factories as well as default resolvable ones [\#1201 / wimski](https://github.com/barryvdh/laravel-ide-helper/pull/1201)
|
||||
|
||||
2021-04-01, 2.9.2
|
||||
-----------------
|
||||
### Added
|
||||
- Model hooks for adding custom information from external sources to model classes through the ModelsCommand [\#945 / wimski](https://github.com/barryvdh/laravel-ide-helper/pull/945)
|
||||
|
||||
### Fixed
|
||||
- Fix ide-helper:models exception if model doesn't have factory [\#1196 / ahmed-aliraqi](https://github.com/barryvdh/laravel-ide-helper/pull/1196)
|
||||
- Running tests triggering post_migrate hooks [\#1193 / netpok](https://github.com/barryvdh/laravel-ide-helper/pull/1193)
|
||||
- Array_merge error when config is cached prior to package install [\#1184 / netpok](https://github.com/barryvdh/laravel-ide-helper/pull/1184)
|
||||
|
||||
2021-03-15, 2.9.1
|
||||
-----------------
|
||||
### Added
|
||||
- Generate PHPDoc for Laravel 8.x factories [\#1074 / ahmed-aliraqi](https://github.com/barryvdh/laravel-ide-helper/pull/1074)
|
||||
- Add a comment to a property like table columns [\#1168 / biiiiiigmonster](https://github.com/barryvdh/laravel-ide-helper/pull/1168)
|
||||
- Added `post_migrate` hook to run commands after a migration [\#1163 / netpok](https://github.com/barryvdh/laravel-ide-helper/pull/1163)
|
||||
- Allow for PhpDoc for macros with union types [\#1148 / riesjart](https://github.com/barryvdh/laravel-ide-helper/pull/1148)
|
||||
|
||||
### Fixed
|
||||
- Error when generating helper for invokable classes [\#1124 / standaniels](https://github.com/barryvdh/laravel-ide-helper/pull/1124)
|
||||
- Fix broken ReflectionUnionTypes [\#1132 / def-studio](https://github.com/barryvdh/laravel-ide-helper/pull/1132)
|
||||
- Relative class names are not converted to fully-qualified class names [\#1005 / SavKS](https://github.com/barryvdh/laravel-ide-helper/pull/1005)
|
||||
|
||||
2020-12-30, 2.9.0
|
||||
-----------------
|
||||
### Changed
|
||||
- Dropped support for Laravel 6 and Laravel 7, as well as support for PHP 7.2 and added support for doctrine/dbal:^3 [\#1114 / mfn](https://github.com/barryvdh/laravel-ide-helper/pull/1114)
|
||||
|
||||
### Fixed
|
||||
- `Macro::initPhpDoc()` will save original docblock if present [\#1116 / LastDragon-ru](https://github.com/barryvdh/laravel-ide-helper/pull/1116)
|
||||
- `Alias` will grab macros from `\Illuminate\Database\Eloquent\Builder` too [\#1118 / LastDragon-ru](https://github.com/barryvdh/laravel-ide-helper/pull/1118)
|
||||
|
||||
2020-12-08, 2.8.2
|
||||
-----------------
|
||||
### Added
|
||||
- Fix phpdoc generate for custom cast with parameter [\#986 / artelkr](https://github.com/barryvdh/laravel-ide-helper/pull/986)
|
||||
- Created a possibility to add custom relation type [\#987 / efinder2](https://github.com/barryvdh/laravel-ide-helper/pull/987)
|
||||
- Added `@see` with macro/mixin definition location to PhpDoc [\#1054 / riesjart](https://github.com/barryvdh/laravel-ide-helper/pull/1054)
|
||||
- Initial compatibility for PHP8 [\#1106 / mfn](https://github.com/barryvdh/laravel-ide-helper/pull/1106)
|
||||
|
||||
### Changed
|
||||
- Implement DeferrableProvider [\#914 / kon-shou](https://github.com/barryvdh/laravel-ide-helper/pull/914)
|
||||
|
||||
### Fixed
|
||||
- Compatibility with Lumen [\#1043 / mfn](https://github.com/barryvdh/laravel-ide-helper/pull/1043)
|
||||
- Allow model_locations to have glob patterns [\#1059 / saackearl](https://github.com/barryvdh/laravel-ide-helper/pull/1059)
|
||||
- Error when generating helper for macroable classes which are not facades and contain a "fake" method [\#1066 / domkrm] (https://github.com/barryvdh/laravel-ide-helper/pull/1066)
|
||||
- Casts with a return type of `static` or `$this` now resolve to an instance of the cast [\#1103 / riesjart](https://github.com/barryvdh/laravel-ide-helper/pull/1103)
|
||||
|
||||
### Removed
|
||||
- Removed format and broken generateJsonHelper [\#1053 / mfn](https://github.com/barryvdh/laravel-ide-helper/pull/1053)
|
||||
|
||||
2020-09-07, 2.8.1
|
||||
-----------------
|
||||
### Added
|
||||
- Support Laravel 8 [\#1022 / barryvdh](https://github.com/barryvdh/laravel-ide-helper/pull/1022)
|
||||
- Add option to force usage of FQN [\#1031 / edvordo](https://github.com/barryvdh/laravel-ide-helper/pull/1031)
|
||||
- Add support for macros of all macroable classes [\#1006 / domkrm](https://github.com/barryvdh/laravel-ide-helper/pull/1006)
|
||||
|
||||
2020-08-11, 2.8.0
|
||||
-----------------
|
||||
### Added
|
||||
- Add static return type to builder methods [\#924 / dmason30](https://github.com/barryvdh/laravel-ide-helper/pull/924)
|
||||
- Add `optonal` to meta generator for PhpStorm [\#932 / halaei](https://github.com/barryvdh/laravel-ide-helper/pull/932)
|
||||
- Decimal columns as string in Models [\#948 / fgibaux](https://github.com/barryvdh/laravel-ide-helper/pull/948)
|
||||
- Simplify full namespaces for already included resources [\#954 / LANGERGabriel](https://github.com/barryvdh/laravel-ide-helper/pull/954)
|
||||
- Make writing relation count properties optional [\#969 / AegirLeet](https://github.com/barryvdh/laravel-ide-helper/pull/969)
|
||||
- Add more methods able to resolve container instances [\#996 / mfn](https://github.com/barryvdh/laravel-ide-helper/pull/996)
|
||||
|
||||
### Fixed
|
||||
- Test `auth` is bound before detect Auth driver [\#946 / zhwei](https://github.com/barryvdh/laravel-ide-helper/pull/946)
|
||||
- Fix inline doc-block for final models [\#944 / Gummibeer](https://github.com/barryvdh/laravel-ide-helper/pull/955)
|
||||
|
||||
2020-04-22, 2.7.0
|
||||
-----------------
|
||||
### Added
|
||||
- Add `ignored_models` as config option [\#890 / pataar](https://github.com/barryvdh/laravel-ide-helper/pull/890)
|
||||
- Infer return type from reflection if no phpdoc given [\#906 / mfn](https://github.com/barryvdh/laravel-ide-helper/pull/906)
|
||||
- Add custom collection support for get and all methods [\#903 / dmason30](https://github.com/barryvdh/laravel-ide-helper/pull/903)
|
||||
- if a model implements interfaces, include them in the stub [\#920 / mr-feek](https://github.com/barryvdh/laravel-ide-helper/pull/920)
|
||||
- Generate noinspections PHPStorm tags [\#905 / mzglinski](https://github.com/barryvdh/laravel-ide-helper/pull/905)
|
||||
- Added support for Laravel 7 custom casts [\#913 / belamov](https://github.com/barryvdh/laravel-ide-helper/pull/913)
|
||||
- Ability to use patterns for model_locations [\#921 / 4n70w4](https://github.com/barryvdh/laravel-ide-helper/pull/921)
|
||||
|
||||
### Fixed
|
||||
- MorphToMany relations with query not working [\#894 / UksusoFF](https://github.com/barryvdh/laravel-ide-helper/pull/894)
|
||||
- Fix camelCase duplicated properties generator [\#881 / bop10](https://github.com/barryvdh/laravel-ide-helper/pull/881)
|
||||
- Prevent generation of invalid code for certain parameter default values [\#901 / loilo](https://github.com/barryvdh/laravel-ide-helper/pull/901)
|
||||
- Make hasOne and morphOne nullable [\#864 / leo108](https://github.com/barryvdh/laravel-ide-helper/pull/864)
|
||||
- Remove unnecessary and wrong definition of SoftDelete methods [\#918 / mfn](https://github.com/barryvdh/laravel-ide-helper/pull/918)
|
||||
- Unregister meta command custom autoloader when it is no longer needed [\#919 / mr-feek](https://github.com/barryvdh/laravel-ide-helper/pull/919)
|
||||
|
||||
2020-02-25, 2.6.7
|
||||
-----------------
|
||||
### Added
|
||||
- Support for Laravel 7 [commit by barryvdh](https://github.com/barryvdh/laravel-ide-helper/commit/edd69c5e0508972c81f1f7173236de2459c45814)
|
||||
|
||||
2019-12-02, 2.6.6
|
||||
-----------------
|
||||
### Added
|
||||
- Add splat operator (...) support [\#860 / ngmy](https://github.com/barryvdh/laravel-ide-helper/pull/860)
|
||||
- Add support for custom date class via Date::use() [\#859 / mfn](https://github.com/barryvdh/laravel-ide-helper/pull/859)
|
||||
|
||||
### Fixed
|
||||
- Prevent undefined property errors [\#877 / matt-allan](https://github.com/barryvdh/laravel-ide-helper/pull/877)
|
||||
|
||||
----
|
||||
Missing an older changelog? Feel free to submit a PR!
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
# The MIT License (MIT)
|
||||
|
||||
Copyright (c) Barry vd. Heuvel <barryvdh@gmail.com>
|
||||
|
||||
> 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.
|
||||
+394
@@ -0,0 +1,394 @@
|
||||
# IDE Helper Generator for Laravel
|
||||
|
||||
[](https://github.com/barryvdh/laravel-ide-helper/actions)
|
||||
[](http://choosealicense.com/licenses/mit/)
|
||||
[](https://packagist.org/packages/barryvdh/laravel-ide-helper)
|
||||
[](https://packagist.org/packages/barryvdh/laravel-ide-helper)
|
||||
[](https://fruitcake.nl/)
|
||||
|
||||
**Complete PHPDocs, directly from the source**
|
||||
|
||||
This package generates helper files that enable your IDE to provide accurate autocompletion.
|
||||
Generation is done based on the files in your project, so they are always up-to-date.
|
||||
|
||||
The 3.x branch supports Laravel 10 and 11. For older version, use the 2.x releases.
|
||||
|
||||
- [Installation](#installation)
|
||||
- [Usage](#usage)
|
||||
- [Automatic PHPDoc generation for Laravel Facades](#automatic-phpdoc-generation-for-laravel-facades)
|
||||
- [Automatic PHPDocs for models](#automatic-phpdocs-for-models)
|
||||
- [Model Directories](#model-directories)
|
||||
- [Ignore Models](#ignore-models)
|
||||
- [Model Hooks](#model-hooks)
|
||||
- [Automatic PHPDocs generation for Laravel Fluent methods](#automatic-phpdocs-generation-for-laravel-fluent-methods)
|
||||
- [Auto-completion for factory builders](#auto-completion-for-factory-builders)
|
||||
- [PhpStorm Meta for Container instances](#phpstorm-meta-for-container-instances)
|
||||
- [License](#license)
|
||||
|
||||
## Installation
|
||||
|
||||
Require this package with composer using the following command:
|
||||
|
||||
```bash
|
||||
composer require --dev barryvdh/laravel-ide-helper
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> If you encounter version conflicts with doctrine/dbal, please try:
|
||||
> `composer require --dev barryvdh/laravel-ide-helper --with-all-dependencies`
|
||||
|
||||
This package makes use of [Laravels package auto-discovery mechanism](https://medium.com/@taylorotwell/package-auto-discovery-in-laravel-5-5-ea9e3ab20518), which means if you don't install dev dependencies in production, it also won't be loaded.
|
||||
|
||||
If for some reason you want manually control this:
|
||||
- add the package to the `extra.laravel.dont-discover` key in `composer.json`, e.g.
|
||||
```json
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"dont-discover": [
|
||||
"barryvdh/laravel-ide-helper"
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
- Add the following class to the `providers` array in `config/app.php`:
|
||||
```php
|
||||
Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class,
|
||||
```
|
||||
If you want to manually load it only in non-production environments, instead you can add this to your `AppServiceProvider` with the `register()` method:
|
||||
```php
|
||||
public function register()
|
||||
{
|
||||
if ($this->app->isLocal()) {
|
||||
$this->app->register(\Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class);
|
||||
}
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
> Note: Avoid caching the configuration in your development environment, it may cause issues after installing this package; respectively clear the cache beforehand via `php artisan cache:clear` if you encounter problems when running the commands
|
||||
|
||||
## Usage
|
||||
|
||||
_Check out [this Laracasts video](https://laracasts.com/series/how-to-be-awesome-in-phpstorm/episodes/15) for a quick introduction/explanation!_
|
||||
|
||||
- `php artisan ide-helper:generate` - [PHPDoc generation for Laravel Facades ](#automatic-phpdoc-generation-for-laravel-facades)
|
||||
- `php artisan ide-helper:models` - [PHPDocs for models](#automatic-phpdocs-for-models)
|
||||
- `php artisan ide-helper:meta` - [PhpStorm Meta file](#phpstorm-meta-for-container-instances)
|
||||
|
||||
|
||||
Note: You do need CodeComplice for Sublime Text: https://github.com/spectacles/CodeComplice
|
||||
|
||||
### Automatic PHPDoc generation for Laravel Facades
|
||||
|
||||
You can now re-generate the docs yourself (for future updates)
|
||||
|
||||
```bash
|
||||
php artisan ide-helper:generate
|
||||
```
|
||||
|
||||
Note: `bootstrap/compiled.php` has to be cleared first, so run `php artisan clear-compiled` before generating.
|
||||
|
||||
This will generate the file `_ide_helper.php` which is expected to be additionally parsed by your IDE for autocomplete. You can use the config `filename` to change its name.
|
||||
|
||||
You can configure your `composer.json` to do this each time you update your dependencies:
|
||||
|
||||
```js
|
||||
"scripts": {
|
||||
"post-update-cmd": [
|
||||
"Illuminate\\Foundation\\ComposerScripts::postUpdate",
|
||||
"@php artisan ide-helper:generate",
|
||||
"@php artisan ide-helper:meta"
|
||||
]
|
||||
},
|
||||
```
|
||||
|
||||
You can also publish the config file to change implementations (ie. interface to specific class) or set defaults for `--helpers`.
|
||||
|
||||
```bash
|
||||
php artisan vendor:publish --provider="Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider" --tag=config
|
||||
```
|
||||
|
||||
The generator tries to identify the real class, but if it cannot be found, you can define it in the config file.
|
||||
|
||||
Some classes need a working database connection. If you do not have a default working connection, some facades will not be included.
|
||||
You can use an in-memory SQLite driver by adding the `-M` option.
|
||||
|
||||
If you use [real-time facades](https://laravel.com/docs/master/facades#real-time-facades) in your app, those will also be included in the generated file using a `@mixin` annotation and extending the original class underneath the facade.
|
||||
|
||||
**Note**: this feature uses the generated real-time facades files in the `storage/framework/cache` folder. Those files are generated on-demand as you use the real-time facade, so if the framework has not generated that first, it will not be included in the helper file. Run the route/command/code first and then regenerate the helper file and this time the real-time facade will be included in it.
|
||||
|
||||
You can choose to include helper files. This is not enabled by default, but you can override it with the `--helpers (-H)` option.
|
||||
The `Illuminate/Support/helpers.php` is already set up, but you can add/remove your own files in the config file.
|
||||
|
||||
### Automatic PHPDoc generation for macros and mixins
|
||||
|
||||
This package can generate PHPDocs for macros and mixins which will be added to the `_ide_helper.php` file.
|
||||
|
||||
But this only works if you use type hinting when declaring a macro.
|
||||
|
||||
```php
|
||||
Str::macro('concat', function(string $str1, string $str2) : string {
|
||||
return $str1 . $str2;
|
||||
});
|
||||
```
|
||||
|
||||
### Automatic PHPDocs for models
|
||||
|
||||
If you don't want to write your properties yourself, you can use the command `php artisan ide-helper:models` to generate
|
||||
PHPDocs, based on table columns, relations and getters/setters.
|
||||
|
||||
> Note: this command requires a working database connection to introspect the table of each model
|
||||
|
||||
By default, you are asked to overwrite or write to a separate file (`_ide_helper_models.php`).
|
||||
You can write the comments directly to your Model file, using the `--write (-W)` option, or
|
||||
force to not write with `--nowrite (-N)`.
|
||||
|
||||
Alternatively using the `--write-mixin (-M)` option will only add a mixin tag to your Model file,
|
||||
writing the rest in (`_ide_helper_models.php`).
|
||||
The class name will be different from the model, avoiding the IDE duplicate annoyance.
|
||||
|
||||
> Please make sure to back up your models, before writing the info.
|
||||
|
||||
Writing to the models should keep the existing comments and only append new properties/methods. It will not update changed properties/methods.
|
||||
|
||||
With the `--reset (-R)` option, the whole existing PHPDoc is replaced, including any comments that have been made.
|
||||
|
||||
```bash
|
||||
php artisan ide-helper:models "App\Models\Post"
|
||||
```
|
||||
|
||||
```php
|
||||
/**
|
||||
* App\Models\Post
|
||||
*
|
||||
* @property integer $id
|
||||
* @property integer $author_id
|
||||
* @property string $title
|
||||
* @property string $text
|
||||
* @property \Illuminate\Support\Carbon $created_at
|
||||
* @property \Illuminate\Support\Carbon $updated_at
|
||||
* @property-read \User $author
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection|\Comment[] $comments
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Post newModelQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Post newQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Post query()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Post whereTitle($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Post forAuthors(\User ...$authors)
|
||||
* …
|
||||
*/
|
||||
```
|
||||
|
||||
With the `--write-mixin (-M)` option
|
||||
```php
|
||||
/**
|
||||
* …
|
||||
* @mixin IdeHelperPost
|
||||
*/
|
||||
```
|
||||
|
||||
#### Model Directories
|
||||
|
||||
By default, models in `app/models` are scanned. The optional argument tells what models to use (also outside app/models).
|
||||
|
||||
```bash
|
||||
php artisan ide-helper:models "App\Models\Post" "App\Models\User"
|
||||
```
|
||||
|
||||
You can also scan a different directory, using the `--dir` option (relative from the base path):
|
||||
|
||||
```bash
|
||||
php artisan ide-helper:models --dir="path/to/models" --dir="app/src/Model"
|
||||
```
|
||||
|
||||
You can publish the config file (`php artisan vendor:publish`) and set the default directories.
|
||||
|
||||
#### Ignore Models
|
||||
|
||||
Models can be ignored using the `--ignore (-I)` option
|
||||
|
||||
```bash
|
||||
php artisan ide-helper:models --ignore="App\Models\Post,App\Models\User"
|
||||
```
|
||||
|
||||
Or can be ignored by setting the `ignored_models` config
|
||||
|
||||
```php
|
||||
'ignored_models' => [
|
||||
App\Post::class,
|
||||
Api\User::class
|
||||
],
|
||||
```
|
||||
|
||||
#### Magic `where*` methods
|
||||
|
||||
Eloquent allows calling `where<Attribute>` on your models, e.g. `Post::whereTitle(…)` and automatically translates this to e.g. `Post::where('title', '=', '…')`.
|
||||
|
||||
If for some reason it's undesired to have them generated (one for each column), you can disable this via config `write_model_magic_where` and setting it to `false`.
|
||||
|
||||
#### Magic `*_count` properties
|
||||
|
||||
You may use the [`::withCount`](https://laravel.com/docs/master/eloquent-relationships#counting-related-models) method to count the number results from a relationship without actually loading them. Those results are then placed in attributes following the `<columname>_count` convention.
|
||||
|
||||
By default, these attributes are generated in the phpdoc. You can turn them off by setting the config `write_model_relation_count_properties` to `false`.
|
||||
|
||||
#### Generics annotations
|
||||
|
||||
Laravel 9 introduced generics annotations in DocBlocks for collections. PhpStorm 2022.3 and above support the use of generics annotations within `@property` and `@property-read` declarations in DocBlocks, e.g. `Collection<User>` instead of `Collection|User[]`.
|
||||
|
||||
These can be disabled by setting the config `use_generics_annotations` to `false`.
|
||||
|
||||
#### Support `@comment` based on DocBlock
|
||||
|
||||
In order to better support IDEs, relations and getters/setters can also add a comment to a property like table columns. Therefore a custom docblock `@comment` is used:
|
||||
```php
|
||||
class Users extends Model
|
||||
{
|
||||
/**
|
||||
* @comment Get User's full name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getFullNameAttribute(): string
|
||||
{
|
||||
return $this->first_name . ' ' .$this->last_name ;
|
||||
}
|
||||
}
|
||||
|
||||
// => after generate models
|
||||
|
||||
/**
|
||||
* App\Models\Users
|
||||
*
|
||||
* @property-read string $full_name Get User's full name
|
||||
* …
|
||||
*/
|
||||
```
|
||||
|
||||
#### Dedicated Eloquent Builder methods
|
||||
|
||||
A new method to the eloquent models was added called `newEloquentBuilder` [Reference](https://timacdonald.me/dedicated-eloquent-model-query-builders/) where we can
|
||||
add support for creating a new dedicated class instead of using local scopes in the model itself.
|
||||
|
||||
If for some reason it's undesired to have them generated (one for each column), you can disable this via config `write_model_external_builder_methods` and setting it to `false`.
|
||||
|
||||
#### Custom Relationship Types
|
||||
|
||||
If you are using relationships not built into Laravel you will need to specify the name and returning class in the config to get proper generation.
|
||||
|
||||
```php
|
||||
'additional_relation_types' => [
|
||||
'externalHasMany' => \My\Package\externalHasMany::class
|
||||
],
|
||||
```
|
||||
|
||||
Found relationships will typically generate a return value based on the name of the relationship.
|
||||
|
||||
If your custom relationships don't follow this traditional naming scheme you can define its return type manually. The available options are `many` and `morphTo`.
|
||||
|
||||
```php
|
||||
'additional_relation_return_types' => [
|
||||
'externalHasMultiple' => 'many'
|
||||
],
|
||||
```
|
||||
|
||||
#### Model Hooks
|
||||
|
||||
If you need additional information on your model from sources that are not handled by default, you can hook in to the
|
||||
generation process with model hooks to add extra information on the fly.
|
||||
Simply create a class that implements `ModelHookInterface` and add it to the `model_hooks` array in the config:
|
||||
|
||||
```php
|
||||
'model_hooks' => [
|
||||
MyCustomHook::class,
|
||||
],
|
||||
```
|
||||
|
||||
The `run` method will be called during generation for every model and receives the current running `ModelsCommand` and the current `Model`, e.g.:
|
||||
|
||||
```php
|
||||
class MyCustomHook implements ModelHookInterface
|
||||
{
|
||||
public function run(ModelsCommand $command, Model $model): void
|
||||
{
|
||||
if (! $model instanceof MyModel) {
|
||||
return;
|
||||
}
|
||||
|
||||
$command->setProperty('custom', 'string', true, false, 'My custom property');
|
||||
$command->unsetMethod('method');
|
||||
$command->setMethod('method', $command->getMethodType($model, '\Some\Class'), ['$param']);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```php
|
||||
/**
|
||||
* MyModel
|
||||
*
|
||||
* @property integer $id
|
||||
* @property-read string $custom
|
||||
```
|
||||
|
||||
### Automatic PHPDocs generation for Laravel Fluent methods
|
||||
|
||||
If you need PHPDocs support for Fluent methods in migration, for example
|
||||
|
||||
```php
|
||||
$table->string("somestring")->nullable()->index();
|
||||
```
|
||||
|
||||
After publishing vendor, simply change the `include_fluent` line in your `config/ide-helper.php` file into:
|
||||
|
||||
```php
|
||||
'include_fluent' => true,
|
||||
```
|
||||
|
||||
Then run `php artisan ide-helper:generate`, you will now see all Fluent methods recognized by your IDE.
|
||||
|
||||
### Auto-completion for factory builders
|
||||
|
||||
If you would like the `factory()->create()` and `factory()->make()` methods to return the correct model class,
|
||||
you can enable custom factory builders with the `include_factory_builders` line in your `config/ide-helper.php` file.
|
||||
Deprecated for Laravel 8 or latest.
|
||||
|
||||
```php
|
||||
'include_factory_builders' => true,
|
||||
```
|
||||
|
||||
For this to work, you must also publish the PhpStorm Meta file (see below).
|
||||
|
||||
## PhpStorm Meta for Container instances
|
||||
|
||||
It's possible to generate a PhpStorm meta file to [add support for factory design pattern](https://www.jetbrains.com/help/phpstorm/ide-advanced-metadata.html).
|
||||
For Laravel, this means we can make PhpStorm understand what kind of object we are resolving from the IoC Container.
|
||||
For example, `events` will return an `Illuminate\Events\Dispatcher` object,
|
||||
so with the meta file you can call `app('events')` and it will autocomplete the Dispatcher methods.
|
||||
|
||||
```bash
|
||||
php artisan ide-helper:meta
|
||||
```
|
||||
|
||||
```php
|
||||
app('events')->fire();
|
||||
\App::make('events')->fire();
|
||||
|
||||
/** @var \Illuminate\Foundation\Application $app */
|
||||
$app->make('events')->fire();
|
||||
|
||||
// When the key is not found, it uses the argument as class name
|
||||
app('App\SomeClass');
|
||||
// Also works with
|
||||
app(App\SomeClass::class);
|
||||
```
|
||||
|
||||
> Note: You might need to restart PhpStorm and make sure `.phpstorm.meta.php` is indexed.
|
||||
>
|
||||
> Note: When you receive a FatalException: class not found, check your config
|
||||
> (for example, remove S3 as cloud driver when you don't have S3 configured. Remove Redis ServiceProvider when you don't use it).
|
||||
|
||||
You can change the generated filename via the config `meta_filename`. This can be useful for cases where you want to take advantage of PhpStorm's support of the _directory_ `.phpstorm.meta.php/`: all files placed there are parsed, should you want to provide additional files to PhpStorm.
|
||||
|
||||
## License
|
||||
|
||||
The Laravel IDE Helper Generator is open-sourced software licensed under the [MIT license](http://opensource.org/licenses/MIT)
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
{
|
||||
"name": "barryvdh/laravel-ide-helper",
|
||||
"description": "Laravel IDE Helper, generates correct PHPDocs for all Facade classes, to improve auto-completion.",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
"laravel",
|
||||
"autocomplete",
|
||||
"ide",
|
||||
"helper",
|
||||
"phpstorm",
|
||||
"netbeans",
|
||||
"sublime",
|
||||
"codeintel",
|
||||
"phpdoc"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Barry vd. Heuvel",
|
||||
"email": "barryvdh@gmail.com"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "^8.1",
|
||||
"ext-json": "*",
|
||||
"barryvdh/reflection-docblock": "^2.1.1",
|
||||
"composer/class-map-generator": "^1.0",
|
||||
"illuminate/console": "^10 || ^11",
|
||||
"illuminate/database": "^10.38 || ^11",
|
||||
"illuminate/filesystem": "^10 || ^11",
|
||||
"illuminate/support": "^10 || ^11",
|
||||
"nikic/php-parser": "^4.18 || ^5",
|
||||
"phpdocumentor/type-resolver": "^1.1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"ext-pdo_sqlite": "*",
|
||||
"friendsofphp/php-cs-fixer": "^3",
|
||||
"illuminate/config": "^9 || ^10 || ^11",
|
||||
"illuminate/view": "^9 || ^10 || ^11",
|
||||
"mockery/mockery": "^1.4",
|
||||
"orchestra/testbench": "^8 || ^9",
|
||||
"phpunit/phpunit": "^10.5",
|
||||
"spatie/phpunit-snapshot-assertions": "^4 || ^5",
|
||||
"vimeo/psalm": "^5.4"
|
||||
},
|
||||
"suggest": {
|
||||
"illuminate/events": "Required for automatic helper generation (^6|^7|^8|^9|^10|^11)."
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"prefer-stable": true,
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Barryvdh\\LaravelIdeHelper\\": "src"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Barryvdh\\LaravelIdeHelper\\Tests\\": "tests"
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"allow-plugins": {
|
||||
"composer/package-versions-deprecated": true
|
||||
},
|
||||
"sort-packages": true
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.1-dev"
|
||||
},
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"Barryvdh\\LaravelIdeHelper\\IdeHelperServiceProvider"
|
||||
]
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"analyze": "psalm",
|
||||
"check-style": [
|
||||
"php-cs-fixer fix --diff --diff-format=udiff --dry-run",
|
||||
"php-cs-fixer fix --diff --diff-format=udiff --dry-run --config=.php_cs.tests.php"
|
||||
],
|
||||
"fix-style": [
|
||||
"php-cs-fixer fix",
|
||||
"php-cs-fixer fix --config=.php-cs-fixer.tests.php"
|
||||
],
|
||||
"psalm-set-baseline": "psalm --set-baseline=psalm-baseline.xml",
|
||||
"test": "phpunit",
|
||||
"test-ci": "phpunit -d --without-creating-snapshots",
|
||||
"test-regenerate": "phpunit -d --update-snapshots"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,314 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Filename
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The default filename.
|
||||
|
|
||||
*/
|
||||
|
||||
'filename' => '_ide_helper.php',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Models filename
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The default filename for the models helper file.
|
||||
|
|
||||
*/
|
||||
|
||||
'models_filename' => '_ide_helper_models.php',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| PhpStorm meta filename
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| PhpStorm also supports the directory `.phpstorm.meta.php/` with arbitrary
|
||||
| files in it, should you need additional files for your project; e.g.
|
||||
| `.phpstorm.meta.php/laravel_ide_Helper.php'.
|
||||
|
|
||||
*/
|
||||
'meta_filename' => '.phpstorm.meta.php',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Fluent helpers
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Set to true to generate commonly used Fluent methods.
|
||||
|
|
||||
*/
|
||||
|
||||
'include_fluent' => false,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Factory builders
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Set to true to generate factory generators for better factory()
|
||||
| method auto-completion.
|
||||
|
|
||||
| Deprecated for Laravel 8 or latest.
|
||||
|
|
||||
*/
|
||||
|
||||
'include_factory_builders' => false,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Write model magic methods
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Set to false to disable write magic methods of model.
|
||||
|
|
||||
*/
|
||||
|
||||
'write_model_magic_where' => true,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Write model external Eloquent builder methods
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Set to false to disable write external Eloquent builder methods.
|
||||
|
|
||||
*/
|
||||
|
||||
'write_model_external_builder_methods' => true,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Write model relation count properties
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Set to false to disable writing of relation count properties to model DocBlocks.
|
||||
|
|
||||
*/
|
||||
|
||||
'write_model_relation_count_properties' => true,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Write Eloquent model mixins
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This will add the necessary DocBlock mixins to the model class
|
||||
| contained in the Laravel framework. This helps the IDE with
|
||||
| auto-completion.
|
||||
|
|
||||
| Please be aware that this setting changes a file within the /vendor directory.
|
||||
|
|
||||
*/
|
||||
|
||||
'write_eloquent_model_mixins' => false,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Helper files to include
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Include helper files. By default not included, but can be toggled with the
|
||||
| -- helpers (-H) option. Extra helper files can be included.
|
||||
|
|
||||
*/
|
||||
|
||||
'include_helpers' => false,
|
||||
|
||||
'helper_files' => [
|
||||
base_path() . '/vendor/laravel/framework/src/Illuminate/Support/helpers.php',
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Model locations to include
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Define in which directories the ide-helper:models command should look
|
||||
| for models.
|
||||
|
|
||||
| glob patterns are supported to easier reach models in sub-directories,
|
||||
| e.g. `app/Services/* /Models` (without the space).
|
||||
|
|
||||
*/
|
||||
|
||||
'model_locations' => [
|
||||
'app',
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Models to ignore
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Define which models should be ignored.
|
||||
|
|
||||
*/
|
||||
|
||||
'ignored_models' => [
|
||||
// App\MyModel::class,
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Models hooks
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Define which hook classes you want to run for models to add custom information.
|
||||
|
|
||||
| Hooks should implement Barryvdh\LaravelIdeHelper\Contracts\ModelHookInterface.
|
||||
|
|
||||
*/
|
||||
|
||||
'model_hooks' => [
|
||||
// App\Support\IdeHelper\MyModelHook::class
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Extra classes
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| These implementations are not really extended, but called with magic functions.
|
||||
|
|
||||
*/
|
||||
|
||||
'extra' => [
|
||||
'Eloquent' => ['Illuminate\Database\Eloquent\Builder', 'Illuminate\Database\Query\Builder'],
|
||||
'Session' => ['Illuminate\Session\Store'],
|
||||
],
|
||||
|
||||
'magic' => [],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Interface implementations
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| These interfaces will be replaced with the implementing class. Some interfaces
|
||||
| are detected by the helpers, others can be listed below.
|
||||
|
|
||||
*/
|
||||
|
||||
'interfaces' => [
|
||||
// App\MyInterface::class => App\MyImplementation::class,
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Support for camel cased models
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| There are some Laravel packages (such as Eloquence) that allow for accessing
|
||||
| Eloquent model properties via camel case, instead of snake case.
|
||||
|
|
||||
| Enabling this option will support these packages by saving all model
|
||||
| properties as camel case, instead of snake case.
|
||||
|
|
||||
| For example, normally you would see this:
|
||||
|
|
||||
| * @property \Illuminate\Support\Carbon $created_at
|
||||
| * @property \Illuminate\Support\Carbon $updated_at
|
||||
|
|
||||
| With this enabled, the properties will be this:
|
||||
|
|
||||
| * @property \Illuminate\Support\Carbon $createdAt
|
||||
| * @property \Illuminate\Support\Carbon $updatedAt
|
||||
|
|
||||
| Note, it is currently an all-or-nothing option.
|
||||
|
|
||||
*/
|
||||
'model_camel_case_properties' => false,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Property casts
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Cast the given "real type" to the given "type".
|
||||
|
|
||||
*/
|
||||
'type_overrides' => [
|
||||
'integer' => 'int',
|
||||
'boolean' => 'bool',
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Include DocBlocks from classes
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Include DocBlocks from classes to allow additional code inspection for
|
||||
| magic methods and properties.
|
||||
|
|
||||
*/
|
||||
'include_class_docblocks' => false,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Force FQN usage
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Use the fully qualified (class) name in DocBlocks,
|
||||
| even if the class exists in the same namespace
|
||||
| or there is an import (use className) of the class.
|
||||
|
|
||||
*/
|
||||
'force_fqn' => false,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Use generics syntax
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Use generics syntax within DocBlocks,
|
||||
| e.g. `Collection<User>` instead of `Collection|User[]`.
|
||||
|
|
||||
*/
|
||||
'use_generics_annotations' => true,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Additional relation types
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Sometimes it's needed to create custom relation types. The key of the array
|
||||
| is the relationship method name. The value of the array is the fully-qualified
|
||||
| class name of the relationship, e.g. `'relationName' => RelationShipClass::class`.
|
||||
|
|
||||
*/
|
||||
'additional_relation_types' => [],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Additional relation return types
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When using custom relation types its possible for the class name to not contain
|
||||
| the proper return type of the relation. The key of the array is the relationship
|
||||
| method name. The value of the array is the return type of the relation ('many'
|
||||
| or 'morphTo').
|
||||
| e.g. `'relationName' => 'many'`.
|
||||
|
|
||||
*/
|
||||
'additional_relation_return_types' => [],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Run artisan commands after migrations to generate model helpers
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The specified commands should run after migrations are finished running.
|
||||
|
|
||||
*/
|
||||
'post_migrate' => [
|
||||
// 'ide-helper:models --nowrite',
|
||||
],
|
||||
|
||||
];
|
||||
@@ -0,0 +1,123 @@
|
||||
<?= '<?php' ?>
|
||||
<?php
|
||||
/**
|
||||
* @var \Barryvdh\LaravelIdeHelper\Alias[][] $namespaces_by_alias_ns
|
||||
* @var \Barryvdh\LaravelIdeHelper\Alias[][] $namespaces_by_extends_ns
|
||||
* @var bool $include_fluent
|
||||
* @var string $helpers
|
||||
*/
|
||||
?>
|
||||
|
||||
/* @noinspection ALL */
|
||||
// @formatter:off
|
||||
// phpcs:ignoreFile
|
||||
|
||||
/**
|
||||
* A helper file for Laravel, to provide autocomplete information to your IDE
|
||||
* Generated for Laravel <?= $version ?>.
|
||||
*
|
||||
* This file should not be included in your code, only analyzed by your IDE!
|
||||
*
|
||||
* @author Barry vd. Heuvel <barryvdh@gmail.com>
|
||||
* @see https://github.com/barryvdh/laravel-ide-helper
|
||||
*/
|
||||
|
||||
<?php foreach ($namespaces_by_extends_ns as $namespace => $aliases) : ?>
|
||||
namespace <?= $namespace == '__root' ? '' : trim($namespace, '\\') ?> {
|
||||
<?php foreach ($aliases as $alias) : ?>
|
||||
<?= trim($alias->getDocComment(' ')) ?>
|
||||
<?= $alias->getClassType() ?> <?= $alias->getExtendsClass() ?> {
|
||||
<?php foreach ($alias->getMethods() as $method) : ?>
|
||||
<?= trim($method->getDocComment(' ')) ?>
|
||||
public static function <?= $method->getName() ?>(<?= $method->getParamsWithDefault() ?>)
|
||||
{<?php if ($method->getDeclaringClass() !== $method->getRoot()) : ?>
|
||||
//Method inherited from <?= $method->getDeclaringClass() ?>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($method->isInstanceCall()) : ?>
|
||||
/** @var <?=$method->getRoot()?> $instance */
|
||||
<?php endif?>
|
||||
<?= $method->shouldReturn() ? 'return ' : '' ?><?= $method->getRootMethodCall() ?>;
|
||||
}
|
||||
<?php endforeach; ?>
|
||||
}
|
||||
<?php endforeach; ?>
|
||||
}
|
||||
|
||||
<?php endforeach; ?>
|
||||
|
||||
<?php foreach ($namespaces_by_alias_ns as $namespace => $aliases) : ?>
|
||||
namespace <?= $namespace == '__root' ? '' : trim($namespace, '\\') ?> {
|
||||
<?php foreach ($aliases as $alias) : ?>
|
||||
<?= $alias->getClassType() ?> <?= $alias->getShortName() ?> extends <?= $alias->getExtends() ?> {<?php if ($alias->getExtendsNamespace() == '\Illuminate\Database\Eloquent') : ?>
|
||||
<?php foreach ($alias->getMethods() as $method) : ?>
|
||||
<?= trim($method->getDocComment(' ')) ?>
|
||||
public static function <?= $method->getName() ?>(<?= $method->getParamsWithDefault() ?>)
|
||||
{<?php if ($method->getDeclaringClass() !== $method->getRoot()) : ?>
|
||||
//Method inherited from <?= $method->getDeclaringClass() ?>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($method->isInstanceCall()) : ?>
|
||||
/** @var <?=$method->getRoot()?> $instance */
|
||||
<?php endif?>
|
||||
<?= $method->shouldReturn() ? 'return ' : '' ?><?= $method->getRootMethodCall() ?>;
|
||||
}
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>}
|
||||
<?php endforeach; ?>
|
||||
}
|
||||
|
||||
<?php endforeach; ?>
|
||||
|
||||
<?php foreach($real_time_facades as $name): ?>
|
||||
<?php $nested = explode('\\', str_replace('\\' . class_basename($name), '', $name)); ?>
|
||||
namespace <?php echo implode('\\', $nested); ?> {
|
||||
/**
|
||||
* @mixin <?= str_replace('Facades', '', $name) ?>
|
||||
*/
|
||||
class <?= class_basename($name) ?> extends <?= str_replace('Facades', '', $name) ?> {}
|
||||
}
|
||||
<?php endforeach; ?>
|
||||
|
||||
<?php if ($helpers) : ?>
|
||||
namespace {
|
||||
<?= $helpers ?>
|
||||
}
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($include_fluent) : ?>
|
||||
namespace Illuminate\Support {
|
||||
/**
|
||||
* Methods commonly used in migrations
|
||||
*
|
||||
* @method Fluent after(string $column) Add the after modifier
|
||||
* @method Fluent charset(string $charset) Add the character set modifier
|
||||
* @method Fluent collation(string $collation) Add the collation modifier
|
||||
* @method Fluent comment(string $comment) Add comment
|
||||
* @method Fluent default($value) Add the default modifier
|
||||
* @method Fluent first() Select first row
|
||||
* @method Fluent index(string $name = null) Add the in dex clause
|
||||
* @method Fluent on(string $table) `on` of a foreign key
|
||||
* @method Fluent onDelete(string $action) `on delete` of a foreign key
|
||||
* @method Fluent onUpdate(string $action) `on update` of a foreign key
|
||||
* @method Fluent primary() Add the primary key modifier
|
||||
* @method Fluent references(string $column) `references` of a foreign key
|
||||
* @method Fluent nullable(bool $value = true) Add the nullable modifier
|
||||
* @method Fluent unique(string $name = null) Add unique index clause
|
||||
* @method Fluent unsigned() Add the unsigned modifier
|
||||
* @method Fluent useCurrent() Add the default timestamp value
|
||||
* @method Fluent change() Add the change modifier
|
||||
*/
|
||||
class Fluent {}
|
||||
}
|
||||
<?php endif ?>
|
||||
|
||||
<?php foreach ($factories as $factory) : ?>
|
||||
namespace <?=$factory->getNamespaceName()?> {
|
||||
/**
|
||||
* @method \Illuminate\Database\Eloquent\Collection|<?=$factory->getShortName()?>[]|<?=$factory->getShortName()?> create($attributes = [])
|
||||
* @method \Illuminate\Database\Eloquent\Collection|<?=$factory->getShortName()?>[]|<?=$factory->getShortName()?> make($attributes = [])
|
||||
*/
|
||||
class <?=$factory->getShortName()?>FactoryBuilder extends \Illuminate\Database\Eloquent\FactoryBuilder {}
|
||||
}
|
||||
<?php endforeach; ?>
|
||||
@@ -0,0 +1,68 @@
|
||||
<?= '<?php' ?>
|
||||
|
||||
/* @noinspection ALL */
|
||||
// @formatter:off
|
||||
// phpcs:ignoreFile
|
||||
|
||||
namespace PHPSTORM_META {
|
||||
|
||||
/**
|
||||
* PhpStorm Meta file, to provide autocomplete information for PhpStorm
|
||||
*
|
||||
* @author Barry vd. Heuvel <barryvdh@gmail.com>
|
||||
* @see https://github.com/barryvdh/laravel-ide-helper
|
||||
*/
|
||||
<?php foreach ($methods as $method) : ?>
|
||||
override(<?= $method ?>, map([
|
||||
'' => '@',
|
||||
<?php foreach ($bindings as $abstract => $class) : ?>
|
||||
'<?= $abstract ?>' => \<?= $class ?>::class,
|
||||
<?php endforeach; ?>
|
||||
]));
|
||||
<?php endforeach; ?>
|
||||
|
||||
<?php if (count($factories)) : ?>
|
||||
override(\factory(0), map([
|
||||
'' => '@FactoryBuilder',
|
||||
<?php foreach ($factories as $factory) : ?>
|
||||
'<?= $factory->getName() ?>' => \<?= $factory->getName() ?>FactoryBuilder::class,
|
||||
<?php endforeach; ?>
|
||||
]));
|
||||
<?php endif; ?>
|
||||
|
||||
override(\Illuminate\Foundation\Testing\Concerns\InteractsWithContainer::mock(0), map(["" => "@&\Mockery\MockInterface"]));
|
||||
override(\Illuminate\Foundation\Testing\Concerns\InteractsWithContainer::partialMock(0), map(["" => "@&\Mockery\MockInterface"]));
|
||||
override(\Illuminate\Foundation\Testing\Concerns\InteractsWithContainer::instance(0), type(1));
|
||||
override(\Illuminate\Foundation\Testing\Concerns\InteractsWithContainer::spy(0), map(["" => "@&\Mockery\MockInterface"]));
|
||||
override(\Illuminate\Support\Arr::add(0), type(0));
|
||||
override(\Illuminate\Support\Arr::except(0), type(0));
|
||||
override(\Illuminate\Support\Arr::first(0), elementType(0));
|
||||
override(\Illuminate\Support\Arr::last(0), elementType(0));
|
||||
override(\Illuminate\Support\Arr::get(0), elementType(0));
|
||||
override(\Illuminate\Support\Arr::only(0), type(0));
|
||||
override(\Illuminate\Support\Arr::prepend(0), type(0));
|
||||
override(\Illuminate\Support\Arr::pull(0), elementType(0));
|
||||
override(\Illuminate\Support\Arr::set(0), type(0));
|
||||
override(\Illuminate\Support\Arr::shuffle(0), type(0));
|
||||
override(\Illuminate\Support\Arr::sort(0), type(0));
|
||||
override(\Illuminate\Support\Arr::sortRecursive(0), type(0));
|
||||
override(\Illuminate\Support\Arr::where(0), type(0));
|
||||
override(\array_add(0), type(0));
|
||||
override(\array_except(0), type(0));
|
||||
override(\array_first(0), elementType(0));
|
||||
override(\array_last(0), elementType(0));
|
||||
override(\array_get(0), elementType(0));
|
||||
override(\array_only(0), type(0));
|
||||
override(\array_prepend(0), type(0));
|
||||
override(\array_pull(0), elementType(0));
|
||||
override(\array_set(0), type(0));
|
||||
override(\array_sort(0), type(0));
|
||||
override(\array_sort_recursive(0), type(0));
|
||||
override(\array_where(0), type(0));
|
||||
override(\head(0), elementType(0));
|
||||
override(\last(0), elementType(0));
|
||||
override(\with(0), type(0));
|
||||
override(\tap(0), type(0));
|
||||
override(\optional(0), type(0));
|
||||
|
||||
}
|
||||
+474
@@ -0,0 +1,474 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Laravel IDE Helper Generator
|
||||
*
|
||||
* @author Barry vd. Heuvel <barryvdh@gmail.com>
|
||||
* @copyright 2014 Barry vd. Heuvel / Fruitcake Studio (http://www.fruitcakestudio.nl)
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||
* @link https://github.com/barryvdh/laravel-ide-helper
|
||||
*/
|
||||
|
||||
namespace Barryvdh\LaravelIdeHelper;
|
||||
|
||||
use Barryvdh\Reflection\DocBlock;
|
||||
use Barryvdh\Reflection\DocBlock\Context;
|
||||
use Barryvdh\Reflection\DocBlock\Serializer as DocBlockSerializer;
|
||||
use Barryvdh\Reflection\DocBlock\Tag\MethodTag;
|
||||
use Closure;
|
||||
use Illuminate\Config\Repository as ConfigRepository;
|
||||
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
|
||||
use Illuminate\Support\Facades\Facade;
|
||||
use ReflectionClass;
|
||||
use Throwable;
|
||||
|
||||
class Alias
|
||||
{
|
||||
protected $alias;
|
||||
/** @psalm-var class-string $facade */
|
||||
protected $facade;
|
||||
protected $extends = null;
|
||||
protected $extendsClass = null;
|
||||
protected $extendsNamespace = null;
|
||||
protected $classType = 'class';
|
||||
protected $short;
|
||||
protected $namespace = '__root';
|
||||
protected $root = null;
|
||||
protected $classes = [];
|
||||
protected $methods = [];
|
||||
protected $usedMethods = [];
|
||||
protected $valid = false;
|
||||
protected $magicMethods = [];
|
||||
protected $interfaces = [];
|
||||
protected $phpdoc = null;
|
||||
protected $classAliases = [];
|
||||
|
||||
/** @var ConfigRepository */
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
* @param ConfigRepository $config
|
||||
* @param string $alias
|
||||
* @psalm-param class-string $facade
|
||||
* @param string $facade
|
||||
* @param array $magicMethods
|
||||
* @param array $interfaces
|
||||
*/
|
||||
public function __construct($config, $alias, $facade, $magicMethods = [], $interfaces = [])
|
||||
{
|
||||
$this->alias = $alias;
|
||||
$this->magicMethods = $magicMethods;
|
||||
$this->interfaces = $interfaces;
|
||||
$this->config = $config;
|
||||
|
||||
// Make the class absolute
|
||||
$facade = '\\' . ltrim($facade, '\\');
|
||||
$this->facade = $facade;
|
||||
|
||||
$this->detectRoot();
|
||||
|
||||
if (!$this->root || $this->isTrait()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->valid = true;
|
||||
|
||||
$this->addClass($this->root);
|
||||
$this->detectFake();
|
||||
$this->detectNamespace();
|
||||
$this->detectClassType();
|
||||
$this->detectExtendsNamespace();
|
||||
|
||||
if (!empty($this->namespace)) {
|
||||
$this->classAliases = (new UsesResolver())->loadFromClass($this->root);
|
||||
|
||||
//Create a DocBlock and serializer instance
|
||||
$this->phpdoc = new DocBlock(new ReflectionClass($alias), new Context($this->namespace, $this->classAliases));
|
||||
}
|
||||
|
||||
if ($facade === '\Illuminate\Database\Eloquent\Model') {
|
||||
$this->usedMethods = ['decrement', 'increment'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add one or more classes to analyze
|
||||
*
|
||||
* @param array|string $classes
|
||||
*/
|
||||
public function addClass($classes)
|
||||
{
|
||||
$classes = (array)$classes;
|
||||
foreach ($classes as $class) {
|
||||
if (class_exists($class) || interface_exists($class)) {
|
||||
$this->classes[] = $class;
|
||||
} else {
|
||||
echo "Class not exists: $class\r\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this class is valid to process.
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid()
|
||||
{
|
||||
return $this->valid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the classtype, 'interface' or 'class'
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getClasstype()
|
||||
{
|
||||
return $this->classType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the class which this alias extends
|
||||
*
|
||||
* @return null|string
|
||||
*/
|
||||
public function getExtends()
|
||||
{
|
||||
return $this->extends;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the class short name which this alias extends
|
||||
*
|
||||
* @return null|string
|
||||
*/
|
||||
public function getExtendsClass()
|
||||
{
|
||||
return $this->extendsClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the namespace of the class which this alias extends
|
||||
*
|
||||
* @return null|string
|
||||
*/
|
||||
public function getExtendsNamespace()
|
||||
{
|
||||
return $this->extendsNamespace;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Alias by which this class is called
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getAlias()
|
||||
{
|
||||
return $this->alias;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the short name (without namespace)
|
||||
*/
|
||||
public function getShortName()
|
||||
{
|
||||
return $this->short;
|
||||
}
|
||||
/**
|
||||
* Get the namespace from the alias
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getNamespace()
|
||||
{
|
||||
return $this->namespace;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the methods found by this Alias
|
||||
*
|
||||
* @return array|Method[]
|
||||
*/
|
||||
public function getMethods()
|
||||
{
|
||||
if (count($this->methods) > 0) {
|
||||
return $this->methods;
|
||||
}
|
||||
|
||||
$this->addMagicMethods();
|
||||
$this->detectMethods();
|
||||
return $this->methods;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect class returned by ::fake()
|
||||
*/
|
||||
protected function detectFake()
|
||||
{
|
||||
$facade = $this->facade;
|
||||
|
||||
if (!is_subclass_of($facade, Facade::class)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!method_exists($facade, 'fake')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$real = $facade::getFacadeRoot();
|
||||
|
||||
try {
|
||||
$facade::fake();
|
||||
$fake = $facade::getFacadeRoot();
|
||||
if ($fake !== $real) {
|
||||
$this->addClass(get_class($fake));
|
||||
}
|
||||
} finally {
|
||||
$facade::swap($real);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect the namespace
|
||||
*/
|
||||
protected function detectNamespace()
|
||||
{
|
||||
if (strpos($this->alias, '\\')) {
|
||||
$nsParts = explode('\\', $this->alias);
|
||||
$this->short = array_pop($nsParts);
|
||||
$this->namespace = implode('\\', $nsParts);
|
||||
} else {
|
||||
$this->short = $this->alias;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect the extends namespace
|
||||
*/
|
||||
protected function detectExtendsNamespace()
|
||||
{
|
||||
if (strpos($this->extends, '\\') !== false) {
|
||||
$nsParts = explode('\\', $this->extends);
|
||||
$this->extendsClass = array_pop($nsParts);
|
||||
$this->extendsNamespace = implode('\\', $nsParts);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect the class type
|
||||
*/
|
||||
protected function detectClassType()
|
||||
{
|
||||
//Some classes extend the facade
|
||||
if (interface_exists($this->facade)) {
|
||||
$this->classType = 'interface';
|
||||
$this->extends = $this->facade;
|
||||
} else {
|
||||
$this->classType = 'class';
|
||||
if (class_exists($this->facade)) {
|
||||
$this->extends = $this->facade;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the real root of a facade
|
||||
*
|
||||
* @return bool|string
|
||||
*/
|
||||
protected function detectRoot()
|
||||
{
|
||||
$facade = $this->facade;
|
||||
|
||||
try {
|
||||
//If possible, get the facade root
|
||||
if (method_exists($facade, 'getFacadeRoot')) {
|
||||
$root = get_class($facade::getFacadeRoot());
|
||||
} else {
|
||||
$root = $facade;
|
||||
}
|
||||
|
||||
//If it doesn't exist, skip it
|
||||
if (!class_exists($root) && !interface_exists($root)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->root = $root;
|
||||
|
||||
//When the database connection is not set, some classes will be skipped
|
||||
} catch (\PDOException $e) {
|
||||
$this->error(
|
||||
'PDOException: ' . $e->getMessage() .
|
||||
"\nPlease configure your database connection correctly, or use the sqlite memory driver (-M)." .
|
||||
" Skipping $facade."
|
||||
);
|
||||
} catch (Throwable $e) {
|
||||
$this->error('Exception: ' . $e->getMessage() . "\nSkipping $facade.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect if this class is a trait or not.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function isTrait()
|
||||
{
|
||||
// Check if the facade is not a Trait
|
||||
return trait_exists($this->facade);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add magic methods, as defined in the configuration files
|
||||
*/
|
||||
protected function addMagicMethods()
|
||||
{
|
||||
foreach ($this->magicMethods as $magic => $real) {
|
||||
[$className, $name] = explode('::', $real);
|
||||
if ((!class_exists($className) && !interface_exists($className)) || !method_exists($className, $name)) {
|
||||
continue;
|
||||
}
|
||||
$method = new \ReflectionMethod($className, $name);
|
||||
$class = new ReflectionClass($className);
|
||||
|
||||
if (!in_array($magic, $this->usedMethods)) {
|
||||
if ($class !== $this->root) {
|
||||
$this->methods[] = new Method($method, $this->alias, $class, $magic, $this->interfaces, $this->classAliases);
|
||||
}
|
||||
$this->usedMethods[] = $magic;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the methods for one or multiple classes.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function detectMethods()
|
||||
{
|
||||
foreach ($this->classes as $class) {
|
||||
$reflection = new ReflectionClass($class);
|
||||
|
||||
$methods = $reflection->getMethods(\ReflectionMethod::IS_PUBLIC);
|
||||
if ($methods) {
|
||||
foreach ($methods as $method) {
|
||||
if (!in_array($method->name, $this->usedMethods)) {
|
||||
// Only add the methods to the output when the root is not the same as the class.
|
||||
// And don't add the __*() methods
|
||||
if ($this->extends !== $class && substr($method->name, 0, 2) !== '__') {
|
||||
$this->methods[] = new Method(
|
||||
$method,
|
||||
$this->alias,
|
||||
$reflection,
|
||||
$method->name,
|
||||
$this->interfaces,
|
||||
$this->classAliases
|
||||
);
|
||||
}
|
||||
$this->usedMethods[] = $method->name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the class is macroable
|
||||
// (Eloquent\Builder is also macroable but doesn't use Macroable trait)
|
||||
$traits = collect($reflection->getTraitNames());
|
||||
if ($traits->contains('Illuminate\Support\Traits\Macroable') || $class === EloquentBuilder::class) {
|
||||
$properties = $reflection->getStaticProperties();
|
||||
$macros = isset($properties['macros']) ? $properties['macros'] : [];
|
||||
foreach ($macros as $macro_name => $macro_func) {
|
||||
if (!in_array($macro_name, $this->usedMethods)) {
|
||||
// Add macros
|
||||
$this->methods[] = new Macro(
|
||||
$this->getMacroFunction($macro_func),
|
||||
$this->alias,
|
||||
$reflection,
|
||||
$macro_name,
|
||||
$this->interfaces,
|
||||
$this->classAliases
|
||||
);
|
||||
$this->usedMethods[] = $macro_name;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $macro_func
|
||||
*
|
||||
* @return \ReflectionFunctionAbstract
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
protected function getMacroFunction($macro_func)
|
||||
{
|
||||
if (is_array($macro_func) && is_callable($macro_func)) {
|
||||
return new \ReflectionMethod($macro_func[0], $macro_func[1]);
|
||||
}
|
||||
|
||||
if (is_object($macro_func) && is_callable($macro_func) && !$macro_func instanceof Closure) {
|
||||
return new \ReflectionMethod($macro_func, '__invoke');
|
||||
}
|
||||
|
||||
return new \ReflectionFunction($macro_func);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the docblock for this alias
|
||||
*
|
||||
* @param string $prefix
|
||||
* @return mixed
|
||||
*/
|
||||
public function getDocComment($prefix = "\t\t")
|
||||
{
|
||||
$serializer = new DocBlockSerializer(1, $prefix);
|
||||
|
||||
if (!$this->phpdoc) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if ($this->config->get('ide-helper.include_class_docblocks')) {
|
||||
// if a class doesn't expose any DocBlock tags
|
||||
// we can perform reflection on the class and
|
||||
// add in the original class DocBlock
|
||||
if (count($this->phpdoc->getTags()) === 0) {
|
||||
$class = new ReflectionClass($this->root);
|
||||
$this->phpdoc = new DocBlock($class->getDocComment());
|
||||
}
|
||||
}
|
||||
|
||||
$this->removeDuplicateMethodsFromPhpDoc();
|
||||
return $serializer->getDocComment($this->phpdoc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes method tags from the doc comment that already appear as functions inside the class.
|
||||
* This prevents duplicate function errors in the IDE.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function removeDuplicateMethodsFromPhpDoc()
|
||||
{
|
||||
$methodNames = array_map(function (Method $method) {
|
||||
return $method->getName();
|
||||
}, $this->getMethods());
|
||||
|
||||
foreach ($this->phpdoc->getTags() as $tag) {
|
||||
if ($tag instanceof MethodTag && in_array($tag->getMethodName(), $methodNames)) {
|
||||
$this->phpdoc->deleteTag($tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Output an error.
|
||||
*
|
||||
* @param string $string
|
||||
* @return void
|
||||
*/
|
||||
protected function error($string)
|
||||
{
|
||||
echo $string . "\r\n";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Laravel IDE Helper Generator - Eloquent Model Mixin
|
||||
*
|
||||
* @author Charles A. Peterson <artistan@gmail.com>
|
||||
* @copyright 2017 Charles A. Peterson / Fruitcake Studio (http://www.fruitcakestudio.nl)
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||
* @link https://github.com/barryvdh/laravel-ide-helper
|
||||
*/
|
||||
|
||||
namespace Barryvdh\LaravelIdeHelper\Console;
|
||||
|
||||
use Barryvdh\LaravelIdeHelper\Eloquent;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Filesystem\Filesystem;
|
||||
|
||||
/**
|
||||
* A command to add \Eloquent mixin to Eloquent\Model
|
||||
*
|
||||
* @author Charles A. Peterson <artistan@gmail.com>
|
||||
*/
|
||||
class EloquentCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The console command name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'ide-helper:eloquent';
|
||||
|
||||
/**
|
||||
* @var Filesystem $files
|
||||
*/
|
||||
protected $files;
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Add \Eloquent helper to \Eloquent\Model';
|
||||
|
||||
/**
|
||||
* @param Filesystem $files
|
||||
*/
|
||||
public function __construct(Filesystem $files)
|
||||
{
|
||||
parent::__construct();
|
||||
$this->files = $files;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
Eloquent::writeEloquentModelHelper($this, $this->files);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Laravel IDE Helper Generator
|
||||
*
|
||||
* @author Barry vd. Heuvel <barryvdh@gmail.com>
|
||||
* @copyright 2014 Barry vd. Heuvel / Fruitcake Studio (http://www.fruitcakestudio.nl)
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||
* @link https://github.com/barryvdh/laravel-ide-helper
|
||||
*/
|
||||
|
||||
namespace Barryvdh\LaravelIdeHelper\Console;
|
||||
|
||||
use Barryvdh\LaravelIdeHelper\Eloquent;
|
||||
use Barryvdh\LaravelIdeHelper\Generator;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Filesystem\Filesystem;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
/**
|
||||
* A command to generate autocomplete information for your IDE
|
||||
*
|
||||
* @author Barry vd. Heuvel <barryvdh@gmail.com>
|
||||
*/
|
||||
class GeneratorCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The console command name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'ide-helper:generate';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Generate a new IDE Helper file.';
|
||||
|
||||
/** @var \Illuminate\Config\Repository */
|
||||
protected $config;
|
||||
|
||||
/** @var Filesystem */
|
||||
protected $files;
|
||||
|
||||
/** @var \Illuminate\View\Factory */
|
||||
protected $view;
|
||||
|
||||
protected $onlyExtend;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @param \Illuminate\Config\Repository $config
|
||||
* @param Filesystem $files
|
||||
* @param \Illuminate\View\Factory $view
|
||||
*/
|
||||
public function __construct(
|
||||
/*ConfigRepository */
|
||||
$config,
|
||||
Filesystem $files,
|
||||
/* Illuminate\View\Factory */
|
||||
$view
|
||||
) {
|
||||
$this->config = $config;
|
||||
$this->files = $files;
|
||||
$this->view = $view;
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
if (
|
||||
file_exists(base_path() . '/vendor/compiled.php') ||
|
||||
file_exists(base_path() . '/bootstrap/cache/compiled.php') ||
|
||||
file_exists(base_path() . '/storage/framework/compiled.php')
|
||||
) {
|
||||
$this->error(
|
||||
'Error generating IDE Helper: first delete your compiled file (php artisan clear-compiled)'
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
$filename = $this->argument('filename');
|
||||
|
||||
// Add the php extension if missing
|
||||
// This is a backwards-compatible shim and can be removed in the future
|
||||
if (substr($filename, -4, 4) !== '.php') {
|
||||
$filename .= '.php';
|
||||
}
|
||||
|
||||
if ($this->option('memory')) {
|
||||
$this->useMemoryDriver();
|
||||
}
|
||||
|
||||
|
||||
$helpers = '';
|
||||
if ($this->option('helpers') || ($this->config->get('ide-helper.include_helpers'))) {
|
||||
foreach ($this->config->get('ide-helper.helper_files', []) as $helper) {
|
||||
if (file_exists($helper)) {
|
||||
$helpers .= str_replace(['<?php', '?>'], '', $this->files->get($helper));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$helpers = '';
|
||||
}
|
||||
|
||||
$generator = new Generator($this->config, $this->view, $this->getOutput(), $helpers);
|
||||
$content = $generator->generate();
|
||||
$written = $this->files->put($filename, $content);
|
||||
|
||||
if ($written !== false) {
|
||||
$this->info("A new helper file was written to $filename");
|
||||
|
||||
if ($this->option('write_mixins')) {
|
||||
Eloquent::writeEloquentModelHelper($this, $this->files);
|
||||
}
|
||||
} else {
|
||||
$this->error("The helper file could not be created at $filename");
|
||||
}
|
||||
}
|
||||
|
||||
protected function useMemoryDriver()
|
||||
{
|
||||
//Use a sqlite database in memory, to avoid connection errors on Database facades
|
||||
$this->config->set(
|
||||
'database.connections.sqlite',
|
||||
[
|
||||
'driver' => 'sqlite',
|
||||
'database' => ':memory:',
|
||||
]
|
||||
);
|
||||
$this->config->set('database.default', 'sqlite');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the console command arguments.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getArguments()
|
||||
{
|
||||
$filename = $this->config->get('ide-helper.filename');
|
||||
|
||||
return [
|
||||
[
|
||||
'filename', InputArgument::OPTIONAL, 'The path to the helper file', $filename,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the console command options.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getOptions()
|
||||
{
|
||||
$writeMixins = $this->config->get('ide-helper.write_eloquent_model_mixins');
|
||||
|
||||
return [
|
||||
['write_mixins', 'W', InputOption::VALUE_OPTIONAL, 'Write mixins to Laravel Model?', $writeMixins],
|
||||
['helpers', 'H', InputOption::VALUE_NONE, 'Include the helper files'],
|
||||
['memory', 'M', InputOption::VALUE_NONE, 'Use sqlite memory driver'],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Laravel IDE Helper Generator
|
||||
*
|
||||
* @author Barry vd. Heuvel <barryvdh@gmail.com>
|
||||
* @copyright 2015 Barry vd. Heuvel / Fruitcake Studio (http://www.fruitcakestudio.nl)
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||
* @link https://github.com/barryvdh/laravel-ide-helper
|
||||
*/
|
||||
|
||||
namespace Barryvdh\LaravelIdeHelper\Console;
|
||||
|
||||
use Barryvdh\LaravelIdeHelper\Factories;
|
||||
use Illuminate\Console\Command;
|
||||
use RuntimeException;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* A command to generate phpstorm meta data
|
||||
*
|
||||
* @author Barry vd. Heuvel <barryvdh@gmail.com>
|
||||
*/
|
||||
class MetaCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The console command name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'ide-helper:meta';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Generate metadata for PhpStorm';
|
||||
|
||||
/** @var \Illuminate\Contracts\Filesystem\Filesystem */
|
||||
protected $files;
|
||||
|
||||
/** @var \Illuminate\Contracts\View\Factory */
|
||||
protected $view;
|
||||
|
||||
/** @var \Illuminate\Contracts\Config\Repository */
|
||||
protected $config;
|
||||
|
||||
protected $methods = [
|
||||
'new \Illuminate\Contracts\Container\Container',
|
||||
'\Illuminate\Container\Container::makeWith(0)',
|
||||
'\Illuminate\Contracts\Container\Container::get(0)',
|
||||
'\Illuminate\Contracts\Container\Container::make(0)',
|
||||
'\Illuminate\Contracts\Container\Container::makeWith(0)',
|
||||
'\App::get(0)',
|
||||
'\App::make(0)',
|
||||
'\App::makeWith(0)',
|
||||
'\app(0)',
|
||||
'\resolve(0)',
|
||||
'\Psr\Container\ContainerInterface::get(0)',
|
||||
];
|
||||
|
||||
/**
|
||||
*
|
||||
* @param \Illuminate\Contracts\Filesystem\Filesystem $files
|
||||
* @param \Illuminate\Contracts\View\Factory $view
|
||||
* @param \Illuminate\Contracts\Config\Repository $config
|
||||
*/
|
||||
public function __construct($files, $view, $config)
|
||||
{
|
||||
$this->files = $files;
|
||||
$this->view = $view;
|
||||
$this->config = $config;
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
// Needs to run before exception handler is registered
|
||||
$factories = $this->config->get('ide-helper.include_factory_builders') ? Factories::all() : [];
|
||||
|
||||
$ourAutoloader = $this->registerClassAutoloadExceptions();
|
||||
|
||||
$bindings = [];
|
||||
foreach ($this->getAbstracts() as $abstract) {
|
||||
// Validator and seeder cause problems
|
||||
if (in_array($abstract, ['validator', 'seeder'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
$concrete = $this->laravel->make($abstract);
|
||||
|
||||
if ($concrete === null) {
|
||||
throw new RuntimeException("Cannot create instance for '$abstract', received 'null'");
|
||||
}
|
||||
|
||||
$reflectionClass = new \ReflectionClass($concrete);
|
||||
if (is_object($concrete) && !$reflectionClass->isAnonymous()) {
|
||||
$bindings[$abstract] = get_class($concrete);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
if ($this->output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE) {
|
||||
$this->comment("Cannot make '$abstract': " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->unregisterClassAutoloadExceptions($ourAutoloader);
|
||||
|
||||
$content = $this->view->make('meta', [
|
||||
'bindings' => $bindings,
|
||||
'methods' => $this->methods,
|
||||
'factories' => $factories,
|
||||
])->render();
|
||||
|
||||
$filename = $this->option('filename');
|
||||
$written = $this->files->put($filename, $content);
|
||||
|
||||
if ($written !== false) {
|
||||
$this->info("A new meta file was written to $filename");
|
||||
} else {
|
||||
$this->error("The meta file could not be created at $filename");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of abstracts from the Laravel Application.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getAbstracts()
|
||||
{
|
||||
$abstracts = $this->laravel->getBindings();
|
||||
|
||||
// Return the abstract names only
|
||||
$keys = array_keys($abstracts);
|
||||
|
||||
sort($keys);
|
||||
|
||||
return $keys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register an autoloader the throws exceptions when a class is not found.
|
||||
*
|
||||
* @return callable
|
||||
*/
|
||||
protected function registerClassAutoloadExceptions(): callable
|
||||
{
|
||||
$autoloader = function ($class) {
|
||||
throw new \ReflectionException("Class '$class' not found.");
|
||||
};
|
||||
spl_autoload_register($autoloader);
|
||||
return $autoloader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the console command options.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getOptions()
|
||||
{
|
||||
$filename = $this->config->get('ide-helper.meta_filename');
|
||||
|
||||
return [
|
||||
['filename', 'F', InputOption::VALUE_OPTIONAL, 'The path to the meta file', $filename],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove our custom autoloader that we pushed onto the autoload stack
|
||||
*
|
||||
* @param callable $ourAutoloader
|
||||
*/
|
||||
private function unregisterClassAutoloadExceptions(callable $ourAutoloader): void
|
||||
{
|
||||
spl_autoload_unregister($ourAutoloader);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace Barryvdh\LaravelIdeHelper\Contracts;
|
||||
|
||||
use Barryvdh\LaravelIdeHelper\Console\ModelsCommand;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
interface ModelHookInterface
|
||||
{
|
||||
public function run(ModelsCommand $command, Model $model): void;
|
||||
}
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Laravel IDE Helper to add \Eloquent mixin to Eloquent\Model
|
||||
*
|
||||
* @author Charles A. Peterson <artistan@gmail.com>
|
||||
*/
|
||||
|
||||
namespace Barryvdh\LaravelIdeHelper;
|
||||
|
||||
use Barryvdh\Reflection\DocBlock;
|
||||
use Barryvdh\Reflection\DocBlock\Context;
|
||||
use Barryvdh\Reflection\DocBlock\Serializer as DocBlockSerializer;
|
||||
use Barryvdh\Reflection\DocBlock\Tag;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Filesystem\Filesystem;
|
||||
|
||||
class Eloquent
|
||||
{
|
||||
/**
|
||||
* Write mixin helper to the Eloquent\Model
|
||||
* This is needed since laravel/framework v5.4.29
|
||||
*
|
||||
* @param Command $command
|
||||
* @param Filesystem $files
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function writeEloquentModelHelper(Command $command, Filesystem $files)
|
||||
{
|
||||
$class = 'Illuminate\Database\Eloquent\Model';
|
||||
|
||||
$reflection = new \ReflectionClass($class);
|
||||
$namespace = $reflection->getNamespaceName();
|
||||
$originalDoc = $reflection->getDocComment();
|
||||
|
||||
if (!$originalDoc) {
|
||||
$command->info('Unexpected no document on ' . $class);
|
||||
}
|
||||
$phpdoc = new DocBlock($reflection, new Context($namespace));
|
||||
|
||||
$mixins = $phpdoc->getTagsByName('mixin');
|
||||
$expectedMixins = [
|
||||
'\Eloquent' => false,
|
||||
'\Illuminate\Database\Eloquent\Builder' => false,
|
||||
'\Illuminate\Database\Query\Builder' => false,
|
||||
];
|
||||
|
||||
foreach ($mixins as $m) {
|
||||
$mixin = $m->getContent();
|
||||
|
||||
if (isset($expectedMixins[$mixin])) {
|
||||
$command->info('Tag Exists: @mixin ' . $mixin . ' in ' . $class);
|
||||
|
||||
$expectedMixins[$mixin] = true;
|
||||
}
|
||||
}
|
||||
|
||||
$changed = false;
|
||||
foreach ($expectedMixins as $expectedMixin => $present) {
|
||||
if ($present === false) {
|
||||
$phpdoc->appendTag(Tag::createInstance('@mixin ' . $expectedMixin, $phpdoc));
|
||||
|
||||
$changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
// If nothing's changed, stop here.
|
||||
if (!$changed) {
|
||||
return;
|
||||
}
|
||||
|
||||
$serializer = new DocBlockSerializer();
|
||||
$serializer->getDocComment($phpdoc);
|
||||
$docComment = $serializer->getDocComment($phpdoc);
|
||||
|
||||
/*
|
||||
The new DocBlock is appended to the beginning of the class declaration.
|
||||
Since there is no DocBlock, the declaration is used as a guide.
|
||||
*/
|
||||
if (!$originalDoc) {
|
||||
$originalDoc = 'abstract class Model implements';
|
||||
|
||||
$docComment .= "\nabstract class Model implements";
|
||||
}
|
||||
|
||||
$filename = $reflection->getFileName();
|
||||
if (!$filename) {
|
||||
$command->error('Filename not found ' . $class);
|
||||
return;
|
||||
}
|
||||
|
||||
$contents = $files->get($filename);
|
||||
if (!$contents) {
|
||||
$command->error('No file contents found ' . $filename);
|
||||
return;
|
||||
}
|
||||
|
||||
$count = 0;
|
||||
$contents = str_replace($originalDoc, $docComment, $contents, $count);
|
||||
if ($count <= 0) {
|
||||
$command->error('Content did not change ' . $contents);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$files->put($filename, $contents)) {
|
||||
$command->error('File write failed to ' . $filename);
|
||||
return;
|
||||
}
|
||||
|
||||
$command->info('Wrote expected docblock to ' . $filename);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace Barryvdh\LaravelIdeHelper;
|
||||
|
||||
use Exception;
|
||||
use Illuminate\Database\Eloquent\Factory;
|
||||
use ReflectionClass;
|
||||
|
||||
class Factories
|
||||
{
|
||||
public static function all()
|
||||
{
|
||||
$factories = [];
|
||||
|
||||
if (static::isLaravelSevenOrLower()) {
|
||||
$factory = app(Factory::class);
|
||||
|
||||
$definitions = (new ReflectionClass(Factory::class))->getProperty('definitions');
|
||||
$definitions->setAccessible(true);
|
||||
|
||||
foreach ($definitions->getValue($factory) as $factory_target => $config) {
|
||||
try {
|
||||
$factories[] = new ReflectionClass($factory_target);
|
||||
} catch (Exception $exception) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $factories;
|
||||
}
|
||||
|
||||
protected static function isLaravelSevenOrLower()
|
||||
{
|
||||
return class_exists('Illuminate\Database\Eloquent\Factory');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,362 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Laravel IDE Helper Generator
|
||||
*
|
||||
* @author Barry vd. Heuvel <barryvdh@gmail.com>
|
||||
* @copyright 2014 Barry vd. Heuvel / Fruitcake Studio (http://www.fruitcakestudio.nl)
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||
* @link https://github.com/barryvdh/laravel-ide-helper
|
||||
*/
|
||||
|
||||
namespace Barryvdh\LaravelIdeHelper;
|
||||
|
||||
use Illuminate\Foundation\AliasLoader;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Facade;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Support\Traits\Macroable;
|
||||
use PhpParser\Lexer\Emulative;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\Namespace_;
|
||||
use PhpParser\Parser\Php7;
|
||||
use ReflectionClass;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class Generator
|
||||
{
|
||||
/** @var \Illuminate\Config\Repository */
|
||||
protected $config;
|
||||
|
||||
/** @var \Illuminate\View\Factory */
|
||||
protected $view;
|
||||
|
||||
/** @var OutputInterface */
|
||||
protected $output;
|
||||
|
||||
protected $extra = [];
|
||||
protected $magic = [];
|
||||
protected $interfaces = [];
|
||||
protected $helpers;
|
||||
|
||||
/**
|
||||
* @param \Illuminate\Config\Repository $config
|
||||
* @param \Illuminate\View\Factory $view
|
||||
* @param OutputInterface $output
|
||||
* @param string $helpers
|
||||
*/
|
||||
public function __construct(
|
||||
/*ConfigRepository */
|
||||
$config,
|
||||
/* Illuminate\View\Factory */
|
||||
$view,
|
||||
OutputInterface $output = null,
|
||||
$helpers = ''
|
||||
) {
|
||||
$this->config = $config;
|
||||
$this->view = $view;
|
||||
|
||||
// Find the drivers to add to the extra/interfaces
|
||||
$this->detectDrivers();
|
||||
|
||||
$this->extra = array_merge($this->extra, $this->config->get('ide-helper.extra'), []);
|
||||
$this->magic = array_merge($this->magic, $this->config->get('ide-helper.magic'), []);
|
||||
$this->interfaces = array_merge($this->interfaces, $this->config->get('ide-helper.interfaces'), []);
|
||||
// Make all interface classes absolute
|
||||
foreach ($this->interfaces as &$interface) {
|
||||
$interface = '\\' . ltrim($interface, '\\');
|
||||
}
|
||||
$this->helpers = $helpers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the helper file contents;
|
||||
*
|
||||
* @return string;
|
||||
*/
|
||||
public function generate()
|
||||
{
|
||||
$app = app();
|
||||
return $this->view->make('helper')
|
||||
->with('namespaces_by_extends_ns', $this->getAliasesByExtendsNamespace())
|
||||
->with('namespaces_by_alias_ns', $this->getAliasesByAliasNamespace())
|
||||
->with('real_time_facades', $this->getRealTimeFacades())
|
||||
->with('helpers', $this->helpers)
|
||||
->with('version', $app->version())
|
||||
->with('include_fluent', $this->config->get('ide-helper.include_fluent', true))
|
||||
->with('factories', $this->config->get('ide-helper.include_factory_builders') ? Factories::all() : [])
|
||||
->render();
|
||||
}
|
||||
|
||||
protected function detectDrivers()
|
||||
{
|
||||
$defaultUserModel = config('auth.providers.users.model', config('auth.model', 'App\User'));
|
||||
$this->interfaces['\Illuminate\Contracts\Auth\Authenticatable'] = $defaultUserModel;
|
||||
|
||||
try {
|
||||
if (
|
||||
class_exists('Auth') && is_a('Auth', '\Illuminate\Support\Facades\Auth', true)
|
||||
&& app()->bound('auth')
|
||||
) {
|
||||
$class = get_class(\Auth::guard());
|
||||
$this->extra['Auth'] = [$class];
|
||||
$this->interfaces['\Illuminate\Auth\UserProviderInterface'] = $class;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
|
||||
try {
|
||||
if (class_exists('DB') && is_a('DB', '\Illuminate\Support\Facades\DB', true)) {
|
||||
$class = get_class(\DB::connection());
|
||||
$this->extra['DB'] = [$class];
|
||||
$this->interfaces['\Illuminate\Database\ConnectionInterface'] = $class;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
|
||||
try {
|
||||
if (class_exists('Cache') && is_a('Cache', '\Illuminate\Support\Facades\Cache', true)) {
|
||||
$driver = get_class(\Cache::driver());
|
||||
$store = get_class(\Cache::getStore());
|
||||
$this->extra['Cache'] = [$driver, $store];
|
||||
$this->interfaces['\Illuminate\Cache\StoreInterface'] = $store;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
|
||||
try {
|
||||
if (class_exists('Queue') && is_a('Queue', '\Illuminate\Support\Facades\Queue', true)) {
|
||||
$class = get_class(\Queue::connection());
|
||||
$this->extra['Queue'] = [$class];
|
||||
$this->interfaces['\Illuminate\Queue\QueueInterface'] = $class;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
|
||||
try {
|
||||
if (class_exists('Storage') && is_a('Storage', '\Illuminate\Support\Facades\Storage', true)) {
|
||||
$class = get_class(\Storage::disk());
|
||||
$this->extra['Storage'] = [$class];
|
||||
$this->interfaces['\Illuminate\Contracts\Filesystem\Filesystem'] = $class;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all aliases that are valid for us to render
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
protected function getValidAliases()
|
||||
{
|
||||
$aliases = new Collection();
|
||||
|
||||
// Get all aliases
|
||||
foreach ($this->getAliases() as $name => $facade) {
|
||||
// Skip the Redis facade, if not available (otherwise Fatal PHP Error)
|
||||
if ($facade == 'Illuminate\Support\Facades\Redis' && $name == 'Redis' && !class_exists('Predis\Client')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip the swoole
|
||||
if ($facade == 'SwooleTW\Http\Server\Facades\Server' && $name == 'Server' && !class_exists('Swoole\Http\Server')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$magicMethods = array_key_exists($name, $this->magic) ? $this->magic[$name] : [];
|
||||
$alias = new Alias($this->config, $name, $facade, $magicMethods, $this->interfaces);
|
||||
if ($alias->isValid()) {
|
||||
//Add extra methods, from other classes (magic static calls)
|
||||
if (array_key_exists($name, $this->extra)) {
|
||||
$alias->addClass($this->extra[$name]);
|
||||
}
|
||||
|
||||
$aliases[] = $alias;
|
||||
}
|
||||
}
|
||||
|
||||
return $aliases;
|
||||
}
|
||||
|
||||
protected function getRealTimeFacades()
|
||||
{
|
||||
$facades = [];
|
||||
$realTimeFacadeFiles = glob(storage_path('framework/cache/facade-*.php'));
|
||||
foreach ($realTimeFacadeFiles as $file) {
|
||||
try {
|
||||
$name = $this->getFullyQualifiedClassNameInFile($file);
|
||||
$facades[$name] = $name;
|
||||
} catch (\Exception $e) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return $facades;
|
||||
}
|
||||
|
||||
protected function getFullyQualifiedClassNameInFile(string $path)
|
||||
{
|
||||
$contents = file_get_contents($path);
|
||||
|
||||
$parsers = new Php7(new Emulative());
|
||||
|
||||
$parsed = collect($parsers->parse($contents) ?: []);
|
||||
|
||||
$namespace = $parsed->first(function ($node) {
|
||||
return $node instanceof Namespace_;
|
||||
});
|
||||
|
||||
if ($namespace) {
|
||||
$name = $namespace->name->toString();
|
||||
|
||||
$class = collect($namespace->stmts)->first(function ($node) {
|
||||
return $node instanceof Class_;
|
||||
});
|
||||
|
||||
if ($class) {
|
||||
$name .= '\\' . $class->name->toString();
|
||||
}
|
||||
|
||||
return $name;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Regroup aliases by namespace of extended classes
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
protected function getAliasesByExtendsNamespace()
|
||||
{
|
||||
$aliases = $this->getValidAliases()->filter(static function (Alias $alias) {
|
||||
return is_subclass_of($alias->getExtends(), Facade::class);
|
||||
});
|
||||
|
||||
$this->addMacroableClasses($aliases);
|
||||
|
||||
return $aliases->groupBy(function (Alias $alias) {
|
||||
return $alias->getExtendsNamespace();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Regroup aliases by namespace of alias
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
protected function getAliasesByAliasNamespace()
|
||||
{
|
||||
return $this->getValidAliases()->groupBy(function (Alias $alias) {
|
||||
return $alias->getNamespace();
|
||||
});
|
||||
}
|
||||
|
||||
protected function getAliases()
|
||||
{
|
||||
// For Laravel, use the AliasLoader
|
||||
if (class_exists('Illuminate\Foundation\AliasLoader')) {
|
||||
return AliasLoader::getInstance()->getAliases();
|
||||
}
|
||||
|
||||
$facades = [
|
||||
'App' => 'Illuminate\Support\Facades\App',
|
||||
'Auth' => 'Illuminate\Support\Facades\Auth',
|
||||
'Bus' => 'Illuminate\Support\Facades\Bus',
|
||||
'DB' => 'Illuminate\Support\Facades\DB',
|
||||
'Cache' => 'Illuminate\Support\Facades\Cache',
|
||||
'Cookie' => 'Illuminate\Support\Facades\Cookie',
|
||||
'Crypt' => 'Illuminate\Support\Facades\Crypt',
|
||||
'Event' => 'Illuminate\Support\Facades\Event',
|
||||
'Hash' => 'Illuminate\Support\Facades\Hash',
|
||||
'Log' => 'Illuminate\Support\Facades\Log',
|
||||
'Mail' => 'Illuminate\Support\Facades\Mail',
|
||||
'Queue' => 'Illuminate\Support\Facades\Queue',
|
||||
'Request' => 'Illuminate\Support\Facades\Request',
|
||||
'Schema' => 'Illuminate\Support\Facades\Schema',
|
||||
'Session' => 'Illuminate\Support\Facades\Session',
|
||||
'Storage' => 'Illuminate\Support\Facades\Storage',
|
||||
'Validator' => 'Illuminate\Support\Facades\Validator',
|
||||
'Gate' => 'Illuminate\Support\Facades\Gate',
|
||||
];
|
||||
|
||||
$facades = array_merge($facades, $this->config->get('app.aliases', []));
|
||||
|
||||
// Only return the ones that actually exist
|
||||
return array_filter(
|
||||
$facades,
|
||||
function ($alias) {
|
||||
return class_exists($alias);
|
||||
},
|
||||
ARRAY_FILTER_USE_KEY
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a string as error output.
|
||||
*
|
||||
* @param string $string
|
||||
* @return void
|
||||
*/
|
||||
protected function error($string)
|
||||
{
|
||||
if ($this->output) {
|
||||
$this->output->writeln("<error>$string</error>");
|
||||
} else {
|
||||
echo $string . "\r\n";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add all macroable classes which are not already loaded as an alias and have defined macros.
|
||||
*
|
||||
* @param Collection $aliases
|
||||
*/
|
||||
protected function addMacroableClasses(Collection $aliases)
|
||||
{
|
||||
$macroable = $this->getMacroableClasses($aliases);
|
||||
|
||||
foreach ($macroable as $class) {
|
||||
$reflection = new ReflectionClass($class);
|
||||
|
||||
if (!$reflection->getStaticProperties()['macros']) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$aliases[] = new Alias($this->config, $class, $class, [], $this->interfaces);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all loaded macroable classes which are not loaded as an alias.
|
||||
*
|
||||
* @param Collection $aliases
|
||||
* @return Collection
|
||||
*/
|
||||
protected function getMacroableClasses(Collection $aliases)
|
||||
{
|
||||
return (new Collection(get_declared_classes()))
|
||||
->filter(function ($class) {
|
||||
$reflection = new ReflectionClass($class);
|
||||
|
||||
// Filter out internal classes and class aliases
|
||||
return !$reflection->isInternal() && $reflection->getName() === $class;
|
||||
})
|
||||
->filter(function ($class) {
|
||||
$traits = class_uses_recursive($class);
|
||||
|
||||
// Filter only classes with the macroable trait
|
||||
return isset($traits[Macroable::class]);
|
||||
})
|
||||
->filter(function ($class) use ($aliases) {
|
||||
$class = Str::start($class, '\\');
|
||||
|
||||
// Filter out aliases
|
||||
return !$aliases->first(function (Alias $alias) use ($class) {
|
||||
return $alias->getExtends() === $class;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Laravel IDE Helper Generator
|
||||
*
|
||||
* @author Barry vd. Heuvel <barryvdh@gmail.com>
|
||||
* @copyright 2014 Barry vd. Heuvel / Fruitcake Studio (http://www.fruitcakestudio.nl)
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||
* @link https://github.com/barryvdh/laravel-ide-helper
|
||||
*/
|
||||
|
||||
namespace Barryvdh\LaravelIdeHelper;
|
||||
|
||||
use Barryvdh\LaravelIdeHelper\Console\EloquentCommand;
|
||||
use Barryvdh\LaravelIdeHelper\Console\GeneratorCommand;
|
||||
use Barryvdh\LaravelIdeHelper\Console\MetaCommand;
|
||||
use Barryvdh\LaravelIdeHelper\Console\ModelsCommand;
|
||||
use Barryvdh\LaravelIdeHelper\Listeners\GenerateModelHelper;
|
||||
use Illuminate\Console\Events\CommandFinished;
|
||||
use Illuminate\Contracts\Support\DeferrableProvider;
|
||||
use Illuminate\Database\Events\MigrationsEnded;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Illuminate\View\Engines\EngineResolver;
|
||||
use Illuminate\View\Engines\PhpEngine;
|
||||
use Illuminate\View\Factory;
|
||||
use Illuminate\View\FileViewFinder;
|
||||
|
||||
class IdeHelperServiceProvider extends ServiceProvider implements DeferrableProvider
|
||||
{
|
||||
/**
|
||||
* Bootstrap the application events.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
if (!$this->app->runningUnitTests() && $this->app['config']->get('ide-helper.post_migrate', [])) {
|
||||
$this->app['events']->listen(CommandFinished::class, GenerateModelHelper::class);
|
||||
$this->app['events']->listen(MigrationsEnded::class, function () {
|
||||
GenerateModelHelper::$shouldRun = true;
|
||||
});
|
||||
}
|
||||
|
||||
if ($this->app->has('view')) {
|
||||
$viewPath = __DIR__ . '/../resources/views';
|
||||
$this->loadViewsFrom($viewPath, 'ide-helper');
|
||||
}
|
||||
|
||||
$configPath = __DIR__ . '/../config/ide-helper.php';
|
||||
if (function_exists('config_path')) {
|
||||
$publishPath = config_path('ide-helper.php');
|
||||
} else {
|
||||
$publishPath = base_path('config/ide-helper.php');
|
||||
}
|
||||
$this->publishes([$configPath => $publishPath], 'config');
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the service provider.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
$configPath = __DIR__ . '/../config/ide-helper.php';
|
||||
$this->mergeConfigFrom($configPath, 'ide-helper');
|
||||
$localViewFactory = $this->createLocalViewFactory();
|
||||
|
||||
$this->app->singleton(
|
||||
'command.ide-helper.generate',
|
||||
function ($app) use ($localViewFactory) {
|
||||
return new GeneratorCommand($app['config'], $app['files'], $localViewFactory);
|
||||
}
|
||||
);
|
||||
|
||||
$this->app->singleton(
|
||||
'command.ide-helper.models',
|
||||
function ($app) {
|
||||
return new ModelsCommand($app['files']);
|
||||
}
|
||||
);
|
||||
|
||||
$this->app->singleton(
|
||||
'command.ide-helper.meta',
|
||||
function ($app) use ($localViewFactory) {
|
||||
return new MetaCommand($app['files'], $localViewFactory, $app['config']);
|
||||
}
|
||||
);
|
||||
|
||||
$this->app->singleton(
|
||||
'command.ide-helper.eloquent',
|
||||
function ($app) {
|
||||
return new EloquentCommand($app['files']);
|
||||
}
|
||||
);
|
||||
|
||||
$this->commands(
|
||||
'command.ide-helper.generate',
|
||||
'command.ide-helper.models',
|
||||
'command.ide-helper.meta',
|
||||
'command.ide-helper.eloquent'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the services provided by the provider.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function provides()
|
||||
{
|
||||
return ['command.ide-helper.generate', 'command.ide-helper.models'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Factory
|
||||
*/
|
||||
private function createLocalViewFactory()
|
||||
{
|
||||
$resolver = new EngineResolver();
|
||||
$resolver->register('php', function () {
|
||||
return new PhpEngine($this->app['files']);
|
||||
});
|
||||
$finder = new FileViewFinder($this->app['files'], [__DIR__ . '/../resources/views']);
|
||||
$factory = new Factory($resolver, $finder, $this->app['events']);
|
||||
$factory->addExtension('php', 'php');
|
||||
|
||||
return $factory;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace Barryvdh\LaravelIdeHelper\Listeners;
|
||||
|
||||
use Illuminate\Console\Events\CommandFinished;
|
||||
use Illuminate\Contracts\Config\Repository as Config;
|
||||
use Illuminate\Contracts\Console\Kernel as Artisan;
|
||||
|
||||
class GenerateModelHelper
|
||||
{
|
||||
/**
|
||||
* Tracks whether we should run the models command on the CommandFinished event or not.
|
||||
* Set to true by the MigrationsEnded event, needs to be cleared before artisan call to prevent infinite loop.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public static $shouldRun = false;
|
||||
|
||||
/** @var Artisan */
|
||||
protected $artisan;
|
||||
|
||||
/** @var Config */
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
* @param Artisan $artisan
|
||||
* @param Config $config
|
||||
*/
|
||||
public function __construct(Artisan $artisan, Config $config)
|
||||
{
|
||||
$this->artisan = $artisan;
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the event.
|
||||
*
|
||||
* @param CommandFinished $event
|
||||
*/
|
||||
public function handle(CommandFinished $event)
|
||||
{
|
||||
if (!self::$shouldRun) {
|
||||
return;
|
||||
}
|
||||
|
||||
self::$shouldRun = false;
|
||||
|
||||
foreach ($this->config->get('ide-helper.post_migrate', []) as $command) {
|
||||
$this->artisan->call($command, [], $event->output);
|
||||
}
|
||||
}
|
||||
}
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
<?php
|
||||
|
||||
namespace Barryvdh\LaravelIdeHelper;
|
||||
|
||||
use Barryvdh\Reflection\DocBlock;
|
||||
use Barryvdh\Reflection\DocBlock\Tag;
|
||||
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
class Macro extends Method
|
||||
{
|
||||
/**
|
||||
* Macro constructor.
|
||||
*
|
||||
* @param \ReflectionFunctionAbstract $method
|
||||
* @param string $alias
|
||||
* @param \ReflectionClass $class
|
||||
* @param null $methodName
|
||||
* @param array $interfaces
|
||||
* @param array $classAliases
|
||||
*/
|
||||
public function __construct(
|
||||
$method,
|
||||
$alias,
|
||||
$class,
|
||||
$methodName = null,
|
||||
$interfaces = [],
|
||||
$classAliases = []
|
||||
) {
|
||||
parent::__construct($method, $alias, $class, $methodName, $interfaces, $classAliases);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \ReflectionFunctionAbstract $method
|
||||
*/
|
||||
protected function initPhpDoc($method)
|
||||
{
|
||||
$this->phpdoc = new DocBlock($method);
|
||||
|
||||
$this->addLocationToPhpDoc();
|
||||
|
||||
// Add macro parameters if they are missed in original docblock
|
||||
if (!$this->phpdoc->hasTag('param')) {
|
||||
foreach ($method->getParameters() as $parameter) {
|
||||
$reflectionType = $parameter->getType();
|
||||
|
||||
$type = $this->concatReflectionTypes($reflectionType);
|
||||
|
||||
/** @psalm-suppress UndefinedClass */
|
||||
if ($reflectionType && !$reflectionType instanceof \ReflectionUnionType && $reflectionType->allowsNull()) {
|
||||
$type .= '|null';
|
||||
}
|
||||
|
||||
$type = $type ?: 'mixed';
|
||||
|
||||
$name = $parameter->isVariadic() ? '...' : '';
|
||||
$name .= '$' . $parameter->getName();
|
||||
|
||||
$this->phpdoc->appendTag(Tag::createInstance("@param {$type} {$name}"));
|
||||
}
|
||||
}
|
||||
|
||||
// Add macro return type if it missed in original docblock
|
||||
if ($method->hasReturnType() && !$this->phpdoc->hasTag('return')) {
|
||||
$builder = EloquentBuilder::class;
|
||||
$return = $method->getReturnType();
|
||||
|
||||
$type = $this->concatReflectionTypes($return);
|
||||
|
||||
/** @psalm-suppress UndefinedClass */
|
||||
if (!$return instanceof \ReflectionUnionType) {
|
||||
$type .= $this->root === "\\{$builder}" && $return->getName() === $builder ? '|static' : '';
|
||||
$type .= $return->allowsNull() ? '|null' : '';
|
||||
}
|
||||
|
||||
$this->phpdoc->appendTag(Tag::createInstance("@return {$type}"));
|
||||
}
|
||||
}
|
||||
|
||||
protected function concatReflectionTypes(?\ReflectionType $type): string
|
||||
{
|
||||
/** @psalm-suppress UndefinedClass */
|
||||
$returnTypes = $type instanceof \ReflectionUnionType
|
||||
? $type->getTypes()
|
||||
: [$type];
|
||||
|
||||
return Collection::make($returnTypes)
|
||||
->filter()
|
||||
->map->getName()
|
||||
->implode('|');
|
||||
}
|
||||
|
||||
protected function addLocationToPhpDoc()
|
||||
{
|
||||
if ($this->method->name === '__invoke') {
|
||||
$enclosingClass = $this->method->getDeclaringClass();
|
||||
} else {
|
||||
$enclosingClass = $this->method->getClosureScopeClass();
|
||||
}
|
||||
|
||||
if (!$enclosingClass) {
|
||||
return;
|
||||
}
|
||||
/** @var \ReflectionMethod $enclosingMethod */
|
||||
$enclosingMethod = Collection::make($enclosingClass->getMethods())
|
||||
->first(function (\ReflectionMethod $method) {
|
||||
return $method->getStartLine() <= $this->method->getStartLine()
|
||||
&& $method->getEndLine() >= $this->method->getEndLine();
|
||||
});
|
||||
|
||||
if ($enclosingMethod) {
|
||||
$this->phpdoc->appendTag(Tag::createInstance(
|
||||
'@see \\' . $enclosingClass->getName() . '::' . $enclosingMethod->getName() . '()'
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \ReflectionFunctionAbstract $method
|
||||
* @param \ReflectionClass $class
|
||||
*/
|
||||
protected function initClassDefinedProperties($method, \ReflectionClass $class)
|
||||
{
|
||||
$this->namespace = $class->getNamespaceName();
|
||||
$this->declaringClassName = '\\' . ltrim($class->name, '\\');
|
||||
}
|
||||
}
|
||||
+379
@@ -0,0 +1,379 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Laravel IDE Helper Generator
|
||||
*
|
||||
* @author Barry vd. Heuvel <barryvdh@gmail.com>
|
||||
* @copyright 2014 Barry vd. Heuvel / Fruitcake Studio (http://www.fruitcakestudio.nl)
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||
* @link https://github.com/barryvdh/laravel-ide-helper
|
||||
*/
|
||||
|
||||
namespace Barryvdh\LaravelIdeHelper;
|
||||
|
||||
use Barryvdh\Reflection\DocBlock;
|
||||
use Barryvdh\Reflection\DocBlock\Context;
|
||||
use Barryvdh\Reflection\DocBlock\Serializer as DocBlockSerializer;
|
||||
use Barryvdh\Reflection\DocBlock\Tag;
|
||||
use Barryvdh\Reflection\DocBlock\Tag\ParamTag;
|
||||
use Barryvdh\Reflection\DocBlock\Tag\ReturnTag;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class Method
|
||||
{
|
||||
/** @var DocBlock */
|
||||
protected $phpdoc;
|
||||
|
||||
/** @var \ReflectionMethod */
|
||||
protected $method;
|
||||
|
||||
protected $output = '';
|
||||
protected $declaringClassName;
|
||||
protected $name;
|
||||
protected $namespace;
|
||||
protected $params = [];
|
||||
protected $params_with_default = [];
|
||||
protected $interfaces = [];
|
||||
protected $real_name;
|
||||
protected $return = null;
|
||||
protected $root;
|
||||
protected $classAliases;
|
||||
|
||||
/**
|
||||
* @param \ReflectionMethod|\ReflectionFunctionAbstract $method
|
||||
* @param string $alias
|
||||
* @param \ReflectionClass $class
|
||||
* @param string|null $methodName
|
||||
* @param array $interfaces
|
||||
* @param array $classAliases
|
||||
*/
|
||||
public function __construct($method, $alias, $class, $methodName = null, $interfaces = [], array $classAliases = [])
|
||||
{
|
||||
$this->method = $method;
|
||||
$this->interfaces = $interfaces;
|
||||
$this->classAliases = $classAliases;
|
||||
$this->name = $methodName ?: $method->name;
|
||||
$this->real_name = $method->isClosure() ? $this->name : $method->name;
|
||||
$this->initClassDefinedProperties($method, $class);
|
||||
|
||||
//Reference the 'real' function in the declaring class
|
||||
$this->root = '\\' . ltrim($method->name === '__invoke' ? $method->getDeclaringClass()->getName() : $class->getName(), '\\');
|
||||
|
||||
//Create a DocBlock and serializer instance
|
||||
$this->initPhpDoc($method);
|
||||
|
||||
//Normalize the description and inherit the docs from parents/interfaces
|
||||
try {
|
||||
$this->normalizeParams($this->phpdoc);
|
||||
$this->normalizeReturn($this->phpdoc);
|
||||
$this->normalizeDescription($this->phpdoc);
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
|
||||
//Get the parameters, including formatted default values
|
||||
$this->getParameters($method);
|
||||
|
||||
//Make the method static
|
||||
$this->phpdoc->appendTag(Tag::createInstance('@static', $this->phpdoc));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \ReflectionMethod $method
|
||||
*/
|
||||
protected function initPhpDoc($method)
|
||||
{
|
||||
$this->phpdoc = new DocBlock($method, new Context($this->namespace, $this->classAliases));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \ReflectionMethod $method
|
||||
* @param \ReflectionClass $class
|
||||
*/
|
||||
protected function initClassDefinedProperties($method, \ReflectionClass $class)
|
||||
{
|
||||
$declaringClass = $method->getDeclaringClass();
|
||||
$this->namespace = $declaringClass->getNamespaceName();
|
||||
$this->declaringClassName = '\\' . ltrim($declaringClass->name, '\\');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the class wherein the function resides
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDeclaringClass()
|
||||
{
|
||||
return $this->declaringClassName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the class from which this function would be called
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getRoot()
|
||||
{
|
||||
return $this->root;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isInstanceCall()
|
||||
{
|
||||
return !($this->method->isClosure() || $this->method->isStatic());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getRootMethodCall()
|
||||
{
|
||||
if ($this->isInstanceCall()) {
|
||||
return "\$instance->{$this->getRealName()}({$this->getParams()})";
|
||||
} else {
|
||||
return "{$this->getRoot()}::{$this->getRealName()}({$this->getParams()})";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the docblock for this method
|
||||
*
|
||||
* @param string $prefix
|
||||
* @return mixed
|
||||
*/
|
||||
public function getDocComment($prefix = "\t\t")
|
||||
{
|
||||
$serializer = new DocBlockSerializer(1, $prefix);
|
||||
return $serializer->getDocComment($this->phpdoc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the method name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the real method name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getRealName()
|
||||
{
|
||||
return $this->real_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the parameters for this method
|
||||
*
|
||||
* @param bool $implode Wether to implode the array or not
|
||||
* @return string
|
||||
*/
|
||||
public function getParams($implode = true)
|
||||
{
|
||||
return $implode ? implode(', ', $this->params) : $this->params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the parameters for this method including default values
|
||||
*
|
||||
* @param bool $implode Wether to implode the array or not
|
||||
* @return string
|
||||
*/
|
||||
public function getParamsWithDefault($implode = true)
|
||||
{
|
||||
return $implode ? implode(', ', $this->params_with_default) : $this->params_with_default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the description and get the inherited docs.
|
||||
*
|
||||
* @param DocBlock $phpdoc
|
||||
*/
|
||||
protected function normalizeDescription(DocBlock $phpdoc)
|
||||
{
|
||||
//Get the short + long description from the DocBlock
|
||||
$description = $phpdoc->getText();
|
||||
|
||||
//Loop through parents/interfaces, to fill in {@inheritdoc}
|
||||
if (strpos($description, '{@inheritdoc}') !== false) {
|
||||
$inheritdoc = $this->getInheritDoc($this->method);
|
||||
$inheritDescription = $inheritdoc->getText();
|
||||
|
||||
$description = str_replace('{@inheritdoc}', $inheritDescription, $description);
|
||||
$phpdoc->setText($description);
|
||||
|
||||
$this->normalizeParams($inheritdoc);
|
||||
$this->normalizeReturn($inheritdoc);
|
||||
|
||||
//Add the tags that are inherited
|
||||
$inheritTags = $inheritdoc->getTags();
|
||||
if ($inheritTags) {
|
||||
/** @var Tag $tag */
|
||||
foreach ($inheritTags as $tag) {
|
||||
$tag->setDocBlock();
|
||||
$phpdoc->appendTag($tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize the parameters
|
||||
*
|
||||
* @param DocBlock $phpdoc
|
||||
*/
|
||||
protected function normalizeParams(DocBlock $phpdoc)
|
||||
{
|
||||
//Get the return type and adjust them for beter autocomplete
|
||||
$paramTags = $phpdoc->getTagsByName('param');
|
||||
if ($paramTags) {
|
||||
/** @var ParamTag $tag */
|
||||
foreach ($paramTags as $tag) {
|
||||
// Convert the keywords
|
||||
$content = $this->convertKeywords($tag->getContent());
|
||||
$tag->setContent($content);
|
||||
|
||||
// Get the expanded type and re-set the content
|
||||
$content = $tag->getType() . ' ' . $tag->getVariableName() . ' ' . $tag->getDescription();
|
||||
$tag->setContent(trim($content));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize the return tag (make full namespace, replace interfaces)
|
||||
*
|
||||
* @param DocBlock $phpdoc
|
||||
*/
|
||||
protected function normalizeReturn(DocBlock $phpdoc)
|
||||
{
|
||||
//Get the return type and adjust them for better autocomplete
|
||||
$returnTags = $phpdoc->getTagsByName('return');
|
||||
|
||||
if (count($returnTags) === 0) {
|
||||
$this->return = null;
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var ReturnTag $tag */
|
||||
$tag = reset($returnTags);
|
||||
// Get the expanded type
|
||||
$returnValue = $tag->getType();
|
||||
|
||||
// Replace the interfaces
|
||||
foreach ($this->interfaces as $interface => $real) {
|
||||
$returnValue = str_replace($interface, $real, $returnValue);
|
||||
}
|
||||
|
||||
// Set the changed content
|
||||
$tag->setContent($returnValue . ' ' . $tag->getDescription());
|
||||
$this->return = $returnValue;
|
||||
|
||||
if ($tag->getType() === '$this') {
|
||||
Str::contains($this->root, Builder::class)
|
||||
? $tag->setType($this->root . '|static')
|
||||
: $tag->setType($this->root);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert keywords that are incorrect.
|
||||
*
|
||||
* @param string $string
|
||||
* @return string
|
||||
*/
|
||||
protected function convertKeywords($string)
|
||||
{
|
||||
$string = str_replace('\Closure', 'Closure', $string);
|
||||
$string = str_replace('Closure', '\Closure', $string);
|
||||
$string = str_replace('dynamic', 'mixed', $string);
|
||||
|
||||
return $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should the function return a value?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function shouldReturn()
|
||||
{
|
||||
if ($this->return !== 'void' && $this->method->name !== '__construct') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the parameters and format them correctly
|
||||
*
|
||||
* @param \ReflectionMethod $method
|
||||
* @return void
|
||||
*/
|
||||
public function getParameters($method)
|
||||
{
|
||||
//Loop through the default values for parameters, and make the correct output string
|
||||
$params = [];
|
||||
$paramsWithDefault = [];
|
||||
foreach ($method->getParameters() as $param) {
|
||||
$paramStr = $param->isVariadic() ? '...$' . $param->getName() : '$' . $param->getName();
|
||||
$params[] = $paramStr;
|
||||
if ($param->isOptional() && !$param->isVariadic()) {
|
||||
$default = $param->isDefaultValueAvailable() ? $param->getDefaultValue() : null;
|
||||
if (is_bool($default)) {
|
||||
$default = $default ? 'true' : 'false';
|
||||
} elseif (is_array($default)) {
|
||||
$default = '[]';
|
||||
} elseif (is_null($default)) {
|
||||
$default = 'null';
|
||||
} elseif (is_int($default)) {
|
||||
//$default = $default;
|
||||
} elseif (is_resource($default)) {
|
||||
//skip to not fail
|
||||
} else {
|
||||
$default = var_export($default, true);
|
||||
}
|
||||
$paramStr .= " = $default";
|
||||
}
|
||||
$paramsWithDefault[] = $paramStr;
|
||||
}
|
||||
|
||||
$this->params = $params;
|
||||
$this->params_with_default = $paramsWithDefault;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \ReflectionMethod $reflectionMethod
|
||||
* @return DocBlock
|
||||
*/
|
||||
protected function getInheritDoc($reflectionMethod)
|
||||
{
|
||||
$parentClass = $reflectionMethod->getDeclaringClass()->getParentClass();
|
||||
|
||||
//Get either a parent or the interface
|
||||
if ($parentClass) {
|
||||
$method = $parentClass->getMethod($reflectionMethod->getName());
|
||||
} else {
|
||||
$method = $reflectionMethod->getPrototype();
|
||||
}
|
||||
if ($method) {
|
||||
$namespace = $method->getDeclaringClass()->getNamespaceName();
|
||||
$phpdoc = new DocBlock($method, new Context($namespace, $this->classAliases));
|
||||
|
||||
if (strpos($phpdoc->getText(), '{@inheritdoc}') !== false) {
|
||||
//Not at the end yet, try another parent/interface..
|
||||
return $this->getInheritDoc($method);
|
||||
}
|
||||
|
||||
return $phpdoc;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Barryvdh\LaravelIdeHelper\Parsers;
|
||||
|
||||
class PhpDocReturnTypeParser
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private string $typeAlias;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private array $namespaceAliases;
|
||||
|
||||
/**
|
||||
* @param string $typeAlias
|
||||
* @param array $namespaceAliases
|
||||
*/
|
||||
public function __construct(string $typeAlias, array $namespaceAliases)
|
||||
{
|
||||
$this->typeAlias = $typeAlias;
|
||||
$this->namespaceAliases = $namespaceAliases;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function parse(): string|null
|
||||
{
|
||||
$matches = [];
|
||||
preg_match('/(\w+)(<.*>)/', $this->typeAlias, $matches);
|
||||
$matchCount = count($matches);
|
||||
|
||||
if ($matchCount === 0 || $matchCount === 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (empty($this->namespaceAliases[$matches[1]])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->namespaceAliases[$matches[1]] . $this->parseTemplate($matches[2] ?? null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $template
|
||||
* @return string
|
||||
*/
|
||||
private function parseTemplate(string|null $template): string
|
||||
{
|
||||
if ($template === null || $template === '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
$type = '';
|
||||
$result = '';
|
||||
|
||||
foreach (str_split($template) as $char) {
|
||||
$match = preg_match('/[A-z]/', $char);
|
||||
|
||||
if (!$match) {
|
||||
$type = $this->namespaceAliases[$type] ?? $type;
|
||||
$result .= $type;
|
||||
$result .= $char;
|
||||
$type = '';
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$type .= $char;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Laravel IDE Helper Generator
|
||||
*
|
||||
* @author Barry vd. Heuvel <barryvdh@gmail.com>
|
||||
* @copyright 2014 Barry vd. Heuvel / Fruitcake Studio (http://www.fruitcakestudio.nl)
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||
* @link https://github.com/barryvdh/laravel-ide-helper
|
||||
*/
|
||||
|
||||
namespace Barryvdh\LaravelIdeHelper;
|
||||
|
||||
use PhpParser\Node\Stmt\GroupUse;
|
||||
use PhpParser\Node\Stmt\Namespace_;
|
||||
use PhpParser\Node\Stmt\Use_;
|
||||
use PhpParser\Node\Stmt\UseUse;
|
||||
use PhpParser\ParserFactory;
|
||||
|
||||
class UsesResolver
|
||||
{
|
||||
/**
|
||||
* @param string $classFQN
|
||||
* @return array
|
||||
*/
|
||||
public function loadFromClass(string $classFQN): array
|
||||
{
|
||||
return $this->loadFromFile(
|
||||
$classFQN,
|
||||
(new \ReflectionClass($classFQN))->getFileName()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $classFQN
|
||||
* @param string $filename
|
||||
* @return array
|
||||
*/
|
||||
public function loadFromFile(string $classFQN, string $filename): array
|
||||
{
|
||||
return $this->loadFromCode(
|
||||
$classFQN,
|
||||
file_get_contents(
|
||||
$filename
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $classFQN
|
||||
* @param string $code
|
||||
* @return array
|
||||
*/
|
||||
public function loadFromCode(string $classFQN, string $code): array
|
||||
{
|
||||
$classFQN = ltrim($classFQN, '\\');
|
||||
|
||||
$namespace = rtrim(
|
||||
preg_replace(
|
||||
'/([^\\\\]+)$/',
|
||||
'',
|
||||
$classFQN
|
||||
),
|
||||
'\\'
|
||||
);
|
||||
|
||||
$parser = (new ParserFactory())->createForHostVersion();
|
||||
$namespaceData = null;
|
||||
|
||||
foreach ($parser->parse($code) as $node) {
|
||||
if ($node instanceof Namespace_ && $node->name->toCodeString() === $namespace) {
|
||||
$namespaceData = $node;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($namespaceData === null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
/** @var Namespace_ $namespaceData */
|
||||
|
||||
$aliases = [];
|
||||
|
||||
foreach ($namespaceData->stmts as $stmt) {
|
||||
if ($stmt instanceof Use_) {
|
||||
if ($stmt->type !== Use_::TYPE_NORMAL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($stmt->uses as $use) {
|
||||
/** @var UseUse $use */
|
||||
|
||||
$alias = $use->alias ?
|
||||
$use->alias->name :
|
||||
self::classBasename($use->name->toCodeString());
|
||||
|
||||
$aliases[$alias] = '\\' . $use->name->toCodeString();
|
||||
}
|
||||
} elseif ($stmt instanceof GroupUse) {
|
||||
foreach ($stmt->uses as $use) {
|
||||
/** @var UseUse $use */
|
||||
|
||||
$alias = $use->alias ?
|
||||
$use->alias->name :
|
||||
self::classBasename($use->name->toCodeString());
|
||||
|
||||
$aliases[$alias] = '\\' . $stmt->prefix->toCodeString() . '\\' . $use->name->toCodeString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $aliases;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $classFQN
|
||||
* @return string
|
||||
*/
|
||||
protected static function classBasename(string $classFQN): string
|
||||
{
|
||||
return preg_replace('/^.*\\\\([^\\\\]+)$/', '$1', $classFQN);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
name: Unit Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- "*"
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
|
||||
jobs:
|
||||
php-tests:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
env:
|
||||
COMPOSER_NO_INTERACTION: 1
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
php: [8.1, 8.0, 7.4, 7.3, 7.2]
|
||||
dependency-version: [prefer-lowest, prefer-stable]
|
||||
|
||||
name: P${{ matrix.php }} - ${{ matrix.dependency-version }}
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php }}
|
||||
coverage: none
|
||||
tools: composer:v2
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
composer update --${{ matrix.dependency-version }} --prefer-dist --no-progress
|
||||
|
||||
- name: Execute Unit Tests
|
||||
run: vendor/bin/phpunit
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2010 Mike van Riel
|
||||
|
||||
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.
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
The ReflectionDocBlock Component [](https://travis-ci.org/phpDocumentor/ReflectionDocBlock)
|
||||
================================
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
The ReflectionDocBlock component of phpDocumentor provides a DocBlock parser
|
||||
that is 100% compatible with the [PHPDoc standard](http://phpdoc.org/docs/latest).
|
||||
|
||||
With this component, a library can provide support for annotations via DocBlocks
|
||||
or otherwise retrieve information that is embedded in a DocBlock.
|
||||
|
||||
> **Note**: *this is a core component of phpDocumentor and is constantly being
|
||||
> optimized for performance.*
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
You can install the component in the following ways:
|
||||
|
||||
* Use the official Github repository (https://github.com/phpDocumentor/ReflectionDocBlock)
|
||||
* Via Composer (http://packagist.org/packages/phpdocumentor/reflection-docblock)
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
The ReflectionDocBlock component is designed to work in an identical fashion to
|
||||
PHP's own Reflection extension (http://php.net/manual/en/book.reflection.php).
|
||||
|
||||
Parsing can be initiated by instantiating the
|
||||
`\phpDocumentor\Reflection\DocBlock()` class and passing it a string containing
|
||||
a DocBlock (including asterisks) or by passing an object supporting the
|
||||
`getDocComment()` method.
|
||||
|
||||
> *Examples of objects having the `getDocComment()` method are the
|
||||
> `ReflectionClass` and the `ReflectionMethod` classes of the PHP
|
||||
> Reflection extension*
|
||||
|
||||
Example:
|
||||
|
||||
$class = new ReflectionClass('MyClass');
|
||||
$phpdoc = new \phpDocumentor\Reflection\DocBlock($class);
|
||||
|
||||
or
|
||||
|
||||
$docblock = <<<DOCBLOCK
|
||||
/**
|
||||
* This is a short description.
|
||||
*
|
||||
* This is a *long* description.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
DOCBLOCK;
|
||||
|
||||
$phpdoc = new \phpDocumentor\Reflection\DocBlock($docblock);
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"name": "barryvdh/reflection-docblock",
|
||||
"type": "library",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{"name": "Mike van Riel", "email": "mike.vanriel@naenius.com"}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.3.3"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-0": {"Barryvdh": ["src/"]}
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^8.5.14|^9"
|
||||
},
|
||||
"suggest": {
|
||||
"dflydev/markdown": "~1.0",
|
||||
"erusev/parsedown": "~1.0"
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.0.x-dev"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,485 @@
|
||||
<?php
|
||||
/**
|
||||
* phpDocumentor
|
||||
*
|
||||
* PHP Version 5.3
|
||||
*
|
||||
* @author Mike van Riel <mike.vanriel@naenius.com>
|
||||
* @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||
* @link http://phpdoc.org
|
||||
*/
|
||||
|
||||
namespace Barryvdh\Reflection;
|
||||
|
||||
use Barryvdh\Reflection\DocBlock\Tag;
|
||||
use Barryvdh\Reflection\DocBlock\Context;
|
||||
use Barryvdh\Reflection\DocBlock\Location;
|
||||
|
||||
/**
|
||||
* Parses the DocBlock for any structure.
|
||||
*
|
||||
* @author Mike van Riel <mike.vanriel@naenius.com>
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||
* @link http://phpdoc.org
|
||||
*/
|
||||
class DocBlock implements \Reflector
|
||||
{
|
||||
/** @var string The opening line for this docblock. */
|
||||
protected $short_description = '';
|
||||
|
||||
/**
|
||||
* @var DocBlock\Description The actual
|
||||
* description for this docblock.
|
||||
*/
|
||||
protected $long_description = null;
|
||||
|
||||
/**
|
||||
* @var Tag[] An array containing all
|
||||
* the tags in this docblock; except inline.
|
||||
*/
|
||||
protected $tags = array();
|
||||
|
||||
/** @var Context Information about the context of this DocBlock. */
|
||||
protected $context = null;
|
||||
|
||||
/** @var Location Information about the location of this DocBlock. */
|
||||
protected $location = null;
|
||||
|
||||
/** @var bool Is this DocBlock (the start of) a template? */
|
||||
protected $isTemplateStart = false;
|
||||
|
||||
/** @var bool Does this DocBlock signify the end of a DocBlock template? */
|
||||
protected $isTemplateEnd = false;
|
||||
|
||||
/**
|
||||
* Parses the given docblock and populates the member fields.
|
||||
*
|
||||
* The constructor may also receive namespace information such as the
|
||||
* current namespace and aliases. This information is used by some tags
|
||||
* (e.g. @return, @param, etc.) to turn a relative Type into a FQCN.
|
||||
*
|
||||
* @param \Reflector|string $docblock A docblock comment (including
|
||||
* asterisks) or reflector supporting the getDocComment method.
|
||||
* @param Context $context The context in which the DocBlock
|
||||
* occurs.
|
||||
* @param Location $location The location within the file that this
|
||||
* DocBlock occurs in.
|
||||
*
|
||||
* @throws \InvalidArgumentException if the given argument does not have the
|
||||
* getDocComment method.
|
||||
*/
|
||||
public function __construct(
|
||||
$docblock,
|
||||
Context $context = null,
|
||||
Location $location = null
|
||||
) {
|
||||
if (is_object($docblock)) {
|
||||
if (!method_exists($docblock, 'getDocComment')) {
|
||||
throw new \InvalidArgumentException(
|
||||
'Invalid object passed; the given reflector must support '
|
||||
. 'the getDocComment method'
|
||||
);
|
||||
}
|
||||
|
||||
$docblock = $docblock->getDocComment();
|
||||
}
|
||||
|
||||
$docblock = $this->cleanInput($docblock);
|
||||
|
||||
list($templateMarker, $short, $long, $tags) = $this->splitDocBlock($docblock);
|
||||
$this->isTemplateStart = $templateMarker === '#@+';
|
||||
$this->isTemplateEnd = $templateMarker === '#@-';
|
||||
$this->short_description = $short;
|
||||
$this->long_description = new DocBlock\Description($long, $this);
|
||||
$this->parseTags($tags);
|
||||
|
||||
$this->context = $context;
|
||||
$this->location = $location;
|
||||
}
|
||||
|
||||
/**
|
||||
* Strips the asterisks from the DocBlock comment.
|
||||
*
|
||||
* @param string $comment String containing the comment text.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function cleanInput($comment)
|
||||
{
|
||||
$comment = trim(
|
||||
preg_replace(
|
||||
'#[ \t]*(?:\/\*\*|\*\/|\*)?[ \t]{0,1}(.*)?#u',
|
||||
'$1',
|
||||
$comment
|
||||
)
|
||||
);
|
||||
|
||||
// reg ex above is not able to remove */ from a single line docblock
|
||||
if (substr($comment, -2) == '*/') {
|
||||
$comment = trim(substr($comment, 0, -2));
|
||||
}
|
||||
|
||||
// normalize strings
|
||||
$comment = str_replace(array("\r\n", "\r"), "\n", $comment);
|
||||
|
||||
return $comment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits the DocBlock into a template marker, summary, description and block of tags.
|
||||
*
|
||||
* @param string $comment Comment to split into the sub-parts.
|
||||
*
|
||||
* @author Richard van Velzen (@_richardJ) Special thanks to Richard for the regex responsible for the split.
|
||||
* @author Mike van Riel <me@mikevanriel.com> for extending the regex with template marker support.
|
||||
*
|
||||
* @return string[] containing the template marker (if any), summary, description and a string containing the tags.
|
||||
*/
|
||||
protected function splitDocBlock($comment)
|
||||
{
|
||||
// Performance improvement cheat: if the first character is an @ then only tags are in this DocBlock. This
|
||||
// method does not split tags so we return this verbatim as the fourth result (tags). This saves us the
|
||||
// performance impact of running a regular expression
|
||||
if (strpos($comment, '@') === 0) {
|
||||
return array('', '', '', $comment);
|
||||
}
|
||||
|
||||
// clears all extra horizontal whitespace from the line endings to prevent parsing issues
|
||||
$comment = preg_replace('/\h*$/Sum', '', $comment);
|
||||
|
||||
/*
|
||||
* Splits the docblock into a template marker, short description, long description and tags section
|
||||
*
|
||||
* - The template marker is empty, #@+ or #@- if the DocBlock starts with either of those (a newline may
|
||||
* occur after it and will be stripped).
|
||||
* - The short description is started from the first character until a dot is encountered followed by a
|
||||
* newline OR two consecutive newlines (horizontal whitespace is taken into account to consider spacing
|
||||
* errors). This is optional.
|
||||
* - The long description, any character until a new line is encountered followed by an @ and word
|
||||
* characters (a tag). This is optional.
|
||||
* - Tags; the remaining characters
|
||||
*
|
||||
* Big thanks to RichardJ for contributing this Regular Expression
|
||||
*/
|
||||
preg_match(
|
||||
'/
|
||||
\A
|
||||
# 1. Extract the template marker
|
||||
(?:(\#\@\+|\#\@\-)\n?)?
|
||||
|
||||
# 2. Extract the summary
|
||||
(?:
|
||||
(?! @\pL ) # The summary may not start with an @
|
||||
(
|
||||
[^\n.]+
|
||||
(?:
|
||||
(?! \. \n | \n{2} ) # End summary upon a dot followed by newline or two newlines
|
||||
[\n.] (?! [ \t]* @\pL ) # End summary when an @ is found as first character on a new line
|
||||
[^\n.]+ # Include anything else
|
||||
)*
|
||||
\.?
|
||||
)?
|
||||
)
|
||||
|
||||
# 3. Extract the description
|
||||
(?:
|
||||
\s* # Some form of whitespace _must_ precede a description because a summary must be there
|
||||
(?! @\pL ) # The description may not start with an @
|
||||
(
|
||||
[^\n]+
|
||||
(?: \n+
|
||||
(?! [ \t]* @\pL ) # End description when an @ is found as first character on a new line
|
||||
[^\n]+ # Include anything else
|
||||
)*
|
||||
)
|
||||
)?
|
||||
|
||||
# 4. Extract the tags (anything that follows)
|
||||
(\s+ [\s\S]*)? # everything that follows
|
||||
/ux',
|
||||
$comment,
|
||||
$matches
|
||||
);
|
||||
array_shift($matches);
|
||||
|
||||
while (count($matches) < 4) {
|
||||
$matches[] = '';
|
||||
}
|
||||
|
||||
return $matches;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the tag objects.
|
||||
*
|
||||
* @param string $tags Tag block to parse.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function parseTags($tags)
|
||||
{
|
||||
$result = array();
|
||||
$tags = trim($tags);
|
||||
if ('' !== $tags) {
|
||||
if ('@' !== $tags[0]) {
|
||||
throw new \LogicException(
|
||||
'A tag block started with text instead of an actual tag,'
|
||||
. ' this makes the tag block invalid: ' . $tags
|
||||
);
|
||||
}
|
||||
foreach (explode("\n", $tags) as $tag_line) {
|
||||
if (isset($tag_line[0]) && ($tag_line[0] === '@')) {
|
||||
$result[] = $tag_line;
|
||||
} else {
|
||||
$result[count($result) - 1] .= "\n" . $tag_line;
|
||||
}
|
||||
}
|
||||
|
||||
// create proper Tag objects
|
||||
foreach ($result as $key => $tag_line) {
|
||||
$result[$key] = Tag::createInstance(trim($tag_line), $this);
|
||||
}
|
||||
}
|
||||
|
||||
$this->tags = $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the text portion of the doc block.
|
||||
*
|
||||
* Gets the text portion (short and long description combined) of the doc
|
||||
* block.
|
||||
*
|
||||
* @return string The text portion of the doc block.
|
||||
*/
|
||||
public function getText()
|
||||
{
|
||||
$short = $this->getShortDescription();
|
||||
$long = $this->getLongDescription()->getContents();
|
||||
|
||||
if ($long) {
|
||||
return "{$short}\n\n{$long}";
|
||||
} else {
|
||||
return $short;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the text portion of the doc block.
|
||||
*
|
||||
* Sets the text portion (short and long description combined) of the doc
|
||||
* block.
|
||||
*
|
||||
* @param string $docblock The new text portion of the doc block.
|
||||
*
|
||||
* @return $this This doc block.
|
||||
*/
|
||||
public function setText($comment)
|
||||
{
|
||||
list(,$short, $long) = $this->splitDocBlock($comment);
|
||||
$this->short_description = $short;
|
||||
$this->long_description = new DocBlock\Description($long, $this);
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Returns the opening line or also known as short description.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getShortDescription()
|
||||
{
|
||||
return $this->short_description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the full description or also known as long description.
|
||||
*
|
||||
* @return DocBlock\Description
|
||||
*/
|
||||
public function getLongDescription()
|
||||
{
|
||||
return $this->long_description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this DocBlock is the start of a Template section.
|
||||
*
|
||||
* A Docblock may serve as template for a series of subsequent DocBlocks. This is indicated by a special marker
|
||||
* (`#@+`) that is appended directly after the opening `/**` of a DocBlock.
|
||||
*
|
||||
* An example of such an opening is:
|
||||
*
|
||||
* ```
|
||||
* /**#@+
|
||||
* * My DocBlock
|
||||
* * /
|
||||
* ```
|
||||
*
|
||||
* The description and tags (not the summary!) are copied onto all subsequent DocBlocks and also applied to all
|
||||
* elements that follow until another DocBlock is found that contains the closing marker (`#@-`).
|
||||
*
|
||||
* @see self::isTemplateEnd() for the check whether a closing marker was provided.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isTemplateStart()
|
||||
{
|
||||
return $this->isTemplateStart;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this DocBlock is the end of a Template section.
|
||||
*
|
||||
* @see self::isTemplateStart() for a more complete description of the Docblock Template functionality.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isTemplateEnd()
|
||||
{
|
||||
return $this->isTemplateEnd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current context.
|
||||
*
|
||||
* @return Context
|
||||
*/
|
||||
public function getContext()
|
||||
{
|
||||
return $this->context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current location.
|
||||
*
|
||||
* @return Location
|
||||
*/
|
||||
public function getLocation()
|
||||
{
|
||||
return $this->location;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the tags for this DocBlock.
|
||||
*
|
||||
* @return Tag[]
|
||||
*/
|
||||
public function getTags()
|
||||
{
|
||||
return $this->tags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of tags matching the given name. If no tags are found
|
||||
* an empty array is returned.
|
||||
*
|
||||
* @param string $name String to search by.
|
||||
*
|
||||
* @return Tag[]
|
||||
*/
|
||||
public function getTagsByName($name)
|
||||
{
|
||||
$result = array();
|
||||
|
||||
/** @var Tag $tag */
|
||||
foreach ($this->getTags() as $tag) {
|
||||
if ($tag->getName() != $name) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$result[] = $tag;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a tag of a certain type is present in this DocBlock.
|
||||
*
|
||||
* @param string $name Tag name to check for.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasTag($name)
|
||||
{
|
||||
/** @var Tag $tag */
|
||||
foreach ($this->getTags() as $tag) {
|
||||
if ($tag->getName() == $name) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a tag at the end of the list of tags.
|
||||
*
|
||||
* @param Tag $tag The tag to add.
|
||||
*
|
||||
* @return Tag The newly added tag.
|
||||
*
|
||||
* @throws \LogicException When the tag belongs to a different DocBlock.
|
||||
*/
|
||||
public function appendTag(Tag $tag)
|
||||
{
|
||||
if (null === $tag->getDocBlock()) {
|
||||
$tag->setDocBlock($this);
|
||||
}
|
||||
|
||||
if ($tag->getDocBlock() === $this) {
|
||||
$this->tags[] = $tag;
|
||||
} else {
|
||||
throw new \LogicException(
|
||||
'This tag belongs to a different DocBlock object.'
|
||||
);
|
||||
}
|
||||
|
||||
return $tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a tag from the list of tags.
|
||||
*
|
||||
* @param Tag $tag The tag to be deleted.
|
||||
*
|
||||
* @return bool True if the tag was deleted.
|
||||
*/
|
||||
public function deleteTag(Tag $tag)
|
||||
{
|
||||
if (($key = array_search($tag, $this->tags)) !== false) {
|
||||
unset($this->tags[$key]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a string representation of this object.
|
||||
*
|
||||
* @todo determine the exact format as used by PHP Reflection and
|
||||
* implement it.
|
||||
*
|
||||
* @return string
|
||||
* @codeCoverageIgnore Not yet implemented
|
||||
*/
|
||||
public static function export()
|
||||
{
|
||||
throw new \Exception('Not yet implemented');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the exported information (we should use the export static method
|
||||
* BUT this throws an exception at this point).
|
||||
*
|
||||
* @return string
|
||||
* @codeCoverageIgnore Not yet implemented
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return 'Not yet implemented';
|
||||
}
|
||||
}
|
||||
+154
@@ -0,0 +1,154 @@
|
||||
<?php
|
||||
/**
|
||||
* phpDocumentor
|
||||
*
|
||||
* PHP Version 5.3
|
||||
*
|
||||
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||
* @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||
* @link http://phpdoc.org
|
||||
*/
|
||||
|
||||
namespace Barryvdh\Reflection\DocBlock;
|
||||
|
||||
/**
|
||||
* The context in which a DocBlock occurs.
|
||||
*
|
||||
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||
* @link http://phpdoc.org
|
||||
*/
|
||||
class Context
|
||||
{
|
||||
/** @var string The current namespace. */
|
||||
protected $namespace = '';
|
||||
|
||||
/** @var array List of namespace aliases => Fully Qualified Namespace. */
|
||||
protected $namespace_aliases = array();
|
||||
|
||||
/** @var string Name of the structural element, within the namespace. */
|
||||
protected $lsen = '';
|
||||
|
||||
/**
|
||||
* Cteates a new context.
|
||||
* @param string $namespace The namespace where this DocBlock
|
||||
* resides in.
|
||||
* @param array $namespace_aliases List of namespace aliases => Fully
|
||||
* Qualified Namespace.
|
||||
* @param string $lsen Name of the structural element, within
|
||||
* the namespace.
|
||||
*/
|
||||
public function __construct(
|
||||
$namespace = '',
|
||||
array $namespace_aliases = array(),
|
||||
$lsen = ''
|
||||
) {
|
||||
if (!empty($namespace)) {
|
||||
$this->setNamespace($namespace);
|
||||
}
|
||||
$this->setNamespaceAliases($namespace_aliases);
|
||||
$this->setLSEN($lsen);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string The namespace where this DocBlock resides in.
|
||||
*/
|
||||
public function getNamespace()
|
||||
{
|
||||
return $this->namespace;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array List of namespace aliases => Fully Qualified Namespace.
|
||||
*/
|
||||
public function getNamespaceAliases()
|
||||
{
|
||||
return $this->namespace_aliases;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Local Structural Element Name.
|
||||
*
|
||||
* @return string Name of the structural element, within the namespace.
|
||||
*/
|
||||
public function getLSEN()
|
||||
{
|
||||
return $this->lsen;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a new namespace.
|
||||
*
|
||||
* Sets a new namespace for the context. Leading and trailing slashes are
|
||||
* trimmed, and the keywords "global" and "default" are treated as aliases
|
||||
* to no namespace.
|
||||
*
|
||||
* @param string $namespace The new namespace to set.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setNamespace($namespace)
|
||||
{
|
||||
if ('global' !== $namespace
|
||||
&& 'default' !== $namespace
|
||||
) {
|
||||
// Srip leading and trailing slash
|
||||
$this->namespace = trim((string)$namespace, '\\');
|
||||
} else {
|
||||
$this->namespace = '';
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the namespace aliases, replacing all previous ones.
|
||||
*
|
||||
* @param array $namespace_aliases List of namespace aliases => Fully
|
||||
* Qualified Namespace.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setNamespaceAliases(array $namespace_aliases)
|
||||
{
|
||||
$this->namespace_aliases = array();
|
||||
foreach ($namespace_aliases as $alias => $fqnn) {
|
||||
$this->setNamespaceAlias($alias, $fqnn);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a namespace alias to the context.
|
||||
*
|
||||
* @param string $alias The alias name (the part after "as", or the last
|
||||
* part of the Fully Qualified Namespace Name) to add.
|
||||
* @param string $fqnn The Fully Qualified Namespace Name for this alias.
|
||||
* Any form of leading/trailing slashes are accepted, but what will be
|
||||
* stored is a name, prefixed with a slash, and no trailing slash.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setNamespaceAlias($alias, $fqnn)
|
||||
{
|
||||
$this->namespace_aliases[$alias] = '\\' . trim((string)$fqnn, '\\');
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a new Local Structural Element Name.
|
||||
*
|
||||
* Sets a new Local Structural Element Name. A local name also contains
|
||||
* punctuation determining the kind of structural element (e.g. trailing "("
|
||||
* and ")" for functions and methods).
|
||||
*
|
||||
* @param string $lsen The new local name of a structural element.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setLSEN($lsen)
|
||||
{
|
||||
$this->lsen = (string)$lsen;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
+223
@@ -0,0 +1,223 @@
|
||||
<?php
|
||||
/**
|
||||
* phpDocumentor
|
||||
*
|
||||
* PHP Version 5.3
|
||||
*
|
||||
* @author Mike van Riel <mike.vanriel@naenius.com>
|
||||
* @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||
* @link http://phpdoc.org
|
||||
*/
|
||||
|
||||
namespace Barryvdh\Reflection\DocBlock;
|
||||
|
||||
use Barryvdh\Reflection\DocBlock;
|
||||
|
||||
/**
|
||||
* Parses a Description of a DocBlock or tag.
|
||||
*
|
||||
* @author Mike van Riel <mike.vanriel@naenius.com>
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||
* @link http://phpdoc.org
|
||||
*/
|
||||
class Description implements \Reflector
|
||||
{
|
||||
/** @var string */
|
||||
protected $contents = '';
|
||||
|
||||
/** @var array The contents, as an array of strings and Tag objects. */
|
||||
protected $parsedContents = null;
|
||||
|
||||
/** @var DocBlock The DocBlock which this description belongs to. */
|
||||
protected $docblock = null;
|
||||
|
||||
/**
|
||||
* Populates the fields of a description.
|
||||
*
|
||||
* @param string $content The description's conetnts.
|
||||
* @param DocBlock $docblock The DocBlock which this description belongs to.
|
||||
*/
|
||||
public function __construct($content, DocBlock $docblock = null)
|
||||
{
|
||||
$this->setContent($content)->setDocBlock($docblock);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the text of this description.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getContents()
|
||||
{
|
||||
return $this->contents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the text of this description.
|
||||
*
|
||||
* @param string $content The new text of this description.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setContent($content)
|
||||
{
|
||||
$this->contents = trim($content);
|
||||
|
||||
$this->parsedContents = null;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the parsed text of this description.
|
||||
*
|
||||
* @return array An array of strings and tag objects, in the order they
|
||||
* occur within the description.
|
||||
*/
|
||||
public function getParsedContents()
|
||||
{
|
||||
if (null === $this->parsedContents) {
|
||||
$this->parsedContents = preg_split(
|
||||
'/\{
|
||||
# "{@}" is not a valid inline tag. This ensures that
|
||||
# we do not treat it as one, but treat it literally.
|
||||
(?!@\})
|
||||
# We want to capture the whole tag line, but without the
|
||||
# inline tag delimiters.
|
||||
(\@
|
||||
# Match everything up to the next delimiter.
|
||||
[^{}]*
|
||||
# Nested inline tag content should not be captured, or
|
||||
# it will appear in the result separately.
|
||||
(?:
|
||||
# Match nested inline tags.
|
||||
(?:
|
||||
# Because we did not catch the tag delimiters
|
||||
# earlier, we must be explicit with them here.
|
||||
# Notice that this also matches "{}", as a way
|
||||
# to later introduce it as an escape sequence.
|
||||
\{(?1)?\}
|
||||
|
|
||||
# Make sure we match hanging "{".
|
||||
\{
|
||||
)
|
||||
# Match content after the nested inline tag.
|
||||
[^{}]*
|
||||
)* # If there are more inline tags, match them as well.
|
||||
# We use "*" since there may not be any nested inline
|
||||
# tags.
|
||||
)
|
||||
\}/Sux',
|
||||
$this->contents,
|
||||
-1,
|
||||
PREG_SPLIT_DELIM_CAPTURE
|
||||
);
|
||||
|
||||
$count = count($this->parsedContents);
|
||||
for ($i=1; $i<$count; $i += 2) {
|
||||
$this->parsedContents[$i] = Tag::createInstance(
|
||||
$this->parsedContents[$i],
|
||||
$this->docblock
|
||||
);
|
||||
}
|
||||
|
||||
//In order to allow "literal" inline tags, the otherwise invalid
|
||||
//sequence "{@}" is changed to "@", and "{}" is changed to "}".
|
||||
//See unit tests for examples.
|
||||
for ($i=0; $i<$count; $i += 2) {
|
||||
$this->parsedContents[$i] = str_replace(
|
||||
array('{@}', '{}'),
|
||||
array('@', '}'),
|
||||
$this->parsedContents[$i]
|
||||
);
|
||||
}
|
||||
}
|
||||
return $this->parsedContents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a formatted variant of the Long Description using MarkDown.
|
||||
*
|
||||
* @todo this should become a more intelligent piece of code where the
|
||||
* configuration contains a setting what format long descriptions are.
|
||||
*
|
||||
* @codeCoverageIgnore Will be removed soon, in favor of adapters at
|
||||
* PhpDocumentor itself that will process text in various formats.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getFormattedContents()
|
||||
{
|
||||
$result = $this->contents;
|
||||
|
||||
// if the long description contains a plain HTML <code> element, surround
|
||||
// it with a pre element. Please note that we explicitly used str_replace
|
||||
// and not preg_replace to gain performance
|
||||
if (strpos($result, '<code>') !== false) {
|
||||
$result = str_replace(
|
||||
array('<code>', "<code>\r\n", "<code>\n", "<code>\r", '</code>'),
|
||||
array('<pre><code>', '<code>', '<code>', '<code>', '</code></pre>'),
|
||||
$result
|
||||
);
|
||||
}
|
||||
|
||||
if (class_exists('Parsedown')) {
|
||||
$markdown = \Parsedown::instance();
|
||||
$result = $markdown->parse($result);
|
||||
} elseif (class_exists('dflydev\markdown\MarkdownExtraParser')) {
|
||||
$markdown = new \dflydev\markdown\MarkdownExtraParser();
|
||||
$result = $markdown->transformMarkdown($result);
|
||||
}
|
||||
|
||||
return trim($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the docblock this tag belongs to.
|
||||
*
|
||||
* @return DocBlock The docblock this description belongs to.
|
||||
*/
|
||||
public function getDocBlock()
|
||||
{
|
||||
return $this->docblock;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the docblock this tag belongs to.
|
||||
*
|
||||
* @param DocBlock $docblock The new docblock this description belongs to.
|
||||
* Setting NULL removes any association.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setDocBlock(DocBlock $docblock = null)
|
||||
{
|
||||
$this->docblock = $docblock;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a string representation of this object.
|
||||
*
|
||||
* @todo determine the exact format as used by PHP Reflection
|
||||
* and implement it.
|
||||
*
|
||||
* @return void
|
||||
* @codeCoverageIgnore Not yet implemented
|
||||
*/
|
||||
public static function export()
|
||||
{
|
||||
throw new \Exception('Not yet implemented');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the long description as a string.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return $this->getContents();
|
||||
}
|
||||
}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
/**
|
||||
* phpDocumentor
|
||||
*
|
||||
* PHP Version 5.3
|
||||
*
|
||||
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||
* @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||
* @link http://phpdoc.org
|
||||
*/
|
||||
|
||||
namespace Barryvdh\Reflection\DocBlock;
|
||||
|
||||
/**
|
||||
* The location a DocBlock occurs within a file.
|
||||
*
|
||||
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||
* @link http://phpdoc.org
|
||||
*/
|
||||
class Location
|
||||
{
|
||||
/** @var int Line where the DocBlock text starts. */
|
||||
protected $lineNumber = 0;
|
||||
|
||||
/** @var int Column where the DocBlock text starts. */
|
||||
protected $columnNumber = 0;
|
||||
|
||||
public function __construct(
|
||||
$lineNumber = 0,
|
||||
$columnNumber = 0
|
||||
) {
|
||||
$this->setLineNumber($lineNumber)->setColumnNumber($columnNumber);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int Line where the DocBlock text starts.
|
||||
*/
|
||||
public function getLineNumber()
|
||||
{
|
||||
return $this->lineNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param type $lineNumber
|
||||
* @return $this
|
||||
*/
|
||||
public function setLineNumber($lineNumber)
|
||||
{
|
||||
$this->lineNumber = (int)$lineNumber;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int Column where the DocBlock text starts.
|
||||
*/
|
||||
public function getColumnNumber()
|
||||
{
|
||||
return $this->columnNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param int $columnNumber
|
||||
* @return $this
|
||||
*/
|
||||
public function setColumnNumber($columnNumber)
|
||||
{
|
||||
$this->columnNumber = (int)$columnNumber;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
+235
@@ -0,0 +1,235 @@
|
||||
<?php
|
||||
/**
|
||||
* phpDocumentor
|
||||
*
|
||||
* PHP Version 5.3
|
||||
*
|
||||
* @author Barry vd. Heuvel <barryvdh@gmail.com>
|
||||
* @copyright 2013 Mike van Riel / Naenius (http://www.naenius.com)
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||
* @link http://phpdoc.org
|
||||
*/
|
||||
|
||||
namespace Barryvdh\Reflection\DocBlock;
|
||||
|
||||
use Barryvdh\Reflection\DocBlock;
|
||||
|
||||
/**
|
||||
* Serializes a DocBlock instance.
|
||||
*
|
||||
* @author Barry vd. Heuvel <barryvdh@gmail.com>
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||
* @link http://phpdoc.org
|
||||
*/
|
||||
class Serializer
|
||||
{
|
||||
|
||||
/** @var string The string to indent the comment with. */
|
||||
protected $indentString = ' ';
|
||||
|
||||
/** @var int The number of times the indent string is repeated. */
|
||||
protected $indent = 0;
|
||||
|
||||
/** @var bool Whether to indent the first line. */
|
||||
protected $isFirstLineIndented = true;
|
||||
|
||||
/** @var int|null The max length of a line. */
|
||||
protected $lineLength = null;
|
||||
|
||||
/** @var bool Separate tag groups. */
|
||||
protected $separateTags = false;
|
||||
|
||||
/**
|
||||
* Create a Serializer instance.
|
||||
*
|
||||
* @param int $indent The number of times the indent string is
|
||||
* repeated.
|
||||
* @param string $indentString The string to indent the comment with.
|
||||
* @param bool $indentFirstLine Whether to indent the first line.
|
||||
* @param int|null $lineLength The max length of a line or NULL to
|
||||
* disable line wrapping.
|
||||
* @param bool $separateTags Separate tag groups.
|
||||
*/
|
||||
public function __construct(
|
||||
$indent = 0,
|
||||
$indentString = ' ',
|
||||
$indentFirstLine = true,
|
||||
$lineLength = null,
|
||||
$separateTags = false
|
||||
) {
|
||||
$this->setIndentationString($indentString);
|
||||
$this->setIndent($indent);
|
||||
$this->setIsFirstLineIndented($indentFirstLine);
|
||||
$this->setLineLength($lineLength);
|
||||
$this->setSeparateTags($separateTags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the string to indent comments with.
|
||||
*
|
||||
* @param string $indentationString The string to indent comments with.
|
||||
*
|
||||
* @return $this This serializer object.
|
||||
*/
|
||||
public function setIndentationString($indentString)
|
||||
{
|
||||
$this->indentString = (string)$indentString;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the string to indent comments with.
|
||||
*
|
||||
* @return string The indent string.
|
||||
*/
|
||||
public function getIndentationString()
|
||||
{
|
||||
return $this->indentString;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the number of indents.
|
||||
*
|
||||
* @param int $indent The number of times the indent string is repeated.
|
||||
*
|
||||
* @return $this This serializer object.
|
||||
*/
|
||||
public function setIndent($indent)
|
||||
{
|
||||
$this->indent = (int)$indent;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of indents.
|
||||
*
|
||||
* @return int The number of times the indent string is repeated.
|
||||
*/
|
||||
public function getIndent()
|
||||
{
|
||||
return $this->indent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether or not the first line should be indented.
|
||||
*
|
||||
* Sets whether or not the first line (the one with the "/**") should be
|
||||
* indented.
|
||||
*
|
||||
* @param bool $indentFirstLine The new value for this setting.
|
||||
*
|
||||
* @return $this This serializer object.
|
||||
*/
|
||||
public function setIsFirstLineIndented($indentFirstLine)
|
||||
{
|
||||
$this->isFirstLineIndented = (bool)$indentFirstLine;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether or not the first line should be indented.
|
||||
*
|
||||
* @return bool Whether or not the first line should be indented.
|
||||
*/
|
||||
public function isFirstLineIndented()
|
||||
{
|
||||
return $this->isFirstLineIndented;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the line length.
|
||||
*
|
||||
* Sets the length of each line in the serialization. Content will be
|
||||
* wrapped within this limit.
|
||||
*
|
||||
* @param int|null $lineLength The length of each line. NULL to disable line
|
||||
* wrapping altogether.
|
||||
*
|
||||
* @return $this This serializer object.
|
||||
*/
|
||||
public function setLineLength($lineLength)
|
||||
{
|
||||
$this->lineLength = null === $lineLength ? null : (int)$lineLength;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the line length.
|
||||
*
|
||||
* @return int|null The length of each line or NULL if line wrapping is
|
||||
* disabled.
|
||||
*/
|
||||
public function getLineLength()
|
||||
{
|
||||
return $this->lineLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether there should be an empty line between tag groups.
|
||||
*
|
||||
* @param bool $separateTags The new value for this setting.
|
||||
*
|
||||
* @return $this This serializer object.
|
||||
*/
|
||||
public function setSeparateTags($separateTags)
|
||||
{
|
||||
$this->separateTags = (bool)$separateTags;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether there should be an empty line between tag groups.
|
||||
*
|
||||
* @return bool Whether there should be an empty line between tag groups.
|
||||
*/
|
||||
public function getSeparateTags()
|
||||
{
|
||||
return $this->separateTags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a DocBlock comment.
|
||||
*
|
||||
* @param DocBlock The DocBlock to serialize.
|
||||
*
|
||||
* @return string The serialized doc block.
|
||||
*/
|
||||
public function getDocComment(DocBlock $docblock)
|
||||
{
|
||||
$indent = str_repeat($this->indentString, $this->indent);
|
||||
$firstIndent = $this->isFirstLineIndented ? $indent : '';
|
||||
|
||||
$text = $docblock->getText();
|
||||
if ($this->lineLength) {
|
||||
//3 === strlen(' * ')
|
||||
$wrapLength = $this->lineLength - strlen($indent) - 3;
|
||||
$text = wordwrap($text, $wrapLength);
|
||||
}
|
||||
$text = str_replace("\n", "\n{$indent} * ", $text);
|
||||
|
||||
$comment = "{$firstIndent}/**\n{$indent} * {$text}\n{$indent} *\n";
|
||||
|
||||
$tags = array_values($docblock->getTags());
|
||||
|
||||
/** @var Tag $tag */
|
||||
foreach ($tags as $key => $tag) {
|
||||
$nextTag = isset($tags[$key + 1]) ? $tags[$key + 1] : null;
|
||||
|
||||
$tagText = (string) $tag;
|
||||
if ($this->lineLength) {
|
||||
$tagText = wordwrap($tagText, $wrapLength);
|
||||
}
|
||||
$tagText = str_replace("\n", "\n{$indent} * ", $tagText);
|
||||
|
||||
$comment .= "{$indent} * {$tagText}\n";
|
||||
|
||||
if ($this->separateTags && $nextTag !== null && ! $tag->inSameGroup($nextTag)) {
|
||||
$comment .= "{$indent} *\n";
|
||||
}
|
||||
}
|
||||
|
||||
$comment .= $indent . ' */';
|
||||
|
||||
return $comment;
|
||||
}
|
||||
}
|
||||
+412
@@ -0,0 +1,412 @@
|
||||
<?php
|
||||
/**
|
||||
* phpDocumentor
|
||||
*
|
||||
* PHP Version 5.3
|
||||
*
|
||||
* @author Mike van Riel <mike.vanriel@naenius.com>
|
||||
* @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||
* @link http://phpdoc.org
|
||||
*/
|
||||
|
||||
namespace Barryvdh\Reflection\DocBlock;
|
||||
|
||||
use Barryvdh\Reflection\DocBlock;
|
||||
|
||||
/**
|
||||
* Parses a tag definition for a DocBlock.
|
||||
*
|
||||
* @author Mike van Riel <mike.vanriel@naenius.com>
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||
* @link http://phpdoc.org
|
||||
*/
|
||||
class Tag implements \Reflector
|
||||
{
|
||||
/**
|
||||
* PCRE regular expression matching a tag name.
|
||||
*/
|
||||
const REGEX_TAGNAME = '[\w\-\_\\\\]+';
|
||||
|
||||
/** @var string Name of the tag */
|
||||
protected $tag = '';
|
||||
|
||||
/**
|
||||
* @var string|null Content of the tag.
|
||||
* When set to NULL, it means it needs to be regenerated.
|
||||
*/
|
||||
protected $content = '';
|
||||
|
||||
/** @var string Description of the content of this tag */
|
||||
protected $description = '';
|
||||
|
||||
/**
|
||||
* @var array|null The description, as an array of strings and Tag objects.
|
||||
* When set to NULL, it means it needs to be regenerated.
|
||||
*/
|
||||
protected $parsedDescription = null;
|
||||
|
||||
/** @var Location Location of the tag. */
|
||||
protected $location = null;
|
||||
|
||||
/** @var DocBlock The DocBlock which this tag belongs to. */
|
||||
protected $docblock = null;
|
||||
|
||||
/**
|
||||
* @var array An array with a tag as a key, and an FQCN to a class that
|
||||
* handles it as an array value. The class is expected to inherit this
|
||||
* class.
|
||||
*/
|
||||
private static $tagHandlerMappings = array(
|
||||
'author'
|
||||
=> '\Barryvdh\Reflection\DocBlock\Tag\AuthorTag',
|
||||
'covers'
|
||||
=> '\Barryvdh\Reflection\DocBlock\Tag\CoversTag',
|
||||
'deprecated'
|
||||
=> '\Barryvdh\Reflection\DocBlock\Tag\DeprecatedTag',
|
||||
'example'
|
||||
=> '\Barryvdh\Reflection\DocBlock\Tag\ExampleTag',
|
||||
'link'
|
||||
=> '\Barryvdh\Reflection\DocBlock\Tag\LinkTag',
|
||||
'method'
|
||||
=> '\Barryvdh\Reflection\DocBlock\Tag\MethodTag',
|
||||
'param'
|
||||
=> '\Barryvdh\Reflection\DocBlock\Tag\ParamTag',
|
||||
'property-read'
|
||||
=> '\Barryvdh\Reflection\DocBlock\Tag\PropertyReadTag',
|
||||
'property'
|
||||
=> '\Barryvdh\Reflection\DocBlock\Tag\PropertyTag',
|
||||
'property-write'
|
||||
=> '\Barryvdh\Reflection\DocBlock\Tag\PropertyWriteTag',
|
||||
'return'
|
||||
=> '\Barryvdh\Reflection\DocBlock\Tag\ReturnTag',
|
||||
'see'
|
||||
=> '\Barryvdh\Reflection\DocBlock\Tag\SeeTag',
|
||||
'since'
|
||||
=> '\Barryvdh\Reflection\DocBlock\Tag\SinceTag',
|
||||
'source'
|
||||
=> '\Barryvdh\Reflection\DocBlock\Tag\SourceTag',
|
||||
'throw'
|
||||
=> '\Barryvdh\Reflection\DocBlock\Tag\ThrowsTag',
|
||||
'throws'
|
||||
=> '\Barryvdh\Reflection\DocBlock\Tag\ThrowsTag',
|
||||
'uses'
|
||||
=> '\Barryvdh\Reflection\DocBlock\Tag\UsesTag',
|
||||
'var'
|
||||
=> '\Barryvdh\Reflection\DocBlock\Tag\VarTag',
|
||||
'version'
|
||||
=> '\Barryvdh\Reflection\DocBlock\Tag\VersionTag',
|
||||
'SuppressWarnings'
|
||||
=> '\Barryvdh\Reflection\DocBlock\Tag\SuppressWarningsTag'
|
||||
);
|
||||
|
||||
/**
|
||||
* Factory method responsible for instantiating the correct sub type.
|
||||
*
|
||||
* @param string $tag_line The text for this tag, including description.
|
||||
* @param DocBlock $docblock The DocBlock which this tag belongs to.
|
||||
* @param Location $location Location of the tag.
|
||||
*
|
||||
* @throws \InvalidArgumentException if an invalid tag line was presented.
|
||||
*
|
||||
* @return static A new tag object.
|
||||
*/
|
||||
final public static function createInstance(
|
||||
$tag_line,
|
||||
DocBlock $docblock = null,
|
||||
Location $location = null
|
||||
) {
|
||||
if (!preg_match(
|
||||
'/^@(' . self::REGEX_TAGNAME . ')(?:\s*([^\s].*)|$)?/us',
|
||||
$tag_line,
|
||||
$matches
|
||||
)) {
|
||||
throw new \InvalidArgumentException(
|
||||
'Invalid tag_line detected: ' . $tag_line
|
||||
);
|
||||
}
|
||||
|
||||
$handler = __CLASS__;
|
||||
if (isset(self::$tagHandlerMappings[$matches[1]])) {
|
||||
$handler = self::$tagHandlerMappings[$matches[1]];
|
||||
} elseif (isset($docblock)) {
|
||||
$tagName = (string)new Type\Collection(
|
||||
array($matches[1]),
|
||||
$docblock->getContext()
|
||||
);
|
||||
|
||||
if (isset(self::$tagHandlerMappings[$tagName])) {
|
||||
$handler = self::$tagHandlerMappings[$tagName];
|
||||
}
|
||||
}
|
||||
|
||||
return new $handler(
|
||||
$matches[1],
|
||||
isset($matches[2]) ? $matches[2] : '',
|
||||
$docblock,
|
||||
$location
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a handler for tags.
|
||||
*
|
||||
* Registers a handler for tags. The class specified is autoloaded if it's
|
||||
* not available. It must inherit from this class.
|
||||
*
|
||||
* @param string $tag Name of tag to regiser a handler for. When
|
||||
* registering a namespaced tag, the full name, along with a prefixing
|
||||
* slash MUST be provided.
|
||||
* @param string|null $handler FQCN of handler. Specifing NULL removes the
|
||||
* handler for the specified tag, if any.
|
||||
*
|
||||
* @return bool TRUE on success, FALSE on failure.
|
||||
*/
|
||||
final public static function registerTagHandler($tag, $handler)
|
||||
{
|
||||
$tag = trim((string)$tag);
|
||||
|
||||
if (null === $handler) {
|
||||
unset(self::$tagHandlerMappings[$tag]);
|
||||
return true;
|
||||
}
|
||||
|
||||
if ('' !== $tag
|
||||
&& class_exists($handler, true)
|
||||
&& is_subclass_of($handler, __CLASS__)
|
||||
&& !strpos($tag, '\\') //Accept no slash, and 1st slash at offset 0.
|
||||
) {
|
||||
self::$tagHandlerMappings[$tag] = $handler;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a tag and populates the member variables.
|
||||
*
|
||||
* @param string $name Name of the tag.
|
||||
* @param string $content The contents of the given tag.
|
||||
* @param DocBlock $docblock The DocBlock which this tag belongs to.
|
||||
* @param Location $location Location of the tag.
|
||||
*/
|
||||
public function __construct(
|
||||
$name,
|
||||
$content,
|
||||
DocBlock $docblock = null,
|
||||
Location $location = null
|
||||
) {
|
||||
$this
|
||||
->setName($name)
|
||||
->setContent($content)
|
||||
->setDocBlock($docblock)
|
||||
->setLocation($location);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of this tag.
|
||||
*
|
||||
* @return string The name of this tag.
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the name of this tag.
|
||||
*
|
||||
* @param string $name The new name of this tag.
|
||||
*
|
||||
* @return $this
|
||||
* @throws \InvalidArgumentException When an invalid tag name is provided.
|
||||
*/
|
||||
public function setName($name)
|
||||
{
|
||||
if (!preg_match('/^' . self::REGEX_TAGNAME . '$/u', $name)) {
|
||||
throw new \InvalidArgumentException(
|
||||
'Invalid tag name supplied: ' . $name
|
||||
);
|
||||
}
|
||||
|
||||
$this->tag = $name;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the content of this tag.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getContent()
|
||||
{
|
||||
if (null === $this->content) {
|
||||
$this->content = $this->description;
|
||||
}
|
||||
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the content of this tag.
|
||||
*
|
||||
* @param string $content The new content of this tag.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setContent($content)
|
||||
{
|
||||
$this->setDescription($content);
|
||||
$this->content = $content;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the description component of this tag.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription()
|
||||
{
|
||||
return $this->description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the description component of this tag.
|
||||
*
|
||||
* @param string $description The new description component of this tag.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setDescription($description)
|
||||
{
|
||||
$this->content = null;
|
||||
$this->parsedDescription = null;
|
||||
$this->description = trim($description);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the parsed text of this description.
|
||||
*
|
||||
* @return array An array of strings and tag objects, in the order they
|
||||
* occur within the description.
|
||||
*/
|
||||
public function getParsedDescription()
|
||||
{
|
||||
if (null === $this->parsedDescription) {
|
||||
$description = new Description($this->description, $this->docblock);
|
||||
$this->parsedDescription = $description->getParsedContents();
|
||||
}
|
||||
return $this->parsedDescription;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the docblock this tag belongs to.
|
||||
*
|
||||
* @return DocBlock The docblock this tag belongs to.
|
||||
*/
|
||||
public function getDocBlock()
|
||||
{
|
||||
return $this->docblock;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the docblock this tag belongs to.
|
||||
*
|
||||
* @param DocBlock $docblock The new docblock this tag belongs to. Setting
|
||||
* NULL removes any association.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setDocBlock(DocBlock $docblock = null)
|
||||
{
|
||||
$this->docblock = $docblock;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the location of the tag.
|
||||
*
|
||||
* @return Location The tag's location.
|
||||
*/
|
||||
public function getLocation()
|
||||
{
|
||||
return $this->location;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the location of the tag.
|
||||
*
|
||||
* @param Location $location The new location of the tag.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setLocation(Location $location = null)
|
||||
{
|
||||
$this->location = $location;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the given tags should be together or apart.
|
||||
*
|
||||
* @param Tag $tag
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function inSameGroup(Tag $tag)
|
||||
{
|
||||
$firstName = $this->getName();
|
||||
$secondName = $tag->getName();
|
||||
|
||||
if ($firstName === $secondName) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$groups = array(
|
||||
array('deprecated', 'link', 'see', 'since'),
|
||||
array('author', 'copyright', 'license'),
|
||||
array('category', 'package', 'subpackage'),
|
||||
array('property', 'property-read', 'property-write'),
|
||||
array('param', 'return'),
|
||||
);
|
||||
|
||||
foreach ($groups as $group) {
|
||||
if (in_array($firstName, $group, true) && in_array($secondName, $group, true)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a string representation of this object.
|
||||
*
|
||||
* @todo determine the exact format as used by PHP Reflection and implement it.
|
||||
*
|
||||
* @return void
|
||||
* @codeCoverageIgnore Not yet implemented
|
||||
*/
|
||||
public static function export()
|
||||
{
|
||||
throw new \Exception('Not yet implemented');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the tag as a serialized string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return "@{$this->getName()} {$this->getContent()}";
|
||||
}
|
||||
}
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
<?php
|
||||
/**
|
||||
* phpDocumentor
|
||||
*
|
||||
* PHP Version 5.3
|
||||
*
|
||||
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||
* @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||
* @link http://phpdoc.org
|
||||
*/
|
||||
|
||||
namespace Barryvdh\Reflection\DocBlock\Tag;
|
||||
|
||||
use Barryvdh\Reflection\DocBlock\Tag;
|
||||
|
||||
/**
|
||||
* Reflection class for an @author tag in a Docblock.
|
||||
*
|
||||
* @author Mike van Riel <mike.vanriel@naenius.com>
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||
* @link http://phpdoc.org
|
||||
*/
|
||||
class AuthorTag extends Tag
|
||||
{
|
||||
/**
|
||||
* PCRE regular expression matching any valid value for the name component.
|
||||
*/
|
||||
const REGEX_AUTHOR_NAME = '[^\<]*';
|
||||
|
||||
/**
|
||||
* PCRE regular expression matching any valid value for the email component.
|
||||
*/
|
||||
const REGEX_AUTHOR_EMAIL = '[^\>]*';
|
||||
|
||||
/** @var string The name of the author */
|
||||
protected $authorName = '';
|
||||
|
||||
/** @var string The email of the author */
|
||||
protected $authorEmail = '';
|
||||
|
||||
public function getContent()
|
||||
{
|
||||
if (null === $this->content) {
|
||||
$this->content = $this->authorName;
|
||||
if ('' != $this->authorEmail) {
|
||||
$this->content .= "<{$this->authorEmail}>";
|
||||
}
|
||||
}
|
||||
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setContent($content)
|
||||
{
|
||||
parent::setContent($content);
|
||||
if (preg_match(
|
||||
'/^(' . self::REGEX_AUTHOR_NAME .
|
||||
')(\<(' . self::REGEX_AUTHOR_EMAIL .
|
||||
')\>)?$/u',
|
||||
$this->description,
|
||||
$matches
|
||||
)) {
|
||||
$this->authorName = trim($matches[1]);
|
||||
if (isset($matches[3])) {
|
||||
$this->authorEmail = trim($matches[3]);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the author's name.
|
||||
*
|
||||
* @return string The author's name.
|
||||
*/
|
||||
public function getAuthorName()
|
||||
{
|
||||
return $this->authorName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the author's name.
|
||||
*
|
||||
* @param string $authorName The new author name.
|
||||
* An invalid value will set an empty string.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setAuthorName($authorName)
|
||||
{
|
||||
$this->content = null;
|
||||
$this->authorName
|
||||
= preg_match('/^' . self::REGEX_AUTHOR_NAME . '$/u', $authorName)
|
||||
? $authorName : '';
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the author's email.
|
||||
*
|
||||
* @return string The author's email.
|
||||
*/
|
||||
public function getAuthorEmail()
|
||||
{
|
||||
return $this->authorEmail;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the author's email.
|
||||
*
|
||||
* @param string $authorEmail The new author email.
|
||||
* An invalid value will set an empty string.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setAuthorEmail($authorEmail)
|
||||
{
|
||||
$this->authorEmail
|
||||
= preg_match('/^' . self::REGEX_AUTHOR_EMAIL . '$/u', $authorEmail)
|
||||
? $authorEmail : '';
|
||||
|
||||
$this->content = null;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
/**
|
||||
* phpDocumentor
|
||||
*
|
||||
* PHP Version 5.3
|
||||
*
|
||||
* @author Mike van Riel <mike.vanriel@naenius.com>
|
||||
* @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||
* @link http://phpdoc.org
|
||||
*/
|
||||
|
||||
namespace Barryvdh\Reflection\DocBlock\Tag;
|
||||
|
||||
/**
|
||||
* Reflection class for a @covers tag in a Docblock.
|
||||
*
|
||||
* @author Mike van Riel <mike.vanriel@naenius.com>
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||
* @link http://phpdoc.org
|
||||
*/
|
||||
class CoversTag extends SeeTag
|
||||
{
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
/**
|
||||
* phpDocumentor
|
||||
*
|
||||
* PHP Version 5.3
|
||||
*
|
||||
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||
* @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||
* @link http://phpdoc.org
|
||||
*/
|
||||
|
||||
namespace Barryvdh\Reflection\DocBlock\Tag;
|
||||
|
||||
use Barryvdh\Reflection\DocBlock\Tag\VersionTag;
|
||||
|
||||
/**
|
||||
* Reflection class for a @deprecated tag in a Docblock.
|
||||
*
|
||||
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||
* @link http://phpdoc.org
|
||||
*/
|
||||
class DeprecatedTag extends VersionTag
|
||||
{
|
||||
}
|
||||
+156
@@ -0,0 +1,156 @@
|
||||
<?php
|
||||
/**
|
||||
* phpDocumentor
|
||||
*
|
||||
* PHP Version 5.3
|
||||
*
|
||||
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||
* @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||
* @link http://phpdoc.org
|
||||
*/
|
||||
|
||||
namespace Barryvdh\Reflection\DocBlock\Tag;
|
||||
|
||||
use Barryvdh\Reflection\DocBlock\Tag;
|
||||
|
||||
/**
|
||||
* Reflection class for a @example tag in a Docblock.
|
||||
*
|
||||
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||
* @link http://phpdoc.org
|
||||
*/
|
||||
class ExampleTag extends SourceTag
|
||||
{
|
||||
/**
|
||||
* @var string Path to a file to use as an example.
|
||||
* May also be an absolute URI.
|
||||
*/
|
||||
protected $filePath = '';
|
||||
|
||||
/**
|
||||
* @var bool Whether the file path component represents an URI.
|
||||
* This determines how the file portion appears at {@link getContent()}.
|
||||
*/
|
||||
protected $isURI = false;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getContent()
|
||||
{
|
||||
if (null === $this->content) {
|
||||
$filePath = '';
|
||||
if ($this->isURI) {
|
||||
if (false === strpos($this->filePath, ':')) {
|
||||
$filePath = str_replace(
|
||||
'%2F',
|
||||
'/',
|
||||
rawurlencode($this->filePath)
|
||||
);
|
||||
} else {
|
||||
$filePath = $this->filePath;
|
||||
}
|
||||
} else {
|
||||
$filePath = '"' . $this->filePath . '"';
|
||||
}
|
||||
|
||||
$this->content = $filePath . ' ' . parent::getContent();
|
||||
}
|
||||
|
||||
return $this->content;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setContent($content)
|
||||
{
|
||||
Tag::setContent($content);
|
||||
if (preg_match(
|
||||
'/^
|
||||
# File component
|
||||
(?:
|
||||
# File path in quotes
|
||||
\"([^\"]+)\"
|
||||
|
|
||||
# File URI
|
||||
(\S+)
|
||||
)
|
||||
# Remaining content (parsed by SourceTag)
|
||||
(?:\s+(.*))?
|
||||
$/sux',
|
||||
$this->description,
|
||||
$matches
|
||||
)) {
|
||||
if ('' !== $matches[1]) {
|
||||
$this->setFilePath($matches[1]);
|
||||
} else {
|
||||
$this->setFileURI($matches[2]);
|
||||
}
|
||||
|
||||
if (isset($matches[3])) {
|
||||
parent::setContent($matches[3]);
|
||||
} else {
|
||||
$this->setDescription('');
|
||||
}
|
||||
$this->content = $content;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the file path.
|
||||
*
|
||||
* @return string Path to a file to use as an example.
|
||||
* May also be an absolute URI.
|
||||
*/
|
||||
public function getFilePath()
|
||||
{
|
||||
return $this->filePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the file path.
|
||||
*
|
||||
* @param string $filePath The new file path to use for the example.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setFilePath($filePath)
|
||||
{
|
||||
$this->isURI = false;
|
||||
$this->filePath = trim($filePath);
|
||||
|
||||
$this->content = null;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the file path as an URI.
|
||||
*
|
||||
* This function is equivalent to {@link setFilePath()}, except that it
|
||||
* convers an URI to a file path before that.
|
||||
*
|
||||
* There is no getFileURI(), as {@link getFilePath()} is compatible.
|
||||
*
|
||||
* @param type $uri The new file URI to use as an example.
|
||||
*/
|
||||
public function setFileURI($uri)
|
||||
{
|
||||
$this->isURI = true;
|
||||
if (false === strpos($uri, ':')) {
|
||||
//Relative URL
|
||||
$this->filePath = rawurldecode(
|
||||
str_replace(array('/', '\\'), '%2F', $uri)
|
||||
);
|
||||
} else {
|
||||
//Absolute URL or URI.
|
||||
$this->filePath = $uri;
|
||||
}
|
||||
|
||||
$this->content = null;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
/**
|
||||
* phpDocumentor
|
||||
*
|
||||
* PHP Version 5.3
|
||||
*
|
||||
* @author Ben Selby <benmatselby@gmail.com>
|
||||
* @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||
* @link http://phpdoc.org
|
||||
*/
|
||||
|
||||
namespace Barryvdh\Reflection\DocBlock\Tag;
|
||||
|
||||
use Barryvdh\Reflection\DocBlock\Tag;
|
||||
|
||||
/**
|
||||
* Reflection class for a @link tag in a Docblock.
|
||||
*
|
||||
* @author Ben Selby <benmatselby@gmail.com>
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||
* @link http://phpdoc.org
|
||||
*/
|
||||
class LinkTag extends Tag
|
||||
{
|
||||
/** @var string */
|
||||
protected $link = '';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getContent()
|
||||
{
|
||||
if (null === $this->content) {
|
||||
$this->content = "{$this->link} {$this->description}";
|
||||
}
|
||||
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setContent($content)
|
||||
{
|
||||
parent::setContent($content);
|
||||
$parts = preg_split('/\s+/Su', $this->description, 2);
|
||||
|
||||
$this->link = $parts[0];
|
||||
|
||||
$this->setDescription(isset($parts[1]) ? $parts[1] : $parts[0]);
|
||||
|
||||
$this->content = $content;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the link
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getLink()
|
||||
{
|
||||
return $this->link;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the link
|
||||
*
|
||||
* @param string $link The link
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setLink($link)
|
||||
{
|
||||
$this->link = $link;
|
||||
|
||||
$this->content = null;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
+217
@@ -0,0 +1,217 @@
|
||||
<?php
|
||||
/**
|
||||
* phpDocumentor
|
||||
*
|
||||
* PHP Version 5.3
|
||||
*
|
||||
* @author Mike van Riel <mike.vanriel@naenius.com>
|
||||
* @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||
* @link http://phpdoc.org
|
||||
*/
|
||||
|
||||
namespace Barryvdh\Reflection\DocBlock\Tag;
|
||||
|
||||
use Barryvdh\Reflection\DocBlock\Tag;
|
||||
|
||||
/**
|
||||
* Reflection class for a @method in a Docblock.
|
||||
*
|
||||
* @author Mike van Riel <mike.vanriel@naenius.com>
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||
* @link http://phpdoc.org
|
||||
*/
|
||||
class MethodTag extends ReturnTag
|
||||
{
|
||||
|
||||
/** @var string */
|
||||
protected $method_name = '';
|
||||
|
||||
/** @var string */
|
||||
protected $arguments = '';
|
||||
|
||||
/** @var bool */
|
||||
protected $isStatic = false;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getContent()
|
||||
{
|
||||
if (null === $this->content) {
|
||||
$this->content = '';
|
||||
if ($this->isStatic) {
|
||||
$this->content .= 'static ';
|
||||
}
|
||||
$this->content .= $this->type .
|
||||
" {$this->method_name}({$this->arguments}) " .
|
||||
$this->description;
|
||||
}
|
||||
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setContent($content)
|
||||
{
|
||||
Tag::setContent($content);
|
||||
// 1. none or more whitespace
|
||||
// 2. optionally the keyword "static" followed by whitespace
|
||||
// 3. optionally a word with underscores followed by whitespace : as
|
||||
// type for the return value
|
||||
// 4. then optionally a word with underscores followed by () and
|
||||
// whitespace : as method name as used by phpDocumentor
|
||||
// 5. then a word with underscores, followed by ( and any character
|
||||
// until a ) and whitespace : as method name with signature
|
||||
// 6. any remaining text : as description
|
||||
if (preg_match(
|
||||
'/^
|
||||
# Static keyword
|
||||
# Declates a static method ONLY if type is also present
|
||||
(?:
|
||||
(static)
|
||||
\s+
|
||||
)?
|
||||
# Return type
|
||||
(?:
|
||||
(
|
||||
(?:[\w\|_\\\\]*\$this[\w\|_\\\\]*)
|
||||
|
|
||||
(?:
|
||||
(?:[\w\|_\\\\]+)
|
||||
# array notation
|
||||
(?:\[\])*
|
||||
)*
|
||||
)
|
||||
\s+
|
||||
)?
|
||||
# Legacy method name (not captured)
|
||||
(?:
|
||||
[\w_]+\(\)\s+
|
||||
)?
|
||||
# Method name
|
||||
([\w\|_\\\\]+)
|
||||
# Arguments
|
||||
\(([^\)]*)\)
|
||||
\s*
|
||||
# Description
|
||||
(.*)
|
||||
$/sux',
|
||||
$this->description,
|
||||
$matches
|
||||
)) {
|
||||
list(
|
||||
,
|
||||
$static,
|
||||
$this->type,
|
||||
$this->method_name,
|
||||
$this->arguments,
|
||||
$this->description
|
||||
) = $matches;
|
||||
if ($static) {
|
||||
if (!$this->type) {
|
||||
$this->type = 'static';
|
||||
} else {
|
||||
$this->isStatic = true;
|
||||
}
|
||||
} else {
|
||||
if (!$this->type) {
|
||||
$this->type = 'void';
|
||||
}
|
||||
}
|
||||
$this->parsedDescription = null;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the name of this method.
|
||||
*
|
||||
* @param string $method_name The name of the method.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setMethodName($method_name)
|
||||
{
|
||||
$this->method_name = $method_name;
|
||||
|
||||
$this->content = null;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the method name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getMethodName()
|
||||
{
|
||||
return $this->method_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the arguments for this method.
|
||||
*
|
||||
* @param string $arguments A comma-separated arguments line.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setArguments($arguments)
|
||||
{
|
||||
$this->arguments = $arguments;
|
||||
|
||||
$this->content = null;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array containing each argument as array of type and name.
|
||||
*
|
||||
* Please note that the argument sub-array may only contain 1 element if no
|
||||
* type was specified.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function getArguments()
|
||||
{
|
||||
if (empty($this->arguments)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$arguments = explode(',', $this->arguments);
|
||||
foreach ($arguments as $key => $value) {
|
||||
$arguments[$key] = explode(' ', trim($value));
|
||||
}
|
||||
|
||||
return $arguments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the method tag describes a static method or not.
|
||||
*
|
||||
* @return bool TRUE if the method declaration is for a static method, FALSE
|
||||
* otherwise.
|
||||
*/
|
||||
public function isStatic()
|
||||
{
|
||||
return $this->isStatic;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a new value for whether the method is static or not.
|
||||
*
|
||||
* @param bool $isStatic The new value to set.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setIsStatic($isStatic)
|
||||
{
|
||||
$this->isStatic = $isStatic;
|
||||
|
||||
$this->content = null;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
<?php
|
||||
/**
|
||||
* phpDocumentor
|
||||
*
|
||||
* PHP Version 5.3
|
||||
*
|
||||
* @author Mike van Riel <mike.vanriel@naenius.com>
|
||||
* @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||
* @link http://phpdoc.org
|
||||
*/
|
||||
|
||||
namespace Barryvdh\Reflection\DocBlock\Tag;
|
||||
|
||||
use Barryvdh\Reflection\DocBlock\Tag;
|
||||
|
||||
/**
|
||||
* Reflection class for a @param tag in a Docblock.
|
||||
*
|
||||
* @author Mike van Riel <mike.vanriel@naenius.com>
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||
* @link http://phpdoc.org
|
||||
*/
|
||||
class ParamTag extends ReturnTag
|
||||
{
|
||||
/** @var string */
|
||||
protected $variableName = '';
|
||||
|
||||
/** @var bool determines whether this is a variadic argument */
|
||||
protected $isVariadic = false;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getContent()
|
||||
{
|
||||
if (null === $this->content) {
|
||||
$this->content
|
||||
= "{$this->type} {$this->variableName} {$this->description}";
|
||||
}
|
||||
return $this->content;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setContent($content)
|
||||
{
|
||||
Tag::setContent($content);
|
||||
$parts = preg_split(
|
||||
'/(\s+)/Su',
|
||||
$this->description,
|
||||
3,
|
||||
PREG_SPLIT_DELIM_CAPTURE
|
||||
);
|
||||
|
||||
// detect generic type
|
||||
if (isset($parts[0]) && isset($parts[2]) && strpos($parts[0], '<') !== false && strpos($parts[2], '>') !== false) {
|
||||
$parts[0] .= ' ' . $parts[2];
|
||||
unset($parts[1]);
|
||||
unset($parts[2]);
|
||||
$parts = array_values($parts);
|
||||
}
|
||||
|
||||
// if the first item that is encountered is not a variable; it is a type
|
||||
if (isset($parts[0])
|
||||
&& (strlen($parts[0]) > 0)
|
||||
&& ($parts[0][0] !== '$')
|
||||
) {
|
||||
$this->type = array_shift($parts);
|
||||
array_shift($parts);
|
||||
}
|
||||
|
||||
// if the next item starts with a $ or ...$ it must be the variable name
|
||||
if (isset($parts[0])
|
||||
&& (strlen($parts[0]) > 0)
|
||||
&& ($parts[0][0] == '$' || substr($parts[0], 0, 4) === '...$')
|
||||
) {
|
||||
$this->variableName = array_shift($parts);
|
||||
array_shift($parts);
|
||||
|
||||
if (substr($this->variableName, 0, 3) === '...') {
|
||||
$this->isVariadic = true;
|
||||
$this->variableName = substr($this->variableName, 3);
|
||||
}
|
||||
}
|
||||
|
||||
$this->setDescription(implode('', $parts));
|
||||
|
||||
$this->content = $content;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the variable's name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getVariableName()
|
||||
{
|
||||
return $this->variableName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the variable's name.
|
||||
*
|
||||
* @param string $name The new name for this variable.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setVariableName($name)
|
||||
{
|
||||
$this->variableName = $name;
|
||||
|
||||
$this->content = null;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this tag is variadic.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isVariadic()
|
||||
{
|
||||
return $this->isVariadic;
|
||||
}
|
||||
}
|
||||
Vendored
+24
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
/**
|
||||
* phpDocumentor
|
||||
*
|
||||
* PHP Version 5.3
|
||||
*
|
||||
* @author Mike van Riel <mike.vanriel@naenius.com>
|
||||
* @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||
* @link http://phpdoc.org
|
||||
*/
|
||||
|
||||
namespace Barryvdh\Reflection\DocBlock\Tag;
|
||||
|
||||
/**
|
||||
* Reflection class for a @property-read tag in a Docblock.
|
||||
*
|
||||
* @author Mike van Riel <mike.vanriel@naenius.com>
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||
* @link http://phpdoc.org
|
||||
*/
|
||||
class PropertyReadTag extends PropertyTag
|
||||
{
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
/**
|
||||
* phpDocumentor
|
||||
*
|
||||
* PHP Version 5.3
|
||||
*
|
||||
* @author Mike van Riel <mike.vanriel@naenius.com>
|
||||
* @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||
* @link http://phpdoc.org
|
||||
*/
|
||||
|
||||
namespace Barryvdh\Reflection\DocBlock\Tag;
|
||||
|
||||
/**
|
||||
* Reflection class for a @property tag in a Docblock.
|
||||
*
|
||||
* @author Mike van Riel <mike.vanriel@naenius.com>
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||
* @link http://phpdoc.org
|
||||
*/
|
||||
class PropertyTag extends ParamTag
|
||||
{
|
||||
}
|
||||
Vendored
+24
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
/**
|
||||
* phpDocumentor
|
||||
*
|
||||
* PHP Version 5.3
|
||||
*
|
||||
* @author Mike van Riel <mike.vanriel@naenius.com>
|
||||
* @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||
* @link http://phpdoc.org
|
||||
*/
|
||||
|
||||
namespace Barryvdh\Reflection\DocBlock\Tag;
|
||||
|
||||
/**
|
||||
* Reflection class for a @property-write tag in a Docblock.
|
||||
*
|
||||
* @author Mike van Riel <mike.vanriel@naenius.com>
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||
* @link http://phpdoc.org
|
||||
*/
|
||||
class PropertyWriteTag extends PropertyTag
|
||||
{
|
||||
}
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
<?php
|
||||
/**
|
||||
* phpDocumentor
|
||||
*
|
||||
* PHP Version 5.3
|
||||
*
|
||||
* @author Mike van Riel <mike.vanriel@naenius.com>
|
||||
* @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||
* @link http://phpdoc.org
|
||||
*/
|
||||
|
||||
namespace Barryvdh\Reflection\DocBlock\Tag;
|
||||
|
||||
use Barryvdh\Reflection\DocBlock\Tag;
|
||||
use Barryvdh\Reflection\DocBlock\Type\Collection;
|
||||
|
||||
/**
|
||||
* Reflection class for a @return tag in a Docblock.
|
||||
*
|
||||
* @author Mike van Riel <mike.vanriel@naenius.com>
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||
* @link http://phpdoc.org
|
||||
*/
|
||||
class ReturnTag extends Tag
|
||||
{
|
||||
/** @var string The raw type component. */
|
||||
protected $type = '';
|
||||
|
||||
/** @var Collection The parsed type component. */
|
||||
protected $types = null;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getContent()
|
||||
{
|
||||
if (null === $this->content) {
|
||||
$this->content = "{$this->getType()} {$this->description}";
|
||||
}
|
||||
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setContent($content)
|
||||
{
|
||||
parent::setContent($content);
|
||||
|
||||
$parts = preg_split('/(?<!,)\s+/Su', $this->description, 2);
|
||||
|
||||
// any output is considered a type
|
||||
$this->type = $parts[0];
|
||||
$this->types = null;
|
||||
|
||||
$this->setDescription(isset($parts[1]) ? $parts[1] : '');
|
||||
|
||||
$this->content = $content;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the unique types of the variable.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function getTypes()
|
||||
{
|
||||
return $this->getTypesCollection()->getArrayCopy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type section of the variable.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getType()
|
||||
{
|
||||
return (string) $this->getTypesCollection();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the type section of the variable
|
||||
*
|
||||
* @param string $type
|
||||
* @return $this
|
||||
*/
|
||||
public function setType($type)
|
||||
{
|
||||
$this->type = $type;
|
||||
$this->types = null;
|
||||
$this->content = null;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a type to the type section of the variable
|
||||
*
|
||||
* @param string $type
|
||||
* @return $this
|
||||
*/
|
||||
public function addType($type)
|
||||
{
|
||||
$this->type = $this->type . Collection::OPERATOR_OR . $type;
|
||||
$this->types = null;
|
||||
$this->content = null;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the type collection.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function getTypesCollection()
|
||||
{
|
||||
if (null === $this->types) {
|
||||
$this->types = new Collection(
|
||||
array($this->type),
|
||||
$this->docblock ? $this->docblock->getContext() : null
|
||||
);
|
||||
}
|
||||
return $this->types;
|
||||
}
|
||||
}
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
/**
|
||||
* phpDocumentor
|
||||
*
|
||||
* PHP Version 5.3
|
||||
*
|
||||
* @author Mike van Riel <mike.vanriel@naenius.com>
|
||||
* @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||
* @link http://phpdoc.org
|
||||
*/
|
||||
|
||||
namespace Barryvdh\Reflection\DocBlock\Tag;
|
||||
|
||||
use Barryvdh\Reflection\DocBlock\Tag;
|
||||
|
||||
/**
|
||||
* Reflection class for a @see tag in a Docblock.
|
||||
*
|
||||
* @author Mike van Riel <mike.vanriel@naenius.com>
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||
* @link http://phpdoc.org
|
||||
*/
|
||||
class SeeTag extends Tag
|
||||
{
|
||||
/** @var string */
|
||||
protected $refers = null;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getContent()
|
||||
{
|
||||
if (null === $this->content) {
|
||||
$this->content = "{$this->refers} {$this->description}";
|
||||
}
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setContent($content)
|
||||
{
|
||||
parent::setContent($content);
|
||||
$parts = preg_split('/\s+/Su', $this->description, 2);
|
||||
|
||||
// any output is considered a type
|
||||
$this->refers = $parts[0];
|
||||
|
||||
$this->setDescription(isset($parts[1]) ? $parts[1] : '');
|
||||
|
||||
$this->content = $content;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the structural element this tag refers to.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getReference()
|
||||
{
|
||||
return $this->refers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the structural element this tag refers to.
|
||||
*
|
||||
* @param string $refers The new type this tag refers to.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setReference($refers)
|
||||
{
|
||||
$this->refers = $refers;
|
||||
|
||||
$this->content = null;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user