diff --git a/.github/workflows/phpstan.yml b/.github/workflows/phpstan.yml new file mode 100644 index 0000000..d8d3f12 --- /dev/null +++ b/.github/workflows/phpstan.yml @@ -0,0 +1,31 @@ +name: PHPStan + +on: + push: + paths: + - '**.php' + - 'phpstan.neon.dist' + pull_request: + paths: + - '**.php' + - 'phpstan.neon.dist' + + +jobs: + phpstan: + name: phpstan + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.3' + coverage: none + + - name: Install composer dependencies + uses: ramsey/composer-install@v1 + + - name: Run PHPStan + run: ./vendor/bin/phpstan --error-format=github diff --git a/composer.json b/composer.json index f0e9c7c..8a18017 100644 --- a/composer.json +++ b/composer.json @@ -22,7 +22,7 @@ "ext-json": "*", "ext-mbstring": "*", "illuminate/support": "^10.0|^11.0", - "spatie/flare-client-php": "^1.3.5", + "spatie/flare-client-php": "^1.5", "spatie/ignition": "^1.14", "symfony/console": "^6.2.3|^7.0", "symfony/var-dumper": "^6.2.3|^7.0" diff --git a/config/flare.php b/config/flare.php index 7c37687..c811478 100644 --- a/config/flare.php +++ b/config/flare.php @@ -6,6 +6,7 @@ use Spatie\FlareClient\FlareMiddleware\CensorRequestHeaders; use Spatie\LaravelIgnition\FlareMiddleware\AddDumps; use Spatie\LaravelIgnition\FlareMiddleware\AddEnvironmentInformation; +use Spatie\LaravelIgnition\FlareMiddleware\AddExceptionHandledStatus; use Spatie\LaravelIgnition\FlareMiddleware\AddExceptionInformation; use Spatie\LaravelIgnition\FlareMiddleware\AddJobs; use Spatie\LaravelIgnition\FlareMiddleware\AddLogs; @@ -55,6 +56,7 @@ 'max_chained_job_reporting_depth' => 5, ], AddContext::class, + AddExceptionHandledStatus::class, CensorRequestBodyFields::class => [ 'censor_fields' => [ 'password', @@ -70,7 +72,7 @@ 'X-CSRF-TOKEN', 'X-XSRF-TOKEN', ] - ] + ], ], /* diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 278fddc..d1dfb40 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1,5 +1,25 @@ parameters: ignoreErrors: + - + message: "#^Call to an undefined method Livewire\\\\LivewireManager\\:\\:getClass\\(\\)\\.$#" + count: 1 + path: src/ContextProviders/LaravelLivewireRequestContextProvider.php + + - + message: "#^Cannot call method get\\(\\) on Symfony\\\\Component\\\\HttpFoundation\\\\Request\\|null\\.$#" + count: 1 + path: src/ContextProviders/LaravelLivewireRequestContextProvider.php + + - + message: "#^Cannot call method has\\(\\) on Symfony\\\\Component\\\\HttpFoundation\\\\Request\\|null\\.$#" + count: 1 + path: src/ContextProviders/LaravelLivewireRequestContextProvider.php + + - + message: "#^Method Spatie\\\\LaravelIgnition\\\\ContextProviders\\\\LaravelLivewireRequestContextProvider\\:\\:resolveUpdates\\(\\) has parameter \\$updates with no value type specified in iterable type array\\.$#" + count: 1 + path: src/ContextProviders/LaravelLivewireRequestContextProvider.php + - message: "#^Cannot call method toFlare\\(\\) on class\\-string\\|object\\.$#" count: 1 @@ -25,6 +45,11 @@ parameters: count: 1 path: src/Exceptions/InvalidConfig.php + - + message: "#^Class Livewire\\\\LivewireComponentsFinder not found\\.$#" + count: 1 + path: src/Solutions/LivewireDiscoverSolution.php + - message: "#^Parameter \\#1 \\$invalidController of method Spatie\\\\LaravelIgnition\\\\Solutions\\\\SolutionProviders\\\\InvalidRouteActionSolutionProvider\\:\\:findRelatedController\\(\\) expects string, string\\|null given\\.$#" count: 1 @@ -46,7 +71,7 @@ parameters: path: src/Solutions/SolutionProviders/UnknownValidationSolutionProvider.php - - message: "#^Parameter \\#1 \\$callback of method Illuminate\\\\Support\\\\Collection\\\\:\\:filter\\(\\) expects \\(callable\\(ReflectionMethod, int\\)\\: bool\\)\\|null, Closure\\(ReflectionMethod\\)\\: 0\\|1\\|false given\\.$#" + message: "#^Parameter \\#1 \\$callback of method Illuminate\\\\Support\\\\Collection\\\\:\\:filter\\(\\) expects \\(callable\\(ReflectionMethod, int\\)\\: bool\\)\\|null, Closure\\(ReflectionMethod\\)\\: \\(0\\|1\\|false\\) given\\.$#" count: 1 path: src/Solutions/SolutionProviders/UnknownValidationSolutionProvider.php diff --git a/src/ContextProviders/LaravelLivewireRequestContextProvider.php b/src/ContextProviders/LaravelLivewireRequestContextProvider.php index cffaef9..ecaa8cb 100644 --- a/src/ContextProviders/LaravelLivewireRequestContextProvider.php +++ b/src/ContextProviders/LaravelLivewireRequestContextProvider.php @@ -38,7 +38,7 @@ public function toArray(): array return $properties; } - /** @return array */ + /** @return array */ protected function getLivewireInformation(): array { if ($this->request->has('components')) { diff --git a/src/FlareMiddleware/AddExceptionHandledStatus.php b/src/FlareMiddleware/AddExceptionHandledStatus.php new file mode 100644 index 0000000..2311cb3 --- /dev/null +++ b/src/FlareMiddleware/AddExceptionHandledStatus.php @@ -0,0 +1,53 @@ +limit(40)->frames(); + $frameCount = count($frames); + + try { + foreach ($frames as $i => $frame) { + // Check first frame, probably Illuminate\Foundation\Exceptions\Handler::report() + // Next frame should be: Illuminate/Foundation/helpers.php::report() + + if ($frame->method !== 'report') { + continue; + } + + if ($frame->class === null) { + continue; + } + + if ($i === $frameCount - 1) { + continue; + } + + if ($frames[$i + 1]->class !== null) { + continue; + } + + if ($frames[$i + 1]->method !== 'report') { + continue; + } + + $report->handled(); + + break; + } + } catch (Throwable) { + // Do nothing + } + + return $next($report); + } +} diff --git a/tests/FlareMiddleware/AddExceptionHandledTest.php b/tests/FlareMiddleware/AddExceptionHandledTest.php new file mode 100644 index 0000000..8acdadc --- /dev/null +++ b/tests/FlareMiddleware/AddExceptionHandledTest.php @@ -0,0 +1,35 @@ +bind(ExceptionHandler::class, fn () => $handler); + + $someTriggeredException = new Exception('This is a test exception'); + + report($someTriggeredException); + + expect($handler::$report)->toBeInstanceOf(Report::class); + expect($handler::$report->toArray()) + ->toHaveKey('handled', true); +}); + +it('will not mark an exception handled when it is not', function () { + $someTriggeredException = new Exception('This is a test exception'); + + $report = Flare::createReport($someTriggeredException); + + expect($report->toArray())->toHaveKey('handled', null); +});