first commit
This commit is contained in:
21
vendor/spatie/laravel-ignition/LICENSE.md
vendored
Normal file
21
vendor/spatie/laravel-ignition/LICENSE.md
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) Spatie <info@spatie.be>
|
||||
|
||||
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.
|
50
vendor/spatie/laravel-ignition/README.md
vendored
Normal file
50
vendor/spatie/laravel-ignition/README.md
vendored
Normal file
@ -0,0 +1,50 @@
|
||||
# Ignition: a beautiful error page for Laravel apps
|
||||
|
||||
[](https://packagist.org/packages/spatie/laravel-ignition)
|
||||

|
||||
[](https://packagist.org/packages/spatie/laravel-ignition)
|
||||
|
||||
[Ignition](https://flareapp.io/docs/ignition-for-laravel/introduction) is a beautiful and customizable error page for Laravel applications. It is the default error page for new Laravel applications. It also allows to publicly share your errors on [Flare](https://flareapp.io). If configured with a valid Flare API key, your errors in production applications will be tracked, and you'll get notified when they happen.
|
||||
|
||||
`spatie/laravel-ignition` works for Laravel 8 and 9 applications running on PHP 8.0 and above. Looking for Ignition for Laravel 5.x, 6.x or 7.x or old PHP versions? `facade/ignition` is still compatible.
|
||||
|
||||

|
||||
|
||||
## Are you a visual learner?
|
||||
|
||||
In [this video on YouTube](https://youtu.be/LEY0N0Bteew?t=739), you'll see a demo of all of the features.
|
||||
|
||||
Do know more about the design decisions we made, read [this blog post](https://freek.dev/2168-ignition-the-most-beautiful-error-page-for-laravel-and-php-got-a-major-redesign).
|
||||
|
||||
## Official Documentation
|
||||
|
||||
The official documentation for Ignition can be found on the [Flare website](https://flareapp.io/docs/ignition/introducing-ignition/overview).
|
||||
|
||||
## Support us
|
||||
|
||||
[<img src="https://github-ads.s3.eu-central-1.amazonaws.com/laravel-ignition.jpg?t=1" width="419px" />](https://spatie.be/github-ad-click/laravel-ignition)
|
||||
|
||||
We invest a lot of resources into creating [best in class open source packages](https://spatie.be/open-source). You can support us by [buying one of our paid products](https://spatie.be/open-source/support-us).
|
||||
|
||||
We highly appreciate you sending us a postcard from your hometown, mentioning which of our package(s) you are using. You'll find our address on [our contact page](https://spatie.be/about-us). We publish all received postcards on [our virtual postcard wall](https://spatie.be/open-source/postcards).
|
||||
|
||||
### Changelog
|
||||
|
||||
Please see [CHANGELOG](CHANGELOG.md) for more information what has changed recently.
|
||||
|
||||
## Contributing
|
||||
|
||||
Please see [CONTRIBUTING](https://github.com/spatie/.github/blob/main/CONTRIBUTING.md) for details.
|
||||
|
||||
## Security Vulnerabilities
|
||||
|
||||
Please review [our security policy](../../security/policy) on how to report security vulnerabilities.
|
||||
|
||||
## Credits
|
||||
|
||||
- [Spatie](https://spatie.be)
|
||||
- [All Contributors](../../contributors)
|
||||
|
||||
## License
|
||||
|
||||
The MIT License (MIT). Please see [License File](LICENSE.md) for more information.
|
92
vendor/spatie/laravel-ignition/composer.json
vendored
Normal file
92
vendor/spatie/laravel-ignition/composer.json
vendored
Normal file
@ -0,0 +1,92 @@
|
||||
{
|
||||
"name": "spatie/laravel-ignition",
|
||||
"description": "A beautiful error page for Laravel applications.",
|
||||
"keywords": [
|
||||
"error",
|
||||
"page",
|
||||
"laravel",
|
||||
"flare"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Spatie",
|
||||
"email": "info@spatie.be",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"homepage": "https://flareapp.io/ignition",
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"php": "^8.1",
|
||||
"ext-curl": "*",
|
||||
"ext-json": "*",
|
||||
"ext-mbstring": "*",
|
||||
"illuminate/support": "^10.0|^11.0",
|
||||
"spatie/flare-client-php": "^1.3.5",
|
||||
"spatie/ignition": "^1.13.2",
|
||||
"symfony/console": "^6.2.3|^7.0",
|
||||
"symfony/var-dumper": "^6.2.3|^7.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"livewire/livewire": "^2.11|^3.3.5",
|
||||
"mockery/mockery": "^1.5.1",
|
||||
"openai-php/client": "^0.8.1",
|
||||
"orchestra/testbench": "^8.0|^9.0",
|
||||
"pestphp/pest": "^2.30",
|
||||
"phpstan/extension-installer": "^1.2",
|
||||
"phpstan/phpstan-deprecation-rules": "^1.1.1",
|
||||
"phpstan/phpstan-phpunit": "^1.3.3",
|
||||
"vlucas/phpdotenv": "^5.5"
|
||||
},
|
||||
"suggest": {
|
||||
"openai-php/client": "Require get solutions from OpenAI",
|
||||
"psr/simple-cache-implementation": "Needed to cache solutions from OpenAI"
|
||||
},
|
||||
"config": {
|
||||
"sort-packages": true,
|
||||
"allow-plugins": {
|
||||
"phpstan/extension-installer": true,
|
||||
"pestphp/pest-plugin": true,
|
||||
"php-http/discovery": false
|
||||
}
|
||||
},
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"Spatie\\LaravelIgnition\\IgnitionServiceProvider"
|
||||
],
|
||||
"aliases": {
|
||||
"Flare": "Spatie\\LaravelIgnition\\Facades\\Flare"
|
||||
}
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Spatie\\LaravelIgnition\\": "src"
|
||||
},
|
||||
"files": [
|
||||
"src/helpers.php"
|
||||
]
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Spatie\\LaravelIgnition\\Tests\\": "tests"
|
||||
}
|
||||
},
|
||||
|
||||
"minimum-stability": "dev",
|
||||
"prefer-stable": true,
|
||||
"scripts": {
|
||||
"analyse": "vendor/bin/phpstan analyse",
|
||||
"baseline": "vendor/bin/phpstan --generate-baseline",
|
||||
"format": "vendor/bin/php-cs-fixer fix --allow-risky=yes",
|
||||
"test": "vendor/bin/pest",
|
||||
"test-coverage": "vendor/bin/phpunit --coverage-html coverage"
|
||||
},
|
||||
"support": {
|
||||
"issues": "https://github.com/spatie/laravel-ignition/issues",
|
||||
"forum": "https://twitter.com/flareappio",
|
||||
"source": "https://github.com/spatie/laravel-ignition",
|
||||
"docs": "https://flareapp.io/docs/ignition-for-laravel/introduction"
|
||||
}
|
||||
}
|
87
vendor/spatie/laravel-ignition/config/flare.php
vendored
Normal file
87
vendor/spatie/laravel-ignition/config/flare.php
vendored
Normal file
@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
use Spatie\FlareClient\FlareMiddleware\AddGitInformation;
|
||||
use Spatie\FlareClient\FlareMiddleware\RemoveRequestIp;
|
||||
use Spatie\FlareClient\FlareMiddleware\CensorRequestBodyFields;
|
||||
use Spatie\FlareClient\FlareMiddleware\CensorRequestHeaders;
|
||||
use Spatie\LaravelIgnition\FlareMiddleware\AddDumps;
|
||||
use Spatie\LaravelIgnition\FlareMiddleware\AddEnvironmentInformation;
|
||||
use Spatie\LaravelIgnition\FlareMiddleware\AddExceptionInformation;
|
||||
use Spatie\LaravelIgnition\FlareMiddleware\AddJobs;
|
||||
use Spatie\LaravelIgnition\FlareMiddleware\AddLogs;
|
||||
use Spatie\LaravelIgnition\FlareMiddleware\AddQueries;
|
||||
use Spatie\LaravelIgnition\FlareMiddleware\AddContext;
|
||||
use Spatie\LaravelIgnition\FlareMiddleware\AddNotifierName;
|
||||
|
||||
return [
|
||||
/*
|
||||
|
|
||||
|--------------------------------------------------------------------------
|
||||
| Flare API key
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Specify Flare's API key below to enable error reporting to the service.
|
||||
|
|
||||
| More info: https://flareapp.io/docs/general/projects
|
||||
|
|
||||
*/
|
||||
|
||||
'key' => env('FLARE_KEY'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Middleware
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| These middleware will modify the contents of the report sent to Flare.
|
||||
|
|
||||
*/
|
||||
|
||||
'flare_middleware' => [
|
||||
RemoveRequestIp::class,
|
||||
AddGitInformation::class,
|
||||
AddNotifierName::class,
|
||||
AddEnvironmentInformation::class,
|
||||
AddExceptionInformation::class,
|
||||
AddDumps::class,
|
||||
AddLogs::class => [
|
||||
'maximum_number_of_collected_logs' => 200,
|
||||
],
|
||||
AddQueries::class => [
|
||||
'maximum_number_of_collected_queries' => 200,
|
||||
'report_query_bindings' => true,
|
||||
],
|
||||
AddJobs::class => [
|
||||
'max_chained_job_reporting_depth' => 5,
|
||||
],
|
||||
AddContext::class,
|
||||
CensorRequestBodyFields::class => [
|
||||
'censor_fields' => [
|
||||
'password',
|
||||
'password_confirmation',
|
||||
],
|
||||
],
|
||||
CensorRequestHeaders::class => [
|
||||
'headers' => [
|
||||
'API-KEY',
|
||||
'Authorization',
|
||||
'Cookie',
|
||||
'Set-Cookie',
|
||||
'X-CSRF-TOKEN',
|
||||
'X-XSRF-TOKEN',
|
||||
]
|
||||
]
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Reporting log statements
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| If this setting is `false` log statements won't be sent as events to Flare,
|
||||
| no matter which error level you specified in the Flare log channel.
|
||||
|
|
||||
*/
|
||||
|
||||
'send_logs_as_events' => true,
|
||||
];
|
285
vendor/spatie/laravel-ignition/config/ignition.php
vendored
Normal file
285
vendor/spatie/laravel-ignition/config/ignition.php
vendored
Normal file
@ -0,0 +1,285 @@
|
||||
<?php
|
||||
|
||||
use Spatie\Ignition\Solutions\SolutionProviders\BadMethodCallSolutionProvider;
|
||||
use Spatie\Ignition\Solutions\SolutionProviders\MergeConflictSolutionProvider;
|
||||
use Spatie\Ignition\Solutions\SolutionProviders\UndefinedPropertySolutionProvider;
|
||||
use Spatie\LaravelIgnition\Recorders\DumpRecorder\DumpRecorder;
|
||||
use Spatie\LaravelIgnition\Recorders\JobRecorder\JobRecorder;
|
||||
use Spatie\LaravelIgnition\Recorders\LogRecorder\LogRecorder;
|
||||
use Spatie\LaravelIgnition\Recorders\QueryRecorder\QueryRecorder;
|
||||
use Spatie\LaravelIgnition\Solutions\SolutionProviders\DefaultDbNameSolutionProvider;
|
||||
use Spatie\LaravelIgnition\Solutions\SolutionProviders\GenericLaravelExceptionSolutionProvider;
|
||||
use Spatie\LaravelIgnition\Solutions\SolutionProviders\IncorrectValetDbCredentialsSolutionProvider;
|
||||
use Spatie\LaravelIgnition\Solutions\SolutionProviders\InvalidRouteActionSolutionProvider;
|
||||
use Spatie\LaravelIgnition\Solutions\SolutionProviders\MissingAppKeySolutionProvider;
|
||||
use Spatie\LaravelIgnition\Solutions\SolutionProviders\MissingColumnSolutionProvider;
|
||||
use Spatie\LaravelIgnition\Solutions\SolutionProviders\MissingImportSolutionProvider;
|
||||
use Spatie\LaravelIgnition\Solutions\SolutionProviders\MissingLivewireComponentSolutionProvider;
|
||||
use Spatie\LaravelIgnition\Solutions\SolutionProviders\MissingMixManifestSolutionProvider;
|
||||
use Spatie\LaravelIgnition\Solutions\SolutionProviders\MissingViteManifestSolutionProvider;
|
||||
use Spatie\LaravelIgnition\Solutions\SolutionProviders\RunningLaravelDuskInProductionProvider;
|
||||
use Spatie\LaravelIgnition\Solutions\SolutionProviders\TableNotFoundSolutionProvider;
|
||||
use Spatie\LaravelIgnition\Solutions\SolutionProviders\UndefinedViewVariableSolutionProvider;
|
||||
use Spatie\LaravelIgnition\Solutions\SolutionProviders\UnknownValidationSolutionProvider;
|
||||
use Spatie\LaravelIgnition\Solutions\SolutionProviders\ViewNotFoundSolutionProvider;
|
||||
use Spatie\LaravelIgnition\Solutions\SolutionProviders\OpenAiSolutionProvider;
|
||||
use Spatie\LaravelIgnition\Solutions\SolutionProviders\SailNetworkSolutionProvider;
|
||||
use Spatie\LaravelIgnition\Solutions\SolutionProviders\UnknownMariadbCollationSolutionProvider;
|
||||
use Spatie\LaravelIgnition\Solutions\SolutionProviders\UnknownMysql8CollationSolutionProvider;
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Editor
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Choose your preferred editor to use when clicking any edit button.
|
||||
|
|
||||
| Supported: "phpstorm", "vscode", "vscode-insiders", "textmate", "emacs",
|
||||
| "sublime", "atom", "nova", "macvim", "idea", "netbeans",
|
||||
| "xdebug", "phpstorm-remote"
|
||||
|
|
||||
*/
|
||||
|
||||
'editor' => env('IGNITION_EDITOR', 'phpstorm'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Theme
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may specify which theme Ignition should use.
|
||||
|
|
||||
| Supported: "light", "dark", "auto"
|
||||
|
|
||||
*/
|
||||
|
||||
'theme' => env('IGNITION_THEME', 'auto'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Sharing
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| You can share local errors with colleagues or others around the world.
|
||||
| Sharing is completely free and doesn't require an account on Flare.
|
||||
|
|
||||
| If necessary, you can completely disable sharing below.
|
||||
|
|
||||
*/
|
||||
|
||||
'enable_share_button' => env('IGNITION_SHARING_ENABLED', true),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Register Ignition commands
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Ignition comes with an additional make command that lets you create
|
||||
| new solution classes more easily. To keep your default Laravel
|
||||
| installation clean, this command is not registered by default.
|
||||
|
|
||||
| You can enable the command registration below.
|
||||
|
|
||||
*/
|
||||
|
||||
'register_commands' => env('REGISTER_IGNITION_COMMANDS', false),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Solution Providers
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| List of solution providers that should be loaded. You may specify additional
|
||||
| providers as fully qualified class names.
|
||||
|
|
||||
*/
|
||||
|
||||
'solution_providers' => [
|
||||
// from spatie/ignition
|
||||
BadMethodCallSolutionProvider::class,
|
||||
MergeConflictSolutionProvider::class,
|
||||
UndefinedPropertySolutionProvider::class,
|
||||
|
||||
// from spatie/laravel-ignition
|
||||
IncorrectValetDbCredentialsSolutionProvider::class,
|
||||
MissingAppKeySolutionProvider::class,
|
||||
DefaultDbNameSolutionProvider::class,
|
||||
TableNotFoundSolutionProvider::class,
|
||||
MissingImportSolutionProvider::class,
|
||||
InvalidRouteActionSolutionProvider::class,
|
||||
ViewNotFoundSolutionProvider::class,
|
||||
RunningLaravelDuskInProductionProvider::class,
|
||||
MissingColumnSolutionProvider::class,
|
||||
UnknownValidationSolutionProvider::class,
|
||||
MissingMixManifestSolutionProvider::class,
|
||||
MissingViteManifestSolutionProvider::class,
|
||||
MissingLivewireComponentSolutionProvider::class,
|
||||
UndefinedViewVariableSolutionProvider::class,
|
||||
GenericLaravelExceptionSolutionProvider::class,
|
||||
OpenAiSolutionProvider::class,
|
||||
SailNetworkSolutionProvider::class,
|
||||
UnknownMysql8CollationSolutionProvider::class,
|
||||
UnknownMariadbCollationSolutionProvider::class,
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Ignored Solution Providers
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| You may specify a list of solution providers (as fully qualified class
|
||||
| names) that shouldn't be loaded. Ignition will ignore these classes
|
||||
| and possible solutions provided by them will never be displayed.
|
||||
|
|
||||
*/
|
||||
|
||||
'ignored_solution_providers' => [
|
||||
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Runnable Solutions
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Some solutions that Ignition displays are runnable and can perform
|
||||
| various tasks. By default, runnable solutions are only enabled when your
|
||||
| app has debug mode enabled and the environment is `local` or
|
||||
| `development`.
|
||||
|
|
||||
| Using the `IGNITION_ENABLE_RUNNABLE_SOLUTIONS` environment variable, you
|
||||
| can override this behaviour and enable or disable runnable solutions
|
||||
| regardless of the application's environment.
|
||||
|
|
||||
| Default: env('IGNITION_ENABLE_RUNNABLE_SOLUTIONS')
|
||||
|
|
||||
*/
|
||||
|
||||
'enable_runnable_solutions' => env('IGNITION_ENABLE_RUNNABLE_SOLUTIONS'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| 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 Ignition 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('IGNITION_REMOTE_SITES_PATH', base_path()),
|
||||
'local_sites_path' => env('IGNITION_LOCAL_SITES_PATH', ''),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Housekeeping Endpoint Prefix
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Ignition registers a couple of routes when it is enabled. Below you may
|
||||
| specify a route prefix that will be used to host all internal links.
|
||||
|
|
||||
*/
|
||||
|
||||
'housekeeping_endpoint_prefix' => '_ignition',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Settings File
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Ignition allows you to save your settings to a specific global file.
|
||||
|
|
||||
| If no path is specified, a file with settings will be saved to the user's
|
||||
| home directory. The directory depends on the OS and its settings but it's
|
||||
| typically `~/.ignition.json`. In this case, the settings will be applied
|
||||
| to all of your projects where Ignition is used and the path is not
|
||||
| specified.
|
||||
|
|
||||
| However, if you want to store your settings on a project basis, or you
|
||||
| want to keep them in another directory, you can specify a path where
|
||||
| the settings file will be saved. The path should be an existing directory
|
||||
| with correct write access.
|
||||
| For example, create a new `ignition` folder in the storage directory and
|
||||
| use `storage_path('ignition')` as the `settings_file_path`.
|
||||
|
|
||||
| Default value: '' (empty string)
|
||||
*/
|
||||
|
||||
'settings_file_path' => '',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Recorders
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Ignition registers a couple of recorders when it is enabled. Below you may
|
||||
| specify a recorders will be used to record specific events.
|
||||
|
|
||||
*/
|
||||
|
||||
'recorders' => [
|
||||
DumpRecorder::class,
|
||||
JobRecorder::class,
|
||||
LogRecorder::class,
|
||||
QueryRecorder::class,
|
||||
],
|
||||
|
||||
/*
|
||||
* When a key is set, we'll send your exceptions to Open AI to generate a solution
|
||||
*/
|
||||
|
||||
'open_ai_key' => env('IGNITION_OPEN_AI_KEY'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Include arguments
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Ignition show you stack traces of exceptions with the arguments that were
|
||||
| passed to each method. This feature can be disabled here.
|
||||
|
|
||||
*/
|
||||
|
||||
'with_stack_frame_arguments' => true,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Argument reducers
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Ignition show you stack traces of exceptions with the arguments that were
|
||||
| passed to each method. To make these variables more readable, you can
|
||||
| specify a list of classes here which summarize the variables.
|
||||
|
|
||||
*/
|
||||
|
||||
'argument_reducers' => [
|
||||
\Spatie\Backtrace\Arguments\Reducers\BaseTypeArgumentReducer::class,
|
||||
\Spatie\Backtrace\Arguments\Reducers\ArrayArgumentReducer::class,
|
||||
\Spatie\Backtrace\Arguments\Reducers\StdClassArgumentReducer::class,
|
||||
\Spatie\Backtrace\Arguments\Reducers\EnumArgumentReducer::class,
|
||||
\Spatie\Backtrace\Arguments\Reducers\ClosureArgumentReducer::class,
|
||||
\Spatie\Backtrace\Arguments\Reducers\DateTimeArgumentReducer::class,
|
||||
\Spatie\Backtrace\Arguments\Reducers\DateTimeZoneArgumentReducer::class,
|
||||
\Spatie\Backtrace\Arguments\Reducers\SymphonyRequestArgumentReducer::class,
|
||||
\Spatie\LaravelIgnition\ArgumentReducers\ModelArgumentReducer::class,
|
||||
\Spatie\LaravelIgnition\ArgumentReducers\CollectionArgumentReducer::class,
|
||||
\Spatie\Backtrace\Arguments\Reducers\StringableArgumentReducer::class,
|
||||
],
|
||||
|
||||
];
|
20
vendor/spatie/laravel-ignition/src/ArgumentReducers/CollectionArgumentReducer.php
vendored
Normal file
20
vendor/spatie/laravel-ignition/src/ArgumentReducers/CollectionArgumentReducer.php
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\ArgumentReducers;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
use Spatie\Backtrace\Arguments\ReducedArgument\ReducedArgumentContract;
|
||||
use Spatie\Backtrace\Arguments\ReducedArgument\UnReducedArgument;
|
||||
use Spatie\Backtrace\Arguments\Reducers\ArrayArgumentReducer;
|
||||
|
||||
class CollectionArgumentReducer extends ArrayArgumentReducer
|
||||
{
|
||||
public function execute(mixed $argument): ReducedArgumentContract
|
||||
{
|
||||
if (! $argument instanceof Collection) {
|
||||
return UnReducedArgument::create();
|
||||
}
|
||||
|
||||
return $this->reduceArgument($argument->toArray(), get_class($argument));
|
||||
}
|
||||
}
|
24
vendor/spatie/laravel-ignition/src/ArgumentReducers/ModelArgumentReducer.php
vendored
Normal file
24
vendor/spatie/laravel-ignition/src/ArgumentReducers/ModelArgumentReducer.php
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\ArgumentReducers;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Spatie\Backtrace\Arguments\ReducedArgument\ReducedArgument;
|
||||
use Spatie\Backtrace\Arguments\ReducedArgument\ReducedArgumentContract;
|
||||
use Spatie\Backtrace\Arguments\ReducedArgument\UnReducedArgument;
|
||||
use Spatie\Backtrace\Arguments\Reducers\ArgumentReducer;
|
||||
|
||||
class ModelArgumentReducer implements ArgumentReducer
|
||||
{
|
||||
public function execute(mixed $argument): ReducedArgumentContract
|
||||
{
|
||||
if (! $argument instanceof Model) {
|
||||
return UnReducedArgument::create();
|
||||
}
|
||||
|
||||
return new ReducedArgument(
|
||||
"{$argument->getKeyName()}:{$argument->getKey()}",
|
||||
get_class($argument)
|
||||
);
|
||||
}
|
||||
}
|
35
vendor/spatie/laravel-ignition/src/Commands/SolutionMakeCommand.php
vendored
Normal file
35
vendor/spatie/laravel-ignition/src/Commands/SolutionMakeCommand.php
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Commands;
|
||||
|
||||
use Illuminate\Console\GeneratorCommand;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
class SolutionMakeCommand extends GeneratorCommand
|
||||
{
|
||||
protected $name = 'ignition:make-solution';
|
||||
|
||||
protected $description = 'Create a new custom Ignition solution class';
|
||||
|
||||
protected $type = 'Solution';
|
||||
|
||||
protected function getStub(): string
|
||||
{
|
||||
return $this->option('runnable')
|
||||
? __DIR__.'/stubs/runnable-solution.stub'
|
||||
: __DIR__.'/stubs/solution.stub';
|
||||
}
|
||||
|
||||
protected function getDefaultNamespace($rootNamespace)
|
||||
{
|
||||
return "{$rootNamespace}\\Solutions";
|
||||
}
|
||||
|
||||
/** @return array<int, mixed> */
|
||||
protected function getOptions(): array
|
||||
{
|
||||
return [
|
||||
['runnable', null, InputOption::VALUE_NONE, 'Create runnable solution'],
|
||||
];
|
||||
}
|
||||
}
|
24
vendor/spatie/laravel-ignition/src/Commands/SolutionProviderMakeCommand.php
vendored
Normal file
24
vendor/spatie/laravel-ignition/src/Commands/SolutionProviderMakeCommand.php
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Commands;
|
||||
|
||||
use Illuminate\Console\GeneratorCommand;
|
||||
|
||||
class SolutionProviderMakeCommand extends GeneratorCommand
|
||||
{
|
||||
protected $name = 'ignition:make-solution-provider';
|
||||
|
||||
protected $description = 'Create a new custom Ignition solution provider class';
|
||||
|
||||
protected $type = 'Solution Provider';
|
||||
|
||||
protected function getStub(): string
|
||||
{
|
||||
return __DIR__.'/stubs/solution-provider.stub';
|
||||
}
|
||||
|
||||
protected function getDefaultNamespace($rootNamespace)
|
||||
{
|
||||
return "{$rootNamespace}\\SolutionProviders";
|
||||
}
|
||||
}
|
131
vendor/spatie/laravel-ignition/src/Commands/TestCommand.php
vendored
Normal file
131
vendor/spatie/laravel-ignition/src/Commands/TestCommand.php
vendored
Normal file
@ -0,0 +1,131 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Commands;
|
||||
|
||||
use Composer\InstalledVersions;
|
||||
use Exception;
|
||||
use Illuminate\Config\Repository;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Log\LogManager;
|
||||
use Spatie\FlareClient\Flare;
|
||||
use Spatie\FlareClient\Http\Exceptions\BadResponseCode;
|
||||
|
||||
class TestCommand extends Command
|
||||
{
|
||||
protected $signature = 'flare:test';
|
||||
|
||||
protected $description = 'Send a test notification to Flare';
|
||||
|
||||
protected Repository $config;
|
||||
|
||||
public function handle(Repository $config): void
|
||||
{
|
||||
$this->config = $config;
|
||||
|
||||
$this->checkFlareKey();
|
||||
|
||||
if (app()->make('log') instanceof LogManager) {
|
||||
$this->checkFlareLogger();
|
||||
}
|
||||
|
||||
$this->sendTestException();
|
||||
}
|
||||
|
||||
protected function checkFlareKey(): self
|
||||
{
|
||||
$message = empty($this->config->get('flare.key'))
|
||||
? '❌ Flare key not specified. Make sure you specify a value in the `key` key of the `flare` config file.'
|
||||
: '✅ Flare key specified';
|
||||
|
||||
$this->info($message);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function checkFlareLogger(): self
|
||||
{
|
||||
$defaultLogChannel = $this->config->get('logging.default');
|
||||
|
||||
$activeStack = $this->config->get("logging.channels.{$defaultLogChannel}");
|
||||
|
||||
if (is_null($activeStack)) {
|
||||
$this->info("❌ The default logging channel `{$defaultLogChannel}` is not configured in the `logging` config file");
|
||||
}
|
||||
|
||||
if (! isset($activeStack['channels']) || ! in_array('flare', $activeStack['channels'])) {
|
||||
$this->info("❌ The logging channel `{$defaultLogChannel}` does not contain the 'flare' channel");
|
||||
}
|
||||
|
||||
if (is_null($this->config->get('logging.channels.flare'))) {
|
||||
$this->info('❌ There is no logging channel named `flare` in the `logging` config file');
|
||||
}
|
||||
|
||||
if ($this->config->get('logging.channels.flare.driver') !== 'flare') {
|
||||
$this->info('❌ The `flare` logging channel defined in the `logging` config file is not set to `flare`.');
|
||||
}
|
||||
|
||||
if ($this->config->get('ignition.with_stack_frame_arguments') && ini_get('zend.exception_ignore_args')) {
|
||||
$this->info('⚠️ The `zend.exception_ignore_args` php ini setting is enabled. This will prevent Flare from showing stack trace arguments.');
|
||||
}
|
||||
|
||||
$this->info('✅ The Flare logging driver was configured correctly.');
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function sendTestException(): void
|
||||
{
|
||||
$testException = new Exception('This is an exception to test if the integration with Flare works.');
|
||||
|
||||
try {
|
||||
app(Flare::class)->sendTestReport($testException);
|
||||
$this->info('');
|
||||
} catch (Exception $exception) {
|
||||
$this->warn('❌ We were unable to send an exception to Flare. ');
|
||||
|
||||
if ($exception instanceof BadResponseCode) {
|
||||
$this->info('');
|
||||
$message = 'Unknown error';
|
||||
|
||||
$body = $exception->response->getBody();
|
||||
|
||||
if (is_array($body) && isset($body['message'])) {
|
||||
$message = $body['message'];
|
||||
}
|
||||
|
||||
$this->warn("{$exception->response->getHttpResponseCode()} - {$message}");
|
||||
} else {
|
||||
$this->warn($exception->getMessage());
|
||||
}
|
||||
|
||||
$this->warn('Make sure that your key is correct and that you have a valid subscription.');
|
||||
$this->info('');
|
||||
$this->info('For more info visit the docs on https://flareapp.io/docs/ignition-for-laravel/introduction');
|
||||
$this->info('You can see the status page of Flare at https://status.flareapp.io');
|
||||
$this->info('Flare support can be reached at support@flareapp.io');
|
||||
|
||||
$this->line('');
|
||||
$this->line('Extra info');
|
||||
$this->table([], [
|
||||
['Platform', PHP_OS],
|
||||
['PHP', phpversion()],
|
||||
['Laravel', app()->version()],
|
||||
['spatie/ignition', InstalledVersions::getVersion('spatie/ignition')],
|
||||
['spatie/laravel-ignition', InstalledVersions::getVersion('spatie/laravel-ignition')],
|
||||
['spatie/flare-client-php', InstalledVersions::getVersion('spatie/flare-client-php')],
|
||||
/** @phpstan-ignore-next-line */
|
||||
['Curl', curl_version()['version'] ?? 'Unknown'],
|
||||
/** @phpstan-ignore-next-line */
|
||||
['SSL', curl_version()['ssl_version'] ?? 'Unknown'],
|
||||
]);
|
||||
|
||||
if ($this->output->isVerbose()) {
|
||||
throw $exception;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->info('We tried to send an exception to Flare. Please check if it arrived!');
|
||||
}
|
||||
}
|
43
vendor/spatie/laravel-ignition/src/Commands/stubs/runnable-solution.stub
vendored
Normal file
43
vendor/spatie/laravel-ignition/src/Commands/stubs/runnable-solution.stub
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace DummyNamespace;
|
||||
|
||||
use Spatie\Ignition\Contracts\RunnableSolution;
|
||||
|
||||
class DummyClass implements RunnableSolution
|
||||
{
|
||||
public function getSolutionTitle(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function getDocumentationLinks(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getSolutionActionDescription(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function getRunButtonText(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function getSolutionDescription(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function getRunParameters(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function run(array $parameters = [])
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
19
vendor/spatie/laravel-ignition/src/Commands/stubs/solution-provider.stub
vendored
Normal file
19
vendor/spatie/laravel-ignition/src/Commands/stubs/solution-provider.stub
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace DummyNamespace;
|
||||
|
||||
use Spatie\Ignition\Contracts\HasSolutionsForThrowable;
|
||||
use Throwable;
|
||||
|
||||
class DummyClass implements HasSolutionsForThrowable
|
||||
{
|
||||
public function canSolve(Throwable $throwable): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getSolutions(Throwable $throwable): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
23
vendor/spatie/laravel-ignition/src/Commands/stubs/solution.stub
vendored
Normal file
23
vendor/spatie/laravel-ignition/src/Commands/stubs/solution.stub
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace DummyNamespace;
|
||||
|
||||
use Spatie\Ignition\Contracts\Solution;
|
||||
|
||||
class DummyClass implements Solution
|
||||
{
|
||||
public function getSolutionTitle(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function getSolutionDescription(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function getDocumentationLinks(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
9
vendor/spatie/laravel-ignition/src/ContextProviders/LaravelConsoleContextProvider.php
vendored
Normal file
9
vendor/spatie/laravel-ignition/src/ContextProviders/LaravelConsoleContextProvider.php
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\ContextProviders;
|
||||
|
||||
use Spatie\FlareClient\Context\ConsoleContextProvider;
|
||||
|
||||
class LaravelConsoleContextProvider extends ConsoleContextProvider
|
||||
{
|
||||
}
|
31
vendor/spatie/laravel-ignition/src/ContextProviders/LaravelContextProviderDetector.php
vendored
Normal file
31
vendor/spatie/laravel-ignition/src/ContextProviders/LaravelContextProviderDetector.php
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\ContextProviders;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Livewire\LivewireManager;
|
||||
use Spatie\FlareClient\Context\ContextProvider;
|
||||
use Spatie\FlareClient\Context\ContextProviderDetector;
|
||||
|
||||
class LaravelContextProviderDetector implements ContextProviderDetector
|
||||
{
|
||||
public function detectCurrentContext(): ContextProvider
|
||||
{
|
||||
if (app()->runningInConsole()) {
|
||||
return new LaravelConsoleContextProvider($_SERVER['argv'] ?? []);
|
||||
}
|
||||
|
||||
$request = app(Request::class);
|
||||
|
||||
if ($this->isRunningLiveWire($request)) {
|
||||
return new LaravelLivewireRequestContextProvider($request, app(LivewireManager::class));
|
||||
}
|
||||
|
||||
return new LaravelRequestContextProvider($request);
|
||||
}
|
||||
|
||||
protected function isRunningLiveWire(Request $request): bool
|
||||
{
|
||||
return $request->hasHeader('x-livewire') && $request->hasHeader('referer');
|
||||
}
|
||||
}
|
100
vendor/spatie/laravel-ignition/src/ContextProviders/LaravelLivewireRequestContextProvider.php
vendored
Normal file
100
vendor/spatie/laravel-ignition/src/ContextProviders/LaravelLivewireRequestContextProvider.php
vendored
Normal file
@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\ContextProviders;
|
||||
|
||||
use Exception;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Arr;
|
||||
use Livewire\LivewireManager;
|
||||
|
||||
class LaravelLivewireRequestContextProvider extends LaravelRequestContextProvider
|
||||
{
|
||||
public function __construct(
|
||||
Request $request,
|
||||
protected LivewireManager $livewireManager
|
||||
) {
|
||||
parent::__construct($request);
|
||||
}
|
||||
|
||||
/** @return array<string, string> */
|
||||
public function getRequest(): array
|
||||
{
|
||||
$properties = parent::getRequest();
|
||||
|
||||
$properties['method'] = $this->livewireManager->originalMethod();
|
||||
$properties['url'] = $this->livewireManager->originalUrl();
|
||||
|
||||
return $properties;
|
||||
}
|
||||
|
||||
/** @return array<int|string, mixed> */
|
||||
public function toArray(): array
|
||||
{
|
||||
$properties = parent::toArray();
|
||||
|
||||
$properties['livewire'] = $this->getLivewireInformation();
|
||||
|
||||
return $properties;
|
||||
}
|
||||
|
||||
/** @return array<string, mixed> */
|
||||
protected function getLivewireInformation(): array
|
||||
{
|
||||
/** @phpstan-ignore-next-line */
|
||||
$componentId = $this->request->input('fingerprint.id');
|
||||
|
||||
/** @phpstan-ignore-next-line */
|
||||
$componentAlias = $this->request->input('fingerprint.name');
|
||||
|
||||
if ($componentAlias === null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
try {
|
||||
$componentClass = $this->livewireManager->getClass($componentAlias);
|
||||
} catch (Exception $e) {
|
||||
$componentClass = null;
|
||||
}
|
||||
|
||||
return [
|
||||
'component_class' => $componentClass,
|
||||
'component_alias' => $componentAlias,
|
||||
'component_id' => $componentId,
|
||||
'data' => $this->resolveData(),
|
||||
'updates' => $this->resolveUpdates(),
|
||||
];
|
||||
}
|
||||
|
||||
/** @return array<string, mixed> */
|
||||
protected function resolveData(): array
|
||||
{
|
||||
/** @phpstan-ignore-next-line */
|
||||
$data = $this->request->input('serverMemo.data') ?? [];
|
||||
|
||||
/** @phpstan-ignore-next-line */
|
||||
$dataMeta = $this->request->input('serverMemo.dataMeta') ?? [];
|
||||
|
||||
foreach ($dataMeta['modelCollections'] ?? [] as $key => $value) {
|
||||
$data[$key] = array_merge($data[$key] ?? [], $value);
|
||||
}
|
||||
|
||||
foreach ($dataMeta['models'] ?? [] as $key => $value) {
|
||||
$data[$key] = array_merge($data[$key] ?? [], $value);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/** @return array<string, mixed> */
|
||||
protected function resolveUpdates(): array
|
||||
{
|
||||
/** @phpstan-ignore-next-line */
|
||||
$updates = $this->request->input('updates') ?? [];
|
||||
|
||||
return array_map(function (array $update) {
|
||||
$update['payload'] = Arr::except($update['payload'] ?? [], ['id']);
|
||||
|
||||
return $update;
|
||||
}, $updates);
|
||||
}
|
||||
}
|
102
vendor/spatie/laravel-ignition/src/ContextProviders/LaravelRequestContextProvider.php
vendored
Normal file
102
vendor/spatie/laravel-ignition/src/ContextProviders/LaravelRequestContextProvider.php
vendored
Normal file
@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\ContextProviders;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Http\Request as LaravelRequest;
|
||||
use Spatie\FlareClient\Context\RequestContextProvider;
|
||||
use Symfony\Component\HttpFoundation\Request as SymphonyRequest;
|
||||
use Throwable;
|
||||
|
||||
class LaravelRequestContextProvider extends RequestContextProvider
|
||||
{
|
||||
protected LaravelRequest|SymphonyRequest|null $request;
|
||||
|
||||
public function __construct(LaravelRequest $request)
|
||||
{
|
||||
$this->request = $request;
|
||||
}
|
||||
|
||||
/** @return null|array<string, mixed> */
|
||||
public function getUser(): array|null
|
||||
{
|
||||
try {
|
||||
/** @var object|null $user */
|
||||
/** @phpstan-ignore-next-line */
|
||||
$user = $this->request?->user();
|
||||
|
||||
if (! $user) {
|
||||
return null;
|
||||
}
|
||||
} catch (Throwable) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
if (method_exists($user, 'toFlare')) {
|
||||
return $user->toFlare();
|
||||
}
|
||||
|
||||
if (method_exists($user, 'toArray')) {
|
||||
return $user->toArray();
|
||||
}
|
||||
} catch (Throwable $e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @return null|array<string, mixed> */
|
||||
public function getRoute(): array|null
|
||||
{
|
||||
/**
|
||||
* @phpstan-ignore-next-line
|
||||
* @var \Illuminate\Routing\Route|null $route
|
||||
*/
|
||||
$route = $this->request->route();
|
||||
|
||||
if (! $route) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return [
|
||||
'route' => $route->getName(),
|
||||
'routeParameters' => $this->getRouteParameters(),
|
||||
'controllerAction' => $route->getActionName(),
|
||||
'middleware' => array_values($route->gatherMiddleware() ?? []),
|
||||
];
|
||||
}
|
||||
|
||||
/** @return array<int, mixed> */
|
||||
protected function getRouteParameters(): array
|
||||
{
|
||||
try {
|
||||
/** @phpstan-ignore-next-line */
|
||||
return collect(optional($this->request->route())->parameters ?? [])
|
||||
->map(fn ($parameter) => $parameter instanceof Model ? $parameter->withoutRelations() : $parameter)
|
||||
->map(function ($parameter) {
|
||||
return method_exists($parameter, 'toFlare') ? $parameter->toFlare() : $parameter;
|
||||
})
|
||||
->toArray();
|
||||
} catch (Throwable) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/** @return array<int, mixed> */
|
||||
public function toArray(): array
|
||||
{
|
||||
$properties = parent::toArray();
|
||||
|
||||
if ($route = $this->getRoute()) {
|
||||
$properties['route'] = $route;
|
||||
}
|
||||
|
||||
if ($user = $this->getUser()) {
|
||||
$properties['user'] = $user;
|
||||
}
|
||||
|
||||
return $properties;
|
||||
}
|
||||
}
|
23
vendor/spatie/laravel-ignition/src/Exceptions/CannotExecuteSolutionForNonLocalIp.php
vendored
Normal file
23
vendor/spatie/laravel-ignition/src/Exceptions/CannotExecuteSolutionForNonLocalIp.php
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Exceptions;
|
||||
|
||||
use Spatie\Ignition\Contracts\BaseSolution;
|
||||
use Spatie\Ignition\Contracts\ProvidesSolution;
|
||||
use Spatie\Ignition\Contracts\Solution;
|
||||
use Symfony\Component\HttpKernel\Exception\HttpException;
|
||||
|
||||
class CannotExecuteSolutionForNonLocalIp extends HttpException implements ProvidesSolution
|
||||
{
|
||||
public static function make(): self
|
||||
{
|
||||
return new self(403, 'Solutions cannot be run from your current IP address.');
|
||||
}
|
||||
|
||||
public function getSolution(): Solution
|
||||
{
|
||||
return BaseSolution::create()
|
||||
->setSolutionTitle('Checking your environment settings')
|
||||
->setSolutionDescription("Solutions can only be executed by requests from a local IP address. Keep in mind that `APP_DEBUG` should set to false on any production environment.");
|
||||
}
|
||||
}
|
31
vendor/spatie/laravel-ignition/src/Exceptions/InvalidConfig.php
vendored
Normal file
31
vendor/spatie/laravel-ignition/src/Exceptions/InvalidConfig.php
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Exceptions;
|
||||
|
||||
use Exception;
|
||||
use Monolog\Level;
|
||||
use Spatie\Ignition\Contracts\BaseSolution;
|
||||
use Spatie\Ignition\Contracts\ProvidesSolution;
|
||||
use Spatie\Ignition\Contracts\Solution;
|
||||
|
||||
class InvalidConfig extends Exception implements ProvidesSolution
|
||||
{
|
||||
public static function invalidLogLevel(string $logLevel): self
|
||||
{
|
||||
return new self("Invalid log level `{$logLevel}` specified.");
|
||||
}
|
||||
|
||||
public function getSolution(): Solution
|
||||
{
|
||||
$validLogLevels = array_map(
|
||||
fn (string $level) => strtolower($level),
|
||||
array_keys(Level::VALUES)
|
||||
);
|
||||
|
||||
$validLogLevelsString = implode(',', $validLogLevels);
|
||||
|
||||
return BaseSolution::create()
|
||||
->setSolutionTitle('You provided an invalid log level')
|
||||
->setSolutionDescription("Please change the log level in your `config/logging.php` file. Valid log levels are {$validLogLevelsString}.");
|
||||
}
|
||||
}
|
55
vendor/spatie/laravel-ignition/src/Exceptions/ViewException.php
vendored
Normal file
55
vendor/spatie/laravel-ignition/src/Exceptions/ViewException.php
vendored
Normal file
@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Exceptions;
|
||||
|
||||
use ErrorException;
|
||||
use Spatie\FlareClient\Contracts\ProvidesFlareContext;
|
||||
use Spatie\LaravelIgnition\Recorders\DumpRecorder\HtmlDumper;
|
||||
|
||||
class ViewException extends ErrorException implements ProvidesFlareContext
|
||||
{
|
||||
/** @var array<string, mixed> */
|
||||
protected array $viewData = [];
|
||||
|
||||
protected string $view = '';
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $data
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setViewData(array $data): void
|
||||
{
|
||||
$this->viewData = $data;
|
||||
}
|
||||
|
||||
/** @return array<string, mixed> */
|
||||
public function getViewData(): array
|
||||
{
|
||||
return $this->viewData;
|
||||
}
|
||||
|
||||
public function setView(string $path): void
|
||||
{
|
||||
$this->view = $path;
|
||||
}
|
||||
|
||||
protected function dumpViewData(mixed $variable): string
|
||||
{
|
||||
return (new HtmlDumper())->dumpVariable($variable);
|
||||
}
|
||||
|
||||
/** @return array<string, mixed> */
|
||||
public function context(): array
|
||||
{
|
||||
$context = [
|
||||
'view' => [
|
||||
'view' => $this->view,
|
||||
],
|
||||
];
|
||||
|
||||
$context['view']['data'] = array_map([$this, 'dumpViewData'], $this->viewData);
|
||||
|
||||
return $context;
|
||||
}
|
||||
}
|
21
vendor/spatie/laravel-ignition/src/Exceptions/ViewExceptionWithSolution.php
vendored
Normal file
21
vendor/spatie/laravel-ignition/src/Exceptions/ViewExceptionWithSolution.php
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Exceptions;
|
||||
|
||||
use Spatie\Ignition\Contracts\ProvidesSolution;
|
||||
use Spatie\Ignition\Contracts\Solution;
|
||||
|
||||
class ViewExceptionWithSolution extends ViewException implements ProvidesSolution
|
||||
{
|
||||
protected Solution $solution;
|
||||
|
||||
public function setSolution(Solution $solution): void
|
||||
{
|
||||
$this->solution = $solution;
|
||||
}
|
||||
|
||||
public function getSolution(): Solution
|
||||
{
|
||||
return $this->solution;
|
||||
}
|
||||
}
|
26
vendor/spatie/laravel-ignition/src/Facades/Flare.php
vendored
Normal file
26
vendor/spatie/laravel-ignition/src/Facades/Flare.php
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Facades;
|
||||
|
||||
use Illuminate\Support\Facades\Facade;
|
||||
use Spatie\LaravelIgnition\Support\SentReports;
|
||||
|
||||
/**
|
||||
* @method static void glow(string $name, string $messageLevel = \Spatie\FlareClient\Enums\MessageLevels::INFO, array $metaData = [])
|
||||
* @method static void context($key, $value)
|
||||
* @method static void group(string $groupName, array $properties)
|
||||
*
|
||||
* @see \Spatie\FlareClient\Flare
|
||||
*/
|
||||
class Flare extends Facade
|
||||
{
|
||||
protected static function getFacadeAccessor()
|
||||
{
|
||||
return \Spatie\FlareClient\Flare::class;
|
||||
}
|
||||
|
||||
public static function sentReports(): SentReports
|
||||
{
|
||||
return app(SentReports::class);
|
||||
}
|
||||
}
|
27
vendor/spatie/laravel-ignition/src/FlareMiddleware/AddContext.php
vendored
Normal file
27
vendor/spatie/laravel-ignition/src/FlareMiddleware/AddContext.php
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\FlareMiddleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Log\Context\Repository;
|
||||
use Illuminate\Support\Facades\Context;
|
||||
use Spatie\FlareClient\FlareMiddleware\FlareMiddleware;
|
||||
use Spatie\FlareClient\Report;
|
||||
|
||||
class AddContext implements FlareMiddleware
|
||||
{
|
||||
public function handle(Report $report, Closure $next)
|
||||
{
|
||||
if (! class_exists(Repository::class)) {
|
||||
return $report;
|
||||
}
|
||||
|
||||
$allContext = Context::all();
|
||||
|
||||
if (count($allContext)) {
|
||||
$report->group('laravel_context', $allContext);
|
||||
}
|
||||
|
||||
return $report;
|
||||
}
|
||||
}
|
25
vendor/spatie/laravel-ignition/src/FlareMiddleware/AddDumps.php
vendored
Normal file
25
vendor/spatie/laravel-ignition/src/FlareMiddleware/AddDumps.php
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\FlareMiddleware;
|
||||
|
||||
use Closure;
|
||||
use Spatie\FlareClient\FlareMiddleware\FlareMiddleware;
|
||||
use Spatie\FlareClient\Report;
|
||||
use Spatie\LaravelIgnition\Recorders\DumpRecorder\DumpRecorder;
|
||||
|
||||
class AddDumps implements FlareMiddleware
|
||||
{
|
||||
protected DumpRecorder $dumpRecorder;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->dumpRecorder = app(DumpRecorder::class);
|
||||
}
|
||||
|
||||
public function handle(Report $report, Closure $next)
|
||||
{
|
||||
$report->group('dumps', $this->dumpRecorder->getDumps());
|
||||
|
||||
return $next($report);
|
||||
}
|
||||
}
|
26
vendor/spatie/laravel-ignition/src/FlareMiddleware/AddEnvironmentInformation.php
vendored
Normal file
26
vendor/spatie/laravel-ignition/src/FlareMiddleware/AddEnvironmentInformation.php
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\FlareMiddleware;
|
||||
|
||||
use Closure;
|
||||
use Spatie\FlareClient\FlareMiddleware\FlareMiddleware;
|
||||
use Spatie\FlareClient\Report;
|
||||
|
||||
class AddEnvironmentInformation implements FlareMiddleware
|
||||
{
|
||||
public function handle(Report $report, Closure $next)
|
||||
{
|
||||
$report->frameworkVersion(app()->version());
|
||||
|
||||
$report->group('env', [
|
||||
'laravel_version' => app()->version(),
|
||||
'laravel_locale' => app()->getLocale(),
|
||||
'laravel_config_cached' => app()->configurationIsCached(),
|
||||
'app_debug' => config('app.debug'),
|
||||
'app_env' => config('app.env'),
|
||||
'php_version' => phpversion(),
|
||||
]);
|
||||
|
||||
return $next($report);
|
||||
}
|
||||
}
|
58
vendor/spatie/laravel-ignition/src/FlareMiddleware/AddExceptionInformation.php
vendored
Normal file
58
vendor/spatie/laravel-ignition/src/FlareMiddleware/AddExceptionInformation.php
vendored
Normal file
@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\FlareMiddleware;
|
||||
|
||||
use Illuminate\Database\QueryException;
|
||||
use Spatie\FlareClient\Contracts\ProvidesFlareContext;
|
||||
use Spatie\FlareClient\FlareMiddleware\FlareMiddleware;
|
||||
use Spatie\FlareClient\Report;
|
||||
|
||||
class AddExceptionInformation implements FlareMiddleware
|
||||
{
|
||||
public function handle(Report $report, $next)
|
||||
{
|
||||
$throwable = $report->getThrowable();
|
||||
|
||||
$this->addUserDefinedContext($report);
|
||||
|
||||
if (! $throwable instanceof QueryException) {
|
||||
return $next($report);
|
||||
}
|
||||
|
||||
$report->group('exception', [
|
||||
'raw_sql' => $throwable->getSql(),
|
||||
]);
|
||||
|
||||
return $next($report);
|
||||
}
|
||||
|
||||
private function addUserDefinedContext(Report $report): void
|
||||
{
|
||||
$throwable = $report->getThrowable();
|
||||
|
||||
if ($throwable === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($throwable instanceof ProvidesFlareContext) {
|
||||
// ProvidesFlareContext writes directly to context groups and is handled in the flare-client-php package.
|
||||
return;
|
||||
}
|
||||
|
||||
if (! method_exists($throwable, 'context')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$context = $throwable->context();
|
||||
|
||||
if (! is_array($context)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$exceptionContextGroup = [];
|
||||
foreach ($context as $key => $value) {
|
||||
$exceptionContextGroup[$key] = $value;
|
||||
}
|
||||
$report->group('exception', $exceptionContextGroup);
|
||||
}
|
||||
}
|
26
vendor/spatie/laravel-ignition/src/FlareMiddleware/AddJobs.php
vendored
Normal file
26
vendor/spatie/laravel-ignition/src/FlareMiddleware/AddJobs.php
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\FlareMiddleware;
|
||||
|
||||
use Spatie\FlareClient\FlareMiddleware\FlareMiddleware;
|
||||
use Spatie\FlareClient\Report;
|
||||
use Spatie\LaravelIgnition\Recorders\JobRecorder\JobRecorder;
|
||||
|
||||
class AddJobs implements FlareMiddleware
|
||||
{
|
||||
protected JobRecorder $jobRecorder;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->jobRecorder = app(JobRecorder::class);
|
||||
}
|
||||
|
||||
public function handle(Report $report, $next)
|
||||
{
|
||||
if ($job = $this->jobRecorder->getJob()) {
|
||||
$report->group('job', $job);
|
||||
}
|
||||
|
||||
return $next($report);
|
||||
}
|
||||
}
|
24
vendor/spatie/laravel-ignition/src/FlareMiddleware/AddLogs.php
vendored
Normal file
24
vendor/spatie/laravel-ignition/src/FlareMiddleware/AddLogs.php
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\FlareMiddleware;
|
||||
|
||||
use Spatie\FlareClient\FlareMiddleware\FlareMiddleware;
|
||||
use Spatie\FlareClient\Report;
|
||||
use Spatie\LaravelIgnition\Recorders\LogRecorder\LogRecorder;
|
||||
|
||||
class AddLogs implements FlareMiddleware
|
||||
{
|
||||
protected LogRecorder $logRecorder;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->logRecorder = app(LogRecorder::class);
|
||||
}
|
||||
|
||||
public function handle(Report $report, $next)
|
||||
{
|
||||
$report->group('logs', $this->logRecorder->getLogMessages());
|
||||
|
||||
return $next($report);
|
||||
}
|
||||
}
|
18
vendor/spatie/laravel-ignition/src/FlareMiddleware/AddNotifierName.php
vendored
Normal file
18
vendor/spatie/laravel-ignition/src/FlareMiddleware/AddNotifierName.php
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\FlareMiddleware;
|
||||
|
||||
use Spatie\FlareClient\FlareMiddleware\FlareMiddleware;
|
||||
use Spatie\FlareClient\Report;
|
||||
|
||||
class AddNotifierName implements FlareMiddleware
|
||||
{
|
||||
public const NOTIFIER_NAME = 'Laravel Client';
|
||||
|
||||
public function handle(Report $report, $next)
|
||||
{
|
||||
$report->notifierName(static::NOTIFIER_NAME);
|
||||
|
||||
return $next($report);
|
||||
}
|
||||
}
|
23
vendor/spatie/laravel-ignition/src/FlareMiddleware/AddQueries.php
vendored
Normal file
23
vendor/spatie/laravel-ignition/src/FlareMiddleware/AddQueries.php
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\FlareMiddleware;
|
||||
|
||||
use Spatie\FlareClient\Report;
|
||||
use Spatie\LaravelIgnition\Recorders\QueryRecorder\QueryRecorder;
|
||||
|
||||
class AddQueries
|
||||
{
|
||||
protected QueryRecorder $queryRecorder;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->queryRecorder = app(QueryRecorder::class);
|
||||
}
|
||||
|
||||
public function handle(Report $report, $next)
|
||||
{
|
||||
$report->group('queries', $this->queryRecorder->getQueries());
|
||||
|
||||
return $next($report);
|
||||
}
|
||||
}
|
52
vendor/spatie/laravel-ignition/src/Http/Controllers/ExecuteSolutionController.php
vendored
Normal file
52
vendor/spatie/laravel-ignition/src/Http/Controllers/ExecuteSolutionController.php
vendored
Normal file
@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Http\Controllers;
|
||||
|
||||
use Illuminate\Foundation\Validation\ValidatesRequests;
|
||||
use Spatie\Ignition\Contracts\SolutionProviderRepository;
|
||||
use Spatie\LaravelIgnition\Exceptions\CannotExecuteSolutionForNonLocalIp;
|
||||
use Spatie\LaravelIgnition\Http\Requests\ExecuteSolutionRequest;
|
||||
use Spatie\LaravelIgnition\Support\RunnableSolutionsGuard;
|
||||
|
||||
class ExecuteSolutionController
|
||||
{
|
||||
use ValidatesRequests;
|
||||
|
||||
public function __invoke(
|
||||
ExecuteSolutionRequest $request,
|
||||
SolutionProviderRepository $solutionProviderRepository
|
||||
) {
|
||||
$this
|
||||
->ensureRunnableSolutionsEnabled()
|
||||
->ensureLocalRequest();
|
||||
|
||||
$solution = $request->getRunnableSolution();
|
||||
|
||||
$solution->run($request->get('parameters', []));
|
||||
|
||||
return response()->noContent();
|
||||
}
|
||||
|
||||
public function ensureRunnableSolutionsEnabled(): self
|
||||
{
|
||||
// Should already be checked in middleware but we want to be 100% certain.
|
||||
abort_unless(RunnableSolutionsGuard::check(), 400);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function ensureLocalRequest(): self
|
||||
{
|
||||
$ipIsPublic = filter_var(
|
||||
request()->ip(),
|
||||
FILTER_VALIDATE_IP,
|
||||
FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE
|
||||
);
|
||||
|
||||
if ($ipIsPublic) {
|
||||
throw CannotExecuteSolutionForNonLocalIp::make();
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
25
vendor/spatie/laravel-ignition/src/Http/Controllers/HealthCheckController.php
vendored
Normal file
25
vendor/spatie/laravel-ignition/src/Http/Controllers/HealthCheckController.php
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Http\Controllers;
|
||||
|
||||
use Illuminate\Support\Facades\Artisan;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class HealthCheckController
|
||||
{
|
||||
public function __invoke()
|
||||
{
|
||||
return [
|
||||
'can_execute_commands' => $this->canExecuteCommands(),
|
||||
];
|
||||
}
|
||||
|
||||
protected function canExecuteCommands(): bool
|
||||
{
|
||||
Artisan::call('help', ['--version']);
|
||||
|
||||
$output = Artisan::output();
|
||||
|
||||
return Str::contains($output, app()->version());
|
||||
}
|
||||
}
|
16
vendor/spatie/laravel-ignition/src/Http/Controllers/UpdateConfigController.php
vendored
Normal file
16
vendor/spatie/laravel-ignition/src/Http/Controllers/UpdateConfigController.php
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Http\Controllers;
|
||||
|
||||
use Spatie\Ignition\Config\IgnitionConfig;
|
||||
use Spatie\LaravelIgnition\Http\Requests\UpdateConfigRequest;
|
||||
|
||||
class UpdateConfigController
|
||||
{
|
||||
public function __invoke(UpdateConfigRequest $request)
|
||||
{
|
||||
$result = (new IgnitionConfig())->saveValues($request->validated());
|
||||
|
||||
return response()->json($result);
|
||||
}
|
||||
}
|
18
vendor/spatie/laravel-ignition/src/Http/Middleware/RunnableSolutionsEnabled.php
vendored
Normal file
18
vendor/spatie/laravel-ignition/src/Http/Middleware/RunnableSolutionsEnabled.php
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Spatie\LaravelIgnition\Support\RunnableSolutionsGuard;
|
||||
|
||||
class RunnableSolutionsEnabled
|
||||
{
|
||||
public function handle($request, Closure $next)
|
||||
{
|
||||
if (! RunnableSolutionsGuard::check()) {
|
||||
abort(404);
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
40
vendor/spatie/laravel-ignition/src/Http/Requests/ExecuteSolutionRequest.php
vendored
Normal file
40
vendor/spatie/laravel-ignition/src/Http/Requests/ExecuteSolutionRequest.php
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Http\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Spatie\Ignition\Contracts\RunnableSolution;
|
||||
use Spatie\Ignition\Contracts\Solution;
|
||||
use Spatie\Ignition\Contracts\SolutionProviderRepository;
|
||||
|
||||
class ExecuteSolutionRequest extends FormRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'solution' => 'required',
|
||||
'parameters' => 'array',
|
||||
];
|
||||
}
|
||||
|
||||
public function getSolution(): Solution
|
||||
{
|
||||
$solution = app(SolutionProviderRepository::class)
|
||||
->getSolutionForClass($this->get('solution'));
|
||||
|
||||
abort_if(is_null($solution), 404, 'Solution could not be found');
|
||||
|
||||
return $solution;
|
||||
}
|
||||
|
||||
public function getRunnableSolution(): RunnableSolution
|
||||
{
|
||||
$solution = $this->getSolution();
|
||||
|
||||
if (! $solution instanceof RunnableSolution) {
|
||||
abort(404, 'Runnable solution could not be found');
|
||||
}
|
||||
|
||||
return $solution;
|
||||
}
|
||||
}
|
18
vendor/spatie/laravel-ignition/src/Http/Requests/UpdateConfigRequest.php
vendored
Normal file
18
vendor/spatie/laravel-ignition/src/Http/Requests/UpdateConfigRequest.php
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Http\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class UpdateConfigRequest extends FormRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'theme' => ['required', Rule::in(['light', 'dark', 'auto'])],
|
||||
'editor' => ['required'],
|
||||
'hide_solutions' => ['required', 'boolean'],
|
||||
];
|
||||
}
|
||||
}
|
349
vendor/spatie/laravel-ignition/src/IgnitionServiceProvider.php
vendored
Normal file
349
vendor/spatie/laravel-ignition/src/IgnitionServiceProvider.php
vendored
Normal file
@ -0,0 +1,349 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition;
|
||||
|
||||
use Exception;
|
||||
use Illuminate\Contracts\Debug\ExceptionHandler;
|
||||
use Illuminate\Foundation\Application;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Illuminate\View\ViewException;
|
||||
use Laravel\Octane\Events\RequestReceived;
|
||||
use Laravel\Octane\Events\RequestTerminated;
|
||||
use Laravel\Octane\Events\TaskReceived;
|
||||
use Laravel\Octane\Events\TickReceived;
|
||||
use Monolog\Level;
|
||||
use Monolog\Logger;
|
||||
use Spatie\FlareClient\Flare;
|
||||
use Spatie\FlareClient\FlareMiddleware\AddSolutions;
|
||||
use Spatie\Ignition\Config\FileConfigManager;
|
||||
use Spatie\Ignition\Config\IgnitionConfig;
|
||||
use Spatie\Ignition\Contracts\ConfigManager;
|
||||
use Spatie\Ignition\Contracts\SolutionProviderRepository as SolutionProviderRepositoryContract;
|
||||
use Spatie\Ignition\Ignition;
|
||||
use Spatie\LaravelIgnition\Commands\SolutionMakeCommand;
|
||||
use Spatie\LaravelIgnition\Commands\SolutionProviderMakeCommand;
|
||||
use Spatie\LaravelIgnition\Commands\TestCommand;
|
||||
use Spatie\LaravelIgnition\ContextProviders\LaravelContextProviderDetector;
|
||||
use Spatie\LaravelIgnition\Exceptions\InvalidConfig;
|
||||
use Spatie\LaravelIgnition\FlareMiddleware\AddJobs;
|
||||
use Spatie\LaravelIgnition\FlareMiddleware\AddLogs;
|
||||
use Spatie\LaravelIgnition\FlareMiddleware\AddQueries;
|
||||
use Spatie\LaravelIgnition\Recorders\DumpRecorder\DumpRecorder;
|
||||
use Spatie\LaravelIgnition\Recorders\JobRecorder\JobRecorder;
|
||||
use Spatie\LaravelIgnition\Recorders\LogRecorder\LogRecorder;
|
||||
use Spatie\LaravelIgnition\Recorders\QueryRecorder\QueryRecorder;
|
||||
use Spatie\LaravelIgnition\Renderers\IgnitionExceptionRenderer;
|
||||
use Spatie\LaravelIgnition\Solutions\SolutionProviders\SolutionProviderRepository;
|
||||
use Spatie\LaravelIgnition\Support\FlareLogHandler;
|
||||
use Spatie\LaravelIgnition\Support\SentReports;
|
||||
use Spatie\LaravelIgnition\Views\ViewExceptionMapper;
|
||||
|
||||
class IgnitionServiceProvider extends ServiceProvider
|
||||
{
|
||||
public function register(): void
|
||||
{
|
||||
$this->registerConfig();
|
||||
$this->registerFlare();
|
||||
$this->registerIgnition();
|
||||
$this->registerRenderer();
|
||||
$this->registerRecorders();
|
||||
$this->registerLogHandler();
|
||||
}
|
||||
|
||||
public function boot()
|
||||
{
|
||||
if ($this->app->runningInConsole()) {
|
||||
$this->registerCommands();
|
||||
$this->publishConfigs();
|
||||
}
|
||||
|
||||
$this->registerRoutes();
|
||||
$this->configureTinker();
|
||||
$this->configureOctane();
|
||||
$this->registerViewExceptionMapper();
|
||||
$this->startRecorders();
|
||||
$this->configureQueue();
|
||||
}
|
||||
|
||||
protected function registerConfig(): void
|
||||
{
|
||||
$this->mergeConfigFrom(__DIR__ . '/../config/flare.php', 'flare');
|
||||
$this->mergeConfigFrom(__DIR__ . '/../config/ignition.php', 'ignition');
|
||||
}
|
||||
|
||||
protected function registerCommands(): void
|
||||
{
|
||||
if ($this->app['config']->get('flare.key')) {
|
||||
$this->commands([
|
||||
TestCommand::class,
|
||||
]);
|
||||
}
|
||||
|
||||
if ($this->app['config']->get('ignition.register_commands')) {
|
||||
$this->commands([
|
||||
SolutionMakeCommand::class,
|
||||
SolutionProviderMakeCommand::class,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
protected function publishConfigs(): void
|
||||
{
|
||||
$this->publishes([
|
||||
__DIR__ . '/../config/ignition.php' => config_path('ignition.php'),
|
||||
], 'ignition-config');
|
||||
|
||||
$this->publishes([
|
||||
__DIR__ . '/../config/flare.php' => config_path('flare.php'),
|
||||
], 'flare-config');
|
||||
}
|
||||
|
||||
protected function registerRenderer(): void
|
||||
{
|
||||
$this->app->bind(
|
||||
'Illuminate\Contracts\Foundation\ExceptionRenderer',
|
||||
fn (Application $app) => $app->make(IgnitionExceptionRenderer::class)
|
||||
);
|
||||
}
|
||||
|
||||
protected function registerFlare(): void
|
||||
{
|
||||
$this->app->singleton(Flare::class, function () {
|
||||
return Flare::make()
|
||||
->setApiToken(config('flare.key') ?? '')
|
||||
->setBaseUrl(config('flare.base_url', 'https://flareapp.io/api'))
|
||||
->applicationPath(base_path())
|
||||
->setStage(app()->environment())
|
||||
->setContextProviderDetector(new LaravelContextProviderDetector())
|
||||
->registerMiddleware($this->getFlareMiddleware())
|
||||
->registerMiddleware(new AddSolutions(new SolutionProviderRepository($this->getSolutionProviders())))
|
||||
->argumentReducers(config('ignition.argument_reducers', []))
|
||||
->withStackFrameArguments(config('ignition.with_stack_frame_arguments', true));
|
||||
});
|
||||
|
||||
$this->app->singleton(SentReports::class);
|
||||
}
|
||||
|
||||
protected function registerIgnition(): void
|
||||
{
|
||||
$this->app->singleton(
|
||||
ConfigManager::class,
|
||||
fn () => new FileConfigManager(config('ignition.settings_file_path', ''))
|
||||
);
|
||||
|
||||
$ignitionConfig = (new IgnitionConfig())
|
||||
->merge(config('ignition', []))
|
||||
->loadConfigFile();
|
||||
|
||||
$solutionProviders = $this->getSolutionProviders();
|
||||
$solutionProviderRepository = new SolutionProviderRepository($solutionProviders);
|
||||
|
||||
$this->app->singleton(IgnitionConfig::class, fn () => $ignitionConfig);
|
||||
|
||||
$this->app->singleton(SolutionProviderRepositoryContract::class, fn () => $solutionProviderRepository);
|
||||
|
||||
$this->app->singleton(
|
||||
Ignition::class,
|
||||
fn () => (new Ignition($this->app->make(Flare::class)))->applicationPath(base_path())
|
||||
);
|
||||
}
|
||||
|
||||
protected function registerRecorders(): void
|
||||
{
|
||||
$this->app->singleton(DumpRecorder::class);
|
||||
|
||||
$this->app->singleton(LogRecorder::class, function (Application $app): LogRecorder {
|
||||
return new LogRecorder(
|
||||
$app,
|
||||
config()->get('flare.flare_middleware.' . AddLogs::class . '.maximum_number_of_collected_logs')
|
||||
);
|
||||
});
|
||||
|
||||
$this->app->singleton(
|
||||
QueryRecorder::class,
|
||||
function (Application $app): QueryRecorder {
|
||||
return new QueryRecorder(
|
||||
$app,
|
||||
config('flare.flare_middleware.' . AddQueries::class . '.report_query_bindings', true),
|
||||
config('flare.flare_middleware.' . AddQueries::class . '.maximum_number_of_collected_queries', 200)
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
$this->app->singleton(JobRecorder::class, function (Application $app): JobRecorder {
|
||||
return new JobRecorder(
|
||||
$app,
|
||||
config('flare.flare_middleware.' . AddJobs::class . '.max_chained_job_reporting_depth', 5)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
public function configureTinker(): void
|
||||
{
|
||||
if ($this->app->runningInConsole()) {
|
||||
if (isset($_SERVER['argv']) && ['artisan', 'tinker'] === $_SERVER['argv']) {
|
||||
app(Flare::class)->sendReportsImmediately();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function configureOctane(): void
|
||||
{
|
||||
if (isset($_SERVER['LARAVEL_OCTANE'])) {
|
||||
$this->setupOctane();
|
||||
}
|
||||
}
|
||||
|
||||
protected function registerViewExceptionMapper(): void
|
||||
{
|
||||
$handler = $this->app->make(ExceptionHandler::class);
|
||||
|
||||
if (! method_exists($handler, 'map')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$handler->map(function (ViewException $viewException) {
|
||||
return $this->app->make(ViewExceptionMapper::class)->map($viewException);
|
||||
});
|
||||
}
|
||||
|
||||
protected function registerRoutes(): void
|
||||
{
|
||||
$this->loadRoutesFrom(realpath(__DIR__ . '/ignition-routes.php'));
|
||||
}
|
||||
|
||||
protected function registerLogHandler(): void
|
||||
{
|
||||
$this->app->singleton('flare.logger', function ($app) {
|
||||
$handler = new FlareLogHandler(
|
||||
$app->make(Flare::class),
|
||||
$app->make(SentReports::class),
|
||||
);
|
||||
|
||||
$logLevelString = config('logging.channels.flare.level', 'error');
|
||||
|
||||
$logLevel = $this->getLogLevel($logLevelString);
|
||||
|
||||
$handler->setMinimumReportLogLevel($logLevel);
|
||||
|
||||
return tap(
|
||||
new Logger('Flare'),
|
||||
fn (Logger $logger) => $logger->pushHandler($handler)
|
||||
);
|
||||
});
|
||||
|
||||
Log::extend('flare', fn ($app) => $app['flare.logger']);
|
||||
}
|
||||
|
||||
protected function startRecorders(): void
|
||||
{
|
||||
foreach ($this->app->config['ignition.recorders'] ?? [] as $recorder) {
|
||||
$this->app->make($recorder)->start();
|
||||
}
|
||||
}
|
||||
|
||||
protected function configureQueue(): void
|
||||
{
|
||||
if (! $this->app->bound('queue')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$queue = $this->app->get('queue');
|
||||
|
||||
// Reset before executing a queue job to make sure the job's log/query/dump recorders are empty.
|
||||
// When using a sync queue this also reports the queued reports from previous exceptions.
|
||||
$queue->before(function () {
|
||||
$this->resetFlareAndLaravelIgnition();
|
||||
app(Flare::class)->sendReportsImmediately();
|
||||
});
|
||||
|
||||
// Send queued reports (and reset) after executing a queue job.
|
||||
$queue->after(function () {
|
||||
$this->resetFlareAndLaravelIgnition();
|
||||
});
|
||||
|
||||
// Note: the $queue->looping() event can't be used because it's not triggered on Vapor
|
||||
}
|
||||
|
||||
protected function getLogLevel(string $logLevelString): int
|
||||
{
|
||||
try {
|
||||
$logLevel = Level::fromName($logLevelString);
|
||||
} catch (Exception $exception) {
|
||||
$logLevel = null;
|
||||
}
|
||||
|
||||
if (! $logLevel) {
|
||||
throw InvalidConfig::invalidLogLevel($logLevelString);
|
||||
}
|
||||
|
||||
return $logLevel->value;
|
||||
}
|
||||
|
||||
protected function getFlareMiddleware(): array
|
||||
{
|
||||
return collect(config('flare.flare_middleware'))
|
||||
->map(function ($value, $key) {
|
||||
if (is_string($key)) {
|
||||
$middlewareClass = $key;
|
||||
$parameters = $value ?? [];
|
||||
} else {
|
||||
$middlewareClass = $value;
|
||||
$parameters = [];
|
||||
}
|
||||
|
||||
return new $middlewareClass(...array_values($parameters));
|
||||
})
|
||||
->values()
|
||||
->toArray();
|
||||
}
|
||||
|
||||
protected function getSolutionProviders(): array
|
||||
{
|
||||
return collect(config('ignition.solution_providers'))
|
||||
->reject(
|
||||
fn (string $class) => in_array($class, config('ignition.ignored_solution_providers'))
|
||||
)
|
||||
->toArray();
|
||||
}
|
||||
|
||||
protected function setupOctane(): void
|
||||
{
|
||||
$this->app['events']->listen(RequestReceived::class, function () {
|
||||
$this->resetFlareAndLaravelIgnition();
|
||||
});
|
||||
|
||||
$this->app['events']->listen(TaskReceived::class, function () {
|
||||
$this->resetFlareAndLaravelIgnition();
|
||||
});
|
||||
|
||||
$this->app['events']->listen(TickReceived::class, function () {
|
||||
$this->resetFlareAndLaravelIgnition();
|
||||
});
|
||||
|
||||
$this->app['events']->listen(RequestTerminated::class, function () {
|
||||
$this->resetFlareAndLaravelIgnition();
|
||||
});
|
||||
}
|
||||
|
||||
protected function resetFlareAndLaravelIgnition(): void
|
||||
{
|
||||
$this->app->get(SentReports::class)->clear();
|
||||
$this->app->get(Ignition::class)->reset();
|
||||
|
||||
if (config('flare.flare_middleware.' . AddLogs::class)) {
|
||||
$this->app->make(LogRecorder::class)->reset();
|
||||
}
|
||||
|
||||
if (config('flare.flare_middleware.' . AddQueries::class)) {
|
||||
$this->app->make(QueryRecorder::class)->reset();
|
||||
}
|
||||
|
||||
if (config('flare.flare_middleware.' . AddJobs::class)) {
|
||||
$this->app->make(JobRecorder::class)->reset();
|
||||
}
|
||||
|
||||
$this->app->make(DumpRecorder::class)->reset();
|
||||
}
|
||||
}
|
33
vendor/spatie/laravel-ignition/src/Recorders/DumpRecorder/Dump.php
vendored
Normal file
33
vendor/spatie/laravel-ignition/src/Recorders/DumpRecorder/Dump.php
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Recorders\DumpRecorder;
|
||||
|
||||
class Dump
|
||||
{
|
||||
protected string $htmlDump;
|
||||
|
||||
protected ?string $file;
|
||||
|
||||
protected ?int $lineNumber;
|
||||
|
||||
protected float $microtime;
|
||||
|
||||
public function __construct(string $htmlDump, ?string $file, ?int $lineNumber, ?float $microtime = null)
|
||||
{
|
||||
$this->htmlDump = $htmlDump;
|
||||
$this->file = $file;
|
||||
$this->lineNumber = $lineNumber;
|
||||
$this->microtime = $microtime ?? microtime(true);
|
||||
}
|
||||
|
||||
/** @return array<string, mixed> */
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'html_dump' => $this->htmlDump,
|
||||
'file' => $this->file,
|
||||
'line_number' => $this->lineNumber,
|
||||
'microtime' => $this->microtime,
|
||||
];
|
||||
}
|
||||
}
|
22
vendor/spatie/laravel-ignition/src/Recorders/DumpRecorder/DumpHandler.php
vendored
Normal file
22
vendor/spatie/laravel-ignition/src/Recorders/DumpRecorder/DumpHandler.php
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Recorders\DumpRecorder;
|
||||
|
||||
use Symfony\Component\VarDumper\Cloner\VarCloner;
|
||||
|
||||
class DumpHandler
|
||||
{
|
||||
protected DumpRecorder $dumpRecorder;
|
||||
|
||||
public function __construct(DumpRecorder $dumpRecorder)
|
||||
{
|
||||
$this->dumpRecorder = $dumpRecorder;
|
||||
}
|
||||
|
||||
public function dump(mixed $value): void
|
||||
{
|
||||
$data = (new VarCloner)->cloneVar($value);
|
||||
|
||||
$this->dumpRecorder->record($data);
|
||||
}
|
||||
}
|
135
vendor/spatie/laravel-ignition/src/Recorders/DumpRecorder/DumpRecorder.php
vendored
Normal file
135
vendor/spatie/laravel-ignition/src/Recorders/DumpRecorder/DumpRecorder.php
vendored
Normal file
@ -0,0 +1,135 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Recorders\DumpRecorder;
|
||||
|
||||
use Illuminate\Contracts\Foundation\Application;
|
||||
use Illuminate\Support\Arr;
|
||||
use ReflectionMethod;
|
||||
use ReflectionProperty;
|
||||
use Symfony\Component\VarDumper\Cloner\Data;
|
||||
use Symfony\Component\VarDumper\VarDumper;
|
||||
|
||||
class DumpRecorder
|
||||
{
|
||||
/** @var array<array<int,mixed>> */
|
||||
protected array $dumps = [];
|
||||
|
||||
protected Application $app;
|
||||
|
||||
protected static bool $registeredHandler = false;
|
||||
|
||||
public function __construct(Application $app)
|
||||
{
|
||||
$this->app = $app;
|
||||
}
|
||||
|
||||
public function start(): self
|
||||
{
|
||||
$multiDumpHandler = new MultiDumpHandler();
|
||||
|
||||
$this->app->singleton(MultiDumpHandler::class, fn () => $multiDumpHandler);
|
||||
|
||||
if (! self::$registeredHandler) {
|
||||
static::$registeredHandler = true;
|
||||
|
||||
$this->ensureOriginalHandlerExists();
|
||||
|
||||
$originalHandler = VarDumper::setHandler(fn ($dumpedVariable) => $multiDumpHandler->dump($dumpedVariable));
|
||||
|
||||
$multiDumpHandler?->addHandler($originalHandler);
|
||||
|
||||
$multiDumpHandler->addHandler(fn ($var) => (new DumpHandler($this))->dump($var));
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function record(Data $data): void
|
||||
{
|
||||
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 11);
|
||||
|
||||
$sourceFrame = $this->findSourceFrame($backtrace);
|
||||
|
||||
$file = (string) Arr::get($sourceFrame, 'file');
|
||||
$lineNumber = (int) Arr::get($sourceFrame, 'line');
|
||||
|
||||
$htmlDump = (new HtmlDumper())->dump($data);
|
||||
|
||||
$this->dumps[] = new Dump($htmlDump, $file, $lineNumber);
|
||||
}
|
||||
|
||||
public function getDumps(): array
|
||||
{
|
||||
return $this->toArray();
|
||||
}
|
||||
|
||||
public function reset()
|
||||
{
|
||||
$this->dumps = [];
|
||||
}
|
||||
|
||||
public function toArray(): array
|
||||
{
|
||||
$dumps = [];
|
||||
|
||||
foreach ($this->dumps as $dump) {
|
||||
$dumps[] = $dump->toArray();
|
||||
}
|
||||
|
||||
return $dumps;
|
||||
}
|
||||
|
||||
/*
|
||||
* Only the `VarDumper` knows how to create the orignal HTML or CLI VarDumper.
|
||||
* Using reflection and the private VarDumper::register() method we can force it
|
||||
* to create and register a new VarDumper::$handler before we'll overwrite it.
|
||||
* Of course, we only need to do this if there isn't a registered VarDumper::$handler.
|
||||
*
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
protected function ensureOriginalHandlerExists(): void
|
||||
{
|
||||
$reflectionProperty = new ReflectionProperty(VarDumper::class, 'handler');
|
||||
$reflectionProperty->setAccessible(true);
|
||||
$handler = $reflectionProperty->getValue();
|
||||
|
||||
if (! $handler) {
|
||||
// No handler registered yet, so we'll force VarDumper to create one.
|
||||
$reflectionMethod = new ReflectionMethod(VarDumper::class, 'register');
|
||||
$reflectionMethod->setAccessible(true);
|
||||
$reflectionMethod->invoke(null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the first meaningful stack frame that is not the `DumpRecorder` itself.
|
||||
*
|
||||
* @template T of array{class?: class-string, function?: string, line?: int, file?: string}
|
||||
*
|
||||
* @param array<T> $stacktrace
|
||||
*
|
||||
* @return null|T
|
||||
*/
|
||||
protected function findSourceFrame(array $stacktrace): ?array
|
||||
{
|
||||
$seenVarDumper = false;
|
||||
|
||||
foreach ($stacktrace as $frame) {
|
||||
// Keep looping until we're past the VarDumper::dump() call in Symfony's helper functions file.
|
||||
if (Arr::get($frame, 'class') === VarDumper::class && Arr::get($frame, 'function') === 'dump') {
|
||||
$seenVarDumper = true;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (! $seenVarDumper) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Return the next frame in the stack after the VarDumper::dump() call:
|
||||
return $frame;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
34
vendor/spatie/laravel-ignition/src/Recorders/DumpRecorder/HtmlDumper.php
vendored
Normal file
34
vendor/spatie/laravel-ignition/src/Recorders/DumpRecorder/HtmlDumper.php
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Recorders\DumpRecorder;
|
||||
|
||||
use Symfony\Component\VarDumper\Cloner\Data;
|
||||
use Symfony\Component\VarDumper\Cloner\VarCloner;
|
||||
use Symfony\Component\VarDumper\Dumper\HtmlDumper as BaseHtmlDumper;
|
||||
|
||||
class HtmlDumper extends BaseHtmlDumper
|
||||
{
|
||||
public function __construct($output = null, string $charset = null, int $flags = 0)
|
||||
{
|
||||
parent::__construct($output, $charset, $flags);
|
||||
|
||||
$this->setDumpHeader('');
|
||||
}
|
||||
|
||||
public function dumpVariable($variable): string
|
||||
{
|
||||
$cloner = new VarCloner();
|
||||
|
||||
$clonedData = $cloner->cloneVar($variable)->withMaxDepth(3);
|
||||
|
||||
return $this->dump($clonedData);
|
||||
}
|
||||
|
||||
public function dump(Data $data, $output = null, array $extraDisplayOptions = []): string
|
||||
{
|
||||
return (string)parent::dump($data, true, [
|
||||
'maxDepth' => 3,
|
||||
'maxStringLength' => 160,
|
||||
]);
|
||||
}
|
||||
}
|
25
vendor/spatie/laravel-ignition/src/Recorders/DumpRecorder/MultiDumpHandler.php
vendored
Normal file
25
vendor/spatie/laravel-ignition/src/Recorders/DumpRecorder/MultiDumpHandler.php
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Recorders\DumpRecorder;
|
||||
|
||||
class MultiDumpHandler
|
||||
{
|
||||
/** @var array<int, callable|null> */
|
||||
protected array $handlers = [];
|
||||
|
||||
public function dump(mixed $value): void
|
||||
{
|
||||
foreach ($this->handlers as $handler) {
|
||||
if ($handler) {
|
||||
$handler($value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function addHandler(callable $callable = null): self
|
||||
{
|
||||
$this->handlers[] = $callable;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
177
vendor/spatie/laravel-ignition/src/Recorders/JobRecorder/JobRecorder.php
vendored
Normal file
177
vendor/spatie/laravel-ignition/src/Recorders/JobRecorder/JobRecorder.php
vendored
Normal file
@ -0,0 +1,177 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Recorders\JobRecorder;
|
||||
|
||||
use DateTime;
|
||||
use Error;
|
||||
use Exception;
|
||||
use Illuminate\Contracts\Encryption\Encrypter;
|
||||
use Illuminate\Contracts\Foundation\Application;
|
||||
use Illuminate\Contracts\Queue\Job;
|
||||
use Illuminate\Queue\CallQueuedClosure;
|
||||
use Illuminate\Queue\Events\JobExceptionOccurred;
|
||||
use Illuminate\Queue\Jobs\RedisJob;
|
||||
use Illuminate\Support\Str;
|
||||
use ReflectionClass;
|
||||
use ReflectionProperty;
|
||||
use RuntimeException;
|
||||
|
||||
class JobRecorder
|
||||
{
|
||||
protected ?Job $job = null;
|
||||
|
||||
public function __construct(
|
||||
protected Application $app,
|
||||
protected int $maxChainedJobReportingDepth = 5,
|
||||
) {
|
||||
}
|
||||
|
||||
public function start(): self
|
||||
{
|
||||
/** @phpstan-ignore-next-line */
|
||||
$this->app['events']->listen(JobExceptionOccurred::class, [$this, 'record']);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function record(JobExceptionOccurred $event): void
|
||||
{
|
||||
$this->job = $event->job;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>|null
|
||||
*/
|
||||
public function getJob(): ?array
|
||||
{
|
||||
if ($this->job === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return array_merge(
|
||||
$this->getJobProperties(),
|
||||
[
|
||||
'name' => $this->job->resolveName(),
|
||||
'connection' => $this->job->getConnectionName(),
|
||||
'queue' => $this->job->getQueue(),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function reset(): void
|
||||
{
|
||||
$this->job = null;
|
||||
}
|
||||
|
||||
protected function getJobProperties(): array
|
||||
{
|
||||
$payload = collect($this->resolveJobPayload());
|
||||
|
||||
$properties = [];
|
||||
|
||||
foreach ($payload as $key => $value) {
|
||||
if (! in_array($key, ['job', 'data', 'displayName'])) {
|
||||
$properties[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
if (is_string($payload['data'])) {
|
||||
$properties['data'] = json_decode($payload['data'], true, 512, JSON_THROW_ON_ERROR);
|
||||
}
|
||||
} catch (Exception $exception) {
|
||||
}
|
||||
|
||||
if ($pushedAt = DateTime::createFromFormat('U.u', $payload->get('pushedAt', ''))) {
|
||||
$properties['pushedAt'] = $pushedAt->format(DATE_ATOM);
|
||||
}
|
||||
|
||||
try {
|
||||
$properties['data'] = $this->resolveCommandProperties(
|
||||
$this->resolveObjectFromCommand($payload['data']['command']),
|
||||
$this->maxChainedJobReportingDepth
|
||||
);
|
||||
} catch (Exception $exception) {
|
||||
}
|
||||
|
||||
return $properties;
|
||||
}
|
||||
|
||||
protected function resolveJobPayload(): array
|
||||
{
|
||||
if (! $this->job instanceof RedisJob) {
|
||||
return $this->job->payload();
|
||||
}
|
||||
|
||||
try {
|
||||
return json_decode($this->job->getReservedJob(), true, 512, JSON_THROW_ON_ERROR);
|
||||
} catch (Exception $e) {
|
||||
return $this->job->payload();
|
||||
}
|
||||
}
|
||||
|
||||
protected function resolveCommandProperties(object $command, int $maxChainDepth): array
|
||||
{
|
||||
$propertiesToIgnore = ['job', 'closure'];
|
||||
|
||||
$properties = collect((new ReflectionClass($command))->getProperties())
|
||||
->reject(function (ReflectionProperty $property) use ($propertiesToIgnore) {
|
||||
return in_array($property->name, $propertiesToIgnore);
|
||||
})
|
||||
->mapWithKeys(function (ReflectionProperty $property) use ($command) {
|
||||
try {
|
||||
$property->setAccessible(true);
|
||||
|
||||
return [$property->name => $property->getValue($command)];
|
||||
} catch (Error $error) {
|
||||
return [$property->name => 'uninitialized'];
|
||||
}
|
||||
});
|
||||
|
||||
if ($properties->has('chained')) {
|
||||
$properties['chained'] = $this->resolveJobChain($properties->get('chained'), $maxChainDepth);
|
||||
}
|
||||
|
||||
return $properties->all();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $chainedCommands
|
||||
* @param int $maxDepth
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function resolveJobChain(array $chainedCommands, int $maxDepth): array
|
||||
{
|
||||
if ($maxDepth === 0) {
|
||||
return ['Ignition stopped recording jobs after this point since the max chain depth was reached'];
|
||||
}
|
||||
|
||||
return array_map(
|
||||
function (string $command) use ($maxDepth) {
|
||||
$commandObject = $this->resolveObjectFromCommand($command);
|
||||
|
||||
return [
|
||||
'name' => $commandObject instanceof CallQueuedClosure ? $commandObject->displayName() : get_class($commandObject),
|
||||
'data' => $this->resolveCommandProperties($commandObject, $maxDepth - 1),
|
||||
];
|
||||
},
|
||||
$chainedCommands
|
||||
);
|
||||
}
|
||||
|
||||
// Taken from Illuminate\Queue\CallQueuedHandler
|
||||
protected function resolveObjectFromCommand(string $command): object
|
||||
{
|
||||
if (Str::startsWith($command, 'O:')) {
|
||||
return unserialize($command);
|
||||
}
|
||||
|
||||
if ($this->app->bound(Encrypter::class)) {
|
||||
/** @phpstan-ignore-next-line */
|
||||
return unserialize($this->app[Encrypter::class]->decrypt($command));
|
||||
}
|
||||
|
||||
throw new RuntimeException('Unable to extract job payload.');
|
||||
}
|
||||
}
|
55
vendor/spatie/laravel-ignition/src/Recorders/LogRecorder/LogMessage.php
vendored
Normal file
55
vendor/spatie/laravel-ignition/src/Recorders/LogRecorder/LogMessage.php
vendored
Normal file
@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Recorders\LogRecorder;
|
||||
|
||||
use Illuminate\Log\Events\MessageLogged;
|
||||
|
||||
class LogMessage
|
||||
{
|
||||
protected ?string $message;
|
||||
|
||||
protected string $level;
|
||||
|
||||
/** @var array<string, string> */
|
||||
protected array $context = [];
|
||||
|
||||
protected ?float $microtime;
|
||||
|
||||
/**
|
||||
* @param string|null $message
|
||||
* @param string $level
|
||||
* @param array<string, string> $context
|
||||
* @param float|null $microtime
|
||||
*/
|
||||
public function __construct(
|
||||
?string $message,
|
||||
string $level,
|
||||
array $context = [],
|
||||
?float $microtime = null
|
||||
) {
|
||||
$this->message = $message;
|
||||
$this->level = $level;
|
||||
$this->context = $context;
|
||||
$this->microtime = $microtime ?? microtime(true);
|
||||
}
|
||||
|
||||
public static function fromMessageLoggedEvent(MessageLogged $event): self
|
||||
{
|
||||
return new self(
|
||||
$event->message,
|
||||
$event->level,
|
||||
$event->context
|
||||
);
|
||||
}
|
||||
|
||||
/** @return array<string, mixed> */
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'message' => $this->message,
|
||||
'level' => $this->level,
|
||||
'context' => $this->context,
|
||||
'microtime' => $this->microtime,
|
||||
];
|
||||
}
|
||||
}
|
93
vendor/spatie/laravel-ignition/src/Recorders/LogRecorder/LogRecorder.php
vendored
Normal file
93
vendor/spatie/laravel-ignition/src/Recorders/LogRecorder/LogRecorder.php
vendored
Normal file
@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Recorders\LogRecorder;
|
||||
|
||||
use Illuminate\Contracts\Foundation\Application;
|
||||
use Illuminate\Log\Events\MessageLogged;
|
||||
use Throwable;
|
||||
|
||||
class LogRecorder
|
||||
{
|
||||
/** @var \Spatie\LaravelIgnition\Recorders\LogRecorder\LogMessage[] */
|
||||
protected array $logMessages = [];
|
||||
|
||||
protected Application $app;
|
||||
|
||||
protected ?int $maxLogs;
|
||||
|
||||
public function __construct(Application $app, ?int $maxLogs = null)
|
||||
{
|
||||
$this->app = $app;
|
||||
|
||||
$this->maxLogs = $maxLogs;
|
||||
}
|
||||
|
||||
public function start(): self
|
||||
{
|
||||
/** @phpstan-ignore-next-line */
|
||||
$this->app['events']->listen(MessageLogged::class, [$this, 'record']);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function record(MessageLogged $event): void
|
||||
{
|
||||
if ($this->shouldIgnore($event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->logMessages[] = LogMessage::fromMessageLoggedEvent($event);
|
||||
|
||||
if (is_int($this->maxLogs)) {
|
||||
$this->logMessages = array_slice($this->logMessages, -$this->maxLogs);
|
||||
}
|
||||
}
|
||||
|
||||
/** @return array<array<int,string>> */
|
||||
public function getLogMessages(): array
|
||||
{
|
||||
return $this->toArray();
|
||||
}
|
||||
|
||||
/** @return array<int, mixed> */
|
||||
public function toArray(): array
|
||||
{
|
||||
$logMessages = [];
|
||||
|
||||
foreach ($this->logMessages as $log) {
|
||||
$logMessages[] = $log->toArray();
|
||||
}
|
||||
|
||||
return $logMessages;
|
||||
}
|
||||
|
||||
protected function shouldIgnore(mixed $event): bool
|
||||
{
|
||||
if (! isset($event->context['exception'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! $event->context['exception'] instanceof Throwable) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function reset(): void
|
||||
{
|
||||
$this->logMessages = [];
|
||||
}
|
||||
|
||||
public function getMaxLogs(): ?int
|
||||
{
|
||||
return $this->maxLogs;
|
||||
}
|
||||
|
||||
public function setMaxLogs(?int $maxLogs): self
|
||||
{
|
||||
$this->maxLogs = $maxLogs;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
65
vendor/spatie/laravel-ignition/src/Recorders/QueryRecorder/Query.php
vendored
Normal file
65
vendor/spatie/laravel-ignition/src/Recorders/QueryRecorder/Query.php
vendored
Normal file
@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Recorders\QueryRecorder;
|
||||
|
||||
use Illuminate\Database\Events\QueryExecuted;
|
||||
|
||||
class Query
|
||||
{
|
||||
protected string $sql;
|
||||
|
||||
protected float $time;
|
||||
|
||||
protected string $connectionName;
|
||||
|
||||
/** @var array<string, string>|null */
|
||||
protected ?array $bindings;
|
||||
|
||||
protected float $microtime;
|
||||
|
||||
public static function fromQueryExecutedEvent(QueryExecuted $queryExecuted, bool $reportBindings = false): self
|
||||
{
|
||||
return new self(
|
||||
$queryExecuted->sql,
|
||||
$queryExecuted->time,
|
||||
/** @phpstan-ignore-next-line */
|
||||
$queryExecuted->connectionName ?? '',
|
||||
$reportBindings ? $queryExecuted->bindings : null
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sql
|
||||
* @param float $time
|
||||
* @param string $connectionName
|
||||
* @param array<string, string>|null $bindings
|
||||
* @param float|null $microtime
|
||||
*/
|
||||
protected function __construct(
|
||||
string $sql,
|
||||
float $time,
|
||||
string $connectionName,
|
||||
?array $bindings = null,
|
||||
?float $microtime = null
|
||||
) {
|
||||
$this->sql = $sql;
|
||||
$this->time = $time;
|
||||
$this->connectionName = $connectionName;
|
||||
$this->bindings = $bindings;
|
||||
$this->microtime = $microtime ?? microtime(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'sql' => $this->sql,
|
||||
'time' => $this->time,
|
||||
'connection_name' => $this->connectionName,
|
||||
'bindings' => $this->bindings,
|
||||
'microtime' => $this->microtime,
|
||||
];
|
||||
}
|
||||
}
|
88
vendor/spatie/laravel-ignition/src/Recorders/QueryRecorder/QueryRecorder.php
vendored
Normal file
88
vendor/spatie/laravel-ignition/src/Recorders/QueryRecorder/QueryRecorder.php
vendored
Normal file
@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Recorders\QueryRecorder;
|
||||
|
||||
use Illuminate\Contracts\Foundation\Application;
|
||||
use Illuminate\Database\Events\QueryExecuted;
|
||||
|
||||
class QueryRecorder
|
||||
{
|
||||
/** @var \Spatie\LaravelIgnition\Recorders\QueryRecorder\Query[] */
|
||||
protected array $queries = [];
|
||||
|
||||
protected Application $app;
|
||||
|
||||
protected bool $reportBindings = true;
|
||||
|
||||
protected ?int $maxQueries;
|
||||
|
||||
public function __construct(
|
||||
Application $app,
|
||||
bool $reportBindings = true,
|
||||
?int $maxQueries = 200
|
||||
) {
|
||||
$this->app = $app;
|
||||
$this->reportBindings = $reportBindings;
|
||||
$this->maxQueries = $maxQueries;
|
||||
}
|
||||
|
||||
public function start(): self
|
||||
{
|
||||
/** @phpstan-ignore-next-line */
|
||||
$this->app['events']->listen(QueryExecuted::class, [$this, 'record']);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function record(QueryExecuted $queryExecuted): void
|
||||
{
|
||||
$this->queries[] = Query::fromQueryExecutedEvent($queryExecuted, $this->reportBindings);
|
||||
|
||||
if (is_int($this->maxQueries)) {
|
||||
$this->queries = array_slice($this->queries, -$this->maxQueries);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
public function getQueries(): array
|
||||
{
|
||||
$queries = [];
|
||||
|
||||
foreach ($this->queries as $query) {
|
||||
$queries[] = $query->toArray();
|
||||
}
|
||||
|
||||
return $queries;
|
||||
}
|
||||
|
||||
public function reset(): void
|
||||
{
|
||||
$this->queries = [];
|
||||
}
|
||||
|
||||
public function getReportBindings(): bool
|
||||
{
|
||||
return $this->reportBindings;
|
||||
}
|
||||
|
||||
public function setReportBindings(bool $reportBindings): self
|
||||
{
|
||||
$this->reportBindings = $reportBindings;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getMaxQueries(): ?int
|
||||
{
|
||||
return $this->maxQueries;
|
||||
}
|
||||
|
||||
public function setMaxQueries(?int $maxQueries): self
|
||||
{
|
||||
$this->maxQueries = $maxQueries;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
41
vendor/spatie/laravel-ignition/src/Renderers/ErrorPageRenderer.php
vendored
Normal file
41
vendor/spatie/laravel-ignition/src/Renderers/ErrorPageRenderer.php
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Renderers;
|
||||
|
||||
use Spatie\FlareClient\Flare;
|
||||
use Spatie\Ignition\Config\IgnitionConfig;
|
||||
use Spatie\Ignition\Contracts\SolutionProviderRepository;
|
||||
use Spatie\Ignition\Ignition;
|
||||
use Spatie\LaravelIgnition\ContextProviders\LaravelContextProviderDetector;
|
||||
use Spatie\LaravelIgnition\Solutions\SolutionTransformers\LaravelSolutionTransformer;
|
||||
use Spatie\LaravelIgnition\Support\LaravelDocumentationLinkFinder;
|
||||
use Throwable;
|
||||
|
||||
class ErrorPageRenderer
|
||||
{
|
||||
public function render(Throwable $throwable): void
|
||||
{
|
||||
$viteJsAutoRefresh = '';
|
||||
|
||||
if (class_exists('Illuminate\Foundation\Vite')) {
|
||||
$vite = app(\Illuminate\Foundation\Vite::class);
|
||||
|
||||
if (is_file($vite->hotFile())) {
|
||||
$viteJsAutoRefresh = $vite->__invoke([]);
|
||||
}
|
||||
}
|
||||
|
||||
app(Ignition::class)
|
||||
->resolveDocumentationLink(
|
||||
fn (Throwable $throwable) => (new LaravelDocumentationLinkFinder())->findLinkForThrowable($throwable)
|
||||
)
|
||||
->setFlare(app(Flare::class))
|
||||
->setConfig(app(IgnitionConfig::class))
|
||||
->setSolutionProviderRepository(app(SolutionProviderRepository::class))
|
||||
->setContextProviderDetector(new LaravelContextProviderDetector())
|
||||
->setSolutionTransformerClass(LaravelSolutionTransformer::class)
|
||||
->applicationPath(base_path())
|
||||
->addCustomHtmlToHead($viteJsAutoRefresh)
|
||||
->renderException($throwable);
|
||||
}
|
||||
}
|
24
vendor/spatie/laravel-ignition/src/Renderers/IgnitionExceptionRenderer.php
vendored
Normal file
24
vendor/spatie/laravel-ignition/src/Renderers/IgnitionExceptionRenderer.php
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Renderers;
|
||||
|
||||
use Illuminate\Contracts\Foundation\ExceptionRenderer;
|
||||
|
||||
class IgnitionExceptionRenderer implements ExceptionRenderer
|
||||
{
|
||||
protected ErrorPageRenderer $errorPageHandler;
|
||||
|
||||
public function __construct(ErrorPageRenderer $errorPageHandler)
|
||||
{
|
||||
$this->errorPageHandler = $errorPageHandler;
|
||||
}
|
||||
|
||||
public function render($throwable)
|
||||
{
|
||||
ob_start();
|
||||
|
||||
$this->errorPageHandler->render($throwable);
|
||||
|
||||
return ob_get_clean();
|
||||
}
|
||||
}
|
46
vendor/spatie/laravel-ignition/src/Solutions/GenerateAppKeySolution.php
vendored
Normal file
46
vendor/spatie/laravel-ignition/src/Solutions/GenerateAppKeySolution.php
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Solutions;
|
||||
|
||||
use Illuminate\Support\Facades\Artisan;
|
||||
use Spatie\Ignition\Contracts\RunnableSolution;
|
||||
|
||||
class GenerateAppKeySolution implements RunnableSolution
|
||||
{
|
||||
public function getSolutionTitle(): string
|
||||
{
|
||||
return 'Your app key is missing';
|
||||
}
|
||||
|
||||
public function getDocumentationLinks(): array
|
||||
{
|
||||
return [
|
||||
'Laravel installation' => 'https://laravel.com/docs/master/installation#configuration',
|
||||
];
|
||||
}
|
||||
|
||||
public function getSolutionActionDescription(): string
|
||||
{
|
||||
return 'Generate your application encryption key using `php artisan key:generate`.';
|
||||
}
|
||||
|
||||
public function getRunButtonText(): string
|
||||
{
|
||||
return 'Generate app key';
|
||||
}
|
||||
|
||||
public function getSolutionDescription(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function getRunParameters(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function run(array $parameters = []): void
|
||||
{
|
||||
Artisan::call('key:generate');
|
||||
}
|
||||
}
|
53
vendor/spatie/laravel-ignition/src/Solutions/LivewireDiscoverSolution.php
vendored
Normal file
53
vendor/spatie/laravel-ignition/src/Solutions/LivewireDiscoverSolution.php
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Solutions;
|
||||
|
||||
use Livewire\LivewireComponentsFinder;
|
||||
use Spatie\Ignition\Contracts\RunnableSolution;
|
||||
|
||||
class LivewireDiscoverSolution implements RunnableSolution
|
||||
{
|
||||
protected string $customTitle;
|
||||
|
||||
public function __construct(string $customTitle = '')
|
||||
{
|
||||
$this->customTitle = $customTitle;
|
||||
}
|
||||
|
||||
public function getSolutionTitle(): string
|
||||
{
|
||||
return $this->customTitle;
|
||||
}
|
||||
|
||||
public function getSolutionDescription(): string
|
||||
{
|
||||
return 'You might have forgotten to discover your Livewire components.';
|
||||
}
|
||||
|
||||
public function getDocumentationLinks(): array
|
||||
{
|
||||
return [
|
||||
'Livewire: Artisan Commands' => 'https://laravel-livewire.com/docs/2.x/artisan-commands',
|
||||
];
|
||||
}
|
||||
|
||||
public function getRunParameters(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getSolutionActionDescription(): string
|
||||
{
|
||||
return 'You can discover your Livewire components using `php artisan livewire:discover`.';
|
||||
}
|
||||
|
||||
public function getRunButtonText(): string
|
||||
{
|
||||
return 'Run livewire:discover';
|
||||
}
|
||||
|
||||
public function run(array $parameters = []): void
|
||||
{
|
||||
app(LivewireComponentsFinder::class)->build();
|
||||
}
|
||||
}
|
142
vendor/spatie/laravel-ignition/src/Solutions/MakeViewVariableOptionalSolution.php
vendored
Normal file
142
vendor/spatie/laravel-ignition/src/Solutions/MakeViewVariableOptionalSolution.php
vendored
Normal file
@ -0,0 +1,142 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Solutions;
|
||||
|
||||
use Illuminate\Support\Facades\Blade;
|
||||
use Illuminate\Support\Str;
|
||||
use Spatie\Ignition\Contracts\RunnableSolution;
|
||||
|
||||
class MakeViewVariableOptionalSolution implements RunnableSolution
|
||||
{
|
||||
protected ?string $variableName;
|
||||
|
||||
protected ?string $viewFile;
|
||||
|
||||
public function __construct(string $variableName = null, string $viewFile = null)
|
||||
{
|
||||
$this->variableName = $variableName;
|
||||
|
||||
$this->viewFile = $viewFile;
|
||||
}
|
||||
|
||||
public function getSolutionTitle(): string
|
||||
{
|
||||
return "$$this->variableName is undefined";
|
||||
}
|
||||
|
||||
public function getDocumentationLinks(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getSolutionActionDescription(): string
|
||||
{
|
||||
$output = [
|
||||
'Make the variable optional in the blade template.',
|
||||
"Replace `{{ $$this->variableName }}` with `{{ $$this->variableName ?? '' }}`",
|
||||
];
|
||||
|
||||
return implode(PHP_EOL, $output);
|
||||
}
|
||||
|
||||
public function getRunButtonText(): string
|
||||
{
|
||||
return 'Make variable optional';
|
||||
}
|
||||
|
||||
public function getSolutionDescription(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function getRunParameters(): array
|
||||
{
|
||||
return [
|
||||
'variableName' => $this->variableName,
|
||||
'viewFile' => $this->viewFile,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $parameters
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isRunnable(array $parameters = []): bool
|
||||
{
|
||||
return $this->makeOptional($this->getRunParameters()) !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, string> $parameters
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function run(array $parameters = []): void
|
||||
{
|
||||
$output = $this->makeOptional($parameters);
|
||||
if ($output !== false) {
|
||||
file_put_contents($parameters['viewFile'], $output);
|
||||
}
|
||||
}
|
||||
|
||||
protected function isSafePath(string $path): bool
|
||||
{
|
||||
if (! Str::startsWith($path, ['/', './'])) {
|
||||
return false;
|
||||
}
|
||||
if (! Str::endsWith($path, '.blade.php')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, string> $parameters
|
||||
*
|
||||
* @return bool|string
|
||||
*/
|
||||
public function makeOptional(array $parameters = []): bool|string
|
||||
{
|
||||
if (! $this->isSafePath($parameters['viewFile'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$originalContents = (string)file_get_contents($parameters['viewFile']);
|
||||
$newContents = str_replace('$'.$parameters['variableName'], '$'.$parameters['variableName']." ?? ''", $originalContents);
|
||||
|
||||
$originalTokens = token_get_all(Blade::compileString($originalContents));
|
||||
$newTokens = token_get_all(Blade::compileString($newContents));
|
||||
|
||||
$expectedTokens = $this->generateExpectedTokens($originalTokens, $parameters['variableName']);
|
||||
|
||||
if ($expectedTokens !== $newTokens) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $newContents;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, mixed> $originalTokens
|
||||
* @param string $variableName
|
||||
*
|
||||
* @return array<int, mixed>
|
||||
*/
|
||||
protected function generateExpectedTokens(array $originalTokens, string $variableName): array
|
||||
{
|
||||
$expectedTokens = [];
|
||||
foreach ($originalTokens as $token) {
|
||||
$expectedTokens[] = $token;
|
||||
if ($token[0] === T_VARIABLE && $token[1] === '$'.$variableName) {
|
||||
$expectedTokens[] = [T_WHITESPACE, ' ', $token[2]];
|
||||
$expectedTokens[] = [T_COALESCE, '??', $token[2]];
|
||||
$expectedTokens[] = [T_WHITESPACE, ' ', $token[2]];
|
||||
$expectedTokens[] = [T_CONSTANT_ENCAPSED_STRING, "''", $token[2]];
|
||||
}
|
||||
}
|
||||
|
||||
return $expectedTokens;
|
||||
}
|
||||
}
|
53
vendor/spatie/laravel-ignition/src/Solutions/RunMigrationsSolution.php
vendored
Normal file
53
vendor/spatie/laravel-ignition/src/Solutions/RunMigrationsSolution.php
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Solutions;
|
||||
|
||||
use Illuminate\Support\Facades\Artisan;
|
||||
use Spatie\Ignition\Contracts\RunnableSolution;
|
||||
|
||||
class RunMigrationsSolution implements RunnableSolution
|
||||
{
|
||||
protected string $customTitle;
|
||||
|
||||
public function __construct(string $customTitle = '')
|
||||
{
|
||||
$this->customTitle = $customTitle;
|
||||
}
|
||||
|
||||
public function getSolutionTitle(): string
|
||||
{
|
||||
return $this->customTitle;
|
||||
}
|
||||
|
||||
public function getSolutionDescription(): string
|
||||
{
|
||||
return 'You might have forgotten to run your database migrations.';
|
||||
}
|
||||
|
||||
public function getDocumentationLinks(): array
|
||||
{
|
||||
return [
|
||||
'Database: Running Migrations docs' => 'https://laravel.com/docs/master/migrations#running-migrations',
|
||||
];
|
||||
}
|
||||
|
||||
public function getRunParameters(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getSolutionActionDescription(): string
|
||||
{
|
||||
return 'You can try to run your migrations using `php artisan migrate`.';
|
||||
}
|
||||
|
||||
public function getRunButtonText(): string
|
||||
{
|
||||
return 'Run migrations';
|
||||
}
|
||||
|
||||
public function run(array $parameters = []): void
|
||||
{
|
||||
Artisan::call('migrate');
|
||||
}
|
||||
}
|
35
vendor/spatie/laravel-ignition/src/Solutions/SolutionProviders/DefaultDbNameSolutionProvider.php
vendored
Normal file
35
vendor/spatie/laravel-ignition/src/Solutions/SolutionProviders/DefaultDbNameSolutionProvider.php
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Solutions\SolutionProviders;
|
||||
|
||||
use Illuminate\Database\QueryException;
|
||||
use Spatie\Ignition\Contracts\HasSolutionsForThrowable;
|
||||
use Spatie\LaravelIgnition\Solutions\SuggestUsingCorrectDbNameSolution;
|
||||
use Throwable;
|
||||
|
||||
class DefaultDbNameSolutionProvider implements HasSolutionsForThrowable
|
||||
{
|
||||
const MYSQL_UNKNOWN_DATABASE_CODE = 1049;
|
||||
|
||||
public function canSolve(Throwable $throwable): bool
|
||||
{
|
||||
if (! $throwable instanceof QueryException) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($throwable->getCode() !== self::MYSQL_UNKNOWN_DATABASE_CODE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! in_array(env('DB_DATABASE'), ['homestead', 'laravel'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getSolutions(Throwable $throwable): array
|
||||
{
|
||||
return [new SuggestUsingCorrectDbNameSolution()];
|
||||
}
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Solutions\SolutionProviders;
|
||||
|
||||
use Illuminate\Broadcasting\BroadcastException;
|
||||
use Spatie\Ignition\Contracts\BaseSolution;
|
||||
use Spatie\Ignition\Contracts\HasSolutionsForThrowable;
|
||||
use Spatie\LaravelIgnition\Support\LaravelVersion;
|
||||
use Throwable;
|
||||
|
||||
class GenericLaravelExceptionSolutionProvider implements HasSolutionsForThrowable
|
||||
{
|
||||
public function canSolve(Throwable $throwable): bool
|
||||
{
|
||||
return ! is_null($this->getSolutionTexts($throwable));
|
||||
}
|
||||
|
||||
public function getSolutions(Throwable $throwable): array
|
||||
{
|
||||
if (! $texts = $this->getSolutionTexts($throwable)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$solution = BaseSolution::create($texts['title'])
|
||||
->setSolutionDescription($texts['description'])
|
||||
->setDocumentationLinks($texts['links']);
|
||||
|
||||
return ([$solution]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Throwable $throwable
|
||||
*
|
||||
* @return array<string, mixed>|null
|
||||
*/
|
||||
protected function getSolutionTexts(Throwable $throwable) : ?array
|
||||
{
|
||||
foreach ($this->getSupportedExceptions() as $supportedClass => $texts) {
|
||||
if ($throwable instanceof $supportedClass) {
|
||||
return $texts;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @return array<string, mixed> */
|
||||
protected function getSupportedExceptions(): array
|
||||
{
|
||||
$majorVersion = LaravelVersion::major();
|
||||
|
||||
return
|
||||
[
|
||||
BroadcastException::class => [
|
||||
'title' => 'Here are some links that might help solve this problem',
|
||||
'description' => '',
|
||||
'links' => [
|
||||
'Laravel docs on authentication' => "https://laravel.com/docs/{$majorVersion}.x/authentication",
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Solutions\SolutionProviders;
|
||||
|
||||
use Illuminate\Database\QueryException;
|
||||
use Spatie\Ignition\Contracts\HasSolutionsForThrowable;
|
||||
use Spatie\LaravelIgnition\Solutions\UseDefaultValetDbCredentialsSolution;
|
||||
use Throwable;
|
||||
|
||||
class IncorrectValetDbCredentialsSolutionProvider implements HasSolutionsForThrowable
|
||||
{
|
||||
const MYSQL_ACCESS_DENIED_CODE = 1045;
|
||||
|
||||
public function canSolve(Throwable $throwable): bool
|
||||
{
|
||||
if (PHP_OS !== 'Darwin') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! $throwable instanceof QueryException) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! $this->isAccessDeniedCode($throwable->getCode())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! $this->envFileExists()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! $this->isValetInstalled()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->usingCorrectDefaultCredentials()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getSolutions(Throwable $throwable): array
|
||||
{
|
||||
return [new UseDefaultValetDbCredentialsSolution()];
|
||||
}
|
||||
|
||||
protected function envFileExists(): bool
|
||||
{
|
||||
return file_exists(base_path('.env'));
|
||||
}
|
||||
|
||||
protected function isAccessDeniedCode(string $code): bool
|
||||
{
|
||||
return $code === static::MYSQL_ACCESS_DENIED_CODE;
|
||||
}
|
||||
|
||||
protected function isValetInstalled(): bool
|
||||
{
|
||||
return file_exists('/usr/local/bin/valet');
|
||||
}
|
||||
|
||||
protected function usingCorrectDefaultCredentials(): bool
|
||||
{
|
||||
return env('DB_USERNAME') === 'root' && env('DB_PASSWORD') === '';
|
||||
}
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Solutions\SolutionProviders;
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
use Spatie\Ignition\Contracts\BaseSolution;
|
||||
use Spatie\Ignition\Contracts\HasSolutionsForThrowable;
|
||||
use Spatie\LaravelIgnition\Support\Composer\ComposerClassMap;
|
||||
use Spatie\LaravelIgnition\Support\StringComparator;
|
||||
use Throwable;
|
||||
use UnexpectedValueException;
|
||||
|
||||
class InvalidRouteActionSolutionProvider implements HasSolutionsForThrowable
|
||||
{
|
||||
protected const REGEX = '/\[([a-zA-Z\\\\]+)\]/m';
|
||||
|
||||
public function canSolve(Throwable $throwable): bool
|
||||
{
|
||||
if (! $throwable instanceof UnexpectedValueException) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! preg_match(self::REGEX, $throwable->getMessage(), $matches)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Str::startsWith($throwable->getMessage(), 'Invalid route action: ');
|
||||
}
|
||||
|
||||
public function getSolutions(Throwable $throwable): array
|
||||
{
|
||||
preg_match(self::REGEX, $throwable->getMessage(), $matches);
|
||||
|
||||
$invalidController = $matches[1] ?? null;
|
||||
|
||||
$suggestedController = $this->findRelatedController($invalidController);
|
||||
|
||||
if ($suggestedController === $invalidController) {
|
||||
return [
|
||||
BaseSolution::create("`{$invalidController}` is not invokable.")
|
||||
->setSolutionDescription("The controller class `{$invalidController}` is not invokable. Did you forget to add the `__invoke` method or is the controller's method missing in your routes file?"),
|
||||
];
|
||||
}
|
||||
|
||||
if ($suggestedController) {
|
||||
return [
|
||||
BaseSolution::create("`{$invalidController}` was not found.")
|
||||
->setSolutionDescription("Controller class `{$invalidController}` for one of your routes was not found. Did you mean `{$suggestedController}`?"),
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
BaseSolution::create("`{$invalidController}` was not found.")
|
||||
->setSolutionDescription("Controller class `{$invalidController}` for one of your routes was not found. Are you sure this controller exists and is imported correctly?"),
|
||||
];
|
||||
}
|
||||
|
||||
protected function findRelatedController(string $invalidController): ?string
|
||||
{
|
||||
$composerClassMap = app(ComposerClassMap::class);
|
||||
|
||||
$controllers = collect($composerClassMap->listClasses())
|
||||
->filter(function (string $file, string $fqcn) {
|
||||
return Str::endsWith($fqcn, 'Controller');
|
||||
})
|
||||
->mapWithKeys(function (string $file, string $fqcn) {
|
||||
return [$fqcn => class_basename($fqcn)];
|
||||
})
|
||||
->toArray();
|
||||
|
||||
$basenameMatch = StringComparator::findClosestMatch($controllers, $invalidController, 4);
|
||||
|
||||
$controllers = array_flip($controllers);
|
||||
|
||||
$fqcnMatch = StringComparator::findClosestMatch($controllers, $invalidController, 4);
|
||||
|
||||
return $fqcnMatch ?? $basenameMatch;
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Solutions\SolutionProviders;
|
||||
|
||||
use Illuminate\Database\LazyLoadingViolationException;
|
||||
use Spatie\Ignition\Contracts\BaseSolution;
|
||||
use Spatie\Ignition\Contracts\HasSolutionsForThrowable;
|
||||
use Spatie\LaravelIgnition\Support\LaravelVersion;
|
||||
use Throwable;
|
||||
|
||||
class LazyLoadingViolationSolutionProvider implements HasSolutionsForThrowable
|
||||
{
|
||||
public function canSolve(Throwable $throwable): bool
|
||||
{
|
||||
if ($throwable instanceof LazyLoadingViolationException) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (! $previous = $throwable->getPrevious()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $previous instanceof LazyLoadingViolationException;
|
||||
}
|
||||
|
||||
public function getSolutions(Throwable $throwable): array
|
||||
{
|
||||
$majorVersion = LaravelVersion::major();
|
||||
|
||||
return [BaseSolution::create(
|
||||
'Lazy loading was disabled to detect N+1 problems'
|
||||
)
|
||||
->setSolutionDescription(
|
||||
'Either avoid lazy loading the relation or allow lazy loading.'
|
||||
)
|
||||
->setDocumentationLinks([
|
||||
'Read the docs on preventing lazy loading' => "https://laravel.com/docs/{$majorVersion}.x/eloquent-relationships#preventing-lazy-loading",
|
||||
'Watch a video on how to deal with the N+1 problem' => 'https://www.youtube.com/watch?v=ZE7KBeraVpc',
|
||||
]),];
|
||||
}
|
||||
}
|
25
vendor/spatie/laravel-ignition/src/Solutions/SolutionProviders/MissingAppKeySolutionProvider.php
vendored
Normal file
25
vendor/spatie/laravel-ignition/src/Solutions/SolutionProviders/MissingAppKeySolutionProvider.php
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Solutions\SolutionProviders;
|
||||
|
||||
use RuntimeException;
|
||||
use Spatie\Ignition\Contracts\HasSolutionsForThrowable;
|
||||
use Spatie\LaravelIgnition\Solutions\GenerateAppKeySolution;
|
||||
use Throwable;
|
||||
|
||||
class MissingAppKeySolutionProvider implements HasSolutionsForThrowable
|
||||
{
|
||||
public function canSolve(Throwable $throwable): bool
|
||||
{
|
||||
if (! $throwable instanceof RuntimeException) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $throwable->getMessage() === 'No application encryption key has been specified.';
|
||||
}
|
||||
|
||||
public function getSolutions(Throwable $throwable): array
|
||||
{
|
||||
return [new GenerateAppKeySolution()];
|
||||
}
|
||||
}
|
35
vendor/spatie/laravel-ignition/src/Solutions/SolutionProviders/MissingColumnSolutionProvider.php
vendored
Normal file
35
vendor/spatie/laravel-ignition/src/Solutions/SolutionProviders/MissingColumnSolutionProvider.php
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Solutions\SolutionProviders;
|
||||
|
||||
use Illuminate\Database\QueryException;
|
||||
use Spatie\Ignition\Contracts\HasSolutionsForThrowable;
|
||||
use Spatie\LaravelIgnition\Solutions\RunMigrationsSolution;
|
||||
use Throwable;
|
||||
|
||||
class MissingColumnSolutionProvider implements HasSolutionsForThrowable
|
||||
{
|
||||
/**
|
||||
* See https://dev.mysql.com/doc/refman/8.0/en/server-error-reference.html#error_er_bad_field_error.
|
||||
*/
|
||||
const MYSQL_BAD_FIELD_CODE = '42S22';
|
||||
|
||||
public function canSolve(Throwable $throwable): bool
|
||||
{
|
||||
if (! $throwable instanceof QueryException) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->isBadTableErrorCode($throwable->getCode());
|
||||
}
|
||||
|
||||
protected function isBadTableErrorCode(string $code): bool
|
||||
{
|
||||
return $code === static::MYSQL_BAD_FIELD_CODE;
|
||||
}
|
||||
|
||||
public function getSolutions(Throwable $throwable): array
|
||||
{
|
||||
return [new RunMigrationsSolution('A column was not found')];
|
||||
}
|
||||
}
|
55
vendor/spatie/laravel-ignition/src/Solutions/SolutionProviders/MissingImportSolutionProvider.php
vendored
Normal file
55
vendor/spatie/laravel-ignition/src/Solutions/SolutionProviders/MissingImportSolutionProvider.php
vendored
Normal file
@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Solutions\SolutionProviders;
|
||||
|
||||
use Spatie\Ignition\Contracts\HasSolutionsForThrowable;
|
||||
use Spatie\LaravelIgnition\Solutions\SuggestImportSolution;
|
||||
use Spatie\LaravelIgnition\Support\Composer\ComposerClassMap;
|
||||
use Throwable;
|
||||
|
||||
class MissingImportSolutionProvider implements HasSolutionsForThrowable
|
||||
{
|
||||
protected ?string $foundClass;
|
||||
|
||||
protected ComposerClassMap $composerClassMap;
|
||||
|
||||
public function canSolve(Throwable $throwable): bool
|
||||
{
|
||||
$pattern = '/Class \"([^\s]+)\" not found/m';
|
||||
|
||||
if (! preg_match($pattern, $throwable->getMessage(), $matches)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$class = $matches[1];
|
||||
|
||||
$this->composerClassMap = new ComposerClassMap();
|
||||
|
||||
$this->search($class);
|
||||
|
||||
return ! is_null($this->foundClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Throwable $throwable
|
||||
*
|
||||
* @return array<int, SuggestImportSolution>
|
||||
*/
|
||||
public function getSolutions(Throwable $throwable): array
|
||||
{
|
||||
if (is_null($this->foundClass)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [new SuggestImportSolution($this->foundClass)];
|
||||
}
|
||||
|
||||
protected function search(string $missingClass): void
|
||||
{
|
||||
$this->foundClass = $this->composerClassMap->searchClassMap($missingClass);
|
||||
|
||||
if (is_null($this->foundClass)) {
|
||||
$this->foundClass = $this->composerClassMap->searchPsrMaps($missingClass);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Solutions\SolutionProviders;
|
||||
|
||||
use Livewire\Exceptions\ComponentNotFoundException;
|
||||
use Livewire\LivewireComponentsFinder;
|
||||
use Spatie\Ignition\Contracts\HasSolutionsForThrowable;
|
||||
use Spatie\LaravelIgnition\Solutions\LivewireDiscoverSolution;
|
||||
use Throwable;
|
||||
|
||||
class MissingLivewireComponentSolutionProvider implements HasSolutionsForThrowable
|
||||
{
|
||||
public function canSolve(Throwable $throwable): bool
|
||||
{
|
||||
if (! $this->livewireIsInstalled()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! $throwable instanceof ComponentNotFoundException) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getSolutions(Throwable $throwable): array
|
||||
{
|
||||
return [new LivewireDiscoverSolution('A Livewire component was not found')];
|
||||
}
|
||||
|
||||
public function livewireIsInstalled(): bool
|
||||
{
|
||||
if (! class_exists(ComponentNotFoundException::class)) {
|
||||
return false;
|
||||
}
|
||||
if (! class_exists(LivewireComponentsFinder::class)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace Spatie\LaravelIgnition\Solutions\SolutionProviders;
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
use Spatie\Ignition\Contracts\BaseSolution;
|
||||
use Spatie\Ignition\Contracts\HasSolutionsForThrowable;
|
||||
use Throwable;
|
||||
|
||||
class MissingMixManifestSolutionProvider implements HasSolutionsForThrowable
|
||||
{
|
||||
public function canSolve(Throwable $throwable): bool
|
||||
{
|
||||
return Str::startsWith($throwable->getMessage(), 'Mix manifest not found');
|
||||
}
|
||||
|
||||
public function getSolutions(Throwable $throwable): array
|
||||
{
|
||||
return [
|
||||
BaseSolution::create('Missing Mix Manifest File')
|
||||
->setSolutionDescription('Did you forget to run `npm install && npm run dev`?'),
|
||||
];
|
||||
}
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Solutions\SolutionProviders;
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
use Spatie\Ignition\Contracts\BaseSolution;
|
||||
use Spatie\Ignition\Contracts\HasSolutionsForThrowable;
|
||||
use Spatie\Ignition\Contracts\Solution;
|
||||
use Throwable;
|
||||
|
||||
class MissingViteManifestSolutionProvider implements HasSolutionsForThrowable
|
||||
{
|
||||
/** @var array<string, string> */
|
||||
protected array $links = [
|
||||
'Asset bundling with Vite' => 'https://laravel.com/docs/9.x/vite#running-vite',
|
||||
];
|
||||
|
||||
public function canSolve(Throwable $throwable): bool
|
||||
{
|
||||
return Str::startsWith($throwable->getMessage(), 'Vite manifest not found');
|
||||
}
|
||||
|
||||
public function getSolutions(Throwable $throwable): array
|
||||
{
|
||||
return [
|
||||
$this->getSolution(),
|
||||
];
|
||||
}
|
||||
|
||||
public function getSolution(): Solution
|
||||
{
|
||||
/** @var string */
|
||||
$baseCommand = collect([
|
||||
'pnpm-lock.yaml' => 'pnpm',
|
||||
'yarn.lock' => 'yarn',
|
||||
])->first(fn ($_, $lockfile) => file_exists(base_path($lockfile)), 'npm run');
|
||||
|
||||
return app()->environment('local')
|
||||
? $this->getLocalSolution($baseCommand)
|
||||
: $this->getProductionSolution($baseCommand);
|
||||
}
|
||||
|
||||
protected function getLocalSolution(string $baseCommand): Solution
|
||||
{
|
||||
return BaseSolution::create('Start the development server')
|
||||
->setSolutionDescription("Run `{$baseCommand} dev` in your terminal and refresh the page.")
|
||||
->setDocumentationLinks($this->links);
|
||||
}
|
||||
|
||||
protected function getProductionSolution(string $baseCommand): Solution
|
||||
{
|
||||
return BaseSolution::create('Build the production assets')
|
||||
->setSolutionDescription("Run `{$baseCommand} build` in your deployment script.")
|
||||
->setDocumentationLinks($this->links);
|
||||
}
|
||||
}
|
38
vendor/spatie/laravel-ignition/src/Solutions/SolutionProviders/OpenAiSolutionProvider.php
vendored
Normal file
38
vendor/spatie/laravel-ignition/src/Solutions/SolutionProviders/OpenAiSolutionProvider.php
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Solutions\SolutionProviders;
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
use OpenAI\Client;
|
||||
use Spatie\Ignition\Contracts\HasSolutionsForThrowable;
|
||||
use Spatie\Ignition\Solutions\OpenAi\OpenAiSolutionProvider as BaseOpenAiSolutionProvider;
|
||||
use Throwable;
|
||||
|
||||
class OpenAiSolutionProvider implements HasSolutionsForThrowable
|
||||
{
|
||||
public function canSolve(Throwable $throwable): bool
|
||||
{
|
||||
if (! class_exists(Client::class)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (config('ignition.open_ai_key') === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getSolutions(Throwable $throwable): array
|
||||
{
|
||||
$solutionProvider = new BaseOpenAiSolutionProvider(
|
||||
openAiKey: config('ignition.open_ai_key'),
|
||||
cache: cache()->store(config('cache.default')),
|
||||
cacheTtlInSeconds: 60,
|
||||
applicationType: 'Laravel ' . Str::before(app()->version(), '.'),
|
||||
applicationPath: base_path(),
|
||||
);
|
||||
|
||||
return $solutionProvider->getSolutions($throwable);
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Solutions\SolutionProviders;
|
||||
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Spatie\Ignition\Contracts\BaseSolution;
|
||||
use Spatie\Ignition\Contracts\HasSolutionsForThrowable;
|
||||
use Spatie\LaravelIgnition\Support\StringComparator;
|
||||
use Symfony\Component\Routing\Exception\RouteNotFoundException;
|
||||
use Throwable;
|
||||
|
||||
class RouteNotDefinedSolutionProvider implements HasSolutionsForThrowable
|
||||
{
|
||||
protected const REGEX = '/Route \[(.*)\] not defined/m';
|
||||
|
||||
public function canSolve(Throwable $throwable): bool
|
||||
{
|
||||
if (! $throwable instanceof RouteNotFoundException) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (bool)preg_match(self::REGEX, $throwable->getMessage(), $matches);
|
||||
}
|
||||
|
||||
public function getSolutions(Throwable $throwable): array
|
||||
{
|
||||
preg_match(self::REGEX, $throwable->getMessage(), $matches);
|
||||
|
||||
$missingRoute = $matches[1] ?? '';
|
||||
|
||||
$suggestedRoute = $this->findRelatedRoute($missingRoute);
|
||||
|
||||
if ($suggestedRoute) {
|
||||
return [
|
||||
BaseSolution::create("{$missingRoute} was not defined.")
|
||||
->setSolutionDescription("Did you mean `{$suggestedRoute}`?"),
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
BaseSolution::create("{$missingRoute} was not defined.")
|
||||
->setSolutionDescription('Are you sure that the route is defined'),
|
||||
];
|
||||
}
|
||||
|
||||
protected function findRelatedRoute(string $missingRoute): ?string
|
||||
{
|
||||
Route::getRoutes()->refreshNameLookups();
|
||||
|
||||
return StringComparator::findClosestMatch(array_keys(Route::getRoutes()->getRoutesByName()), $missingRoute);
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Solutions\SolutionProviders;
|
||||
|
||||
use Exception;
|
||||
use Spatie\Ignition\Contracts\BaseSolution;
|
||||
use Spatie\Ignition\Contracts\HasSolutionsForThrowable;
|
||||
use Throwable;
|
||||
|
||||
class RunningLaravelDuskInProductionProvider implements HasSolutionsForThrowable
|
||||
{
|
||||
public function canSolve(Throwable $throwable): bool
|
||||
{
|
||||
if (! $throwable instanceof Exception) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $throwable->getMessage() === 'It is unsafe to run Dusk in production.';
|
||||
}
|
||||
|
||||
public function getSolutions(Throwable $throwable): array
|
||||
{
|
||||
return [
|
||||
BaseSolution::create()
|
||||
->setSolutionTitle('Laravel Dusk should not be run in production.')
|
||||
->setSolutionDescription('Install the dependencies with the `--no-dev` flag.'),
|
||||
|
||||
BaseSolution::create()
|
||||
->setSolutionTitle('Laravel Dusk can be run in other environments.')
|
||||
->setSolutionDescription('Consider setting the `APP_ENV` to something other than `production` like `local` for example.'),
|
||||
];
|
||||
}
|
||||
}
|
30
vendor/spatie/laravel-ignition/src/Solutions/SolutionProviders/SailNetworkSolutionProvider.php
vendored
Normal file
30
vendor/spatie/laravel-ignition/src/Solutions/SolutionProviders/SailNetworkSolutionProvider.php
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Solutions\SolutionProviders;
|
||||
|
||||
use Spatie\Ignition\Contracts\BaseSolution;
|
||||
use Spatie\Ignition\Contracts\HasSolutionsForThrowable;
|
||||
use Throwable;
|
||||
|
||||
class SailNetworkSolutionProvider implements HasSolutionsForThrowable
|
||||
{
|
||||
public function canSolve(Throwable $throwable): bool
|
||||
{
|
||||
return app()->runningInConsole()
|
||||
&& str_contains($throwable->getMessage(), 'php_network_getaddresses')
|
||||
&& file_exists(base_path('vendor/bin/sail'))
|
||||
&& file_exists(base_path('docker-compose.yml'))
|
||||
&& env('LARAVEL_SAIL') === null;
|
||||
}
|
||||
|
||||
public function getSolutions(Throwable $throwable): array
|
||||
{
|
||||
return [
|
||||
BaseSolution::create('Network address not found')
|
||||
->setSolutionDescription('Did you mean to use `sail artisan`?')
|
||||
->setDocumentationLinks([
|
||||
'Sail: Executing Artisan Commands' => 'https://laravel.com/docs/sail#executing-artisan-commands',
|
||||
]),
|
||||
];
|
||||
}
|
||||
}
|
99
vendor/spatie/laravel-ignition/src/Solutions/SolutionProviders/SolutionProviderRepository.php
vendored
Normal file
99
vendor/spatie/laravel-ignition/src/Solutions/SolutionProviders/SolutionProviderRepository.php
vendored
Normal file
@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Solutions\SolutionProviders;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
use Spatie\Ignition\Contracts\HasSolutionsForThrowable;
|
||||
use Spatie\Ignition\Contracts\ProvidesSolution;
|
||||
use Spatie\Ignition\Contracts\Solution;
|
||||
use Spatie\Ignition\Contracts\SolutionProviderRepository as SolutionProviderRepositoryContract;
|
||||
use Throwable;
|
||||
|
||||
class SolutionProviderRepository implements SolutionProviderRepositoryContract
|
||||
{
|
||||
/**
|
||||
* @param array<int, ProvidesSolution> $solutionProviders
|
||||
*/
|
||||
protected Collection $solutionProviders;
|
||||
|
||||
/**
|
||||
* @param array<int, ProvidesSolution> $solutionProviders
|
||||
*/
|
||||
public function __construct(array $solutionProviders = [])
|
||||
{
|
||||
$this->solutionProviders = Collection::make($solutionProviders);
|
||||
}
|
||||
|
||||
public function registerSolutionProvider(string $solutionProviderClass): SolutionProviderRepositoryContract
|
||||
{
|
||||
$this->solutionProviders->push($solutionProviderClass);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function registerSolutionProviders(array $solutionProviderClasses): SolutionProviderRepositoryContract
|
||||
{
|
||||
$this->solutionProviders = $this->solutionProviders->merge($solutionProviderClasses);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getSolutionsForThrowable(Throwable $throwable): array
|
||||
{
|
||||
$solutions = [];
|
||||
|
||||
if ($throwable instanceof Solution) {
|
||||
$solutions[] = $throwable;
|
||||
}
|
||||
|
||||
if ($throwable instanceof ProvidesSolution) {
|
||||
$solutions[] = $throwable->getSolution();
|
||||
}
|
||||
|
||||
/** @phpstan-ignore-next-line */
|
||||
$providedSolutions = $this->solutionProviders
|
||||
->filter(function (string $solutionClass) {
|
||||
if (! in_array(HasSolutionsForThrowable::class, class_implements($solutionClass) ?: [])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (in_array($solutionClass, config('ignition.ignored_solution_providers', []))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
})
|
||||
->map(fn (string $solutionClass) => app($solutionClass))
|
||||
->filter(function (HasSolutionsForThrowable $solutionProvider) use ($throwable) {
|
||||
try {
|
||||
return $solutionProvider->canSolve($throwable);
|
||||
} catch (Throwable $e) {
|
||||
return false;
|
||||
}
|
||||
})
|
||||
->map(function (HasSolutionsForThrowable $solutionProvider) use ($throwable) {
|
||||
try {
|
||||
return $solutionProvider->getSolutions($throwable);
|
||||
} catch (Throwable $e) {
|
||||
return [];
|
||||
}
|
||||
})
|
||||
->flatten()
|
||||
->toArray();
|
||||
|
||||
return array_merge($solutions, $providedSolutions);
|
||||
}
|
||||
|
||||
public function getSolutionForClass(string $solutionClass): ?Solution
|
||||
{
|
||||
if (! class_exists($solutionClass)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (! in_array(Solution::class, class_implements($solutionClass) ?: [])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return app($solutionClass);
|
||||
}
|
||||
}
|
35
vendor/spatie/laravel-ignition/src/Solutions/SolutionProviders/TableNotFoundSolutionProvider.php
vendored
Normal file
35
vendor/spatie/laravel-ignition/src/Solutions/SolutionProviders/TableNotFoundSolutionProvider.php
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Solutions\SolutionProviders;
|
||||
|
||||
use Illuminate\Database\QueryException;
|
||||
use Spatie\Ignition\Contracts\HasSolutionsForThrowable;
|
||||
use Spatie\LaravelIgnition\Solutions\RunMigrationsSolution;
|
||||
use Throwable;
|
||||
|
||||
class TableNotFoundSolutionProvider implements HasSolutionsForThrowable
|
||||
{
|
||||
/**
|
||||
* See https://dev.mysql.com/doc/refman/8.0/en/server-error-reference.html#error_er_bad_table_error.
|
||||
*/
|
||||
const MYSQL_BAD_TABLE_CODE = '42S02';
|
||||
|
||||
public function canSolve(Throwable $throwable): bool
|
||||
{
|
||||
if (! $throwable instanceof QueryException) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->isBadTableErrorCode($throwable->getCode());
|
||||
}
|
||||
|
||||
protected function isBadTableErrorCode(string $code): bool
|
||||
{
|
||||
return $code === static::MYSQL_BAD_TABLE_CODE;
|
||||
}
|
||||
|
||||
public function getSolutions(Throwable $throwable): array
|
||||
{
|
||||
return [new RunMigrationsSolution('A table was not found')];
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Solutions\SolutionProviders;
|
||||
|
||||
use Livewire\Exceptions\MethodNotFoundException;
|
||||
use Spatie\Ignition\Contracts\HasSolutionsForThrowable;
|
||||
use Spatie\LaravelIgnition\Solutions\SuggestLivewireMethodNameSolution;
|
||||
use Spatie\LaravelIgnition\Support\LivewireComponentParser;
|
||||
use Throwable;
|
||||
|
||||
class UndefinedLivewireMethodSolutionProvider implements HasSolutionsForThrowable
|
||||
{
|
||||
public function canSolve(Throwable $throwable): bool
|
||||
{
|
||||
return $throwable instanceof MethodNotFoundException;
|
||||
}
|
||||
|
||||
public function getSolutions(Throwable $throwable): array
|
||||
{
|
||||
['methodName' => $methodName, 'component' => $component] = $this->getMethodAndComponent($throwable);
|
||||
|
||||
if ($methodName === null || $component === null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$parsed = LivewireComponentParser::create($component);
|
||||
|
||||
return $parsed->getMethodNamesLike($methodName)
|
||||
->map(function (string $suggested) use ($parsed, $methodName) {
|
||||
return new SuggestLivewireMethodNameSolution(
|
||||
$methodName,
|
||||
$parsed->getComponentClass(),
|
||||
$suggested
|
||||
);
|
||||
})
|
||||
->toArray();
|
||||
}
|
||||
|
||||
/** @return array<string, string|null> */
|
||||
protected function getMethodAndComponent(Throwable $throwable): array
|
||||
{
|
||||
preg_match_all('/\[([\d\w\-_]*)\]/m', $throwable->getMessage(), $matches, PREG_SET_ORDER);
|
||||
|
||||
return [
|
||||
'methodName' => $matches[0][1] ?? null,
|
||||
'component' => $matches[1][1] ?? null,
|
||||
];
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Solutions\SolutionProviders;
|
||||
|
||||
use Livewire\Exceptions\PropertyNotFoundException;
|
||||
use Spatie\Ignition\Contracts\HasSolutionsForThrowable;
|
||||
use Spatie\LaravelIgnition\Solutions\SuggestLivewirePropertyNameSolution;
|
||||
use Spatie\LaravelIgnition\Support\LivewireComponentParser;
|
||||
use Throwable;
|
||||
|
||||
class UndefinedLivewirePropertySolutionProvider implements HasSolutionsForThrowable
|
||||
{
|
||||
public function canSolve(Throwable $throwable): bool
|
||||
{
|
||||
return $throwable instanceof PropertyNotFoundException;
|
||||
}
|
||||
|
||||
public function getSolutions(Throwable $throwable): array
|
||||
{
|
||||
['variable' => $variable, 'component' => $component] = $this->getMethodAndComponent($throwable);
|
||||
|
||||
if ($variable === null || $component === null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$parsed = LivewireComponentParser::create($component);
|
||||
|
||||
return $parsed->getPropertyNamesLike($variable)
|
||||
->map(function (string $suggested) use ($parsed, $variable) {
|
||||
return new SuggestLivewirePropertyNameSolution(
|
||||
$variable,
|
||||
$parsed->getComponentClass(),
|
||||
'$'.$suggested
|
||||
);
|
||||
})
|
||||
->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Throwable $throwable
|
||||
*
|
||||
* @return array<string, string|null>
|
||||
*/
|
||||
protected function getMethodAndComponent(Throwable $throwable): array
|
||||
{
|
||||
preg_match_all('/\[([\d\w\-_\$]*)\]/m', $throwable->getMessage(), $matches, PREG_SET_ORDER, 0);
|
||||
|
||||
return [
|
||||
'variable' => $matches[0][1] ?? null,
|
||||
'component' => $matches[1][1] ?? null,
|
||||
];
|
||||
}
|
||||
}
|
@ -0,0 +1,110 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Solutions\SolutionProviders;
|
||||
|
||||
use Spatie\Ignition\Contracts\BaseSolution;
|
||||
use Spatie\Ignition\Contracts\HasSolutionsForThrowable;
|
||||
use Spatie\Ignition\Contracts\Solution;
|
||||
use Spatie\LaravelIgnition\Exceptions\ViewException;
|
||||
use Spatie\LaravelIgnition\Solutions\MakeViewVariableOptionalSolution;
|
||||
use Spatie\LaravelIgnition\Solutions\SuggestCorrectVariableNameSolution;
|
||||
use Throwable;
|
||||
|
||||
class UndefinedViewVariableSolutionProvider implements HasSolutionsForThrowable
|
||||
{
|
||||
protected string $variableName;
|
||||
|
||||
protected string $viewFile;
|
||||
|
||||
public function canSolve(Throwable $throwable): bool
|
||||
{
|
||||
if (! $throwable instanceof ViewException) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->getNameAndView($throwable) !== null;
|
||||
}
|
||||
|
||||
public function getSolutions(Throwable $throwable): array
|
||||
{
|
||||
$solutions = [];
|
||||
|
||||
/** @phpstan-ignore-next-line */
|
||||
extract($this->getNameAndView($throwable));
|
||||
|
||||
if (! isset($variableName)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (isset($viewFile)) {
|
||||
/** @phpstan-ignore-next-line */
|
||||
$solutions = $this->findCorrectVariableSolutions($throwable, $variableName, $viewFile);
|
||||
$solutions[] = $this->findOptionalVariableSolution($variableName, $viewFile);
|
||||
}
|
||||
|
||||
|
||||
return $solutions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Spatie\LaravelIgnition\Exceptions\ViewException $throwable
|
||||
* @param string $variableName
|
||||
* @param string $viewFile
|
||||
*
|
||||
* @return array<int, \Spatie\Ignition\Contracts\Solution>
|
||||
*/
|
||||
protected function findCorrectVariableSolutions(
|
||||
ViewException $throwable,
|
||||
string $variableName,
|
||||
string $viewFile
|
||||
): array {
|
||||
return collect($throwable->getViewData())
|
||||
->map(function ($value, $key) use ($variableName) {
|
||||
similar_text($variableName, $key, $percentage);
|
||||
|
||||
return ['match' => $percentage, 'value' => $value];
|
||||
})
|
||||
->sortByDesc('match')
|
||||
->filter(fn ($var) => $var['match'] > 40)
|
||||
->keys()
|
||||
->map(fn ($suggestion) => new SuggestCorrectVariableNameSolution($variableName, $viewFile, $suggestion))
|
||||
->map(function ($solution) {
|
||||
return $solution->isRunnable()
|
||||
? $solution
|
||||
: BaseSolution::create($solution->getSolutionTitle())
|
||||
->setSolutionDescription($solution->getSolutionDescription());
|
||||
})
|
||||
->toArray();
|
||||
}
|
||||
|
||||
protected function findOptionalVariableSolution(string $variableName, string $viewFile): Solution
|
||||
{
|
||||
$optionalSolution = new MakeViewVariableOptionalSolution($variableName, $viewFile);
|
||||
|
||||
return $optionalSolution->isRunnable()
|
||||
? $optionalSolution
|
||||
: BaseSolution::create($optionalSolution->getSolutionTitle())
|
||||
->setSolutionDescription($optionalSolution->getSolutionDescription());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Throwable $throwable
|
||||
*
|
||||
* @return array<string, string>|null
|
||||
*/
|
||||
protected function getNameAndView(Throwable $throwable): ?array
|
||||
{
|
||||
$pattern = '/Undefined variable:? (.*?) \(View: (.*?)\)/';
|
||||
|
||||
preg_match($pattern, $throwable->getMessage(), $matches);
|
||||
|
||||
if (count($matches) === 3) {
|
||||
[, $variableName, $viewFile] = $matches;
|
||||
$variableName = ltrim($variableName, '$');
|
||||
|
||||
return compact('variableName', 'viewFile');
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Solutions\SolutionProviders;
|
||||
|
||||
use Illuminate\Database\QueryException;
|
||||
use Spatie\Ignition\Contracts\HasSolutionsForThrowable;
|
||||
use Spatie\LaravelIgnition\Solutions\SuggestUsingMariadbDatabaseSolution;
|
||||
use Throwable;
|
||||
|
||||
class UnknownMariadbCollationSolutionProvider implements HasSolutionsForThrowable
|
||||
{
|
||||
const MYSQL_UNKNOWN_COLLATION_CODE = 1273;
|
||||
|
||||
public function canSolve(Throwable $throwable): bool
|
||||
{
|
||||
if (! $throwable instanceof QueryException) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($throwable->getCode() !== self::MYSQL_UNKNOWN_COLLATION_CODE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return str_contains(
|
||||
$throwable->getMessage(),
|
||||
'Unknown collation: \'utf8mb4_uca1400_ai_ci\''
|
||||
);
|
||||
}
|
||||
|
||||
public function getSolutions(Throwable $throwable): array
|
||||
{
|
||||
return [new SuggestUsingMariadbDatabaseSolution()];
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Solutions\SolutionProviders;
|
||||
|
||||
use Illuminate\Database\QueryException;
|
||||
use Spatie\Ignition\Contracts\HasSolutionsForThrowable;
|
||||
use Spatie\LaravelIgnition\Solutions\SuggestUsingMysql8DatabaseSolution;
|
||||
use Throwable;
|
||||
|
||||
class UnknownMysql8CollationSolutionProvider implements HasSolutionsForThrowable
|
||||
{
|
||||
const MYSQL_UNKNOWN_COLLATION_CODE = 1273;
|
||||
|
||||
public function canSolve(Throwable $throwable): bool
|
||||
{
|
||||
if (! $throwable instanceof QueryException) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($throwable->getCode() !== self::MYSQL_UNKNOWN_COLLATION_CODE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return str_contains(
|
||||
$throwable->getMessage(),
|
||||
'Unknown collation: \'utf8mb4_0900_ai_ci\''
|
||||
);
|
||||
}
|
||||
|
||||
public function getSolutions(Throwable $throwable): array
|
||||
{
|
||||
return [new SuggestUsingMysql8DatabaseSolution()];
|
||||
}
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Solutions\SolutionProviders;
|
||||
|
||||
use BadMethodCallException;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Validation\Validator;
|
||||
use ReflectionClass;
|
||||
use ReflectionMethod;
|
||||
use Spatie\Ignition\Contracts\BaseSolution;
|
||||
use Spatie\Ignition\Contracts\HasSolutionsForThrowable;
|
||||
use Spatie\LaravelIgnition\Support\StringComparator;
|
||||
use Throwable;
|
||||
|
||||
class UnknownValidationSolutionProvider implements HasSolutionsForThrowable
|
||||
{
|
||||
protected const REGEX = '/Illuminate\\\\Validation\\\\Validator::(?P<method>validate(?!(Attribute|UsingCustomRule))[A-Z][a-zA-Z]+)/m';
|
||||
|
||||
public function canSolve(Throwable $throwable): bool
|
||||
{
|
||||
if (! $throwable instanceof BadMethodCallException) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ! is_null($this->getMethodFromExceptionMessage($throwable->getMessage()));
|
||||
}
|
||||
|
||||
public function getSolutions(Throwable $throwable): array
|
||||
{
|
||||
return [
|
||||
BaseSolution::create()
|
||||
->setSolutionTitle('Unknown Validation Rule')
|
||||
->setSolutionDescription($this->getSolutionDescription($throwable)),
|
||||
];
|
||||
}
|
||||
|
||||
protected function getSolutionDescription(Throwable $throwable): string
|
||||
{
|
||||
$method = (string)$this->getMethodFromExceptionMessage($throwable->getMessage());
|
||||
|
||||
$possibleMethod = StringComparator::findSimilarText(
|
||||
$this->getAvailableMethods()->toArray(),
|
||||
$method
|
||||
);
|
||||
|
||||
if (empty($possibleMethod)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$rule = Str::snake(str_replace('validate', '', $possibleMethod));
|
||||
|
||||
return "Did you mean `{$rule}` ?";
|
||||
}
|
||||
|
||||
protected function getMethodFromExceptionMessage(string $message): ?string
|
||||
{
|
||||
if (! preg_match(self::REGEX, $message, $matches)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $matches['method'];
|
||||
}
|
||||
|
||||
protected function getAvailableMethods(): Collection
|
||||
{
|
||||
$class = new ReflectionClass(Validator::class);
|
||||
|
||||
$extensions = Collection::make((app('validator')->make([], []))->extensions)
|
||||
->keys()
|
||||
->map(fn (string $extension) => 'validate'.Str::studly($extension));
|
||||
|
||||
return Collection::make($class->getMethods())
|
||||
->filter(fn (ReflectionMethod $method) => preg_match('/(validate(?!(Attribute|UsingCustomRule))[A-Z][a-zA-Z]+)/', $method->name))
|
||||
->map(fn (ReflectionMethod $method) => $method->name)
|
||||
->merge($extensions);
|
||||
}
|
||||
}
|
116
vendor/spatie/laravel-ignition/src/Solutions/SolutionProviders/ViewNotFoundSolutionProvider.php
vendored
Normal file
116
vendor/spatie/laravel-ignition/src/Solutions/SolutionProviders/ViewNotFoundSolutionProvider.php
vendored
Normal file
@ -0,0 +1,116 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Solutions\SolutionProviders;
|
||||
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\View;
|
||||
use InvalidArgumentException;
|
||||
use Spatie\Ignition\Contracts\BaseSolution;
|
||||
use Spatie\Ignition\Contracts\HasSolutionsForThrowable;
|
||||
use Spatie\LaravelIgnition\Exceptions\ViewException;
|
||||
use Spatie\LaravelIgnition\Support\StringComparator;
|
||||
use Symfony\Component\Finder\Finder;
|
||||
use Symfony\Component\Finder\SplFileInfo;
|
||||
use Throwable;
|
||||
|
||||
class ViewNotFoundSolutionProvider implements HasSolutionsForThrowable
|
||||
{
|
||||
protected const REGEX = '/View \[(.*)\] not found/m';
|
||||
|
||||
public function canSolve(Throwable $throwable): bool
|
||||
{
|
||||
if (! $throwable instanceof InvalidArgumentException && ! $throwable instanceof ViewException) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (bool)preg_match(self::REGEX, $throwable->getMessage(), $matches);
|
||||
}
|
||||
|
||||
public function getSolutions(Throwable $throwable): array
|
||||
{
|
||||
preg_match(self::REGEX, $throwable->getMessage(), $matches);
|
||||
|
||||
$missingView = $matches[1] ?? null;
|
||||
|
||||
$suggestedView = $this->findRelatedView($missingView);
|
||||
|
||||
if ($suggestedView) {
|
||||
return [
|
||||
BaseSolution::create()
|
||||
->setSolutionTitle("{$missingView} was not found.")
|
||||
->setSolutionDescription("Did you mean `{$suggestedView}`?"),
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
BaseSolution::create()
|
||||
->setSolutionTitle("{$missingView} was not found.")
|
||||
->setSolutionDescription('Are you sure the view exists and is a `.blade.php` file?'),
|
||||
];
|
||||
}
|
||||
|
||||
protected function findRelatedView(string $missingView): ?string
|
||||
{
|
||||
$views = $this->getAllViews();
|
||||
|
||||
return StringComparator::findClosestMatch($views, $missingView);
|
||||
}
|
||||
|
||||
/** @return array<int, string> */
|
||||
protected function getAllViews(): array
|
||||
{
|
||||
/** @var \Illuminate\View\FileViewFinder $fileViewFinder */
|
||||
$fileViewFinder = View::getFinder();
|
||||
|
||||
$extensions = $fileViewFinder->getExtensions();
|
||||
|
||||
$viewsForHints = collect($fileViewFinder->getHints())
|
||||
->flatMap(function ($paths, string $namespace) use ($extensions) {
|
||||
$paths = Arr::wrap($paths);
|
||||
|
||||
return collect($paths)
|
||||
->flatMap(fn (string $path) => $this->getViewsInPath($path, $extensions))
|
||||
->map(fn (string $view) => "{$namespace}::{$view}")
|
||||
->toArray();
|
||||
});
|
||||
|
||||
$viewsForViewPaths = collect($fileViewFinder->getPaths())
|
||||
->flatMap(fn (string $path) => $this->getViewsInPath($path, $extensions));
|
||||
|
||||
return $viewsForHints->merge($viewsForViewPaths)->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
* @param array<int, string> $extensions
|
||||
*
|
||||
* @return array<int, string>
|
||||
*/
|
||||
protected function getViewsInPath(string $path, array $extensions): array
|
||||
{
|
||||
$filePatterns = array_map(fn (string $extension) => "*.{$extension}", $extensions);
|
||||
|
||||
$extensionsWithDots = array_map(fn (string $extension) => ".{$extension}", $extensions);
|
||||
|
||||
$files = (new Finder())
|
||||
->in($path)
|
||||
->files();
|
||||
|
||||
foreach ($filePatterns as $filePattern) {
|
||||
$files->name($filePattern);
|
||||
}
|
||||
|
||||
$views = [];
|
||||
|
||||
foreach ($files as $file) {
|
||||
if ($file instanceof SplFileInfo) {
|
||||
$view = $file->getRelativePathname();
|
||||
$view = str_replace($extensionsWithDots, '', $view);
|
||||
$view = str_replace('/', '.', $view);
|
||||
$views[] = $view;
|
||||
}
|
||||
}
|
||||
|
||||
return $views;
|
||||
}
|
||||
}
|
61
vendor/spatie/laravel-ignition/src/Solutions/SolutionTransformers/LaravelSolutionTransformer.php
vendored
Normal file
61
vendor/spatie/laravel-ignition/src/Solutions/SolutionTransformers/LaravelSolutionTransformer.php
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Solutions\SolutionTransformers;
|
||||
|
||||
use Spatie\Ignition\Contracts\RunnableSolution;
|
||||
use Spatie\Ignition\Solutions\SolutionTransformer;
|
||||
use Spatie\LaravelIgnition\Http\Controllers\ExecuteSolutionController;
|
||||
use Throwable;
|
||||
|
||||
class LaravelSolutionTransformer extends SolutionTransformer
|
||||
{
|
||||
/** @return array<string|mixed> */
|
||||
public function toArray(): array
|
||||
{
|
||||
$baseProperties = parent::toArray();
|
||||
|
||||
if (! $this->isRunnable()) {
|
||||
return $baseProperties;
|
||||
}
|
||||
|
||||
/** @var RunnableSolution $solution Type shenanigans */
|
||||
$solution = $this->solution;
|
||||
|
||||
$runnableProperties = [
|
||||
'is_runnable' => true,
|
||||
'action_description' => $solution->getSolutionActionDescription(),
|
||||
'run_button_text' => $solution->getRunButtonText(),
|
||||
'execute_endpoint' => $this->executeEndpoint(),
|
||||
'run_parameters' => $solution->getRunParameters(),
|
||||
];
|
||||
|
||||
return array_merge($baseProperties, $runnableProperties);
|
||||
}
|
||||
|
||||
protected function isRunnable(): bool
|
||||
{
|
||||
if (! $this->solution instanceof RunnableSolution) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! $this->executeEndpoint()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function executeEndpoint(): ?string
|
||||
{
|
||||
try {
|
||||
// The action class needs to be prefixed with a `\` to Laravel from trying
|
||||
// to add its own global namespace from RouteServiceProvider::$namespace.
|
||||
|
||||
return action('\\'.ExecuteSolutionController::class);
|
||||
} catch (Throwable $exception) {
|
||||
report($exception);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
43
vendor/spatie/laravel-ignition/src/Solutions/SuggestCorrectVariableNameSolution.php
vendored
Normal file
43
vendor/spatie/laravel-ignition/src/Solutions/SuggestCorrectVariableNameSolution.php
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Solutions;
|
||||
|
||||
use Spatie\Ignition\Contracts\Solution;
|
||||
|
||||
class SuggestCorrectVariableNameSolution implements Solution
|
||||
{
|
||||
protected ?string $variableName;
|
||||
|
||||
protected ?string $viewFile;
|
||||
|
||||
protected ?string $suggested;
|
||||
|
||||
public function __construct(string $variableName = null, string $viewFile = null, string $suggested = null)
|
||||
{
|
||||
$this->variableName = $variableName;
|
||||
|
||||
$this->viewFile = $viewFile;
|
||||
|
||||
$this->suggested = $suggested;
|
||||
}
|
||||
|
||||
public function getSolutionTitle(): string
|
||||
{
|
||||
return 'Possible typo $'.$this->variableName;
|
||||
}
|
||||
|
||||
public function getDocumentationLinks(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getSolutionDescription(): string
|
||||
{
|
||||
return "Did you mean `$$this->suggested`?";
|
||||
}
|
||||
|
||||
public function isRunnable(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
30
vendor/spatie/laravel-ignition/src/Solutions/SuggestImportSolution.php
vendored
Normal file
30
vendor/spatie/laravel-ignition/src/Solutions/SuggestImportSolution.php
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Solutions;
|
||||
|
||||
use Spatie\Ignition\Contracts\Solution;
|
||||
|
||||
class SuggestImportSolution implements Solution
|
||||
{
|
||||
protected string $class;
|
||||
|
||||
public function __construct(string $class = '')
|
||||
{
|
||||
$this->class = $class;
|
||||
}
|
||||
|
||||
public function getSolutionTitle(): string
|
||||
{
|
||||
return 'A class import is missing';
|
||||
}
|
||||
|
||||
public function getSolutionDescription(): string
|
||||
{
|
||||
return 'You have a missing class import. Try importing this class: `'.$this->class.'`.';
|
||||
}
|
||||
|
||||
public function getDocumentationLinks(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
35
vendor/spatie/laravel-ignition/src/Solutions/SuggestLivewireMethodNameSolution.php
vendored
Normal file
35
vendor/spatie/laravel-ignition/src/Solutions/SuggestLivewireMethodNameSolution.php
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Solutions;
|
||||
|
||||
use Spatie\Ignition\Contracts\Solution;
|
||||
|
||||
class SuggestLivewireMethodNameSolution implements Solution
|
||||
{
|
||||
public function __construct(
|
||||
protected string $methodName,
|
||||
protected string $componentClass,
|
||||
protected string $suggested
|
||||
) {
|
||||
}
|
||||
|
||||
public function getSolutionTitle(): string
|
||||
{
|
||||
return "Possible typo `{$this->componentClass}::{$this->methodName}`";
|
||||
}
|
||||
|
||||
public function getDocumentationLinks(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getSolutionDescription(): string
|
||||
{
|
||||
return "Did you mean `{$this->componentClass}::{$this->suggested}`?";
|
||||
}
|
||||
|
||||
public function isRunnable(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
35
vendor/spatie/laravel-ignition/src/Solutions/SuggestLivewirePropertyNameSolution.php
vendored
Normal file
35
vendor/spatie/laravel-ignition/src/Solutions/SuggestLivewirePropertyNameSolution.php
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Solutions;
|
||||
|
||||
use Spatie\Ignition\Contracts\Solution;
|
||||
|
||||
class SuggestLivewirePropertyNameSolution implements Solution
|
||||
{
|
||||
public function __construct(
|
||||
protected string $variableName,
|
||||
protected string $componentClass,
|
||||
protected string $suggested,
|
||||
) {
|
||||
}
|
||||
|
||||
public function getSolutionTitle(): string
|
||||
{
|
||||
return "Possible typo {$this->variableName}";
|
||||
}
|
||||
|
||||
public function getDocumentationLinks(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getSolutionDescription(): string
|
||||
{
|
||||
return "Did you mean `$this->suggested`?";
|
||||
}
|
||||
|
||||
public function isRunnable(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
28
vendor/spatie/laravel-ignition/src/Solutions/SuggestUsingCorrectDbNameSolution.php
vendored
Normal file
28
vendor/spatie/laravel-ignition/src/Solutions/SuggestUsingCorrectDbNameSolution.php
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Solutions;
|
||||
|
||||
use Spatie\Ignition\Contracts\Solution;
|
||||
|
||||
class SuggestUsingCorrectDbNameSolution implements Solution
|
||||
{
|
||||
public function getSolutionTitle(): string
|
||||
{
|
||||
return 'Database name seems incorrect';
|
||||
}
|
||||
|
||||
public function getSolutionDescription(): string
|
||||
{
|
||||
$defaultDatabaseName = env('DB_DATABASE');
|
||||
|
||||
return "You're using the default database name `$defaultDatabaseName`. This database does not exist.\n\nEdit the `.env` file and use the correct database name in the `DB_DATABASE` key.";
|
||||
}
|
||||
|
||||
/** @return array<string, string> */
|
||||
public function getDocumentationLinks(): array
|
||||
{
|
||||
return [
|
||||
'Database: Getting Started docs' => 'https://laravel.com/docs/master/database#configuration',
|
||||
];
|
||||
}
|
||||
}
|
26
vendor/spatie/laravel-ignition/src/Solutions/SuggestUsingMariadbDatabaseSolution.php
vendored
Normal file
26
vendor/spatie/laravel-ignition/src/Solutions/SuggestUsingMariadbDatabaseSolution.php
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Solutions;
|
||||
|
||||
use Spatie\Ignition\Contracts\Solution;
|
||||
|
||||
class SuggestUsingMariadbDatabaseSolution implements Solution
|
||||
{
|
||||
public function getSolutionTitle(): string
|
||||
{
|
||||
return 'Database is not a MariaDB database';
|
||||
}
|
||||
|
||||
public function getSolutionDescription(): string
|
||||
{
|
||||
return "Laravel 11 changed the default collation for MySQL and MariaDB. It seems you are trying to use the MariaDB collation `utf8mb4_uca1400_ai_ci` with a MySQL database.\n\nEdit the `.env` file and use the correct database in the `DB_CONNECTION` key.";
|
||||
}
|
||||
|
||||
/** @return array<string, string> */
|
||||
public function getDocumentationLinks(): array
|
||||
{
|
||||
return [
|
||||
'Database: Getting Started docs' => 'https://laravel.com/docs/master/database#configuration',
|
||||
];
|
||||
}
|
||||
}
|
26
vendor/spatie/laravel-ignition/src/Solutions/SuggestUsingMysql8DatabaseSolution.php
vendored
Normal file
26
vendor/spatie/laravel-ignition/src/Solutions/SuggestUsingMysql8DatabaseSolution.php
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Solutions;
|
||||
|
||||
use Spatie\Ignition\Contracts\Solution;
|
||||
|
||||
class SuggestUsingMysql8DatabaseSolution implements Solution
|
||||
{
|
||||
public function getSolutionTitle(): string
|
||||
{
|
||||
return 'Database is not a MySQL 8 database';
|
||||
}
|
||||
|
||||
public function getSolutionDescription(): string
|
||||
{
|
||||
return "Laravel 11 changed the default collation for MySQL and MariaDB. It seems you are trying to use the MySQL 8 collation `utf8mb4_0900_ai_ci` with a MariaDB or MySQL 5.7 database.\n\nEdit the `.env` file and use the correct database in the `DB_CONNECTION` key.";
|
||||
}
|
||||
|
||||
/** @return array<string, string> */
|
||||
public function getDocumentationLinks(): array
|
||||
{
|
||||
return [
|
||||
'Database: Getting Started docs' => 'https://laravel.com/docs/master/database#configuration',
|
||||
];
|
||||
}
|
||||
}
|
62
vendor/spatie/laravel-ignition/src/Solutions/UseDefaultValetDbCredentialsSolution.php
vendored
Normal file
62
vendor/spatie/laravel-ignition/src/Solutions/UseDefaultValetDbCredentialsSolution.php
vendored
Normal file
@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Solutions;
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
use Spatie\Ignition\Contracts\RunnableSolution;
|
||||
|
||||
class UseDefaultValetDbCredentialsSolution implements RunnableSolution
|
||||
{
|
||||
public function getSolutionActionDescription(): string
|
||||
{
|
||||
return 'Pressing the button will change `DB_USER` and `DB_PASSWORD` in your `.env` file.';
|
||||
}
|
||||
|
||||
public function getRunButtonText(): string
|
||||
{
|
||||
return 'Use default Valet credentials';
|
||||
}
|
||||
|
||||
public function getSolutionTitle(): string
|
||||
{
|
||||
return 'Could not connect to database';
|
||||
}
|
||||
|
||||
public function run(array $parameters = []): void
|
||||
{
|
||||
if (! file_exists(base_path('.env'))) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->ensureLineExists('DB_USERNAME', 'root');
|
||||
$this->ensureLineExists('DB_PASSWORD', '');
|
||||
}
|
||||
|
||||
protected function ensureLineExists(string $key, string $value): void
|
||||
{
|
||||
$envPath = base_path('.env');
|
||||
|
||||
$envLines = array_map(fn (string $envLine) => Str::startsWith($envLine, $key)
|
||||
? "{$key}={$value}".PHP_EOL
|
||||
: $envLine, file($envPath) ?: []);
|
||||
|
||||
file_put_contents($envPath, implode('', $envLines));
|
||||
}
|
||||
|
||||
public function getRunParameters(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getDocumentationLinks(): array
|
||||
{
|
||||
return [
|
||||
'Valet documentation' => 'https://laravel.com/docs/master/valet',
|
||||
];
|
||||
}
|
||||
|
||||
public function getSolutionDescription(): string
|
||||
{
|
||||
return 'You seem to be using Valet, but the .env file does not contain the right default database credentials.';
|
||||
}
|
||||
}
|
15
vendor/spatie/laravel-ignition/src/Support/Composer/Composer.php
vendored
Normal file
15
vendor/spatie/laravel-ignition/src/Support/Composer/Composer.php
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Support\Composer;
|
||||
|
||||
interface Composer
|
||||
{
|
||||
/** @return array<string, mixed> */
|
||||
public function getClassMap(): array;
|
||||
|
||||
/** @return array<string, mixed> */
|
||||
public function getPrefixes(): array;
|
||||
|
||||
/** @return array<string, mixed> */
|
||||
public function getPrefixesPsr4(): array;
|
||||
}
|
127
vendor/spatie/laravel-ignition/src/Support/Composer/ComposerClassMap.php
vendored
Normal file
127
vendor/spatie/laravel-ignition/src/Support/Composer/ComposerClassMap.php
vendored
Normal file
@ -0,0 +1,127 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Support\Composer;
|
||||
|
||||
use function app_path;
|
||||
use function base_path;
|
||||
use Illuminate\Support\Str;
|
||||
use Symfony\Component\Finder\Finder;
|
||||
use Symfony\Component\Finder\SplFileInfo;
|
||||
|
||||
class ComposerClassMap
|
||||
{
|
||||
/** @var \Spatie\LaravelIgnition\Support\Composer\Composer */
|
||||
protected object $composer;
|
||||
|
||||
protected string $basePath;
|
||||
|
||||
public function __construct(?string $autoloaderPath = null)
|
||||
{
|
||||
$autoloaderPath = $autoloaderPath ?? base_path('/vendor/autoload.php');
|
||||
|
||||
$this->composer = file_exists($autoloaderPath)
|
||||
? require $autoloaderPath
|
||||
: new FakeComposer();
|
||||
|
||||
$this->basePath = app_path();
|
||||
}
|
||||
|
||||
/** @return array<string, string> */
|
||||
public function listClasses(): array
|
||||
{
|
||||
$classes = $this->composer->getClassMap();
|
||||
|
||||
return array_merge($classes, $this->listClassesInPsrMaps());
|
||||
}
|
||||
|
||||
public function searchClassMap(string $missingClass): ?string
|
||||
{
|
||||
foreach ($this->composer->getClassMap() as $fqcn => $file) {
|
||||
$basename = basename($file, '.php');
|
||||
|
||||
if ($basename === $missingClass) {
|
||||
return $fqcn;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @return array<string, mixed> */
|
||||
public function listClassesInPsrMaps(): array
|
||||
{
|
||||
// TODO: This is incorrect. Doesnt list all fqcns. Need to parse namespace? e.g. App\LoginController is wrong
|
||||
|
||||
$prefixes = array_merge(
|
||||
$this->composer->getPrefixes(),
|
||||
$this->composer->getPrefixesPsr4()
|
||||
);
|
||||
|
||||
$classes = [];
|
||||
|
||||
foreach ($prefixes as $namespace => $directories) {
|
||||
foreach ($directories as $directory) {
|
||||
if (file_exists($directory)) {
|
||||
$files = (new Finder)
|
||||
->in($directory)
|
||||
->files()
|
||||
->name('*.php');
|
||||
|
||||
foreach ($files as $file) {
|
||||
if ($file instanceof SplFileInfo) {
|
||||
$fqcn = $this->getFullyQualifiedClassNameFromFile($namespace, $file);
|
||||
|
||||
$classes[$fqcn] = $file->getRelativePathname();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $classes;
|
||||
}
|
||||
|
||||
public function searchPsrMaps(string $missingClass): ?string
|
||||
{
|
||||
$prefixes = array_merge(
|
||||
$this->composer->getPrefixes(),
|
||||
$this->composer->getPrefixesPsr4()
|
||||
);
|
||||
|
||||
foreach ($prefixes as $namespace => $directories) {
|
||||
foreach ($directories as $directory) {
|
||||
if (file_exists($directory)) {
|
||||
$files = (new Finder)
|
||||
->in($directory)
|
||||
->files()
|
||||
->name('*.php');
|
||||
|
||||
foreach ($files as $file) {
|
||||
if ($file instanceof SplFileInfo) {
|
||||
$basename = basename($file->getRelativePathname(), '.php');
|
||||
|
||||
if ($basename === $missingClass) {
|
||||
return $namespace . basename($file->getRelativePathname(), '.php');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected function getFullyQualifiedClassNameFromFile(string $rootNamespace, SplFileInfo $file): string
|
||||
{
|
||||
$class = trim(str_replace($this->basePath, '', (string)$file->getRealPath()), DIRECTORY_SEPARATOR);
|
||||
|
||||
$class = str_replace(
|
||||
[DIRECTORY_SEPARATOR, 'App\\'],
|
||||
['\\', app()->getNamespace()],
|
||||
ucfirst(Str::replaceLast('.php', '', $class))
|
||||
);
|
||||
|
||||
return $rootNamespace . $class;
|
||||
}
|
||||
}
|
24
vendor/spatie/laravel-ignition/src/Support/Composer/FakeComposer.php
vendored
Normal file
24
vendor/spatie/laravel-ignition/src/Support/Composer/FakeComposer.php
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Support\Composer;
|
||||
|
||||
class FakeComposer implements Composer
|
||||
{
|
||||
/** @return array<string, mixed> */
|
||||
public function getClassMap(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/** @return array<string, mixed> */
|
||||
public function getPrefixes(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/** @return array<string, mixed> */
|
||||
public function getPrefixesPsr4(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
107
vendor/spatie/laravel-ignition/src/Support/FlareLogHandler.php
vendored
Normal file
107
vendor/spatie/laravel-ignition/src/Support/FlareLogHandler.php
vendored
Normal file
@ -0,0 +1,107 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Support;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use Monolog\Handler\AbstractProcessingHandler;
|
||||
use Monolog\Level;
|
||||
use Monolog\Logger;
|
||||
use Monolog\LogRecord;
|
||||
use Spatie\FlareClient\Flare;
|
||||
use Spatie\FlareClient\Report;
|
||||
use Throwable;
|
||||
|
||||
class FlareLogHandler extends AbstractProcessingHandler
|
||||
{
|
||||
protected Flare $flare;
|
||||
|
||||
protected SentReports $sentReports;
|
||||
|
||||
protected int $minimumReportLogLevel;
|
||||
|
||||
public function __construct(Flare $flare, SentReports $sentReports, $level = Level::Debug, $bubble = true)
|
||||
{
|
||||
$this->flare = $flare;
|
||||
|
||||
$this->minimumReportLogLevel = Level::Error->value;
|
||||
|
||||
$this->sentReports = $sentReports;
|
||||
|
||||
parent::__construct($level, $bubble);
|
||||
}
|
||||
|
||||
public function setMinimumReportLogLevel(int $level): void
|
||||
{
|
||||
if (! in_array($level, Level::VALUES)) {
|
||||
throw new InvalidArgumentException('The given minimum log level is not supported.');
|
||||
}
|
||||
|
||||
$this->minimumReportLogLevel = $level;
|
||||
}
|
||||
|
||||
protected function write(LogRecord $record): void
|
||||
{
|
||||
if (! $this->shouldReport($record->toArray())) {
|
||||
return;
|
||||
}
|
||||
if ($this->hasException($record->toArray())) {
|
||||
$report = $this->flare->report($record['context']['exception']);
|
||||
|
||||
if ($report) {
|
||||
$this->sentReports->add($report);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (config('flare.send_logs_as_events')) {
|
||||
if ($this->hasValidLogLevel($record->toArray())) {
|
||||
$this->flare->reportMessage(
|
||||
$record['message'],
|
||||
'Log ' . Logger::toMonologLevel($record['level'])->getName(),
|
||||
function (Report $flareReport) use ($record) {
|
||||
foreach ($record['context'] as $key => $value) {
|
||||
$flareReport->context($key, $value);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $report
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function shouldReport(array $report): bool
|
||||
{
|
||||
if (! config('flare.key')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->hasException($report) || $this->hasValidLogLevel($report);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $report
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function hasException(array $report): bool
|
||||
{
|
||||
$context = $report['context'];
|
||||
|
||||
return isset($context['exception']) && $context['exception'] instanceof Throwable;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $report
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function hasValidLogLevel(array $report): bool
|
||||
{
|
||||
return $report['level'] >= $this->minimumReportLogLevel;
|
||||
}
|
||||
}
|
61
vendor/spatie/laravel-ignition/src/Support/LaravelDocumentationLinkFinder.php
vendored
Normal file
61
vendor/spatie/laravel-ignition/src/Support/LaravelDocumentationLinkFinder.php
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Support;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Str;
|
||||
use Spatie\LaravelIgnition\Exceptions\ViewException;
|
||||
use Throwable;
|
||||
|
||||
class LaravelDocumentationLinkFinder
|
||||
{
|
||||
public function findLinkForThrowable(Throwable $throwable): ?string
|
||||
{
|
||||
if ($throwable instanceof ViewException) {
|
||||
$throwable = $throwable->getPrevious();
|
||||
}
|
||||
|
||||
$majorVersion = LaravelVersion::major();
|
||||
|
||||
if (str_contains($throwable->getMessage(), Collection::class)) {
|
||||
return "https://laravel.com/docs/{$majorVersion}.x/collections#available-methods";
|
||||
}
|
||||
|
||||
$type = $this->getType($throwable);
|
||||
|
||||
if (! $type) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return match ($type) {
|
||||
'Auth' => "https://laravel.com/docs/{$majorVersion}.x/authentication",
|
||||
'Broadcasting' => "https://laravel.com/docs/{$majorVersion}.x/broadcasting",
|
||||
'Container' => "https://laravel.com/docs/{$majorVersion}.x/container",
|
||||
'Database' => "https://laravel.com/docs/{$majorVersion}.x/eloquent",
|
||||
'Pagination' => "https://laravel.com/docs/{$majorVersion}.x/pagination",
|
||||
'Queue' => "https://laravel.com/docs/{$majorVersion}.x/queues",
|
||||
'Routing' => "https://laravel.com/docs/{$majorVersion}.x/routing",
|
||||
'Session' => "https://laravel.com/docs/{$majorVersion}.x/session",
|
||||
'Validation' => "https://laravel.com/docs/{$majorVersion}.x/validation",
|
||||
'View' => "https://laravel.com/docs/{$majorVersion}.x/views",
|
||||
default => null,
|
||||
};
|
||||
}
|
||||
|
||||
protected function getType(?Throwable $throwable): ?string
|
||||
{
|
||||
if (! $throwable) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (str_contains($throwable::class, 'Illuminate')) {
|
||||
return Str::between($throwable::class, 'Illuminate\\', '\\');
|
||||
}
|
||||
|
||||
if (str_contains($throwable->getMessage(), 'Illuminate')) {
|
||||
return explode('\\', Str::between($throwable->getMessage(), 'Illuminate\\', '\\'))[0];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
11
vendor/spatie/laravel-ignition/src/Support/LaravelVersion.php
vendored
Normal file
11
vendor/spatie/laravel-ignition/src/Support/LaravelVersion.php
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Support;
|
||||
|
||||
class LaravelVersion
|
||||
{
|
||||
public static function major(): string
|
||||
{
|
||||
return explode('.', app()->version())[0];
|
||||
}
|
||||
}
|
80
vendor/spatie/laravel-ignition/src/Support/LivewireComponentParser.php
vendored
Normal file
80
vendor/spatie/laravel-ignition/src/Support/LivewireComponentParser.php
vendored
Normal file
@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Support;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Str;
|
||||
use Livewire\LivewireManager;
|
||||
use ReflectionClass;
|
||||
use ReflectionMethod;
|
||||
use ReflectionProperty;
|
||||
|
||||
class LivewireComponentParser
|
||||
{
|
||||
protected string $componentClass;
|
||||
|
||||
protected ReflectionClass $reflectionClass;
|
||||
|
||||
public static function create(string $componentAlias): self
|
||||
{
|
||||
return new self($componentAlias);
|
||||
}
|
||||
|
||||
public function __construct(protected string $componentAlias)
|
||||
{
|
||||
$this->componentClass = app(LivewireManager::class)->getClass($this->componentAlias);
|
||||
$this->reflectionClass = new ReflectionClass($this->componentClass);
|
||||
}
|
||||
|
||||
public function getComponentClass(): string
|
||||
{
|
||||
return $this->componentClass;
|
||||
}
|
||||
|
||||
public function getPropertyNamesLike(string $similar): Collection
|
||||
{
|
||||
$properties = collect($this->reflectionClass->getProperties(ReflectionProperty::IS_PUBLIC))
|
||||
// @phpstan-ignore-next-line
|
||||
->reject(fn (ReflectionProperty $reflectionProperty) => $reflectionProperty->class !== $this->reflectionClass->name)
|
||||
->map(fn (ReflectionProperty $reflectionProperty) => $reflectionProperty->name);
|
||||
|
||||
$computedProperties = collect($this->reflectionClass->getMethods(ReflectionMethod::IS_PUBLIC))
|
||||
// @phpstan-ignore-next-line
|
||||
->reject(fn (ReflectionMethod $reflectionMethod) => $reflectionMethod->class !== $this->reflectionClass->name)
|
||||
->filter(fn (ReflectionMethod $reflectionMethod) => str_starts_with($reflectionMethod->name, 'get') && str_ends_with($reflectionMethod->name, 'Property'))
|
||||
->map(fn (ReflectionMethod $reflectionMethod) => lcfirst(Str::of($reflectionMethod->name)->after('get')->before('Property')));
|
||||
|
||||
return $this->filterItemsBySimilarity(
|
||||
$properties->merge($computedProperties),
|
||||
$similar
|
||||
);
|
||||
}
|
||||
|
||||
public function getMethodNamesLike(string $similar): Collection
|
||||
{
|
||||
$methods = collect($this->reflectionClass->getMethods(ReflectionMethod::IS_PUBLIC))
|
||||
// @phpstan-ignore-next-line
|
||||
->reject(fn (ReflectionMethod $reflectionMethod) => $reflectionMethod->class !== $this->reflectionClass->name)
|
||||
->map(fn (ReflectionMethod $reflectionMethod) => $reflectionMethod->name);
|
||||
|
||||
return $this->filterItemsBySimilarity($methods, $similar);
|
||||
}
|
||||
|
||||
protected function filterItemsBySimilarity(Collection $items, string $similar): Collection
|
||||
{
|
||||
return $items
|
||||
->map(function (string $name) use ($similar) {
|
||||
similar_text($similar, $name, $percentage);
|
||||
|
||||
return ['match' => $percentage, 'value' => $name];
|
||||
})
|
||||
->sortByDesc('match')
|
||||
->filter(function (array $item) {
|
||||
return $item['match'] > 40;
|
||||
})
|
||||
->map(function (array $item) {
|
||||
return $item['value'];
|
||||
})
|
||||
->values();
|
||||
}
|
||||
}
|
37
vendor/spatie/laravel-ignition/src/Support/RunnableSolutionsGuard.php
vendored
Normal file
37
vendor/spatie/laravel-ignition/src/Support/RunnableSolutionsGuard.php
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Support;
|
||||
|
||||
class RunnableSolutionsGuard
|
||||
{
|
||||
/**
|
||||
* Check if runnable solutions are allowed based on the current
|
||||
* environment and config.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function check(): bool
|
||||
{
|
||||
if (! config('app.debug')) {
|
||||
// Never run solutions in when debug mode is not enabled.
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (config('ignition.enable_runnable_solutions') !== null) {
|
||||
// Allow enabling or disabling runnable solutions regardless of environment
|
||||
// if the IGNITION_ENABLE_RUNNABLE_SOLUTIONS env var is explicitly set.
|
||||
|
||||
return config('ignition.enable_runnable_solutions');
|
||||
}
|
||||
|
||||
if (! app()->environment('local') && ! app()->environment('development')) {
|
||||
// Never run solutions on non-local environments. This avoids exposing
|
||||
// applications that are somehow APP_ENV=production with APP_DEBUG=true.
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return config('app.debug');
|
||||
}
|
||||
}
|
54
vendor/spatie/laravel-ignition/src/Support/SentReports.php
vendored
Normal file
54
vendor/spatie/laravel-ignition/src/Support/SentReports.php
vendored
Normal file
@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Support;
|
||||
|
||||
use Illuminate\Support\Arr;
|
||||
use Spatie\FlareClient\Report;
|
||||
|
||||
class SentReports
|
||||
{
|
||||
/** @var array<int, Report> */
|
||||
protected array $reports = [];
|
||||
|
||||
public function add(Report $report): self
|
||||
{
|
||||
$this->reports[] = $report;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/** @return array<int, Report> */
|
||||
public function all(): array
|
||||
{
|
||||
return $this->reports;
|
||||
}
|
||||
|
||||
/** @return array<int, string> */
|
||||
public function uuids(): array
|
||||
{
|
||||
return array_map(fn (Report $report) => $report->trackingUuid(), $this->reports);
|
||||
}
|
||||
|
||||
/** @return array<int, string> */
|
||||
public function urls(): array
|
||||
{
|
||||
return array_map(function (string $trackingUuid) {
|
||||
return "https://flareapp.io/tracked-occurrence/{$trackingUuid}";
|
||||
}, $this->uuids());
|
||||
}
|
||||
|
||||
public function latestUuid(): ?string
|
||||
{
|
||||
return Arr::last($this->reports)?->trackingUuid();
|
||||
}
|
||||
|
||||
public function latestUrl(): ?string
|
||||
{
|
||||
return Arr::last($this->urls());
|
||||
}
|
||||
|
||||
public function clear(): void
|
||||
{
|
||||
$this->reports = [];
|
||||
}
|
||||
}
|
65
vendor/spatie/laravel-ignition/src/Support/StringComparator.php
vendored
Normal file
65
vendor/spatie/laravel-ignition/src/Support/StringComparator.php
vendored
Normal file
@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Support;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
class StringComparator
|
||||
{
|
||||
/**
|
||||
* @param array<int|string, string> $strings
|
||||
* @param string $input
|
||||
* @param int $sensitivity
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public static function findClosestMatch(array $strings, string $input, int $sensitivity = 4): ?string
|
||||
{
|
||||
$closestDistance = -1;
|
||||
|
||||
$closestMatch = null;
|
||||
|
||||
foreach ($strings as $string) {
|
||||
$levenshteinDistance = levenshtein($input, $string);
|
||||
|
||||
if ($levenshteinDistance === 0) {
|
||||
$closestMatch = $string;
|
||||
$closestDistance = 0;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if ($levenshteinDistance <= $closestDistance || $closestDistance < 0) {
|
||||
$closestMatch = $string;
|
||||
$closestDistance = $levenshteinDistance;
|
||||
}
|
||||
}
|
||||
|
||||
if ($closestDistance <= $sensitivity) {
|
||||
return $closestMatch;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, string> $strings
|
||||
* @param string $input
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public static function findSimilarText(array $strings, string $input): ?string
|
||||
{
|
||||
if (empty($strings)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Collection::make($strings)
|
||||
->sortByDesc(function (string $string) use ($input) {
|
||||
similar_text($input, $string, $percentage);
|
||||
|
||||
return $percentage;
|
||||
})
|
||||
->first();
|
||||
}
|
||||
}
|
145
vendor/spatie/laravel-ignition/src/Views/BladeSourceMapCompiler.php
vendored
Normal file
145
vendor/spatie/laravel-ignition/src/Views/BladeSourceMapCompiler.php
vendored
Normal file
@ -0,0 +1,145 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Views;
|
||||
|
||||
use Illuminate\View\Compilers\BladeCompiler;
|
||||
use Throwable;
|
||||
|
||||
class BladeSourceMapCompiler
|
||||
{
|
||||
protected BladeCompiler $bladeCompiler;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->bladeCompiler = app('blade.compiler');
|
||||
}
|
||||
|
||||
public function detectLineNumber(string $filename, int $compiledLineNumber): int
|
||||
{
|
||||
$map = $this->compileSourcemap((string)file_get_contents($filename));
|
||||
|
||||
return $this->findClosestLineNumberMapping($map, $compiledLineNumber);
|
||||
}
|
||||
|
||||
protected function compileSourcemap(string $value): string
|
||||
{
|
||||
try {
|
||||
$value = $this->addEchoLineNumbers($value);
|
||||
|
||||
$value = $this->addStatementLineNumbers($value);
|
||||
|
||||
$value = $this->addBladeComponentLineNumbers($value);
|
||||
|
||||
$value = $this->bladeCompiler->compileString($value);
|
||||
|
||||
return $this->trimEmptyLines($value);
|
||||
} catch (Throwable $e) {
|
||||
report($e);
|
||||
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
protected function addEchoLineNumbers(string $value): string
|
||||
{
|
||||
$echoPairs = [['{{', '}}'], ['{{{', '}}}'], ['{!!', '!!}']];
|
||||
|
||||
foreach ($echoPairs as $pair) {
|
||||
// Matches {{ $value }}, {!! $value !!} and {{{ $value }}} depending on $pair
|
||||
$pattern = sprintf('/(@)?%s\s*(.+?)\s*%s(\r?\n)?/s', $pair[0], $pair[1]);
|
||||
|
||||
if (preg_match_all($pattern, $value, $matches, PREG_OFFSET_CAPTURE)) {
|
||||
foreach (array_reverse($matches[0]) as $match) {
|
||||
$position = mb_strlen(substr($value, 0, $match[1]));
|
||||
|
||||
$value = $this->insertLineNumberAtPosition($position, $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
protected function addStatementLineNumbers(string $value): string
|
||||
{
|
||||
// Matches @bladeStatements() like @if, @component(...), @etc;
|
||||
$shouldInsertLineNumbers = preg_match_all(
|
||||
'/\B@(@?\w+(?:::\w+)?)([ \t]*)(\( ( (?>[^()]+) | (?3) )* \))?/x',
|
||||
$value,
|
||||
$matches,
|
||||
PREG_OFFSET_CAPTURE
|
||||
);
|
||||
|
||||
if ($shouldInsertLineNumbers) {
|
||||
foreach (array_reverse($matches[0]) as $match) {
|
||||
$position = mb_strlen(substr($value, 0, $match[1]));
|
||||
|
||||
$value = $this->insertLineNumberAtPosition($position, $value);
|
||||
}
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
protected function addBladeComponentLineNumbers(string $value): string
|
||||
{
|
||||
// Matches the start of `<x-blade-component`
|
||||
$shouldInsertLineNumbers = preg_match_all(
|
||||
'/<\s*x[-:]([\w\-:.]*)/mx',
|
||||
$value,
|
||||
$matches,
|
||||
PREG_OFFSET_CAPTURE
|
||||
);
|
||||
|
||||
if ($shouldInsertLineNumbers) {
|
||||
foreach (array_reverse($matches[0]) as $match) {
|
||||
$position = mb_strlen(substr($value, 0, $match[1]));
|
||||
|
||||
$value = $this->insertLineNumberAtPosition($position, $value);
|
||||
}
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
protected function insertLineNumberAtPosition(int $position, string $value): string
|
||||
{
|
||||
$before = mb_substr($value, 0, $position);
|
||||
$lineNumber = count(explode("\n", $before));
|
||||
|
||||
return mb_substr($value, 0, $position)."|---LINE:{$lineNumber}---|".mb_substr($value, $position);
|
||||
}
|
||||
|
||||
protected function trimEmptyLines(string $value): string
|
||||
{
|
||||
$value = preg_replace('/^\|---LINE:([0-9]+)---\|$/m', '', $value);
|
||||
|
||||
return ltrim((string)$value, PHP_EOL);
|
||||
}
|
||||
|
||||
protected function findClosestLineNumberMapping(string $map, int $compiledLineNumber): int
|
||||
{
|
||||
$map = explode("\n", $map);
|
||||
|
||||
// Max 20 lines between compiled and source line number.
|
||||
// Blade components can span multiple lines and the compiled line number is often
|
||||
// a couple lines below the source-mapped `<x-component>` code.
|
||||
$maxDistance = 20;
|
||||
|
||||
$pattern = '/\|---LINE:(?P<line>[0-9]+)---\|/m';
|
||||
$lineNumberToCheck = $compiledLineNumber - 1;
|
||||
|
||||
while (true) {
|
||||
if ($lineNumberToCheck < $compiledLineNumber - $maxDistance) {
|
||||
// Something wrong. Return the $compiledLineNumber (unless it's out of range)
|
||||
return min($compiledLineNumber, count($map));
|
||||
}
|
||||
|
||||
if (preg_match($pattern, $map[$lineNumberToCheck] ?? '', $matches)) {
|
||||
return (int)$matches['line'];
|
||||
}
|
||||
|
||||
$lineNumberToCheck--;
|
||||
}
|
||||
}
|
||||
}
|
177
vendor/spatie/laravel-ignition/src/Views/ViewExceptionMapper.php
vendored
Normal file
177
vendor/spatie/laravel-ignition/src/Views/ViewExceptionMapper.php
vendored
Normal file
@ -0,0 +1,177 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelIgnition\Views;
|
||||
|
||||
use Exception;
|
||||
use Illuminate\Contracts\View\Engine;
|
||||
use Illuminate\Foundation\Application;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\View\Engines\PhpEngine;
|
||||
use Illuminate\View\ViewException;
|
||||
use ReflectionClass;
|
||||
use ReflectionProperty;
|
||||
use Spatie\Ignition\Contracts\ProvidesSolution;
|
||||
use Spatie\LaravelIgnition\Exceptions\ViewException as IgnitionViewException;
|
||||
use Spatie\LaravelIgnition\Exceptions\ViewExceptionWithSolution;
|
||||
use Throwable;
|
||||
|
||||
class ViewExceptionMapper
|
||||
{
|
||||
protected Engine $compilerEngine;
|
||||
protected BladeSourceMapCompiler $bladeSourceMapCompiler;
|
||||
protected array $knownPaths;
|
||||
|
||||
public function __construct(BladeSourceMapCompiler $bladeSourceMapCompiler)
|
||||
{
|
||||
$resolver = app('view.engine.resolver');
|
||||
|
||||
$this->compilerEngine = $resolver->resolve('blade');
|
||||
|
||||
$this->bladeSourceMapCompiler = $bladeSourceMapCompiler;
|
||||
}
|
||||
|
||||
public function map(ViewException $viewException): IgnitionViewException
|
||||
{
|
||||
$baseException = $this->getRealException($viewException);
|
||||
|
||||
if ($baseException instanceof IgnitionViewException) {
|
||||
return $baseException;
|
||||
}
|
||||
|
||||
preg_match('/\(View: (?P<path>.*?)\)/', $viewException->getMessage(), $matches);
|
||||
|
||||
$compiledViewPath = $matches['path'];
|
||||
|
||||
$exception = $this->createException($baseException);
|
||||
|
||||
if ($baseException instanceof ProvidesSolution) {
|
||||
/** @var ViewExceptionWithSolution $exception */
|
||||
$exception->setSolution($baseException->getSolution());
|
||||
}
|
||||
|
||||
$this->modifyViewsInTrace($exception);
|
||||
|
||||
$exception->setView($compiledViewPath);
|
||||
$exception->setViewData($this->getViewData($exception));
|
||||
|
||||
return $exception;
|
||||
}
|
||||
|
||||
protected function createException(Throwable $baseException): IgnitionViewException
|
||||
{
|
||||
$viewExceptionClass = $baseException instanceof ProvidesSolution
|
||||
? ViewExceptionWithSolution::class
|
||||
: IgnitionViewException::class;
|
||||
|
||||
$viewFile = $this->findCompiledView($baseException->getFile());
|
||||
$file = $viewFile ?? $baseException->getFile();
|
||||
$line = $viewFile ? $this->getBladeLineNumber($file, $baseException->getLine()) : $baseException->getLine();
|
||||
|
||||
return new $viewExceptionClass(
|
||||
$baseException->getMessage(),
|
||||
0,
|
||||
1,
|
||||
$file,
|
||||
$line,
|
||||
$baseException
|
||||
);
|
||||
}
|
||||
|
||||
protected function modifyViewsInTrace(IgnitionViewException $exception): void
|
||||
{
|
||||
$trace = Collection::make($exception->getPrevious()->getTrace())
|
||||
->map(function ($trace) {
|
||||
if ($originalPath = $this->findCompiledView(Arr::get($trace, 'file', ''))) {
|
||||
$trace['file'] = $originalPath;
|
||||
$trace['line'] = $this->getBladeLineNumber($trace['file'], $trace['line']);
|
||||
}
|
||||
|
||||
return $trace;
|
||||
})->toArray();
|
||||
|
||||
$traceProperty = new ReflectionProperty('Exception', 'trace');
|
||||
$traceProperty->setAccessible(true);
|
||||
$traceProperty->setValue($exception, $trace);
|
||||
}
|
||||
|
||||
/**
|
||||
* Look at the previous exceptions to find the original exception.
|
||||
* This is usually the first Exception that is not a ViewException.
|
||||
*/
|
||||
protected function getRealException(Throwable $exception): Throwable
|
||||
{
|
||||
$rootException = $exception->getPrevious() ?? $exception;
|
||||
|
||||
while ($rootException instanceof ViewException && $rootException->getPrevious()) {
|
||||
$rootException = $rootException->getPrevious();
|
||||
}
|
||||
|
||||
return $rootException;
|
||||
}
|
||||
|
||||
protected function findCompiledView(string $compiledPath): ?string
|
||||
{
|
||||
$this->knownPaths ??= $this->getKnownPaths();
|
||||
|
||||
return $this->knownPaths[$compiledPath] ?? null;
|
||||
}
|
||||
|
||||
protected function getKnownPaths(): array
|
||||
{
|
||||
$compilerEngineReflection = new ReflectionClass($this->compilerEngine);
|
||||
|
||||
if (! $compilerEngineReflection->hasProperty('lastCompiled') && $compilerEngineReflection->hasProperty('engine')) {
|
||||
$compilerEngine = $compilerEngineReflection->getProperty('engine');
|
||||
$compilerEngine->setAccessible(true);
|
||||
$compilerEngine = $compilerEngine->getValue($this->compilerEngine);
|
||||
$lastCompiled = new ReflectionProperty($compilerEngine, 'lastCompiled');
|
||||
$lastCompiled->setAccessible(true);
|
||||
$lastCompiled = $lastCompiled->getValue($compilerEngine);
|
||||
} else {
|
||||
$lastCompiled = $compilerEngineReflection->getProperty('lastCompiled');
|
||||
$lastCompiled->setAccessible(true);
|
||||
$lastCompiled = $lastCompiled->getValue($this->compilerEngine);
|
||||
}
|
||||
|
||||
$knownPaths = [];
|
||||
foreach ($lastCompiled as $lastCompiledPath) {
|
||||
$compiledPath = $this->compilerEngine->getCompiler()->getCompiledPath($lastCompiledPath);
|
||||
|
||||
$knownPaths[realpath($compiledPath ?? $lastCompiledPath)] = realpath($lastCompiledPath);
|
||||
}
|
||||
|
||||
return $knownPaths;
|
||||
}
|
||||
|
||||
protected function getBladeLineNumber(string $view, int $compiledLineNumber): int
|
||||
{
|
||||
return $this->bladeSourceMapCompiler->detectLineNumber($view, $compiledLineNumber);
|
||||
}
|
||||
|
||||
protected function getViewData(Throwable $exception): array
|
||||
{
|
||||
foreach ($exception->getTrace() as $frame) {
|
||||
if (Arr::get($frame, 'class') === PhpEngine::class) {
|
||||
$data = Arr::get($frame, 'args.1', []);
|
||||
|
||||
return $this->filterViewData($data);
|
||||
}
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
protected function filterViewData(array $data): array
|
||||
{
|
||||
// By default, Laravel views get two data keys:
|
||||
// __env and app. We try to filter them out.
|
||||
return array_filter($data, function ($value, $key) {
|
||||
if ($key === 'app') {
|
||||
return ! $value instanceof Application;
|
||||
}
|
||||
|
||||
return $key !== '__env';
|
||||
}, ARRAY_FILTER_USE_BOTH);
|
||||
}
|
||||
}
|
24
vendor/spatie/laravel-ignition/src/helpers.php
vendored
Normal file
24
vendor/spatie/laravel-ignition/src/helpers.php
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
use Spatie\LaravelIgnition\Renderers\ErrorPageRenderer;
|
||||
|
||||
if (! function_exists('ddd')) {
|
||||
function ddd()
|
||||
{
|
||||
$args = func_get_args();
|
||||
|
||||
if (count($args) === 0) {
|
||||
throw new Exception('You should pass at least 1 argument to `ddd`');
|
||||
}
|
||||
|
||||
call_user_func_array('dump', $args);
|
||||
|
||||
$renderer = app()->make(ErrorPageRenderer::class);
|
||||
|
||||
$exception = new Exception('Dump, Die, Debug');
|
||||
|
||||
$renderer->render($exception);
|
||||
|
||||
die();
|
||||
}
|
||||
}
|
20
vendor/spatie/laravel-ignition/src/ignition-routes.php
vendored
Normal file
20
vendor/spatie/laravel-ignition/src/ignition-routes.php
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Spatie\LaravelIgnition\Http\Controllers\ExecuteSolutionController;
|
||||
use Spatie\LaravelIgnition\Http\Controllers\HealthCheckController;
|
||||
use Spatie\LaravelIgnition\Http\Controllers\UpdateConfigController;
|
||||
use Spatie\LaravelIgnition\Http\Middleware\RunnableSolutionsEnabled;
|
||||
|
||||
Route::group([
|
||||
'as' => 'ignition.',
|
||||
'prefix' => config('ignition.housekeeping_endpoint_prefix'),
|
||||
'middleware' => [RunnableSolutionsEnabled::class],
|
||||
], function () {
|
||||
Route::get('health-check', HealthCheckController::class)->name('healthCheck');
|
||||
|
||||
Route::post('execute-solution', ExecuteSolutionController::class)
|
||||
->name('executeSolution');
|
||||
|
||||
Route::post('update-config', UpdateConfigController::class)->name('updateConfig');
|
||||
});
|
Reference in New Issue
Block a user