Initial commit

This commit is contained in:
Ahrom
2025-11-16 12:43:07 +03:30
commit 4bbe56b83f
16778 changed files with 1914371 additions and 0 deletions

View File

@@ -0,0 +1,35 @@
<?php
$finder = Symfony\Component\Finder\Finder::create()
->in([
__DIR__ . '/src',
__DIR__ . '/tests',
])
->name('*.php')
->notName('*.blade.php')
->ignoreDotFiles(true)
->ignoreVCS(true);
return (new PhpCsFixer\Config())
->setRules([
'@PSR2' => true,
'array_syntax' => ['syntax' => 'short'],
'ordered_imports' => ['sort_algorithm' => 'alpha'],
'no_unused_imports' => true,
'not_operator_with_successor_space' => true,
'trailing_comma_in_multiline' => true,
'phpdoc_scalar' => true,
'unary_operator_spaces' => true,
'binary_operator_spaces' => true,
'blank_line_before_statement' => [
'statements' => ['break', 'continue', 'declare', 'return', 'throw', 'try'],
],
'phpdoc_single_line_var_spacing' => true,
'phpdoc_var_without_name' => true,
'method_argument_space' => [
'on_multiline' => 'ensure_fully_multiline',
'keep_multiple_spaces_after_comma' => true,
],
'single_trait_insert_per_statement' => true,
])
->setFinder($finder);

View File

@@ -0,0 +1,76 @@
# Changelog
All notable changes to `error-solutions` will be documented in this file.
## 1.1.1 - 2024-07-25
### What's Changed
* Fix OpenAI response text links by @Lukaaashek in https://github.com/spatie/error-solutions/pull/9
### New Contributors
* @Lukaaashek made their first contribution in https://github.com/spatie/error-solutions/pull/9
**Full Changelog**: https://github.com/spatie/error-solutions/compare/1.1.0...1.1.1
## 1.1.0 - 2024-07-22
### What's Changed
* Allow to customize OpenAI Model by @arnebr in https://github.com/spatie/error-solutions/pull/7
### New Contributors
* @arnebr made their first contribution in https://github.com/spatie/error-solutions/pull/7
**Full Changelog**: https://github.com/spatie/error-solutions/compare/1.0.5...1.1.0
## 1.0.5 - 2024-07-09
### What's Changed
* Legacy `RunnableSolution` should continue to extend legacy `Solution` by @duncanmcclean in https://github.com/spatie/error-solutions/pull/6
### New Contributors
* @duncanmcclean made their first contribution in https://github.com/spatie/error-solutions/pull/6
**Full Changelog**: https://github.com/spatie/error-solutions/compare/1.0.4...1.0.5
## 1.0.4 - 2024-06-28
**Full Changelog**: https://github.com/spatie/error-solutions/compare/1.0.3...1.0.4
## 1.0.3 - 2024-06-27
**Full Changelog**: https://github.com/spatie/error-solutions/compare/1.0.2...1.0.3
## 1.0.2 - 2024-06-26
### What's Changed
* Fix AI solutions
* Bump dependabot/fetch-metadata from 1.6.0 to 2.1.0 by @dependabot in https://github.com/spatie/error-solutions/pull/1
### New Contributors
* @dependabot made their first contribution in https://github.com/spatie/error-solutions/pull/1
**Full Changelog**: https://github.com/spatie/error-solutions/compare/0.0.1...1.0.2
## 1.0.1 - 2024-06-21
- Add the legacy string comperator
**Full Changelog**: https://github.com/spatie/error-solutions/compare/1.0.0...1.0.1
## 1.0.0 - 2024-06-12
- Initial release
**Full Changelog**: https://github.com/spatie/error-solutions/compare/0.0.1...1.0.0
## 0.0.1 - 2024-06-11
**Full Changelog**: https://github.com/spatie/error-solutions/commits/0.0.1

View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) Spatie <ruben@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.

57
vendor/spatie/error-solutions/README.md vendored Normal file
View File

@@ -0,0 +1,57 @@
# Error solutions
[![Latest Version on Packagist](https://img.shields.io/packagist/v/spatie/error-solutions.svg?style=flat-square)](https://packagist.org/packages/spatie/error-solutions)
[![Tests](https://img.shields.io/github/actions/workflow/status/spatie/error-solutions/run-tests.yml?branch=main&label=tests&style=flat-square)](https://github.com/spatie/error-solutions/actions/workflows/run-tests.yml)
[![Total Downloads](https://img.shields.io/packagist/dt/spatie/error-solutions.svg?style=flat-square)](https://packagist.org/packages/spatie/error-solutions)
At Spatie we develop multiple packages handling errors and providing solutions for these errors. This package is a collection of all these solutions.
## Support us
[<img src="https://github-ads.s3.eu-central-1.amazonaws.com/error-solutions.jpg?t=1" width="419px" />](https://spatie.be/github-ad-click/error-solutions)
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).
## Installation
You can install the package via composer:
```bash
composer require spatie/error-solutions
```
## Usage
We've got some excellent documentation on how to use solutions:
- [Flare](https://flareapp.io/docs/ignition/solutions/implementing-solutions)
- [Ignition](https://github.com/spatie/ignition/?tab=readme-ov-file#displaying-solutions)
## Testing
```bash
composer test
```
## Changelog
Please see [CHANGELOG](CHANGELOG.md) for more information on 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
- [Ruben Van Assche](https://github.com/rubenvanassche)
- [All Contributors](../../contributors)
## License
The MIT License (MIT). Please see [License File](LICENSE.md) for more information.

View File

@@ -0,0 +1,69 @@
{
"name" : "spatie/error-solutions",
"description" : "This is my package error-solutions",
"keywords" : [
"Spatie",
"error-solutions"
],
"homepage" : "https://github.com/spatie/error-solutions",
"license" : "MIT",
"authors" : [
{
"name" : "Ruben Van Assche",
"email" : "ruben@spatie.be",
"role" : "Developer"
}
],
"require" : {
"php": "^8.0"
},
"require-dev" : {
"livewire/livewire": "^2.11|^3.3.5",
"illuminate/support": "^10.0|^11.0",
"illuminate/broadcasting" : "^10.0|^11.0",
"openai-php/client": "^0.10.1",
"illuminate/cache" : "^10.0|^11.0",
"pestphp/pest" : "^2.20",
"phpstan/phpstan" : "^1.11",
"psr/simple-cache-implementation" : "^3.0",
"psr/simple-cache" : "^3.0",
"spatie/ray" : "^1.28",
"symfony/cache" : "^5.4|^6.0|^7.0",
"symfony/process" : "^5.4|^6.0|^7.0",
"vlucas/phpdotenv" : "^5.5",
"orchestra/testbench": "^7.0|8.22.3|^9.0"
},
"autoload" : {
"psr-4" : {
"Spatie\\ErrorSolutions\\" : "src",
"Spatie\\Ignition\\" : "legacy/ignition",
"Spatie\\LaravelIgnition\\" : "legacy/laravel-ignition"
}
},
"autoload-dev" : {
"psr-4" : {
"Spatie\\ErrorSolutions\\Tests\\" : "tests"
}
},
"suggest" : {
"openai-php/client" : "Require get solutions from OpenAI",
"simple-cache-implementation" : "To cache solutions from OpenAI"
},
"scripts" : {
"analyse" : "vendor/bin/phpstan analyse",
"baseline" : "vendor/bin/phpstan analyse --generate-baseline",
"test" : "vendor/bin/pest",
"test-coverage" : "vendor/bin/pest --coverage",
"format" : "vendor/bin/pint"
},
"config" : {
"sort-packages" : true,
"allow-plugins" : {
"pestphp/pest-plugin": true,
"phpstan/extension-installer": true,
"php-http/discovery": false
}
},
"minimum-stability" : "dev",
"prefer-stable" : true
}

View File

@@ -0,0 +1,7 @@
<?php
namespace Spatie\Ignition\Contracts;
class BaseSolution extends \Spatie\ErrorSolutions\Contracts\BaseSolution implements Solution
{
}

View File

@@ -0,0 +1,7 @@
<?php
namespace Spatie\Ignition\Contracts;
interface HasSolutionsForThrowable extends \Spatie\ErrorSolutions\Contracts\HasSolutionsForThrowable
{
}

View File

@@ -0,0 +1,7 @@
<?php
namespace Spatie\Ignition\Contracts;
interface ProvidesSolution extends \Spatie\ErrorSolutions\Contracts\ProvidesSolution
{
}

View File

@@ -0,0 +1,16 @@
<?php
namespace Spatie\Ignition\Contracts;
interface RunnableSolution extends Solution
{
public function getSolutionActionDescription(): string;
public function getRunButtonText(): string;
/** @param array<string, mixed> $parameters */
public function run(array $parameters = []): void;
/** @return array<string, mixed> */
public function getRunParameters(): array;
}

View File

@@ -0,0 +1,7 @@
<?php
namespace Spatie\Ignition\Contracts;
interface Solution extends \Spatie\ErrorSolutions\Contracts\Solution
{
}

View File

@@ -0,0 +1,8 @@
<?php
namespace Spatie\Ignition\Contracts;
interface SolutionProviderRepository extends \Spatie\ErrorSolutions\Contracts\SolutionProviderRepository
{
}

View File

@@ -0,0 +1,8 @@
<?php
namespace Spatie\Ignition\Solutions\OpenAi;
class DummyCache extends \Spatie\ErrorSolutions\Solutions\OpenAi\DummyCache
{
}

View File

@@ -0,0 +1,7 @@
<?php
namespace Spatie\Ignition\Solutions\OpenAi;
class OpenAiPromptViewModel extends \Spatie\ErrorSolutions\Solutions\OpenAi\OpenAiPromptViewModel
{
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Spatie\Ignition\Solutions\OpenAi;
use Spatie\Ignition\Contracts\Solution;
class OpenAiSolution extends \Spatie\ErrorSolutions\Solutions\OpenAi\OpenAiSolution implements Solution
{
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Spatie\Ignition\Solutions\OpenAi;
use Spatie\Ignition\Contracts\HasSolutionsForThrowable;
class OpenAiSolutionProvider extends \Spatie\ErrorSolutions\Solutions\OpenAi\OpenAiSolutionProvider implements HasSolutionsForThrowable
{
}

View File

@@ -0,0 +1,8 @@
<?php
namespace Spatie\Ignition\Solutions\OpenAi;
class OpenAiSolutionResponse extends \Spatie\ErrorSolutions\Solutions\OpenAi\OpenAiSolutionResponse
{
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Spatie\Ignition\Solutions\SolutionProviders;
use Spatie\ErrorSolutions\SolutionProviders\BadMethodCallSolutionProvider as BaseBadMethodCallSolutionProviderAlias;
use Spatie\Ignition\Contracts\HasSolutionsForThrowable;
class BadMethodCallSolutionProvider extends BaseBadMethodCallSolutionProviderAlias implements HasSolutionsForThrowable
{
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Spatie\Ignition\Solutions\SolutionProviders;
use Spatie\ErrorSolutions\SolutionProviders\MergeConflictSolutionProvider as BaseMergeConflictSolutionProviderAlias;
use Spatie\Ignition\Contracts\HasSolutionsForThrowable;
class MergeConflictSolutionProvider extends BaseMergeConflictSolutionProviderAlias implements HasSolutionsForThrowable
{
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Spatie\Ignition\Solutions\SolutionProviders;
use Spatie\ErrorSolutions\SolutionProviderRepository as BaseSolutionProviderRepositoryAlias;
use Spatie\Ignition\Contracts\SolutionProviderRepository as SolutionProviderRepositoryContract;
class SolutionProviderRepository extends BaseSolutionProviderRepositoryAlias implements SolutionProviderRepositoryContract
{
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Spatie\Ignition\Solutions\SolutionProviders;
use Spatie\ErrorSolutions\SolutionProviders\UndefinedPropertySolutionProvider as BaseUndefinedPropertySolutionProviderAlias;
use Spatie\Ignition\Contracts\HasSolutionsForThrowable;
class UndefinedPropertySolutionProvider extends BaseUndefinedPropertySolutionProviderAlias implements HasSolutionsForThrowable
{
}

View File

@@ -0,0 +1,7 @@
<?php
namespace Spatie\Ignition\Solutions;
class SolutionTransformer extends \Spatie\ErrorSolutions\Solutions\SolutionTransformer
{
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Spatie\Ignition\Solutions;
use Spatie\ErrorSolutions\Solutions\SuggestCorrectVariableNameSolution as BaseSuggestCorrectVariableNameSolutionAlias;
use Spatie\Ignition\Contracts\Solution;
class SuggestCorrectVariableNameSolution extends BaseSuggestCorrectVariableNameSolutionAlias implements Solution
{
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Spatie\Ignition\Solutions;
use Spatie\ErrorSolutions\Solutions\SuggestImportSolution as BaseSuggestImportSolutionAlias;
use Spatie\Ignition\Contracts\Solution;
class SuggestImportSolution extends BaseSuggestImportSolutionAlias implements Solution
{
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Spatie\LaravelIgnition\Solutions;
use Spatie\ErrorSolutions\Solutions\Laravel\GenerateAppKeySolution as BaseGenerateAppKeySolutionAlias;
use Spatie\Ignition\Contracts\Solution;
class GenerateAppKeySolution extends BaseGenerateAppKeySolutionAlias implements Solution
{
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Spatie\LaravelIgnition\Solutions;
use Spatie\ErrorSolutions\Solutions\Laravel\LivewireDiscoverSolution as BaseLivewireDiscoverSolutionAlias;
use Spatie\Ignition\Contracts\Solution;
class LivewireDiscoverSolution extends BaseLivewireDiscoverSolutionAlias implements Solution
{
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Spatie\LaravelIgnition\Solutions;
use Spatie\ErrorSolutions\Solutions\Laravel\MakeViewVariableOptionalSolution as BaseMakeViewVariableOptionalSolutionAlias;
use Spatie\Ignition\Contracts\Solution;
class MakeViewVariableOptionalSolution extends BaseMakeViewVariableOptionalSolutionAlias implements Solution
{
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Spatie\LaravelIgnition\Solutions;
use Spatie\ErrorSolutions\Solutions\Laravel\RunMigrationsSolution as BaseRunMigrationsSolutionAlias;
use Spatie\Ignition\Contracts\Solution;
class RunMigrationsSolution extends BaseRunMigrationsSolutionAlias implements Solution
{
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Spatie\LaravelIgnition\Solutions\SolutionProviders;
use Spatie\ErrorSolutions\SolutionProviders\Laravel\DefaultDbNameSolutionProvider as BaseDefaultDbNameSolutionProviderAlias;
use Spatie\Ignition\Contracts\HasSolutionsForThrowable;
class DefaultDbNameSolutionProvider extends BaseDefaultDbNameSolutionProviderAlias implements HasSolutionsForThrowable
{
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Spatie\LaravelIgnition\Solutions\SolutionProviders;
use Spatie\ErrorSolutions\SolutionProviders\Laravel\GenericLaravelExceptionSolutionProvider as BaseGenericLaravelExceptionSolutionProviderAlias;
use Spatie\Ignition\Contracts\HasSolutionsForThrowable;
class GenericLaravelExceptionSolutionProvider extends BaseGenericLaravelExceptionSolutionProviderAlias implements HasSolutionsForThrowable
{
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Spatie\LaravelIgnition\Solutions\SolutionProviders;
use Spatie\ErrorSolutions\SolutionProviders\Laravel\IncorrectValetDbCredentialsSolutionProvider as BaseIncorrectValetDbCredentialsSolutionProviderAlias;
use Spatie\Ignition\Contracts\HasSolutionsForThrowable;
class IncorrectValetDbCredentialsSolutionProvider extends BaseIncorrectValetDbCredentialsSolutionProviderAlias implements HasSolutionsForThrowable
{
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Spatie\LaravelIgnition\Solutions\SolutionProviders;
use Spatie\ErrorSolutions\SolutionProviders\Laravel\InvalidRouteActionSolutionProvider as BaseInvalidRouteActionSolutionProviderAlias;
use Spatie\Ignition\Contracts\HasSolutionsForThrowable;
class InvalidRouteActionSolutionProvider extends BaseInvalidRouteActionSolutionProviderAlias implements HasSolutionsForThrowable
{
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Spatie\LaravelIgnition\Solutions\SolutionProviders;
use Spatie\Ignition\Contracts\HasSolutionsForThrowable;
use Spatie\ErrorSolutions\SolutionProviders\Laravel\LazyLoadingViolationSolutionProvider as BaseLazyLoadingViolationSolutionProviderAlias;
class LazyLoadingViolationSolutionProvider extends BaseLazyLoadingViolationSolutionProviderAlias implements HasSolutionsForThrowable
{
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Spatie\LaravelIgnition\Solutions\SolutionProviders;
use Spatie\ErrorSolutions\SolutionProviders\Laravel\MissingAppKeySolutionProvider as BaseMissingAppKeySolutionProviderAlias;
use Spatie\Ignition\Contracts\HasSolutionsForThrowable;
class MissingAppKeySolutionProvider extends BaseMissingAppKeySolutionProviderAlias implements HasSolutionsForThrowable
{
}

View File

@@ -0,0 +1,12 @@
<?php
namespace Spatie\LaravelIgnition\Solutions\SolutionProviders;
use Spatie\ErrorSolutions\SolutionProviders\Laravel\MissingColumnSolutionProvider as BaseMissingColumnSolutionProviderAlias;
use Spatie\Ignition\Contracts\HasSolutionsForThrowable;
class MissingColumnSolutionProvider extends BaseMissingColumnSolutionProviderAlias implements HasSolutionsForThrowable
{
}

View File

@@ -0,0 +1,12 @@
<?php
namespace Spatie\LaravelIgnition\Solutions\SolutionProviders;
use Spatie\ErrorSolutions\SolutionProviders\Laravel\MissingImportSolutionProvider as BaseMissingImportSolutionProviderAlias;
use Spatie\Ignition\Contracts\HasSolutionsForThrowable;
class MissingImportSolutionProvider extends BaseMissingImportSolutionProviderAlias implements HasSolutionsForThrowable
{
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Spatie\LaravelIgnition\Solutions\SolutionProviders;
use Spatie\ErrorSolutions\SolutionProviders\Laravel\MissingLivewireComponentSolutionProvider as BaseMissingLivewireComponentSolutionProviderAlias;
use Spatie\Ignition\Contracts\HasSolutionsForThrowable;
class MissingLivewireComponentSolutionProvider extends BaseMissingLivewireComponentSolutionProviderAlias implements HasSolutionsForThrowable
{
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Spatie\LaravelIgnition\Solutions\SolutionProviders;
use Spatie\ErrorSolutions\SolutionProviders\Laravel\MissingMixManifestSolutionProvider as BaseMissingMixManifestSolutionProviderAlias;
use Spatie\Ignition\Contracts\HasSolutionsForThrowable;
class MissingMixManifestSolutionProvider extends BaseMissingMixManifestSolutionProviderAlias implements HasSolutionsForThrowable
{
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Spatie\LaravelIgnition\Solutions\SolutionProviders;
use Spatie\ErrorSolutions\SolutionProviders\Laravel\MissingViteManifestSolutionProvider as BaseMissingViteManifestSolutionProviderAlias;
use Spatie\Ignition\Contracts\HasSolutionsForThrowable;
class MissingViteManifestSolutionProvider extends BaseMissingViteManifestSolutionProviderAlias implements HasSolutionsForThrowable
{
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Spatie\LaravelIgnition\Solutions\SolutionProviders;
use Spatie\ErrorSolutions\SolutionProviders\Laravel\OpenAiSolutionProvider as BaseOpenAiSolutionProviderAlias;
use Spatie\Ignition\Contracts\HasSolutionsForThrowable;
class OpenAiSolutionProvider extends BaseOpenAiSolutionProviderAlias implements HasSolutionsForThrowable
{
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Spatie\LaravelIgnition\Solutions\SolutionProviders;
use Spatie\ErrorSolutions\SolutionProviders\Laravel\RouteNotDefinedSolutionProvider as BaseRouteNotDefinedSolutionProviderAlias;
use Spatie\Ignition\Contracts\HasSolutionsForThrowable;
class RouteNotDefinedSolutionProvider extends BaseRouteNotDefinedSolutionProviderAlias implements HasSolutionsForThrowable
{
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Spatie\LaravelIgnition\Solutions\SolutionProviders;
use Spatie\ErrorSolutions\SolutionProviders\Laravel\RunningLaravelDuskInProductionProvider as BaseRunningLaravelDuskInProductionProviderAlias;
use Spatie\Ignition\Contracts\HasSolutionsForThrowable;
class RunningLaravelDuskInProductionProvider extends BaseRunningLaravelDuskInProductionProviderAlias implements HasSolutionsForThrowable
{
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Spatie\LaravelIgnition\Solutions\SolutionProviders;
use Spatie\ErrorSolutions\SolutionProviders\Laravel\SailNetworkSolutionProvider as BaseSailNetworkSolutionProviderAlias;
use Spatie\Ignition\Contracts\HasSolutionsForThrowable;
class SailNetworkSolutionProvider extends BaseSailNetworkSolutionProviderAlias implements HasSolutionsForThrowable
{
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Spatie\LaravelIgnition\Solutions\SolutionProviders;
use Spatie\ErrorSolutions\SolutionProviderRepository as BaseSolutionProviderRepositoryAlias;
use Spatie\Ignition\Contracts\SolutionProviderRepository as SolutionProviderRepositoryContract;
class SolutionProviderRepository extends BaseSolutionProviderRepositoryAlias implements SolutionProviderRepositoryContract
{
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Spatie\LaravelIgnition\Solutions\SolutionProviders;
use Spatie\ErrorSolutions\SolutionProviders\Laravel\TableNotFoundSolutionProvider as BaseTableNotFoundSolutionProviderAlias;
use Spatie\Ignition\Contracts\HasSolutionsForThrowable;
class TableNotFoundSolutionProvider extends BaseTableNotFoundSolutionProviderAlias implements HasSolutionsForThrowable
{
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Spatie\LaravelIgnition\Solutions\SolutionProviders;
use Spatie\ErrorSolutions\SolutionProviders\Laravel\UndefinedLivewireMethodSolutionProvider as BaseUndefinedLivewireMethodSolutionProviderAlias;
use Spatie\Ignition\Contracts\HasSolutionsForThrowable;
class UndefinedLivewireMethodSolutionProvider extends BaseUndefinedLivewireMethodSolutionProviderAlias implements HasSolutionsForThrowable
{
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Spatie\LaravelIgnition\Solutions\SolutionProviders;
use Spatie\ErrorSolutions\SolutionProviders\Laravel\UndefinedLivewirePropertySolutionProvider as BaseUndefinedLivewirePropertySolutionProviderAlias;
use Spatie\Ignition\Contracts\HasSolutionsForThrowable;
class UndefinedLivewirePropertySolutionProvider extends BaseUndefinedLivewirePropertySolutionProviderAlias implements HasSolutionsForThrowable
{
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Spatie\LaravelIgnition\Solutions\SolutionProviders;
use Spatie\ErrorSolutions\SolutionProviders\Laravel\UndefinedViewVariableSolutionProvider as BaseUndefinedViewVariableSolutionProviderAlias;
use Spatie\Ignition\Contracts\HasSolutionsForThrowable;
class UndefinedViewVariableSolutionProvider extends BaseUndefinedViewVariableSolutionProviderAlias implements HasSolutionsForThrowable
{
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Spatie\LaravelIgnition\Solutions\SolutionProviders;
use Spatie\ErrorSolutions\SolutionProviders\Laravel\UnknownMariadbCollationSolutionProvider as BaseUnknownMariadbCollationSolutionProviderAlias;
use Spatie\Ignition\Contracts\HasSolutionsForThrowable;
class UnknownMariadbCollationSolutionProvider extends BaseUnknownMariadbCollationSolutionProviderAlias implements HasSolutionsForThrowable
{
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Spatie\LaravelIgnition\Solutions\SolutionProviders;
use Spatie\ErrorSolutions\SolutionProviders\Laravel\UnknownMysql8CollationSolutionProvider as BaseUnknownMysql8CollationSolutionProviderAlias;
use Spatie\Ignition\Contracts\HasSolutionsForThrowable;
class UnknownMysql8CollationSolutionProvider extends BaseUnknownMysql8CollationSolutionProviderAlias implements HasSolutionsForThrowable
{
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Spatie\LaravelIgnition\Solutions\SolutionProviders;
use Spatie\ErrorSolutions\SolutionProviders\Laravel\UnknownValidationSolutionProvider as BaseUnknownValidationSolutionProviderAlias;
use Spatie\Ignition\Contracts\HasSolutionsForThrowable;
class UnknownValidationSolutionProvider extends BaseUnknownValidationSolutionProviderAlias implements HasSolutionsForThrowable
{
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Spatie\LaravelIgnition\Solutions\SolutionProviders;
use Spatie\ErrorSolutions\SolutionProviders\Laravel\ViewNotFoundSolutionProvider as BaseViewNotFoundSolutionProviderAlias;
use Spatie\Ignition\Contracts\HasSolutionsForThrowable;
class ViewNotFoundSolutionProvider extends BaseViewNotFoundSolutionProviderAlias implements HasSolutionsForThrowable
{
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Spatie\LaravelIgnition\Solutions;
use Spatie\ErrorSolutions\Solutions\SuggestCorrectVariableNameSolution as BaseSuggestCorrectVariableNameSolutionAlias;
use Spatie\Ignition\Contracts\Solution;
class SuggestCorrectVariableNameSolution extends BaseSuggestCorrectVariableNameSolutionAlias implements Solution
{
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Spatie\LaravelIgnition\Solutions;
use Spatie\ErrorSolutions\Solutions\SuggestImportSolution as BaseSuggestImportSolutionAlias;
use Spatie\Ignition\Contracts\Solution;
class SuggestImportSolution extends BaseSuggestImportSolutionAlias implements Solution
{
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Spatie\LaravelIgnition\Solutions;
use Spatie\ErrorSolutions\Solutions\Laravel\SuggestLivewireMethodNameSolution as BaseSuggestLivewireMethodNameSolutionAlias;
use Spatie\Ignition\Contracts\Solution;
class SuggestLivewireMethodNameSolution extends BaseSuggestLivewireMethodNameSolutionAlias implements Solution
{
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Spatie\LaravelIgnition\Solutions;
use Spatie\ErrorSolutions\Solutions\Laravel\SuggestLivewirePropertyNameSolution as BaseSuggestLivewirePropertyNameSolutionAlias;
use Spatie\Ignition\Contracts\Solution;
class SuggestLivewirePropertyNameSolution extends BaseSuggestLivewirePropertyNameSolutionAlias implements Solution
{
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Spatie\LaravelIgnition\Solutions;
use Spatie\ErrorSolutions\Solutions\Laravel\SuggestUsingCorrectDbNameSolution as BaseSuggestUsingCorrectDbNameSolutionAlias;
use Spatie\Ignition\Contracts\Solution;
class SuggestUsingCorrectDbNameSolution extends BaseSuggestUsingCorrectDbNameSolutionAlias implements Solution
{
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Spatie\LaravelIgnition\Solutions;
use Spatie\ErrorSolutions\Solutions\Laravel\SuggestUsingMariadbDatabaseSolution as BaseSuggestUsingMariadbDatabaseSolutionAlias;
use Spatie\Ignition\Contracts\Solution;
class SuggestUsingMariadbDatabaseSolution extends BaseSuggestUsingMariadbDatabaseSolutionAlias implements Solution
{
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Spatie\LaravelIgnition\Solutions;
use Spatie\ErrorSolutions\Solutions\Laravel\SuggestUsingMysql8DatabaseSolution as BaseSuggestUsingMysql8DatabaseSolutionAlias;
use Spatie\Ignition\Contracts\Solution;
class SuggestUsingMysql8DatabaseSolution extends BaseSuggestUsingMysql8DatabaseSolutionAlias implements Solution
{
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Spatie\LaravelIgnition\Solutions;
use Spatie\ErrorSolutions\Solutions\Laravel\UseDefaultValetDbCredentialsSolution as BaseUseDefaultValetDbCredentialsSolutionAlias;
use Spatie\Ignition\Contracts\Solution;
class UseDefaultValetDbCredentialsSolution extends BaseUseDefaultValetDbCredentialsSolutionAlias implements Solution
{
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Spatie\LaravelIgnition\Support;
use Spatie\ErrorSolutions\Support\Laravel\StringComparator as BaseStringComparator;
class StringComparator extends BaseStringComparator
{
}

View File

@@ -0,0 +1,181 @@
parameters:
ignoreErrors:
-
message: "#^PHPDoc tag @param for parameter \\$solutionProvider with type class\\-string\\<Spatie\\\\ErrorSolutions\\\\Contracts\\\\HasSolutionsForThrowable\\>\\|Spatie\\\\ErrorSolutions\\\\Contracts\\\\HasSolutionsForThrowable is not subtype of native type string\\.$#"
count: 1
path: src/Contracts/SolutionProviderRepository.php
-
message: "#^Method Spatie\\\\ErrorSolutions\\\\DiscoverSolutionProviders\\:\\:getProviderClassesForType\\(\\) should return array\\<Spatie\\\\ErrorSolutions\\\\Contracts\\\\HasSolutionsForThrowable\\> but returns array\\<int, string\\>\\.$#"
count: 1
path: src/DiscoverSolutionProviders.php
-
message: "#^Unable to resolve the template type TKey in call to function collect$#"
count: 1
path: src/SolutionProviders/Laravel/InvalidRouteActionSolutionProvider.php
-
message: "#^Unable to resolve the template type TValue in call to function collect$#"
count: 1
path: src/SolutionProviders/Laravel/InvalidRouteActionSolutionProvider.php
-
message: "#^Call to method getSolutionDescription\\(\\) on an unknown class Spatie\\\\ErrorSolutions\\\\Solutions\\\\Laravel\\\\SuggestCorrectVariableNameSolution\\.$#"
count: 1
path: src/SolutionProviders/Laravel/UndefinedViewVariableSolutionProvider.php
-
message: "#^Call to method getSolutionTitle\\(\\) on an unknown class Spatie\\\\ErrorSolutions\\\\Solutions\\\\Laravel\\\\SuggestCorrectVariableNameSolution\\.$#"
count: 1
path: src/SolutionProviders/Laravel/UndefinedViewVariableSolutionProvider.php
-
message: "#^Call to method getViewData\\(\\) on an unknown class Spatie\\\\LaravelFlare\\\\Exceptions\\\\ViewException\\.$#"
count: 1
path: src/SolutionProviders/Laravel/UndefinedViewVariableSolutionProvider.php
-
message: "#^Call to method getViewData\\(\\) on an unknown class Spatie\\\\LaravelIgnition\\\\Exceptions\\\\ViewException\\.$#"
count: 1
path: src/SolutionProviders/Laravel/UndefinedViewVariableSolutionProvider.php
-
message: "#^Call to method isRunnable\\(\\) on an unknown class Spatie\\\\ErrorSolutions\\\\Solutions\\\\Laravel\\\\SuggestCorrectVariableNameSolution\\.$#"
count: 1
path: src/SolutionProviders/Laravel/UndefinedViewVariableSolutionProvider.php
-
message: "#^Class Spatie\\\\LaravelFlare\\\\Exceptions\\\\ViewException not found\\.$#"
count: 1
path: src/SolutionProviders/Laravel/UndefinedViewVariableSolutionProvider.php
-
message: "#^Class Spatie\\\\LaravelIgnition\\\\Exceptions\\\\ViewException not found\\.$#"
count: 1
path: src/SolutionProviders/Laravel/UndefinedViewVariableSolutionProvider.php
-
message: "#^Instantiated class Spatie\\\\ErrorSolutions\\\\Solutions\\\\Laravel\\\\SuggestCorrectVariableNameSolution not found\\.$#"
count: 1
path: src/SolutionProviders/Laravel/UndefinedViewVariableSolutionProvider.php
-
message: "#^Parameter \\$throwable of method Spatie\\\\ErrorSolutions\\\\SolutionProviders\\\\Laravel\\\\UndefinedViewVariableSolutionProvider\\:\\:findCorrectVariableSolutions\\(\\) has invalid type Spatie\\\\LaravelFlare\\\\Exceptions\\\\ViewException\\.$#"
count: 2
path: src/SolutionProviders/Laravel/UndefinedViewVariableSolutionProvider.php
-
message: "#^Parameter \\$throwable of method Spatie\\\\ErrorSolutions\\\\SolutionProviders\\\\Laravel\\\\UndefinedViewVariableSolutionProvider\\:\\:findCorrectVariableSolutions\\(\\) has invalid type Spatie\\\\LaravelIgnition\\\\Exceptions\\\\ViewException\\.$#"
count: 2
path: src/SolutionProviders/Laravel/UndefinedViewVariableSolutionProvider.php
-
message: "#^Unable to resolve the template type TKey in call to function collect$#"
count: 1
path: src/SolutionProviders/Laravel/UndefinedViewVariableSolutionProvider.php
-
message: "#^Unable to resolve the template type TValue in call to function collect$#"
count: 1
path: src/SolutionProviders/Laravel/UndefinedViewVariableSolutionProvider.php
-
message: "#^Method Spatie\\\\ErrorSolutions\\\\SolutionProviders\\\\Laravel\\\\UnknownValidationSolutionProvider\\:\\:getAvailableMethods\\(\\) return type with generic class Illuminate\\\\Support\\\\Collection does not specify its types\\: TKey, TValue$#"
count: 1
path: src/SolutionProviders/Laravel/UnknownValidationSolutionProvider.php
-
message: "#^Parameter \\#1 \\$callback of method Illuminate\\\\Support\\\\Collection\\<int,ReflectionMethod\\>\\:\\:filter\\(\\) expects \\(callable\\(ReflectionMethod, int\\)\\: bool\\)\\|null, Closure\\(ReflectionMethod\\)\\: \\(0\\|1\\|false\\) given\\.$#"
count: 1
path: src/SolutionProviders/Laravel/UnknownValidationSolutionProvider.php
-
message: "#^Unable to resolve the template type TMakeKey in call to method static method Illuminate\\\\Support\\\\Collection\\<\\(int\\|string\\),mixed\\>\\:\\:make\\(\\)$#"
count: 1
path: src/SolutionProviders/Laravel/UnknownValidationSolutionProvider.php
-
message: "#^Unable to resolve the template type TMakeValue in call to method static method Illuminate\\\\Support\\\\Collection\\<\\(int\\|string\\),mixed\\>\\:\\:make\\(\\)$#"
count: 1
path: src/SolutionProviders/Laravel/UnknownValidationSolutionProvider.php
-
message: "#^Call to method getMessage\\(\\) on an unknown class Spatie\\\\Ignition\\\\Exceptions\\\\ViewException\\.$#"
count: 1
path: src/SolutionProviders/Laravel/ViewNotFoundSolutionProvider.php
-
message: "#^Call to method getMessage\\(\\) on an unknown class Spatie\\\\LaravelFlare\\\\Exceptions\\\\ViewException\\.$#"
count: 1
path: src/SolutionProviders/Laravel/ViewNotFoundSolutionProvider.php
-
message: "#^Class Spatie\\\\Ignition\\\\Exceptions\\\\ViewException not found\\.$#"
count: 1
path: src/SolutionProviders/Laravel/ViewNotFoundSolutionProvider.php
-
message: "#^Class Spatie\\\\LaravelFlare\\\\Exceptions\\\\ViewException not found\\.$#"
count: 1
path: src/SolutionProviders/Laravel/ViewNotFoundSolutionProvider.php
-
message: "#^Parameter \\#1 \\$missingView of method Spatie\\\\ErrorSolutions\\\\SolutionProviders\\\\Laravel\\\\ViewNotFoundSolutionProvider\\:\\:findRelatedView\\(\\) expects string, string\\|null given\\.$#"
count: 1
path: src/SolutionProviders/Laravel/ViewNotFoundSolutionProvider.php
-
message: "#^Class Livewire\\\\LivewireComponentsFinder not found\\.$#"
count: 1
path: src/Solutions/Laravel/LivewireDiscoverSolution.php
-
message: "#^Method Spatie\\\\ErrorSolutions\\\\Solutions\\\\OpenAi\\\\DummyCache\\:\\:setMultiple\\(\\) has parameter \\$values with no value type specified in iterable type iterable\\.$#"
count: 1
path: src/Solutions/OpenAi/DummyCache.php
-
message: "#^Cannot call method get\\(\\) on Psr\\\\SimpleCache\\\\CacheInterface\\|null\\.$#"
count: 1
path: src/Solutions/OpenAi/OpenAiSolution.php
-
message: "#^Cannot call method getSnippetAsString\\(\\) on Spatie\\\\Backtrace\\\\Frame\\|null\\.$#"
count: 1
path: src/Solutions/OpenAi/OpenAiSolution.php
-
message: "#^Cannot call method set\\(\\) on Psr\\\\SimpleCache\\\\CacheInterface\\|null\\.$#"
count: 1
path: src/Solutions/OpenAi/OpenAiSolution.php
-
message: "#^Parameter \\#1 \\$rawText of class Spatie\\\\ErrorSolutions\\\\Solutions\\\\OpenAi\\\\OpenAiSolutionResponse constructor expects string, string\\|null given\\.$#"
count: 1
path: src/Solutions/OpenAi/OpenAiSolution.php
-
message: "#^Parameter \\$line of class Spatie\\\\ErrorSolutions\\\\Solutions\\\\OpenAi\\\\OpenAiPromptViewModel constructor expects string, int given\\.$#"
count: 1
path: src/Solutions/OpenAi/OpenAiSolution.php
-
message: "#^Property Spatie\\\\ErrorSolutions\\\\Solutions\\\\OpenAi\\\\OpenAiSolution\\:\\:\\$openAiSolutionResponse \\(Spatie\\\\ErrorSolutions\\\\Solutions\\\\OpenAi\\\\OpenAiSolutionResponse\\) does not accept Spatie\\\\ErrorSolutions\\\\Solutions\\\\OpenAi\\\\OpenAiSolutionResponse\\|null\\.$#"
count: 1
path: src/Solutions/OpenAi/OpenAiSolution.php
-
message: "#^Method Spatie\\\\ErrorSolutions\\\\Solutions\\\\OpenAi\\\\OpenAiSolutionResponse\\:\\:links\\(\\) return type has no value type specified in iterable type array\\.$#"
count: 1
path: src/Solutions/OpenAi/OpenAiSolutionResponse.php
-
message: "#^Method Spatie\\\\ErrorSolutions\\\\Support\\\\AiPromptRenderer\\:\\:renderAsString\\(\\) should return string but returns string\\|false\\.$#"
count: 1
path: src/Support/AiPromptRenderer.php
-
message: "#^Property Spatie\\\\ErrorSolutions\\\\Support\\\\Laravel\\\\LivewireComponentParser\\:\\:\\$reflectionClass \\(ReflectionClass\\<Livewire\\\\Component\\>\\) does not accept ReflectionClass\\<object\\>\\.$#"
count: 1
path: src/Support/Laravel/LivewireComponentParser.php

View File

@@ -0,0 +1,11 @@
includes:
- phpstan-baseline.neon
parameters:
level: 8
paths:
- src
tmpDir: build/phpstan
checkMissingIterableValueType: true

View File

@@ -0,0 +1,36 @@
<?php /** @var \Spatie\Ignition\ignition\Solutions\OpenAi\OpenAiPromptViewModel $viewModel */ ?>
You are a very skilled PHP programmer.
<?php if($viewModel->applicationType()) { ?>
You are working on a <?php echo $viewModel->applicationType() ?> application.
<?php } ?>
Use the following context to find a possible fix for the exception message at the end. Limit your answer to 4 or 5 sentences. Also include a few links to documentation that might help.
Use this format in your answer, make sure links are json:
FIX
insert the possible fix here
ENDFIX
LINKS
{"title": "Title link 1", "url": "URL link 1"}
{"title": "Title link 2", "url": "URL link 2"}
ENDLINKS
---
Here comes the context and the exception message:
Line: <?php echo $viewModel->line() ?>
File:
<?php echo $viewModel->file() ?>
Snippet including line numbers:
<?php echo $viewModel->snippet() ?>
Exception class:
<?php echo $viewModel->exceptionClass() ?>
Exception message:
<?php echo $viewModel->exceptionMessage() ?>

View File

@@ -0,0 +1,65 @@
<?php
namespace Spatie\ErrorSolutions\Contracts;
class BaseSolution implements Solution
{
protected string $title;
protected string $description = '';
/** @var array<string, string> */
protected array $links = [];
public static function create(string $title = ''): static
{
// It's important to keep the return type as static because
// the old Facade Ignition contracts extend from this method.
/** @phpstan-ignore-next-line */
return new static($title);
}
public function __construct(string $title = '')
{
$this->title = $title;
}
public function getSolutionTitle(): string
{
return $this->title;
}
public function setSolutionTitle(string $title): self
{
$this->title = $title;
return $this;
}
public function getSolutionDescription(): string
{
return $this->description;
}
public function setSolutionDescription(string $description): self
{
$this->description = $description;
return $this;
}
/** @return array<string, string> */
public function getDocumentationLinks(): array
{
return $this->links;
}
/** @param array<string, string> $links */
public function setDocumentationLinks(array $links): self
{
$this->links = $links;
return $this;
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace Spatie\ErrorSolutions\Contracts;
use Throwable;
/**
* Interface used for SolutionProviders.
*/
interface HasSolutionsForThrowable
{
public function canSolve(Throwable $throwable): bool;
/** @return array<int, Solution> */
public function getSolutions(Throwable $throwable): array;
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Spatie\ErrorSolutions\Contracts;
/**
* Interface to be used on exceptions that provide their own solution.
*/
interface ProvidesSolution
{
public function getSolution(): Solution;
}

View File

@@ -0,0 +1,16 @@
<?php
namespace Spatie\ErrorSolutions\Contracts;
interface RunnableSolution extends Solution
{
public function getSolutionActionDescription(): string;
public function getRunButtonText(): string;
/** @param array<string, mixed> $parameters */
public function run(array $parameters = []): void;
/** @return array<string, mixed> */
public function getRunParameters(): array;
}

View File

@@ -0,0 +1,13 @@
<?php
namespace Spatie\ErrorSolutions\Contracts;
interface Solution
{
public function getSolutionTitle(): string;
public function getSolutionDescription(): string;
/** @return array<string, string> */
public function getDocumentationLinks(): array;
}

View File

@@ -0,0 +1,36 @@
<?php
namespace Spatie\ErrorSolutions\Contracts;
use Throwable;
interface SolutionProviderRepository
{
/**
* @param class-string<HasSolutionsForThrowable>|HasSolutionsForThrowable $solutionProvider
*
* @return $this
*/
public function registerSolutionProvider(string $solutionProvider): self;
/**
* @param array<class-string<HasSolutionsForThrowable>|HasSolutionsForThrowable> $solutionProviders
*
* @return $this
*/
public function registerSolutionProviders(array $solutionProviders): self;
/**
* @param Throwable $throwable
*
* @return array<int, Solution>
*/
public function getSolutionsForThrowable(Throwable $throwable): array;
/**
* @param class-string<Solution> $solutionClass
*
* @return null|Solution
*/
public function getSolutionForClass(string $solutionClass): ?Solution;
}

View File

@@ -0,0 +1,91 @@
<?php
namespace Spatie\ErrorSolutions;
use Spatie\ErrorSolutions\Contracts\HasSolutionsForThrowable;
class DiscoverSolutionProviders
{
/** @var array<string, string> */
protected array $config = [
'ai' => 'SolutionProviders/OpenAi',
'php' => 'SolutionProviders',
'laravel' => 'SolutionProviders/Laravel',
];
/**
* @param array<string> $types
*
* @return array<HasSolutionsForThrowable>
*/
public static function for(array $types): array
{
if (in_array('php', $types)) {
$types[] = 'ai';
}
return (new self($types))->get();
}
/**
* @param array<string> $types
*/
public function __construct(protected array $types)
{
}
/** @return array<HasSolutionsForThrowable> */
public function get(): array
{
$providers = [];
foreach ($this->types as $type) {
$providers = array_merge($providers, $this->getProviderClassesForType($type));
}
return $providers;
}
/** @return array<HasSolutionsForThrowable> */
protected function getProviderClassesForType(string $type): array
{
$relativePath = $this->config[$type] ?? null;
if (! $relativePath) {
return [];
}
$namespace = $this->getNamespaceForPath($relativePath);
$globPattern = __DIR__ . '/' . $relativePath . '/*.php';
$files = glob($globPattern);
if (! $files) {
return [];
}
$solutionProviders = array_map(function (string $solutionProviderFilePath) use ($namespace) {
$fileName = pathinfo($solutionProviderFilePath, PATHINFO_FILENAME);
$fqcn = $namespace . '\\' . $fileName;
$validClass = in_array(HasSolutionsForThrowable::class, class_implements($fqcn) ?: []);
return $validClass ? $fqcn : null;
}, $files);
return array_values(array_filter($solutionProviders));
}
protected function getNamespaceForPath(string $relativePath): string
{
$namespacePath = str_replace('/', '\\', $relativePath);
$namespace = 'Spatie\\ErrorSolutions\\' . $namespacePath;
return $namespace;
}
}

View File

@@ -0,0 +1,111 @@
<?php
namespace Spatie\ErrorSolutions;
use Illuminate\Support\Collection;
use Spatie\ErrorSolutions\Contracts\HasSolutionsForThrowable;
use Spatie\ErrorSolutions\Contracts\ProvidesSolution;
use Spatie\ErrorSolutions\Contracts\Solution;
use Spatie\ErrorSolutions\Contracts\SolutionProviderRepository as SolutionProviderRepositoryContract;
use Throwable;
class SolutionProviderRepository implements SolutionProviderRepositoryContract
{
/** @var Collection<int, class-string<HasSolutionsForThrowable>|HasSolutionsForThrowable> */
protected Collection $solutionProviders;
/** @param array<int, class-string<HasSolutionsForThrowable>|HasSolutionsForThrowable> $solutionProviders */
public function __construct(array $solutionProviders = [])
{
$this->solutionProviders = Collection::make($solutionProviders);
}
public function registerSolutionProvider(string|HasSolutionsForThrowable $solutionProvider): SolutionProviderRepositoryContract
{
$this->solutionProviders->push($solutionProvider);
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();
}
$providedSolutions = $this
->initialiseSolutionProviderRepositories()
->filter(function (HasSolutionsForThrowable $solutionProvider) use ($throwable) {
try {
return $solutionProvider->canSolve($throwable);
} catch (Throwable $exception) {
return false;
}
})
->map(function (HasSolutionsForThrowable $solutionProvider) use ($throwable) {
try {
return $solutionProvider->getSolutions($throwable);
} catch (Throwable $exception) {
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;
}
if (! function_exists('app')) {
return null;
}
return app($solutionClass);
}
/** @return Collection<int, HasSolutionsForThrowable> */
protected function initialiseSolutionProviderRepositories(): Collection
{
return $this->solutionProviders
->filter(function (HasSolutionsForThrowable|string $provider) {
if (! in_array(HasSolutionsForThrowable::class, class_implements($provider) ?: [])) {
return false;
}
if (function_exists('config') && in_array($provider, config('ErrorSolutions.ignored_solution_providers', []))) {
return false;
}
return true;
})
->map(function (string|HasSolutionsForThrowable $provider): HasSolutionsForThrowable {
if (is_string($provider)) {
return new $provider;
}
return $provider;
});
}
}

View File

@@ -0,0 +1,98 @@
<?php
namespace Spatie\ErrorSolutions\SolutionProviders;
use BadMethodCallException;
use Illuminate\Support\Collection;
use ReflectionClass;
use ReflectionMethod;
use Spatie\ErrorSolutions\Contracts\BaseSolution;
use Spatie\ErrorSolutions\Contracts\HasSolutionsForThrowable;
use Throwable;
class BadMethodCallSolutionProvider implements HasSolutionsForThrowable
{
protected const REGEX = '/([a-zA-Z\\\\]+)::([a-zA-Z]+)/m';
public function canSolve(Throwable $throwable): bool
{
if (! $throwable instanceof BadMethodCallException) {
return false;
}
if (is_null($this->getClassAndMethodFromExceptionMessage($throwable->getMessage()))) {
return false;
}
return true;
}
public function getSolutions(Throwable $throwable): array
{
return [
BaseSolution::create('Bad Method Call')
->setSolutionDescription($this->getSolutionDescription($throwable)),
];
}
public function getSolutionDescription(Throwable $throwable): string
{
if (! $this->canSolve($throwable)) {
return '';
}
/** @phpstan-ignore-next-line */
extract($this->getClassAndMethodFromExceptionMessage($throwable->getMessage()), EXTR_OVERWRITE);
$possibleMethod = $this->findPossibleMethod($class ?? '', $method ?? '');
$class ??= 'UnknownClass';
return "Did you mean {$class}::{$possibleMethod?->name}() ?";
}
/**
* @param string $message
*
* @return null|array<string, mixed>
*/
protected function getClassAndMethodFromExceptionMessage(string $message): ?array
{
if (! preg_match(self::REGEX, $message, $matches)) {
return null;
}
return [
'class' => $matches[1],
'method' => $matches[2],
];
}
/**
* @param class-string $class
* @param string $invalidMethodName
*
* @return \ReflectionMethod|null
*/
protected function findPossibleMethod(string $class, string $invalidMethodName): ?ReflectionMethod
{
return $this->getAvailableMethods($class)
->sortByDesc(function (ReflectionMethod $method) use ($invalidMethodName) {
similar_text($invalidMethodName, $method->name, $percentage);
return $percentage;
})->first();
}
/**
* @param class-string $class
*
* @return \Illuminate\Support\Collection<int, ReflectionMethod>
*/
protected function getAvailableMethods(string $class): Collection
{
$class = new ReflectionClass($class);
return Collection::make($class->getMethods());
}
}

View File

@@ -0,0 +1,35 @@
<?php
namespace Spatie\ErrorSolutions\SolutionProviders\Laravel;
use Illuminate\Database\QueryException;
use Spatie\ErrorSolutions\Contracts\HasSolutionsForThrowable;
use Spatie\ErrorSolutions\Solutions\Laravel\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()];
}
}

View File

@@ -0,0 +1,63 @@
<?php
namespace Spatie\ErrorSolutions\SolutionProviders\Laravel;
use Illuminate\Broadcasting\BroadcastException;
use Spatie\ErrorSolutions\Contracts\BaseSolution;
use Spatie\ErrorSolutions\Contracts\HasSolutionsForThrowable;
use Spatie\ErrorSolutions\Support\Laravel\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",
],
],
];
}
}

View File

@@ -0,0 +1,67 @@
<?php
namespace Spatie\ErrorSolutions\SolutionProviders\Laravel;
use Illuminate\Database\QueryException;
use Spatie\ErrorSolutions\Contracts\HasSolutionsForThrowable;
use Spatie\ErrorSolutions\Solutions\Laravel\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') === '';
}
}

View File

@@ -0,0 +1,83 @@
<?php
namespace Spatie\ErrorSolutions\SolutionProviders\Laravel;
use Illuminate\Support\Str;
use Spatie\ErrorSolutions\Contracts\BaseSolution;
use Spatie\ErrorSolutions\Contracts\HasSolutionsForThrowable;
use Spatie\ErrorSolutions\Support\Laravel\Composer\ComposerClassMap;
use Spatie\ErrorSolutions\Support\Laravel\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;
if (! $invalidController) {
return [];
}
$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;
}
}

View File

@@ -0,0 +1,41 @@
<?php
namespace Spatie\ErrorSolutions\SolutionProviders\Laravel;
use Illuminate\Database\LazyLoadingViolationException;
use Spatie\ErrorSolutions\Contracts\BaseSolution;
use Spatie\ErrorSolutions\Contracts\HasSolutionsForThrowable;
use Spatie\ErrorSolutions\Support\Laravel\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',
]),];
}
}

View File

@@ -0,0 +1,25 @@
<?php
namespace Spatie\ErrorSolutions\SolutionProviders\Laravel;
use RuntimeException;
use Spatie\ErrorSolutions\Contracts\HasSolutionsForThrowable;
use Spatie\ErrorSolutions\Solutions\Laravel\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()];
}
}

View File

@@ -0,0 +1,35 @@
<?php
namespace Spatie\ErrorSolutions\SolutionProviders\Laravel;
use Illuminate\Database\QueryException;
use Spatie\ErrorSolutions\Contracts\HasSolutionsForThrowable;
use Spatie\ErrorSolutions\Solutions\Laravel\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')];
}
}

View File

@@ -0,0 +1,55 @@
<?php
namespace Spatie\ErrorSolutions\SolutionProviders\Laravel;
use Spatie\ErrorSolutions\Contracts\HasSolutionsForThrowable;
use Spatie\ErrorSolutions\Solutions\SuggestImportSolution;
use Spatie\ErrorSolutions\Support\Laravel\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);
}
}
}

View File

@@ -0,0 +1,42 @@
<?php
namespace Spatie\ErrorSolutions\SolutionProviders\Laravel;
use Livewire\Exceptions\ComponentNotFoundException;
use Livewire\LivewireComponentsFinder;
use Spatie\ErrorSolutions\Contracts\HasSolutionsForThrowable;
use Spatie\ErrorSolutions\Solutions\Laravel\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;
}
}

View File

@@ -0,0 +1,25 @@
<?php
namespace Spatie\ErrorSolutions\SolutionProviders\Laravel;
use Illuminate\Support\Str;
use Spatie\ErrorSolutions\Contracts\BaseSolution;
use Spatie\ErrorSolutions\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`?'),
];
}
}

View File

@@ -0,0 +1,62 @@
<?php
namespace Spatie\ErrorSolutions\SolutionProviders\Laravel;
use Illuminate\Support\Str;
use Spatie\ErrorSolutions\Contracts\BaseSolution;
use Spatie\ErrorSolutions\Contracts\HasSolutionsForThrowable;
use Spatie\ErrorSolutions\Contracts\Solution;
use Spatie\ErrorSolutions\Support\Laravel\LaravelVersion;
use Throwable;
class MissingViteManifestSolutionProvider implements HasSolutionsForThrowable
{
/** @var array<string, string> */
protected array $links = [];
public function __construct()
{
$this->links = [
'Asset bundling with Vite' => 'https://laravel.com/docs/'.LaravelVersion::major().'.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);
}
}

View File

@@ -0,0 +1,39 @@
<?php
namespace Spatie\ErrorSolutions\SolutionProviders\Laravel;
use Illuminate\Support\Str;
use OpenAI\Client;
use Spatie\ErrorSolutions\Contracts\HasSolutionsForThrowable;
use Spatie\ErrorSolutions\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('error-solutions.open_ai_key') === null) {
return false;
}
return true;
}
public function getSolutions(Throwable $throwable): array
{
$solutionProvider = new BaseOpenAiSolutionProvider(
openAiKey: config('error-solutions.open_ai_key'),
cache: cache()->store(config('cache.default')),
cacheTtlInSeconds: 60,
applicationType: 'Laravel ' . Str::before(app()->version(), '.'),
applicationPath: base_path(),
openAiModel: config('error-solutions.open_ai_model', 'gpt-3.5-turbo'),
);
return $solutionProvider->getSolutions($throwable);
}
}

View File

@@ -0,0 +1,52 @@
<?php
namespace Spatie\ErrorSolutions\SolutionProviders\Laravel;
use Illuminate\Support\Facades\Route;
use Spatie\ErrorSolutions\Contracts\BaseSolution;
use Spatie\ErrorSolutions\Contracts\HasSolutionsForThrowable;
use Spatie\ErrorSolutions\Support\Laravel\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);
}
}

View File

@@ -0,0 +1,33 @@
<?php
namespace Spatie\ErrorSolutions\SolutionProviders\Laravel;
use Exception;
use Spatie\ErrorSolutions\Contracts\BaseSolution;
use Spatie\ErrorSolutions\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.'),
];
}
}

View File

@@ -0,0 +1,30 @@
<?php
namespace Spatie\ErrorSolutions\SolutionProviders\Laravel;
use Spatie\ErrorSolutions\Contracts\BaseSolution;
use Spatie\ErrorSolutions\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',
]),
];
}
}

View File

@@ -0,0 +1,35 @@
<?php
namespace Spatie\ErrorSolutions\SolutionProviders\Laravel;
use Illuminate\Database\QueryException;
use Spatie\ErrorSolutions\Contracts\HasSolutionsForThrowable;
use Spatie\ErrorSolutions\Solutions\Laravel\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')];
}
}

View File

@@ -0,0 +1,49 @@
<?php
namespace Spatie\ErrorSolutions\SolutionProviders\Laravel;
use Livewire\Exceptions\MethodNotFoundException;
use Spatie\ErrorSolutions\Contracts\HasSolutionsForThrowable;
use Spatie\ErrorSolutions\Solutions\Laravel\SuggestLivewireMethodNameSolution;
use Spatie\ErrorSolutions\Support\Laravel\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,
];
}
}

View File

@@ -0,0 +1,53 @@
<?php
namespace Spatie\ErrorSolutions\SolutionProviders\Laravel;
use Livewire\Exceptions\PropertyNotFoundException;
use Spatie\ErrorSolutions\Contracts\HasSolutionsForThrowable;
use Spatie\ErrorSolutions\Solutions\Laravel\SuggestLivewirePropertyNameSolution;
use Spatie\ErrorSolutions\Support\Laravel\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,
];
}
}

View File

@@ -0,0 +1,111 @@
<?php
namespace Spatie\ErrorSolutions\SolutionProviders\Laravel;
use Spatie\ErrorSolutions\Contracts\BaseSolution;
use Spatie\ErrorSolutions\Contracts\HasSolutionsForThrowable;
use Spatie\ErrorSolutions\Contracts\Solution;
use Spatie\ErrorSolutions\Solutions\Laravel\MakeViewVariableOptionalSolution;
use Spatie\ErrorSolutions\Solutions\Laravel\SuggestCorrectVariableNameSolution;
use Spatie\LaravelFlare\Exceptions\ViewException as FlareViewException;
use Spatie\LaravelIgnition\Exceptions\ViewException as IgnitionViewException;
use Throwable;
class UndefinedViewVariableSolutionProvider implements HasSolutionsForThrowable
{
protected string $variableName;
protected string $viewFile;
public function canSolve(Throwable $throwable): bool
{
if (! $throwable instanceof IgnitionViewException && ! $throwable instanceof FlareViewException) {
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 IgnitionViewException|FlareViewException $throwable
* @param string $variableName
* @param string $viewFile
*
* @return array<int, \Spatie\ErrorSolutions\Contracts\Solution>
*/
protected function findCorrectVariableSolutions(
IgnitionViewException|FlareViewException $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;
}
}

View File

@@ -0,0 +1,34 @@
<?php
namespace Spatie\ErrorSolutions\SolutionProviders\Laravel;
use Illuminate\Database\QueryException;
use Spatie\ErrorSolutions\Contracts\HasSolutionsForThrowable;
use Spatie\ErrorSolutions\Solutions\Laravel\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()];
}
}

View File

@@ -0,0 +1,34 @@
<?php
namespace Spatie\ErrorSolutions\SolutionProviders\Laravel;
use Illuminate\Database\QueryException;
use Spatie\ErrorSolutions\Contracts\HasSolutionsForThrowable;
use Spatie\ErrorSolutions\Solutions\Laravel\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()];
}
}

View File

@@ -0,0 +1,78 @@
<?php
namespace Spatie\ErrorSolutions\SolutionProviders\Laravel;
use BadMethodCallException;
use Illuminate\Support\Collection;
use Illuminate\Support\Str;
use Illuminate\Validation\Validator;
use ReflectionClass;
use ReflectionMethod;
use Spatie\ErrorSolutions\Contracts\BaseSolution;
use Spatie\ErrorSolutions\Contracts\HasSolutionsForThrowable;
use Spatie\ErrorSolutions\Support\Laravel\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);
}
}

View File

@@ -0,0 +1,117 @@
<?php
namespace Spatie\ErrorSolutions\SolutionProviders\Laravel;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\View;
use InvalidArgumentException;
use Spatie\ErrorSolutions\Contracts\BaseSolution;
use Spatie\ErrorSolutions\Contracts\HasSolutionsForThrowable;
use Spatie\ErrorSolutions\Support\Laravel\StringComparator;
use Spatie\Ignition\Exceptions\ViewException as IgnitionViewException;
use Spatie\LaravelFlare\Exceptions\ViewException as FlareViewException;
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 IgnitionViewException || ! $throwable instanceof FlareViewException)) {
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;
}
}

View File

@@ -0,0 +1,74 @@
<?php
namespace Spatie\ErrorSolutions\SolutionProviders;
use Illuminate\Support\Str;
use ParseError;
use Spatie\ErrorSolutions\Contracts\BaseSolution;
use Spatie\ErrorSolutions\Contracts\HasSolutionsForThrowable;
use Throwable;
class MergeConflictSolutionProvider implements HasSolutionsForThrowable
{
public function canSolve(Throwable $throwable): bool
{
if (! ($throwable instanceof ParseError)) {
return false;
}
if (! $this->hasMergeConflictExceptionMessage($throwable)) {
return false;
}
$file = (string)file_get_contents($throwable->getFile());
if (! str_contains($file, '=======')) {
return false;
}
if (! str_contains($file, '>>>>>>>')) {
return false;
}
return true;
}
public function getSolutions(Throwable $throwable): array
{
$file = (string)file_get_contents($throwable->getFile());
preg_match('/\>\>\>\>\>\>\> (.*?)\n/', $file, $matches);
$source = $matches[1];
$target = $this->getCurrentBranch(basename($throwable->getFile()));
return [
BaseSolution::create("Merge conflict from branch '$source' into $target")
->setSolutionDescription('You have a Git merge conflict. To undo your merge do `git reset --hard HEAD`'),
];
}
protected function getCurrentBranch(string $directory): string
{
$branch = "'".trim((string)shell_exec("cd {$directory}; git branch | grep \\* | cut -d ' ' -f2"))."'";
if ($branch === "''") {
$branch = 'current branch';
}
return $branch;
}
protected function hasMergeConflictExceptionMessage(Throwable $throwable): bool
{
// For PHP 7.x and below
if (Str::startsWith($throwable->getMessage(), 'syntax error, unexpected \'<<\'')) {
return true;
}
// For PHP 8+
if (Str::startsWith($throwable->getMessage(), 'syntax error, unexpected token "<<"')) {
return true;
}
return false;
}
}

View File

@@ -0,0 +1,121 @@
<?php
namespace Spatie\ErrorSolutions\SolutionProviders;
use ErrorException;
use Illuminate\Support\Collection;
use ReflectionClass;
use ReflectionProperty;
use Spatie\ErrorSolutions\Contracts\BaseSolution;
use Spatie\ErrorSolutions\Contracts\HasSolutionsForThrowable;
use Throwable;
class UndefinedPropertySolutionProvider implements HasSolutionsForThrowable
{
protected const REGEX = '/([a-zA-Z\\\\]+)::\$([a-zA-Z]+)/m';
protected const MINIMUM_SIMILARITY = 80;
public function canSolve(Throwable $throwable): bool
{
if (! $throwable instanceof ErrorException) {
return false;
}
if (is_null($this->getClassAndPropertyFromExceptionMessage($throwable->getMessage()))) {
return false;
}
if (! $this->similarPropertyExists($throwable)) {
return false;
}
return true;
}
public function getSolutions(Throwable $throwable): array
{
return [
BaseSolution::create('Unknown Property')
->setSolutionDescription($this->getSolutionDescription($throwable)),
];
}
public function getSolutionDescription(Throwable $throwable): string
{
if (! $this->canSolve($throwable) || ! $this->similarPropertyExists($throwable)) {
return '';
}
extract(
/** @phpstan-ignore-next-line */
$this->getClassAndPropertyFromExceptionMessage($throwable->getMessage()),
EXTR_OVERWRITE,
);
$possibleProperty = $this->findPossibleProperty($class ?? '', $property ?? '');
$class = $class ?? '';
return "Did you mean {$class}::\${$possibleProperty->name} ?";
}
protected function similarPropertyExists(Throwable $throwable): bool
{
/** @phpstan-ignore-next-line */
extract($this->getClassAndPropertyFromExceptionMessage($throwable->getMessage()), EXTR_OVERWRITE);
$possibleProperty = $this->findPossibleProperty($class ?? '', $property ?? '');
return $possibleProperty !== null;
}
/**
* @param string $message
*
* @return null|array<string, string>
*/
protected function getClassAndPropertyFromExceptionMessage(string $message): ?array
{
if (! preg_match(self::REGEX, $message, $matches)) {
return null;
}
return [
'class' => $matches[1],
'property' => $matches[2],
];
}
/**
* @param class-string $class
* @param string $invalidPropertyName
*
* @return mixed
*/
protected function findPossibleProperty(string $class, string $invalidPropertyName): mixed
{
return $this->getAvailableProperties($class)
->sortByDesc(function (ReflectionProperty $property) use ($invalidPropertyName) {
similar_text($invalidPropertyName, $property->name, $percentage);
return $percentage;
})
->filter(function (ReflectionProperty $property) use ($invalidPropertyName) {
similar_text($invalidPropertyName, $property->name, $percentage);
return $percentage >= self::MINIMUM_SIMILARITY;
})->first();
}
/**
* @param class-string $class
*
* @return Collection<int, ReflectionProperty>
*/
protected function getAvailableProperties(string $class): Collection
{
$class = new ReflectionClass($class);
return Collection::make($class->getProperties());
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace Spatie\ErrorSolutions\Solutions\Concerns;
trait IsProvidedByFlare
{
public function solutionProvidedByName(): string
{
return 'Flare';
}
public function solutionProvidedByLink(): string
{
return 'https://flareapp.io';
}
}

View File

@@ -0,0 +1,49 @@
<?php
namespace Spatie\ErrorSolutions\Solutions\Laravel;
use Illuminate\Support\Facades\Artisan;
use Spatie\ErrorSolutions\Contracts\RunnableSolution;
use Spatie\ErrorSolutions\Solutions\Concerns\IsProvidedByFlare;
class GenerateAppKeySolution implements RunnableSolution
{
use IsProvidedByFlare;
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 $this->getSolutionActionDescription();
}
public function getRunParameters(): array
{
return [];
}
public function run(array $parameters = []): void
{
Artisan::call('key:generate');
}
}

View File

@@ -0,0 +1,56 @@
<?php
namespace Spatie\ErrorSolutions\Solutions\Laravel;
use Livewire\LivewireComponentsFinder;
use Spatie\ErrorSolutions\Contracts\RunnableSolution;
use Spatie\ErrorSolutions\Solutions\Concerns\IsProvidedByFlare;
class LivewireDiscoverSolution implements RunnableSolution
{
use IsProvidedByFlare;
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();
}
}

Some files were not shown because too many files have changed in this diff Show More