first commit
This commit is contained in:
21
vendor/spatie/flare-client-php/LICENSE.md
vendored
Normal file
21
vendor/spatie/flare-client-php/LICENSE.md
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) Facade <info@facade.company>
|
||||
|
||||
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.
|
39
vendor/spatie/flare-client-php/README.md
vendored
Normal file
39
vendor/spatie/flare-client-php/README.md
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
# Send PHP errors to Flare
|
||||
|
||||
[](https://packagist.org/packages/spatie/flare-client-php)
|
||||
[](https://github.com/spatie/flare-client-php/actions/workflows/run-tests.yml)
|
||||
[](https://github.com/spatie/flare-client-php/actions/workflows/phpstan.yml)
|
||||
[](https://packagist.org/packages/spatie/flare-client-php)
|
||||
|
||||
This repository contains the PHP client to send errors and exceptions to [Flare](https://flareapp.io). The client can be installed using composer and works for PHP 8.0 and above.
|
||||
|
||||
Using Laravel? You probably want to use [Ignition for Laravel](https://github.com/spatie/laravel-ignition). It comes with a beautiful error page and has the Flare client built in.
|
||||
|
||||

|
||||
|
||||
## Documentation
|
||||
|
||||
You can find the documentation of this package at [the docs of Flare](https://flareapp.io/docs/flare/general/welcome-to-flare).
|
||||
|
||||
## Changelog
|
||||
|
||||
Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.
|
||||
|
||||
## Testing
|
||||
|
||||
``` bash
|
||||
composer test
|
||||
```
|
||||
|
||||
## Contributing
|
||||
|
||||
Please see [CONTRIBUTING](https://github.com/spatie/.github/blob/main/CONTRIBUTING.md) for details.
|
||||
|
||||
## Security
|
||||
|
||||
If you discover any security related issues, please email support@flareapp.io instead of using the issue tracker.
|
||||
|
||||
## License
|
||||
|
||||
The MIT License (MIT). Please see [License File](LICENSE.md) for more information.
|
||||
|
63
vendor/spatie/flare-client-php/composer.json
vendored
Normal file
63
vendor/spatie/flare-client-php/composer.json
vendored
Normal file
@ -0,0 +1,63 @@
|
||||
{
|
||||
"name": "spatie/flare-client-php",
|
||||
"description": "Send PHP errors to Flare",
|
||||
"keywords": [
|
||||
"spatie",
|
||||
"flare",
|
||||
"exception",
|
||||
"reporting"
|
||||
],
|
||||
"homepage": "https://github.com/spatie/flare-client-php",
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"php": "^8.0",
|
||||
"illuminate/pipeline": "^8.0|^9.0|^10.0|^11.0",
|
||||
"spatie/backtrace": "^1.5.2",
|
||||
"symfony/http-foundation": "^5.2|^6.0|^7.0",
|
||||
"symfony/mime": "^5.2|^6.0|^7.0",
|
||||
"symfony/process": "^5.2|^6.0|^7.0",
|
||||
"symfony/var-dumper": "^5.2|^6.0|^7.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"dms/phpunit-arraysubset-asserts": "^0.5.0",
|
||||
"phpstan/extension-installer": "^1.1",
|
||||
"phpstan/phpstan-deprecation-rules": "^1.0",
|
||||
"phpstan/phpstan-phpunit": "^1.0",
|
||||
"spatie/phpunit-snapshot-assertions": "^4.0|^5.0",
|
||||
"pestphp/pest": "^1.20|^2.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Spatie\\FlareClient\\": "src"
|
||||
},
|
||||
"files": [
|
||||
"src/helpers.php"
|
||||
]
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Spatie\\FlareClient\\Tests\\": "tests"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"analyse": "vendor/bin/phpstan analyse",
|
||||
"baseline": "vendor/bin/phpstan analyse --generate-baseline",
|
||||
"format": "vendor/bin/php-cs-fixer fix --allow-risky=yes",
|
||||
"test": "vendor/bin/pest",
|
||||
"test-coverage": "vendor/bin/phpunit --coverage-html coverage"
|
||||
},
|
||||
"config": {
|
||||
"sort-packages": true,
|
||||
"allow-plugins": {
|
||||
"pestphp/pest-plugin": true,
|
||||
"phpstan/extension-installer": true
|
||||
}
|
||||
},
|
||||
"prefer-stable": true,
|
||||
"minimum-stability": "dev",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "1.3.x-dev"
|
||||
}
|
||||
}
|
||||
}
|
86
vendor/spatie/flare-client-php/src/Api.php
vendored
Normal file
86
vendor/spatie/flare-client-php/src/Api.php
vendored
Normal file
@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\FlareClient;
|
||||
|
||||
use Exception;
|
||||
use Spatie\FlareClient\Http\Client;
|
||||
use Spatie\FlareClient\Truncation\ReportTrimmer;
|
||||
|
||||
class Api
|
||||
{
|
||||
protected Client $client;
|
||||
|
||||
protected bool $sendReportsImmediately = false;
|
||||
|
||||
/** @var array<int, Report> */
|
||||
protected array $queue = [];
|
||||
|
||||
public function __construct(Client $client)
|
||||
{
|
||||
$this->client = $client;
|
||||
|
||||
register_shutdown_function([$this, 'sendQueuedReports']);
|
||||
}
|
||||
|
||||
public function sendReportsImmediately(): self
|
||||
{
|
||||
$this->sendReportsImmediately = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function report(Report $report): void
|
||||
{
|
||||
try {
|
||||
$this->sendReportsImmediately
|
||||
? $this->sendReportToApi($report)
|
||||
: $this->addReportToQueue($report);
|
||||
} catch (Exception $e) {
|
||||
//
|
||||
}
|
||||
}
|
||||
|
||||
public function sendTestReport(Report $report): self
|
||||
{
|
||||
$this->sendReportToApi($report);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function addReportToQueue(Report $report): self
|
||||
{
|
||||
$this->queue[] = $report;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function sendQueuedReports(): void
|
||||
{
|
||||
try {
|
||||
foreach ($this->queue as $report) {
|
||||
$this->sendReportToApi($report);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
//
|
||||
} finally {
|
||||
$this->queue = [];
|
||||
}
|
||||
}
|
||||
|
||||
protected function sendReportToApi(Report $report): void
|
||||
{
|
||||
$payload = $this->truncateReport($report->toArray());
|
||||
|
||||
$this->client->post('reports', $payload);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int|string, mixed> $payload
|
||||
*
|
||||
* @return array<int|string, mixed>
|
||||
*/
|
||||
protected function truncateReport(array $payload): array
|
||||
{
|
||||
return (new ReportTrimmer())->trim($payload);
|
||||
}
|
||||
}
|
63
vendor/spatie/flare-client-php/src/Concerns/HasContext.php
vendored
Normal file
63
vendor/spatie/flare-client-php/src/Concerns/HasContext.php
vendored
Normal file
@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\FlareClient\Concerns;
|
||||
|
||||
trait HasContext
|
||||
{
|
||||
protected ?string $messageLevel = null;
|
||||
|
||||
protected ?string $stage = null;
|
||||
|
||||
/**
|
||||
* @var array<string, mixed>
|
||||
*/
|
||||
protected array $userProvidedContext = [];
|
||||
|
||||
public function stage(?string $stage): self
|
||||
{
|
||||
$this->stage = $stage;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function messageLevel(?string $messageLevel): self
|
||||
{
|
||||
$this->messageLevel = $messageLevel;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $groupName
|
||||
* @param mixed $default
|
||||
*
|
||||
* @return array<int, mixed>
|
||||
*/
|
||||
public function getGroup(string $groupName = 'context', $default = []): array
|
||||
{
|
||||
return $this->userProvidedContext[$groupName] ?? $default;
|
||||
}
|
||||
|
||||
public function context(string $key, mixed $value): self
|
||||
{
|
||||
return $this->group('context', [$key => $value]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $groupName
|
||||
* @param array<string, mixed> $properties
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function group(string $groupName, array $properties): self
|
||||
{
|
||||
$group = $this->userProvidedContext[$groupName] ?? [];
|
||||
|
||||
$this->userProvidedContext[$groupName] = array_merge_recursive_distinct(
|
||||
$group,
|
||||
$properties
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
23
vendor/spatie/flare-client-php/src/Concerns/UsesTime.php
vendored
Normal file
23
vendor/spatie/flare-client-php/src/Concerns/UsesTime.php
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\FlareClient\Concerns;
|
||||
|
||||
use Spatie\FlareClient\Time\SystemTime;
|
||||
use Spatie\FlareClient\Time\Time;
|
||||
|
||||
trait UsesTime
|
||||
{
|
||||
public static Time $time;
|
||||
|
||||
public static function useTime(Time $time): void
|
||||
{
|
||||
self::$time = $time;
|
||||
}
|
||||
|
||||
public function getCurrentTime(): int
|
||||
{
|
||||
$time = self::$time ?? new SystemTime();
|
||||
|
||||
return $time->getCurrentTime();
|
||||
}
|
||||
}
|
28
vendor/spatie/flare-client-php/src/Context/BaseContextProviderDetector.php
vendored
Normal file
28
vendor/spatie/flare-client-php/src/Context/BaseContextProviderDetector.php
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\FlareClient\Context;
|
||||
|
||||
class BaseContextProviderDetector implements ContextProviderDetector
|
||||
{
|
||||
public function detectCurrentContext(): ContextProvider
|
||||
{
|
||||
if ($this->runningInConsole()) {
|
||||
return new ConsoleContextProvider($_SERVER['argv'] ?? []);
|
||||
}
|
||||
|
||||
return new RequestContextProvider();
|
||||
}
|
||||
|
||||
protected function runningInConsole(): bool
|
||||
{
|
||||
if (isset($_ENV['APP_RUNNING_IN_CONSOLE'])) {
|
||||
return $_ENV['APP_RUNNING_IN_CONSOLE'] === 'true';
|
||||
}
|
||||
|
||||
if (isset($_ENV['FLARE_FAKE_WEB_REQUEST'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return in_array(php_sapi_name(), ['cli', 'phpdb']);
|
||||
}
|
||||
}
|
29
vendor/spatie/flare-client-php/src/Context/ConsoleContextProvider.php
vendored
Normal file
29
vendor/spatie/flare-client-php/src/Context/ConsoleContextProvider.php
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\FlareClient\Context;
|
||||
|
||||
class ConsoleContextProvider implements ContextProvider
|
||||
{
|
||||
/**
|
||||
* @var array<string, mixed>
|
||||
*/
|
||||
protected array $arguments = [];
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $arguments
|
||||
*/
|
||||
public function __construct(array $arguments = [])
|
||||
{
|
||||
$this->arguments = $arguments;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int|string, mixed>
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'arguments' => $this->arguments,
|
||||
];
|
||||
}
|
||||
}
|
11
vendor/spatie/flare-client-php/src/Context/ContextProvider.php
vendored
Normal file
11
vendor/spatie/flare-client-php/src/Context/ContextProvider.php
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\FlareClient\Context;
|
||||
|
||||
interface ContextProvider
|
||||
{
|
||||
/**
|
||||
* @return array<int, string|mixed>
|
||||
*/
|
||||
public function toArray(): array;
|
||||
}
|
8
vendor/spatie/flare-client-php/src/Context/ContextProviderDetector.php
vendored
Normal file
8
vendor/spatie/flare-client-php/src/Context/ContextProviderDetector.php
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\FlareClient\Context;
|
||||
|
||||
interface ContextProviderDetector
|
||||
{
|
||||
public function detectCurrentContext(): ContextProvider;
|
||||
}
|
157
vendor/spatie/flare-client-php/src/Context/RequestContextProvider.php
vendored
Normal file
157
vendor/spatie/flare-client-php/src/Context/RequestContextProvider.php
vendored
Normal file
@ -0,0 +1,157 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\FlareClient\Context;
|
||||
|
||||
use RuntimeException;
|
||||
use Symfony\Component\HttpFoundation\File\UploadedFile;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\Mime\Exception\InvalidArgumentException;
|
||||
use Throwable;
|
||||
|
||||
class RequestContextProvider implements ContextProvider
|
||||
{
|
||||
protected ?Request $request;
|
||||
|
||||
public function __construct(Request $request = null)
|
||||
{
|
||||
$this->request = $request ?? Request::createFromGlobals();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function getRequest(): array
|
||||
{
|
||||
return [
|
||||
'url' => $this->request->getUri(),
|
||||
'ip' => $this->request->getClientIp(),
|
||||
'method' => $this->request->getMethod(),
|
||||
'useragent' => $this->request->headers->get('User-Agent'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, mixed>
|
||||
*/
|
||||
protected function getFiles(): array
|
||||
{
|
||||
if (is_null($this->request->files)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $this->mapFiles($this->request->files->all());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, mixed> $files
|
||||
*
|
||||
* @return array<string, string>
|
||||
*/
|
||||
protected function mapFiles(array $files): array
|
||||
{
|
||||
return array_map(function ($file) {
|
||||
if (is_array($file)) {
|
||||
return $this->mapFiles($file);
|
||||
}
|
||||
|
||||
if (! $file instanceof UploadedFile) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$fileSize = $file->getSize();
|
||||
} catch (RuntimeException $e) {
|
||||
$fileSize = 0;
|
||||
}
|
||||
|
||||
try {
|
||||
$mimeType = $file->getMimeType();
|
||||
} catch (InvalidArgumentException $e) {
|
||||
$mimeType = 'undefined';
|
||||
}
|
||||
|
||||
return [
|
||||
'pathname' => $file->getPathname(),
|
||||
'size' => $fileSize,
|
||||
'mimeType' => $mimeType,
|
||||
];
|
||||
}, $files);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function getSession(): array
|
||||
{
|
||||
try {
|
||||
$session = $this->request->getSession();
|
||||
} catch (Throwable $exception) {
|
||||
$session = [];
|
||||
}
|
||||
|
||||
return $session ? $this->getValidSessionData($session) : [];
|
||||
}
|
||||
|
||||
protected function getValidSessionData($session): array
|
||||
{
|
||||
if (! method_exists($session, 'all')) {
|
||||
return [];
|
||||
}
|
||||
|
||||
try {
|
||||
json_encode($session->all());
|
||||
} catch (Throwable $e) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $session->all();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int|string, mixed
|
||||
*/
|
||||
public function getCookies(): array
|
||||
{
|
||||
return $this->request->cookies->all();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function getHeaders(): array
|
||||
{
|
||||
/** @var array<string, list<string|null>> $headers */
|
||||
$headers = $this->request->headers->all();
|
||||
|
||||
return array_filter(
|
||||
array_map(
|
||||
fn (array $header) => $header[0],
|
||||
$headers
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function getRequestData(): array
|
||||
{
|
||||
return [
|
||||
'queryString' => $this->request->query->all(),
|
||||
'body' => $this->request->request->all(),
|
||||
'files' => $this->getFiles(),
|
||||
];
|
||||
}
|
||||
|
||||
/** @return array<string, mixed> */
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'request' => $this->getRequest(),
|
||||
'request_data' => $this->getRequestData(),
|
||||
'headers' => $this->getHeaders(),
|
||||
'cookies' => $this->getCookies(),
|
||||
'session' => $this->getSession(),
|
||||
];
|
||||
}
|
||||
}
|
11
vendor/spatie/flare-client-php/src/Contracts/ProvidesFlareContext.php
vendored
Normal file
11
vendor/spatie/flare-client-php/src/Contracts/ProvidesFlareContext.php
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\FlareClient\Contracts;
|
||||
|
||||
interface ProvidesFlareContext
|
||||
{
|
||||
/**
|
||||
* @return array<int|string, mixed>
|
||||
*/
|
||||
public function context(): array;
|
||||
}
|
12
vendor/spatie/flare-client-php/src/Enums/MessageLevels.php
vendored
Normal file
12
vendor/spatie/flare-client-php/src/Enums/MessageLevels.php
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\FlareClient\Enums;
|
||||
|
||||
class MessageLevels
|
||||
{
|
||||
const INFO = 'info';
|
||||
const DEBUG = 'debug';
|
||||
const WARNING = 'warning';
|
||||
const ERROR = 'error';
|
||||
const CRITICAL = 'critical';
|
||||
}
|
459
vendor/spatie/flare-client-php/src/Flare.php
vendored
Normal file
459
vendor/spatie/flare-client-php/src/Flare.php
vendored
Normal file
@ -0,0 +1,459 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\FlareClient;
|
||||
|
||||
use Error;
|
||||
use ErrorException;
|
||||
use Exception;
|
||||
use Illuminate\Contracts\Container\Container;
|
||||
use Illuminate\Pipeline\Pipeline;
|
||||
use Spatie\Backtrace\Arguments\ArgumentReducers;
|
||||
use Spatie\Backtrace\Arguments\Reducers\ArgumentReducer;
|
||||
use Spatie\FlareClient\Concerns\HasContext;
|
||||
use Spatie\FlareClient\Context\BaseContextProviderDetector;
|
||||
use Spatie\FlareClient\Context\ContextProviderDetector;
|
||||
use Spatie\FlareClient\Enums\MessageLevels;
|
||||
use Spatie\FlareClient\FlareMiddleware\AddEnvironmentInformation;
|
||||
use Spatie\FlareClient\FlareMiddleware\AddGlows;
|
||||
use Spatie\FlareClient\FlareMiddleware\CensorRequestBodyFields;
|
||||
use Spatie\FlareClient\FlareMiddleware\FlareMiddleware;
|
||||
use Spatie\FlareClient\FlareMiddleware\RemoveRequestIp;
|
||||
use Spatie\FlareClient\Glows\Glow;
|
||||
use Spatie\FlareClient\Glows\GlowRecorder;
|
||||
use Spatie\FlareClient\Http\Client;
|
||||
use Throwable;
|
||||
|
||||
class Flare
|
||||
{
|
||||
use HasContext;
|
||||
|
||||
protected Client $client;
|
||||
|
||||
protected Api $api;
|
||||
|
||||
/** @var array<int, FlareMiddleware|class-string<FlareMiddleware>> */
|
||||
protected array $middleware = [];
|
||||
|
||||
protected GlowRecorder $recorder;
|
||||
|
||||
protected ?string $applicationPath = null;
|
||||
|
||||
protected ContextProviderDetector $contextDetector;
|
||||
|
||||
protected $previousExceptionHandler = null;
|
||||
|
||||
/** @var null|callable */
|
||||
protected $previousErrorHandler = null;
|
||||
|
||||
/** @var null|callable */
|
||||
protected $determineVersionCallable = null;
|
||||
|
||||
protected ?int $reportErrorLevels = null;
|
||||
|
||||
/** @var null|callable */
|
||||
protected $filterExceptionsCallable = null;
|
||||
|
||||
/** @var null|callable */
|
||||
protected $filterReportsCallable = null;
|
||||
|
||||
protected ?string $stage = null;
|
||||
|
||||
protected ?string $requestId = null;
|
||||
|
||||
protected ?Container $container = null;
|
||||
|
||||
/** @var array<class-string<ArgumentReducer>|ArgumentReducer>|ArgumentReducers|null */
|
||||
protected null|array|ArgumentReducers $argumentReducers = null;
|
||||
|
||||
protected bool $withStackFrameArguments = true;
|
||||
|
||||
public static function make(
|
||||
string $apiKey = null,
|
||||
ContextProviderDetector $contextDetector = null
|
||||
): self {
|
||||
$client = new Client($apiKey);
|
||||
|
||||
return new self($client, $contextDetector);
|
||||
}
|
||||
|
||||
public function setApiToken(string $apiToken): self
|
||||
{
|
||||
$this->client->setApiToken($apiToken);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function apiTokenSet(): bool
|
||||
{
|
||||
return $this->client->apiTokenSet();
|
||||
}
|
||||
|
||||
public function setBaseUrl(string $baseUrl): self
|
||||
{
|
||||
$this->client->setBaseUrl($baseUrl);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setStage(?string $stage): self
|
||||
{
|
||||
$this->stage = $stage;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function sendReportsImmediately(): self
|
||||
{
|
||||
$this->api->sendReportsImmediately();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function determineVersionUsing(callable $determineVersionCallable): self
|
||||
{
|
||||
$this->determineVersionCallable = $determineVersionCallable;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function reportErrorLevels(int $reportErrorLevels): self
|
||||
{
|
||||
$this->reportErrorLevels = $reportErrorLevels;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function filterExceptionsUsing(callable $filterExceptionsCallable): self
|
||||
{
|
||||
$this->filterExceptionsCallable = $filterExceptionsCallable;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function filterReportsUsing(callable $filterReportsCallable): self
|
||||
{
|
||||
$this->filterReportsCallable = $filterReportsCallable;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/** @param array<class-string<ArgumentReducer>|ArgumentReducer>|ArgumentReducers|null $argumentReducers */
|
||||
public function argumentReducers(null|array|ArgumentReducers $argumentReducers): self
|
||||
{
|
||||
$this->argumentReducers = $argumentReducers;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withStackFrameArguments(bool $withStackFrameArguments = true): self
|
||||
{
|
||||
$this->withStackFrameArguments = $withStackFrameArguments;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function version(): ?string
|
||||
{
|
||||
if (! $this->determineVersionCallable) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return ($this->determineVersionCallable)();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Spatie\FlareClient\Http\Client $client
|
||||
* @param \Spatie\FlareClient\Context\ContextProviderDetector|null $contextDetector
|
||||
* @param array<int, FlareMiddleware> $middleware
|
||||
*/
|
||||
public function __construct(
|
||||
Client $client,
|
||||
ContextProviderDetector $contextDetector = null,
|
||||
array $middleware = [],
|
||||
) {
|
||||
$this->client = $client;
|
||||
$this->recorder = new GlowRecorder();
|
||||
$this->contextDetector = $contextDetector ?? new BaseContextProviderDetector();
|
||||
$this->middleware = $middleware;
|
||||
$this->api = new Api($this->client);
|
||||
|
||||
$this->registerDefaultMiddleware();
|
||||
}
|
||||
|
||||
/** @return array<int, FlareMiddleware|class-string<FlareMiddleware>> */
|
||||
public function getMiddleware(): array
|
||||
{
|
||||
return $this->middleware;
|
||||
}
|
||||
|
||||
public function setContextProviderDetector(ContextProviderDetector $contextDetector): self
|
||||
{
|
||||
$this->contextDetector = $contextDetector;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setContainer(Container $container): self
|
||||
{
|
||||
$this->container = $container;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function registerFlareHandlers(): self
|
||||
{
|
||||
$this->registerExceptionHandler();
|
||||
|
||||
$this->registerErrorHandler();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function registerExceptionHandler(): self
|
||||
{
|
||||
/** @phpstan-ignore-next-line */
|
||||
$this->previousExceptionHandler = set_exception_handler([$this, 'handleException']);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function registerErrorHandler(): self
|
||||
{
|
||||
$this->previousErrorHandler = set_error_handler([$this, 'handleError']);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function registerDefaultMiddleware(): self
|
||||
{
|
||||
return $this->registerMiddleware([
|
||||
new AddGlows($this->recorder),
|
||||
new AddEnvironmentInformation(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param FlareMiddleware|array<FlareMiddleware>|class-string<FlareMiddleware>|callable $middleware
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function registerMiddleware($middleware): self
|
||||
{
|
||||
if (! is_array($middleware)) {
|
||||
$middleware = [$middleware];
|
||||
}
|
||||
|
||||
$this->middleware = array_merge($this->middleware, $middleware);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int,FlareMiddleware|class-string<FlareMiddleware>>
|
||||
*/
|
||||
public function getMiddlewares(): array
|
||||
{
|
||||
return $this->middleware;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param string $messageLevel
|
||||
* @param array<int, mixed> $metaData
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function glow(
|
||||
string $name,
|
||||
string $messageLevel = MessageLevels::INFO,
|
||||
array $metaData = []
|
||||
): self {
|
||||
$this->recorder->record(new Glow($name, $messageLevel, $metaData));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function handleException(Throwable $throwable): void
|
||||
{
|
||||
$this->report($throwable);
|
||||
|
||||
if ($this->previousExceptionHandler && is_callable($this->previousExceptionHandler)) {
|
||||
call_user_func($this->previousExceptionHandler, $throwable);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function handleError(mixed $code, string $message, string $file = '', int $line = 0)
|
||||
{
|
||||
$exception = new ErrorException($message, 0, $code, $file, $line);
|
||||
|
||||
$this->report($exception);
|
||||
|
||||
if ($this->previousErrorHandler) {
|
||||
return call_user_func(
|
||||
$this->previousErrorHandler,
|
||||
$message,
|
||||
$code,
|
||||
$file,
|
||||
$line
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function applicationPath(string $applicationPath): self
|
||||
{
|
||||
$this->applicationPath = $applicationPath;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function report(Throwable $throwable, callable $callback = null, Report $report = null): ?Report
|
||||
{
|
||||
if (! $this->shouldSendReport($throwable)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$report ??= $this->createReport($throwable);
|
||||
|
||||
if (! is_null($callback)) {
|
||||
call_user_func($callback, $report);
|
||||
}
|
||||
|
||||
$this->recorder->reset();
|
||||
|
||||
$this->sendReportToApi($report);
|
||||
|
||||
return $report;
|
||||
}
|
||||
|
||||
protected function shouldSendReport(Throwable $throwable): bool
|
||||
{
|
||||
if (isset($this->reportErrorLevels) && $throwable instanceof Error) {
|
||||
return (bool) ($this->reportErrorLevels & $throwable->getCode());
|
||||
}
|
||||
|
||||
if (isset($this->reportErrorLevels) && $throwable instanceof ErrorException) {
|
||||
return (bool) ($this->reportErrorLevels & $throwable->getSeverity());
|
||||
}
|
||||
|
||||
if ($this->filterExceptionsCallable && $throwable instanceof Exception) {
|
||||
return (bool) (call_user_func($this->filterExceptionsCallable, $throwable));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function reportMessage(string $message, string $logLevel, callable $callback = null): void
|
||||
{
|
||||
$report = $this->createReportFromMessage($message, $logLevel);
|
||||
|
||||
if (! is_null($callback)) {
|
||||
call_user_func($callback, $report);
|
||||
}
|
||||
|
||||
$this->sendReportToApi($report);
|
||||
}
|
||||
|
||||
public function sendTestReport(Throwable $throwable): void
|
||||
{
|
||||
$this->api->sendTestReport($this->createReport($throwable));
|
||||
}
|
||||
|
||||
protected function sendReportToApi(Report $report): void
|
||||
{
|
||||
if ($this->filterReportsCallable) {
|
||||
if (! call_user_func($this->filterReportsCallable, $report)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$this->api->report($report);
|
||||
} catch (Exception $exception) {
|
||||
}
|
||||
}
|
||||
|
||||
public function reset(): void
|
||||
{
|
||||
$this->api->sendQueuedReports();
|
||||
|
||||
$this->userProvidedContext = [];
|
||||
|
||||
$this->recorder->reset();
|
||||
}
|
||||
|
||||
protected function applyAdditionalParameters(Report $report): void
|
||||
{
|
||||
$report
|
||||
->stage($this->stage)
|
||||
->messageLevel($this->messageLevel)
|
||||
->setApplicationPath($this->applicationPath)
|
||||
->userProvidedContext($this->userProvidedContext);
|
||||
}
|
||||
|
||||
public function anonymizeIp(): self
|
||||
{
|
||||
$this->registerMiddleware(new RemoveRequestIp());
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, string> $fieldNames
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function censorRequestBodyFields(array $fieldNames): self
|
||||
{
|
||||
$this->registerMiddleware(new CensorRequestBodyFields($fieldNames));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function createReport(Throwable $throwable): Report
|
||||
{
|
||||
$report = Report::createForThrowable(
|
||||
$throwable,
|
||||
$this->contextDetector->detectCurrentContext(),
|
||||
$this->applicationPath,
|
||||
$this->version(),
|
||||
$this->argumentReducers,
|
||||
$this->withStackFrameArguments
|
||||
);
|
||||
|
||||
return $this->applyMiddlewareToReport($report);
|
||||
}
|
||||
|
||||
public function createReportFromMessage(string $message, string $logLevel): Report
|
||||
{
|
||||
$report = Report::createForMessage(
|
||||
$message,
|
||||
$logLevel,
|
||||
$this->contextDetector->detectCurrentContext(),
|
||||
$this->applicationPath,
|
||||
$this->argumentReducers,
|
||||
$this->withStackFrameArguments
|
||||
);
|
||||
|
||||
return $this->applyMiddlewareToReport($report);
|
||||
}
|
||||
|
||||
protected function applyMiddlewareToReport(Report $report): Report
|
||||
{
|
||||
$this->applyAdditionalParameters($report);
|
||||
$middleware = array_map(function ($singleMiddleware) {
|
||||
return is_string($singleMiddleware)
|
||||
? new $singleMiddleware
|
||||
: $singleMiddleware;
|
||||
}, $this->middleware);
|
||||
|
||||
$report = (new Pipeline())
|
||||
->send($report)
|
||||
->through($middleware)
|
||||
->then(fn ($report) => $report);
|
||||
|
||||
return $report;
|
||||
}
|
||||
}
|
56
vendor/spatie/flare-client-php/src/FlareMiddleware/AddDocumentationLinks.php
vendored
Normal file
56
vendor/spatie/flare-client-php/src/FlareMiddleware/AddDocumentationLinks.php
vendored
Normal file
@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\FlareClient\FlareMiddleware;
|
||||
|
||||
use ArrayObject;
|
||||
use Closure;
|
||||
use Spatie\FlareClient\Report;
|
||||
|
||||
class AddDocumentationLinks implements FlareMiddleware
|
||||
{
|
||||
protected ArrayObject $documentationLinkResolvers;
|
||||
|
||||
public function __construct(ArrayObject $documentationLinkResolvers)
|
||||
{
|
||||
$this->documentationLinkResolvers = $documentationLinkResolvers;
|
||||
}
|
||||
|
||||
public function handle(Report $report, Closure $next)
|
||||
{
|
||||
if (! $throwable = $report->getThrowable()) {
|
||||
return $next($report);
|
||||
}
|
||||
|
||||
$links = $this->getLinks($throwable);
|
||||
|
||||
if (count($links)) {
|
||||
$report->addDocumentationLinks($links);
|
||||
}
|
||||
|
||||
return $next($report);
|
||||
}
|
||||
|
||||
/** @return array<int, string> */
|
||||
protected function getLinks(\Throwable $throwable): array
|
||||
{
|
||||
$allLinks = [];
|
||||
|
||||
foreach ($this->documentationLinkResolvers as $resolver) {
|
||||
$resolvedLinks = $resolver($throwable);
|
||||
|
||||
if (is_null($resolvedLinks)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (is_string($resolvedLinks)) {
|
||||
$resolvedLinks = [$resolvedLinks];
|
||||
}
|
||||
|
||||
foreach ($resolvedLinks as $link) {
|
||||
$allLinks[] = $link;
|
||||
}
|
||||
}
|
||||
|
||||
return array_values(array_unique($allLinks));
|
||||
}
|
||||
}
|
18
vendor/spatie/flare-client-php/src/FlareMiddleware/AddEnvironmentInformation.php
vendored
Normal file
18
vendor/spatie/flare-client-php/src/FlareMiddleware/AddEnvironmentInformation.php
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\FlareClient\FlareMiddleware;
|
||||
|
||||
use Closure;
|
||||
use Spatie\FlareClient\Report;
|
||||
|
||||
class AddEnvironmentInformation implements FlareMiddleware
|
||||
{
|
||||
public function handle(Report $report, Closure $next)
|
||||
{
|
||||
$report->group('env', [
|
||||
'php_version' => phpversion(),
|
||||
]);
|
||||
|
||||
return $next($report);
|
||||
}
|
||||
}
|
89
vendor/spatie/flare-client-php/src/FlareMiddleware/AddGitInformation.php
vendored
Normal file
89
vendor/spatie/flare-client-php/src/FlareMiddleware/AddGitInformation.php
vendored
Normal file
@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\FlareClient\FlareMiddleware;
|
||||
|
||||
use Closure;
|
||||
use Spatie\FlareClient\Report;
|
||||
use Symfony\Component\Process\Process;
|
||||
use Throwable;
|
||||
|
||||
class AddGitInformation
|
||||
{
|
||||
protected ?string $baseDir = null;
|
||||
|
||||
public function handle(Report $report, Closure $next)
|
||||
{
|
||||
try {
|
||||
$this->baseDir = $this->getGitBaseDirectory();
|
||||
|
||||
if (! $this->baseDir) {
|
||||
return $next($report);
|
||||
}
|
||||
|
||||
$report->group('git', [
|
||||
'hash' => $this->hash(),
|
||||
'message' => $this->message(),
|
||||
'tag' => $this->tag(),
|
||||
'remote' => $this->remote(),
|
||||
'isDirty' => ! $this->isClean(),
|
||||
]);
|
||||
} catch (Throwable) {
|
||||
}
|
||||
|
||||
return $next($report);
|
||||
}
|
||||
|
||||
protected function hash(): ?string
|
||||
{
|
||||
return $this->command("git log --pretty=format:'%H' -n 1") ?: null;
|
||||
}
|
||||
|
||||
protected function message(): ?string
|
||||
{
|
||||
return $this->command("git log --pretty=format:'%s' -n 1") ?: null;
|
||||
}
|
||||
|
||||
protected function tag(): ?string
|
||||
{
|
||||
return $this->command('git describe --tags --abbrev=0') ?: null;
|
||||
}
|
||||
|
||||
protected function remote(): ?string
|
||||
{
|
||||
return $this->command('git config --get remote.origin.url') ?: null;
|
||||
}
|
||||
|
||||
protected function isClean(): bool
|
||||
{
|
||||
return empty($this->command('git status -s'));
|
||||
}
|
||||
|
||||
protected function getGitBaseDirectory(): ?string
|
||||
{
|
||||
/** @var Process $process */
|
||||
$process = Process::fromShellCommandline("echo $(git rev-parse --show-toplevel)")->setTimeout(1);
|
||||
|
||||
$process->run();
|
||||
|
||||
if (! $process->isSuccessful()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$directory = trim($process->getOutput());
|
||||
|
||||
if (! file_exists($directory)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $directory;
|
||||
}
|
||||
|
||||
protected function command($command)
|
||||
{
|
||||
$process = Process::fromShellCommandline($command, $this->baseDir)->setTimeout(1);
|
||||
|
||||
$process->run();
|
||||
|
||||
return trim($process->getOutput());
|
||||
}
|
||||
}
|
28
vendor/spatie/flare-client-php/src/FlareMiddleware/AddGlows.php
vendored
Normal file
28
vendor/spatie/flare-client-php/src/FlareMiddleware/AddGlows.php
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\FlareClient\FlareMiddleware;
|
||||
|
||||
namespace Spatie\FlareClient\FlareMiddleware;
|
||||
|
||||
use Closure;
|
||||
use Spatie\FlareClient\Glows\GlowRecorder;
|
||||
use Spatie\FlareClient\Report;
|
||||
|
||||
class AddGlows implements FlareMiddleware
|
||||
{
|
||||
protected GlowRecorder $recorder;
|
||||
|
||||
public function __construct(GlowRecorder $recorder)
|
||||
{
|
||||
$this->recorder = $recorder;
|
||||
}
|
||||
|
||||
public function handle(Report $report, Closure $next)
|
||||
{
|
||||
foreach ($this->recorder->glows() as $glow) {
|
||||
$report->addGlow($glow);
|
||||
}
|
||||
|
||||
return $next($report);
|
||||
}
|
||||
}
|
17
vendor/spatie/flare-client-php/src/FlareMiddleware/AddNotifierName.php
vendored
Normal file
17
vendor/spatie/flare-client-php/src/FlareMiddleware/AddNotifierName.php
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\FlareClient\FlareMiddleware;
|
||||
|
||||
use Spatie\FlareClient\Report;
|
||||
|
||||
class AddNotifierName implements FlareMiddleware
|
||||
{
|
||||
public const NOTIFIER_NAME = 'Flare Client';
|
||||
|
||||
public function handle(Report $report, $next)
|
||||
{
|
||||
$report->notifierName(static::NOTIFIER_NAME);
|
||||
|
||||
return $next($report);
|
||||
}
|
||||
}
|
30
vendor/spatie/flare-client-php/src/FlareMiddleware/AddSolutions.php
vendored
Normal file
30
vendor/spatie/flare-client-php/src/FlareMiddleware/AddSolutions.php
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\FlareClient\FlareMiddleware;
|
||||
|
||||
use Closure;
|
||||
use Spatie\FlareClient\Report;
|
||||
use Spatie\Ignition\Contracts\SolutionProviderRepository;
|
||||
|
||||
class AddSolutions implements FlareMiddleware
|
||||
{
|
||||
protected SolutionProviderRepository $solutionProviderRepository;
|
||||
|
||||
public function __construct(SolutionProviderRepository $solutionProviderRepository)
|
||||
{
|
||||
$this->solutionProviderRepository = $solutionProviderRepository;
|
||||
}
|
||||
|
||||
public function handle(Report $report, Closure $next)
|
||||
{
|
||||
if ($throwable = $report->getThrowable()) {
|
||||
$solutions = $this->solutionProviderRepository->getSolutionsForThrowable($throwable);
|
||||
|
||||
foreach ($solutions as $solution) {
|
||||
$report->addSolution($solution);
|
||||
}
|
||||
}
|
||||
|
||||
return $next($report);
|
||||
}
|
||||
}
|
30
vendor/spatie/flare-client-php/src/FlareMiddleware/CensorRequestBodyFields.php
vendored
Normal file
30
vendor/spatie/flare-client-php/src/FlareMiddleware/CensorRequestBodyFields.php
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\FlareClient\FlareMiddleware;
|
||||
|
||||
use Spatie\FlareClient\Report;
|
||||
|
||||
class CensorRequestBodyFields implements FlareMiddleware
|
||||
{
|
||||
protected array $fieldNames = [];
|
||||
|
||||
public function __construct(array $fieldNames)
|
||||
{
|
||||
$this->fieldNames = $fieldNames;
|
||||
}
|
||||
|
||||
public function handle(Report $report, $next)
|
||||
{
|
||||
$context = $report->allContext();
|
||||
|
||||
foreach ($this->fieldNames as $fieldName) {
|
||||
if (isset($context['request_data']['body'][$fieldName])) {
|
||||
$context['request_data']['body'][$fieldName] = '<CENSORED>';
|
||||
}
|
||||
}
|
||||
|
||||
$report->userProvidedContext($context);
|
||||
|
||||
return $next($report);
|
||||
}
|
||||
}
|
32
vendor/spatie/flare-client-php/src/FlareMiddleware/CensorRequestHeaders.php
vendored
Normal file
32
vendor/spatie/flare-client-php/src/FlareMiddleware/CensorRequestHeaders.php
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\FlareClient\FlareMiddleware;
|
||||
|
||||
use Spatie\FlareClient\Report;
|
||||
|
||||
class CensorRequestHeaders implements FlareMiddleware
|
||||
{
|
||||
protected array $headers = [];
|
||||
|
||||
public function __construct(array $headers)
|
||||
{
|
||||
$this->headers = $headers;
|
||||
}
|
||||
|
||||
public function handle(Report $report, $next)
|
||||
{
|
||||
$context = $report->allContext();
|
||||
|
||||
foreach ($this->headers as $header) {
|
||||
$header = strtolower($header);
|
||||
|
||||
if (isset($context['headers'][$header])) {
|
||||
$context['headers'][$header] = '<CENSORED>';
|
||||
}
|
||||
}
|
||||
|
||||
$report->userProvidedContext($context);
|
||||
|
||||
return $next($report);
|
||||
}
|
||||
}
|
11
vendor/spatie/flare-client-php/src/FlareMiddleware/FlareMiddleware.php
vendored
Normal file
11
vendor/spatie/flare-client-php/src/FlareMiddleware/FlareMiddleware.php
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\FlareClient\FlareMiddleware;
|
||||
|
||||
use Closure;
|
||||
use Spatie\FlareClient\Report;
|
||||
|
||||
interface FlareMiddleware
|
||||
{
|
||||
public function handle(Report $report, Closure $next);
|
||||
}
|
19
vendor/spatie/flare-client-php/src/FlareMiddleware/RemoveRequestIp.php
vendored
Normal file
19
vendor/spatie/flare-client-php/src/FlareMiddleware/RemoveRequestIp.php
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\FlareClient\FlareMiddleware;
|
||||
|
||||
use Spatie\FlareClient\Report;
|
||||
|
||||
class RemoveRequestIp implements FlareMiddleware
|
||||
{
|
||||
public function handle(Report $report, $next)
|
||||
{
|
||||
$context = $report->allContext();
|
||||
|
||||
$context['request']['ip'] = null;
|
||||
|
||||
$report->userProvidedContext($context);
|
||||
|
||||
return $next($report);
|
||||
}
|
||||
}
|
32
vendor/spatie/flare-client-php/src/Frame.php
vendored
Normal file
32
vendor/spatie/flare-client-php/src/Frame.php
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\FlareClient;
|
||||
|
||||
use Spatie\Backtrace\Frame as SpatieFrame;
|
||||
|
||||
class Frame
|
||||
{
|
||||
public static function fromSpatieFrame(
|
||||
SpatieFrame $frame,
|
||||
): self {
|
||||
return new self($frame);
|
||||
}
|
||||
|
||||
public function __construct(
|
||||
protected SpatieFrame $frame,
|
||||
) {
|
||||
}
|
||||
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'file' => $this->frame->file,
|
||||
'line_number' => $this->frame->lineNumber,
|
||||
'method' => $this->frame->method,
|
||||
'class' => $this->frame->class,
|
||||
'code_snippet' => $this->frame->getSnippet(30),
|
||||
'arguments' => $this->frame->arguments,
|
||||
'application_frame' => $this->frame->applicationFrame,
|
||||
];
|
||||
}
|
||||
}
|
52
vendor/spatie/flare-client-php/src/Glows/Glow.php
vendored
Normal file
52
vendor/spatie/flare-client-php/src/Glows/Glow.php
vendored
Normal file
@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\FlareClient\Glows;
|
||||
|
||||
use Spatie\FlareClient\Concerns\UsesTime;
|
||||
use Spatie\FlareClient\Enums\MessageLevels;
|
||||
|
||||
class Glow
|
||||
{
|
||||
use UsesTime;
|
||||
|
||||
protected string $name;
|
||||
|
||||
/** @var array<int, mixed> */
|
||||
protected array $metaData = [];
|
||||
|
||||
protected string $messageLevel;
|
||||
|
||||
protected float $microtime;
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param string $messageLevel
|
||||
* @param array<int, mixed> $metaData
|
||||
* @param float|null $microtime
|
||||
*/
|
||||
public function __construct(
|
||||
string $name,
|
||||
string $messageLevel = MessageLevels::INFO,
|
||||
array $metaData = [],
|
||||
?float $microtime = null
|
||||
) {
|
||||
$this->name = $name;
|
||||
$this->messageLevel = $messageLevel;
|
||||
$this->metaData = $metaData;
|
||||
$this->microtime = $microtime ?? microtime(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{time: int, name: string, message_level: string, meta_data: array, microtime: float}
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'time' => $this->getCurrentTime(),
|
||||
'name' => $this->name,
|
||||
'message_level' => $this->messageLevel,
|
||||
'meta_data' => $this->metaData,
|
||||
'microtime' => $this->microtime,
|
||||
];
|
||||
}
|
||||
}
|
29
vendor/spatie/flare-client-php/src/Glows/GlowRecorder.php
vendored
Normal file
29
vendor/spatie/flare-client-php/src/Glows/GlowRecorder.php
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\FlareClient\Glows;
|
||||
|
||||
class GlowRecorder
|
||||
{
|
||||
const GLOW_LIMIT = 30;
|
||||
|
||||
/** @var array<int, Glow> */
|
||||
protected array $glows = [];
|
||||
|
||||
public function record(Glow $glow): void
|
||||
{
|
||||
$this->glows[] = $glow;
|
||||
|
||||
$this->glows = array_slice($this->glows, static::GLOW_LIMIT * -1, static::GLOW_LIMIT);
|
||||
}
|
||||
|
||||
/** @return array<int, Glow> */
|
||||
public function glows(): array
|
||||
{
|
||||
return $this->glows;
|
||||
}
|
||||
|
||||
public function reset(): void
|
||||
{
|
||||
$this->glows = [];
|
||||
}
|
||||
}
|
228
vendor/spatie/flare-client-php/src/Http/Client.php
vendored
Normal file
228
vendor/spatie/flare-client-php/src/Http/Client.php
vendored
Normal file
@ -0,0 +1,228 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\FlareClient\Http;
|
||||
|
||||
use Spatie\FlareClient\Http\Exceptions\BadResponseCode;
|
||||
use Spatie\FlareClient\Http\Exceptions\InvalidData;
|
||||
use Spatie\FlareClient\Http\Exceptions\MissingParameter;
|
||||
use Spatie\FlareClient\Http\Exceptions\NotFound;
|
||||
|
||||
class Client
|
||||
{
|
||||
protected ?string $apiToken;
|
||||
|
||||
protected ?string $baseUrl;
|
||||
|
||||
protected int $timeout;
|
||||
|
||||
protected $lastRequest = null;
|
||||
|
||||
public function __construct(
|
||||
?string $apiToken = null,
|
||||
string $baseUrl = 'https://reporting.flareapp.io/api',
|
||||
int $timeout = 10
|
||||
) {
|
||||
$this->apiToken = $apiToken;
|
||||
|
||||
if (! $baseUrl) {
|
||||
throw MissingParameter::create('baseUrl');
|
||||
}
|
||||
|
||||
$this->baseUrl = $baseUrl;
|
||||
|
||||
if (! $timeout) {
|
||||
throw MissingParameter::create('timeout');
|
||||
}
|
||||
|
||||
$this->timeout = $timeout;
|
||||
}
|
||||
|
||||
public function setApiToken(string $apiToken): self
|
||||
{
|
||||
$this->apiToken = $apiToken;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function apiTokenSet(): bool
|
||||
{
|
||||
return ! empty($this->apiToken);
|
||||
}
|
||||
|
||||
public function setBaseUrl(string $baseUrl): self
|
||||
{
|
||||
$this->baseUrl = $baseUrl;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $url
|
||||
* @param array $arguments
|
||||
*
|
||||
* @return array|false
|
||||
*/
|
||||
public function get(string $url, array $arguments = [])
|
||||
{
|
||||
return $this->makeRequest('get', $url, $arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $url
|
||||
* @param array $arguments
|
||||
*
|
||||
* @return array|false
|
||||
*/
|
||||
public function post(string $url, array $arguments = [])
|
||||
{
|
||||
return $this->makeRequest('post', $url, $arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $url
|
||||
* @param array $arguments
|
||||
*
|
||||
* @return array|false
|
||||
*/
|
||||
public function patch(string $url, array $arguments = [])
|
||||
{
|
||||
return $this->makeRequest('patch', $url, $arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $url
|
||||
* @param array $arguments
|
||||
*
|
||||
* @return array|false
|
||||
*/
|
||||
public function put(string $url, array $arguments = [])
|
||||
{
|
||||
return $this->makeRequest('put', $url, $arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $method
|
||||
* @param array $arguments
|
||||
*
|
||||
* @return array|false
|
||||
*/
|
||||
public function delete(string $method, array $arguments = [])
|
||||
{
|
||||
return $this->makeRequest('delete', $method, $arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $httpVerb
|
||||
* @param string $url
|
||||
* @param array $arguments
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function makeRequest(string $httpVerb, string $url, array $arguments = [])
|
||||
{
|
||||
$queryString = http_build_query([
|
||||
'key' => $this->apiToken,
|
||||
]);
|
||||
|
||||
$fullUrl = "{$this->baseUrl}/{$url}?{$queryString}";
|
||||
|
||||
$headers = [
|
||||
'x-api-token: '.$this->apiToken,
|
||||
];
|
||||
|
||||
$response = $this->makeCurlRequest($httpVerb, $fullUrl, $headers, $arguments);
|
||||
|
||||
if ($response->getHttpResponseCode() === 422) {
|
||||
throw InvalidData::createForResponse($response);
|
||||
}
|
||||
|
||||
if ($response->getHttpResponseCode() === 404) {
|
||||
throw NotFound::createForResponse($response);
|
||||
}
|
||||
|
||||
if ($response->getHttpResponseCode() !== 200 && $response->getHttpResponseCode() !== 204) {
|
||||
throw BadResponseCode::createForResponse($response);
|
||||
}
|
||||
|
||||
return $response->getBody();
|
||||
}
|
||||
|
||||
public function makeCurlRequest(string $httpVerb, string $fullUrl, array $headers = [], array $arguments = []): Response
|
||||
{
|
||||
$curlHandle = $this->getCurlHandle($fullUrl, $headers);
|
||||
|
||||
switch ($httpVerb) {
|
||||
case 'post':
|
||||
curl_setopt($curlHandle, CURLOPT_POST, true);
|
||||
$this->attachRequestPayload($curlHandle, $arguments);
|
||||
|
||||
break;
|
||||
|
||||
case 'get':
|
||||
curl_setopt($curlHandle, CURLOPT_URL, $fullUrl.'&'.http_build_query($arguments));
|
||||
|
||||
break;
|
||||
|
||||
case 'delete':
|
||||
curl_setopt($curlHandle, CURLOPT_CUSTOMREQUEST, 'DELETE');
|
||||
|
||||
break;
|
||||
|
||||
case 'patch':
|
||||
curl_setopt($curlHandle, CURLOPT_CUSTOMREQUEST, 'PATCH');
|
||||
$this->attachRequestPayload($curlHandle, $arguments);
|
||||
|
||||
break;
|
||||
|
||||
case 'put':
|
||||
curl_setopt($curlHandle, CURLOPT_CUSTOMREQUEST, 'PUT');
|
||||
$this->attachRequestPayload($curlHandle, $arguments);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
$body = json_decode(curl_exec($curlHandle), true);
|
||||
$headers = curl_getinfo($curlHandle);
|
||||
$error = curl_error($curlHandle);
|
||||
|
||||
return new Response($headers, $body, $error);
|
||||
}
|
||||
|
||||
protected function attachRequestPayload(&$curlHandle, array $data)
|
||||
{
|
||||
$encoded = json_encode($data);
|
||||
|
||||
$this->lastRequest['body'] = $encoded;
|
||||
curl_setopt($curlHandle, CURLOPT_POSTFIELDS, $encoded);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $fullUrl
|
||||
* @param array $headers
|
||||
*
|
||||
* @return resource
|
||||
*/
|
||||
protected function getCurlHandle(string $fullUrl, array $headers = [])
|
||||
{
|
||||
$curlHandle = curl_init();
|
||||
|
||||
curl_setopt($curlHandle, CURLOPT_URL, $fullUrl);
|
||||
|
||||
curl_setopt($curlHandle, CURLOPT_HTTPHEADER, array_merge([
|
||||
'Accept: application/json',
|
||||
'Content-Type: application/json',
|
||||
], $headers));
|
||||
|
||||
curl_setopt($curlHandle, CURLOPT_USERAGENT, 'Laravel/Flare API 1.0');
|
||||
curl_setopt($curlHandle, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($curlHandle, CURLOPT_TIMEOUT, $this->timeout);
|
||||
curl_setopt($curlHandle, CURLOPT_SSL_VERIFYPEER, true);
|
||||
curl_setopt($curlHandle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
|
||||
curl_setopt($curlHandle, CURLOPT_ENCODING, '');
|
||||
curl_setopt($curlHandle, CURLINFO_HEADER_OUT, true);
|
||||
curl_setopt($curlHandle, CURLOPT_FOLLOWLOCATION, true);
|
||||
curl_setopt($curlHandle, CURLOPT_MAXREDIRS, 1);
|
||||
|
||||
return $curlHandle;
|
||||
}
|
||||
}
|
20
vendor/spatie/flare-client-php/src/Http/Exceptions/BadResponse.php
vendored
Normal file
20
vendor/spatie/flare-client-php/src/Http/Exceptions/BadResponse.php
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\FlareClient\Http\Exceptions;
|
||||
|
||||
use Exception;
|
||||
use Spatie\FlareClient\Http\Response;
|
||||
|
||||
class BadResponse extends Exception
|
||||
{
|
||||
public Response $response;
|
||||
|
||||
public static function createForResponse(Response $response): self
|
||||
{
|
||||
$exception = new self("Could not perform request because: {$response->getError()}");
|
||||
|
||||
$exception->response = $response;
|
||||
|
||||
return $exception;
|
||||
}
|
||||
}
|
34
vendor/spatie/flare-client-php/src/Http/Exceptions/BadResponseCode.php
vendored
Normal file
34
vendor/spatie/flare-client-php/src/Http/Exceptions/BadResponseCode.php
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\FlareClient\Http\Exceptions;
|
||||
|
||||
use Exception;
|
||||
use Spatie\FlareClient\Http\Response;
|
||||
|
||||
class BadResponseCode extends Exception
|
||||
{
|
||||
public Response $response;
|
||||
|
||||
/**
|
||||
* @var array<int, mixed>
|
||||
*/
|
||||
public array $errors = [];
|
||||
|
||||
public static function createForResponse(Response $response): self
|
||||
{
|
||||
$exception = new self(static::getMessageForResponse($response));
|
||||
|
||||
$exception->response = $response;
|
||||
|
||||
$bodyErrors = isset($response->getBody()['errors']) ? $response->getBody()['errors'] : [];
|
||||
|
||||
$exception->errors = $bodyErrors;
|
||||
|
||||
return $exception;
|
||||
}
|
||||
|
||||
public static function getMessageForResponse(Response $response): string
|
||||
{
|
||||
return "Response code {$response->getHttpResponseCode()} returned";
|
||||
}
|
||||
}
|
13
vendor/spatie/flare-client-php/src/Http/Exceptions/InvalidData.php
vendored
Normal file
13
vendor/spatie/flare-client-php/src/Http/Exceptions/InvalidData.php
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\FlareClient\Http\Exceptions;
|
||||
|
||||
use Spatie\FlareClient\Http\Response;
|
||||
|
||||
class InvalidData extends BadResponseCode
|
||||
{
|
||||
public static function getMessageForResponse(Response $response): string
|
||||
{
|
||||
return 'Invalid data found';
|
||||
}
|
||||
}
|
13
vendor/spatie/flare-client-php/src/Http/Exceptions/MissingParameter.php
vendored
Normal file
13
vendor/spatie/flare-client-php/src/Http/Exceptions/MissingParameter.php
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\FlareClient\Http\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class MissingParameter extends Exception
|
||||
{
|
||||
public static function create(string $parameterName): self
|
||||
{
|
||||
return new self("`$parameterName` is a required parameter");
|
||||
}
|
||||
}
|
13
vendor/spatie/flare-client-php/src/Http/Exceptions/NotFound.php
vendored
Normal file
13
vendor/spatie/flare-client-php/src/Http/Exceptions/NotFound.php
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\FlareClient\Http\Exceptions;
|
||||
|
||||
use Spatie\FlareClient\Http\Response;
|
||||
|
||||
class NotFound extends BadResponseCode
|
||||
{
|
||||
public static function getMessageForResponse(Response $response): string
|
||||
{
|
||||
return 'Not found';
|
||||
}
|
||||
}
|
50
vendor/spatie/flare-client-php/src/Http/Response.php
vendored
Normal file
50
vendor/spatie/flare-client-php/src/Http/Response.php
vendored
Normal file
@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\FlareClient\Http;
|
||||
|
||||
class Response
|
||||
{
|
||||
protected mixed $headers;
|
||||
|
||||
protected mixed $body;
|
||||
|
||||
protected mixed $error;
|
||||
|
||||
public function __construct(mixed $headers, mixed $body, mixed $error)
|
||||
{
|
||||
$this->headers = $headers;
|
||||
|
||||
$this->body = $body;
|
||||
|
||||
$this->error = $error;
|
||||
}
|
||||
|
||||
public function getHeaders(): mixed
|
||||
{
|
||||
return $this->headers;
|
||||
}
|
||||
|
||||
public function getBody(): mixed
|
||||
{
|
||||
return $this->body;
|
||||
}
|
||||
|
||||
public function hasBody(): bool
|
||||
{
|
||||
return $this->body != false;
|
||||
}
|
||||
|
||||
public function getError(): mixed
|
||||
{
|
||||
return $this->error;
|
||||
}
|
||||
|
||||
public function getHttpResponseCode(): ?int
|
||||
{
|
||||
if (! isset($this->headers['http_code'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (int) $this->headers['http_code'];
|
||||
}
|
||||
}
|
398
vendor/spatie/flare-client-php/src/Report.php
vendored
Normal file
398
vendor/spatie/flare-client-php/src/Report.php
vendored
Normal file
@ -0,0 +1,398 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\FlareClient;
|
||||
|
||||
use ErrorException;
|
||||
use Spatie\Backtrace\Arguments\ArgumentReducers;
|
||||
use Spatie\Backtrace\Arguments\Reducers\ArgumentReducer;
|
||||
use Spatie\Backtrace\Backtrace;
|
||||
use Spatie\Backtrace\Frame as SpatieFrame;
|
||||
use Spatie\FlareClient\Concerns\HasContext;
|
||||
use Spatie\FlareClient\Concerns\UsesTime;
|
||||
use Spatie\FlareClient\Context\ContextProvider;
|
||||
use Spatie\FlareClient\Contracts\ProvidesFlareContext;
|
||||
use Spatie\FlareClient\Glows\Glow;
|
||||
use Spatie\FlareClient\Solutions\ReportSolution;
|
||||
use Spatie\Ignition\Contracts\Solution;
|
||||
use Spatie\LaravelIgnition\Exceptions\ViewException;
|
||||
use Throwable;
|
||||
|
||||
class Report
|
||||
{
|
||||
use UsesTime;
|
||||
use HasContext;
|
||||
|
||||
protected Backtrace $stacktrace;
|
||||
|
||||
protected string $exceptionClass = '';
|
||||
|
||||
protected string $message = '';
|
||||
|
||||
/** @var array<int, array{time: int, name: string, message_level: string, meta_data: array, microtime: float}> */
|
||||
protected array $glows = [];
|
||||
|
||||
/** @var array<int, array<int|string, mixed>> */
|
||||
protected array $solutions = [];
|
||||
|
||||
/** @var array<int, string> */
|
||||
public array $documentationLinks = [];
|
||||
|
||||
protected ContextProvider $context;
|
||||
|
||||
protected ?string $applicationPath = null;
|
||||
|
||||
protected ?string $applicationVersion = null;
|
||||
|
||||
/** @var array<int|string, mixed> */
|
||||
protected array $userProvidedContext = [];
|
||||
|
||||
/** @var array<int|string, mixed> */
|
||||
protected array $exceptionContext = [];
|
||||
|
||||
protected ?Throwable $throwable = null;
|
||||
|
||||
protected string $notifierName = 'Flare Client';
|
||||
|
||||
protected ?string $languageVersion = null;
|
||||
|
||||
protected ?string $frameworkVersion = null;
|
||||
|
||||
protected ?int $openFrameIndex = null;
|
||||
|
||||
protected string $trackingUuid;
|
||||
|
||||
protected ?View $view;
|
||||
|
||||
public static ?string $fakeTrackingUuid = null;
|
||||
|
||||
/** @param array<class-string<ArgumentReducer>|ArgumentReducer>|ArgumentReducers|null $argumentReducers */
|
||||
public static function createForThrowable(
|
||||
Throwable $throwable,
|
||||
ContextProvider $context,
|
||||
?string $applicationPath = null,
|
||||
?string $version = null,
|
||||
null|array|ArgumentReducers $argumentReducers = null,
|
||||
bool $withStackTraceArguments = true,
|
||||
): self {
|
||||
$stacktrace = Backtrace::createForThrowable($throwable)
|
||||
->withArguments($withStackTraceArguments)
|
||||
->reduceArguments($argumentReducers)
|
||||
->applicationPath($applicationPath ?? '');
|
||||
|
||||
return (new self())
|
||||
->setApplicationPath($applicationPath)
|
||||
->throwable($throwable)
|
||||
->useContext($context)
|
||||
->exceptionClass(self::getClassForThrowable($throwable))
|
||||
->message($throwable->getMessage())
|
||||
->stackTrace($stacktrace)
|
||||
->exceptionContext($throwable)
|
||||
->setApplicationVersion($version);
|
||||
}
|
||||
|
||||
protected static function getClassForThrowable(Throwable $throwable): string
|
||||
{
|
||||
/** @phpstan-ignore-next-line */
|
||||
if ($throwable::class === ViewException::class) {
|
||||
/** @phpstan-ignore-next-line */
|
||||
if ($previous = $throwable->getPrevious()) {
|
||||
return get_class($previous);
|
||||
}
|
||||
}
|
||||
|
||||
return get_class($throwable);
|
||||
}
|
||||
|
||||
/** @param array<class-string<ArgumentReducer>|ArgumentReducer>|ArgumentReducers|null $argumentReducers */
|
||||
public static function createForMessage(
|
||||
string $message,
|
||||
string $logLevel,
|
||||
ContextProvider $context,
|
||||
?string $applicationPath = null,
|
||||
null|array|ArgumentReducers $argumentReducers = null,
|
||||
bool $withStackTraceArguments = true,
|
||||
): self {
|
||||
$stacktrace = Backtrace::create()
|
||||
->withArguments($withStackTraceArguments)
|
||||
->reduceArguments($argumentReducers)
|
||||
->applicationPath($applicationPath ?? '');
|
||||
|
||||
return (new self())
|
||||
->setApplicationPath($applicationPath)
|
||||
->message($message)
|
||||
->useContext($context)
|
||||
->exceptionClass($logLevel)
|
||||
->stacktrace($stacktrace)
|
||||
->openFrameIndex($stacktrace->firstApplicationFrameIndex());
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->trackingUuid = self::$fakeTrackingUuid ?? $this->generateUuid();
|
||||
}
|
||||
|
||||
public function trackingUuid(): string
|
||||
{
|
||||
return $this->trackingUuid;
|
||||
}
|
||||
|
||||
public function exceptionClass(string $exceptionClass): self
|
||||
{
|
||||
$this->exceptionClass = $exceptionClass;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getExceptionClass(): string
|
||||
{
|
||||
return $this->exceptionClass;
|
||||
}
|
||||
|
||||
public function throwable(Throwable $throwable): self
|
||||
{
|
||||
$this->throwable = $throwable;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getThrowable(): ?Throwable
|
||||
{
|
||||
return $this->throwable;
|
||||
}
|
||||
|
||||
public function message(string $message): self
|
||||
{
|
||||
$this->message = $message;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getMessage(): string
|
||||
{
|
||||
return $this->message;
|
||||
}
|
||||
|
||||
public function stacktrace(Backtrace $stacktrace): self
|
||||
{
|
||||
$this->stacktrace = $stacktrace;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getStacktrace(): Backtrace
|
||||
{
|
||||
return $this->stacktrace;
|
||||
}
|
||||
|
||||
public function notifierName(string $notifierName): self
|
||||
{
|
||||
$this->notifierName = $notifierName;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function languageVersion(string $languageVersion): self
|
||||
{
|
||||
$this->languageVersion = $languageVersion;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function frameworkVersion(string $frameworkVersion): self
|
||||
{
|
||||
$this->frameworkVersion = $frameworkVersion;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function useContext(ContextProvider $request): self
|
||||
{
|
||||
$this->context = $request;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function openFrameIndex(?int $index): self
|
||||
{
|
||||
$this->openFrameIndex = $index;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setApplicationPath(?string $applicationPath): self
|
||||
{
|
||||
$this->applicationPath = $applicationPath;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getApplicationPath(): ?string
|
||||
{
|
||||
return $this->applicationPath;
|
||||
}
|
||||
|
||||
public function setApplicationVersion(?string $applicationVersion): self
|
||||
{
|
||||
$this->applicationVersion = $applicationVersion;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getApplicationVersion(): ?string
|
||||
{
|
||||
return $this->applicationVersion;
|
||||
}
|
||||
|
||||
public function view(?View $view): self
|
||||
{
|
||||
$this->view = $view;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function addGlow(Glow $glow): self
|
||||
{
|
||||
$this->glows[] = $glow->toArray();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function addSolution(Solution $solution): self
|
||||
{
|
||||
$this->solutions[] = ReportSolution::fromSolution($solution)->toArray();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, string> $documentationLinks
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function addDocumentationLinks(array $documentationLinks): self
|
||||
{
|
||||
$this->documentationLinks = $documentationLinks;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int|string, mixed> $userProvidedContext
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function userProvidedContext(array $userProvidedContext): self
|
||||
{
|
||||
$this->userProvidedContext = $userProvidedContext;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int|string, mixed>
|
||||
*/
|
||||
public function allContext(): array
|
||||
{
|
||||
$context = $this->context->toArray();
|
||||
|
||||
$context = array_merge_recursive_distinct($context, $this->exceptionContext);
|
||||
|
||||
return array_merge_recursive_distinct($context, $this->userProvidedContext);
|
||||
}
|
||||
|
||||
protected function exceptionContext(Throwable $throwable): self
|
||||
{
|
||||
if ($throwable instanceof ProvidesFlareContext) {
|
||||
$this->exceptionContext = $throwable->context();
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int|string, mixed>
|
||||
*/
|
||||
protected function stracktraceAsArray(): array
|
||||
{
|
||||
return array_map(
|
||||
fn (SpatieFrame $frame) => Frame::fromSpatieFrame($frame)->toArray(),
|
||||
$this->cleanupStackTraceForError($this->stacktrace->frames()),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<SpatieFrame> $frames
|
||||
*
|
||||
* @return array<SpatieFrame>
|
||||
*/
|
||||
protected function cleanupStackTraceForError(array $frames): array
|
||||
{
|
||||
if ($this->throwable === null || get_class($this->throwable) !== ErrorException::class) {
|
||||
return $frames;
|
||||
}
|
||||
|
||||
$firstErrorFrameIndex = null;
|
||||
|
||||
$restructuredFrames = array_values(array_slice($frames, 1)); // remove the first frame where error was created
|
||||
|
||||
foreach ($restructuredFrames as $index => $frame) {
|
||||
if ($frame->file === $this->throwable->getFile()) {
|
||||
$firstErrorFrameIndex = $index;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($firstErrorFrameIndex === null) {
|
||||
return $frames;
|
||||
}
|
||||
|
||||
$restructuredFrames[$firstErrorFrameIndex]->arguments = null; // Remove error arguments
|
||||
|
||||
return array_values(array_slice($restructuredFrames, $firstErrorFrameIndex));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'notifier' => $this->notifierName ?? 'Flare Client',
|
||||
'language' => 'PHP',
|
||||
'framework_version' => $this->frameworkVersion,
|
||||
'language_version' => $this->languageVersion ?? phpversion(),
|
||||
'exception_class' => $this->exceptionClass,
|
||||
'seen_at' => $this->getCurrentTime(),
|
||||
'message' => $this->message,
|
||||
'glows' => $this->glows,
|
||||
'solutions' => $this->solutions,
|
||||
'documentation_links' => $this->documentationLinks,
|
||||
'stacktrace' => $this->stracktraceAsArray(),
|
||||
'context' => $this->allContext(),
|
||||
'stage' => $this->stage,
|
||||
'message_level' => $this->messageLevel,
|
||||
'open_frame_index' => $this->openFrameIndex,
|
||||
'application_path' => $this->applicationPath,
|
||||
'application_version' => $this->applicationVersion,
|
||||
'tracking_uuid' => $this->trackingUuid,
|
||||
];
|
||||
}
|
||||
|
||||
/*
|
||||
* Found on https://stackoverflow.com/questions/2040240/php-function-to-generate-v4-uuid/15875555#15875555
|
||||
*/
|
||||
protected function generateUuid(): string
|
||||
{
|
||||
// Generate 16 bytes (128 bits) of random data or use the data passed into the function.
|
||||
$data = random_bytes(16);
|
||||
|
||||
// Set version to 0100
|
||||
$data[6] = chr(ord($data[6]) & 0x0f | 0x40);
|
||||
// Set bits 6-7 to 10
|
||||
$data[8] = chr(ord($data[8]) & 0x3f | 0x80);
|
||||
|
||||
// Output the 36 character UUID.
|
||||
return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
|
||||
}
|
||||
}
|
40
vendor/spatie/flare-client-php/src/Solutions/ReportSolution.php
vendored
Normal file
40
vendor/spatie/flare-client-php/src/Solutions/ReportSolution.php
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\FlareClient\Solutions;
|
||||
|
||||
use Spatie\Ignition\Contracts\RunnableSolution;
|
||||
use Spatie\Ignition\Contracts\Solution as SolutionContract;
|
||||
|
||||
class ReportSolution
|
||||
{
|
||||
protected SolutionContract $solution;
|
||||
|
||||
public function __construct(SolutionContract $solution)
|
||||
{
|
||||
$this->solution = $solution;
|
||||
}
|
||||
|
||||
public static function fromSolution(SolutionContract $solution): self
|
||||
{
|
||||
return new self($solution);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
$isRunnable = ($this->solution instanceof RunnableSolution);
|
||||
|
||||
return [
|
||||
'class' => get_class($this->solution),
|
||||
'title' => $this->solution->getSolutionTitle(),
|
||||
'description' => $this->solution->getSolutionDescription(),
|
||||
'links' => $this->solution->getDocumentationLinks(),
|
||||
/** @phpstan-ignore-next-line */
|
||||
'action_description' => $isRunnable ? $this->solution->getSolutionActionDescription() : null,
|
||||
'is_runnable' => $isRunnable,
|
||||
'ai_generated' => $this->solution->aiGenerated ?? false,
|
||||
];
|
||||
}
|
||||
}
|
13
vendor/spatie/flare-client-php/src/Time/SystemTime.php
vendored
Normal file
13
vendor/spatie/flare-client-php/src/Time/SystemTime.php
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\FlareClient\Time;
|
||||
|
||||
use DateTimeImmutable;
|
||||
|
||||
class SystemTime implements Time
|
||||
{
|
||||
public function getCurrentTime(): int
|
||||
{
|
||||
return (new DateTimeImmutable())->getTimestamp();
|
||||
}
|
||||
}
|
8
vendor/spatie/flare-client-php/src/Time/Time.php
vendored
Normal file
8
vendor/spatie/flare-client-php/src/Time/Time.php
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\FlareClient\Time;
|
||||
|
||||
interface Time
|
||||
{
|
||||
public function getCurrentTime(): int;
|
||||
}
|
13
vendor/spatie/flare-client-php/src/Truncation/AbstractTruncationStrategy.php
vendored
Normal file
13
vendor/spatie/flare-client-php/src/Truncation/AbstractTruncationStrategy.php
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\FlareClient\Truncation;
|
||||
|
||||
abstract class AbstractTruncationStrategy implements TruncationStrategy
|
||||
{
|
||||
protected ReportTrimmer $reportTrimmer;
|
||||
|
||||
public function __construct(ReportTrimmer $reportTrimmer)
|
||||
{
|
||||
$this->reportTrimmer = $reportTrimmer;
|
||||
}
|
||||
}
|
53
vendor/spatie/flare-client-php/src/Truncation/ReportTrimmer.php
vendored
Normal file
53
vendor/spatie/flare-client-php/src/Truncation/ReportTrimmer.php
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\FlareClient\Truncation;
|
||||
|
||||
class ReportTrimmer
|
||||
{
|
||||
protected static int $maxPayloadSize = 524288;
|
||||
|
||||
/** @var array<int, class-string<\Spatie\FlareClient\Truncation\TruncationStrategy>> */
|
||||
protected array $strategies = [
|
||||
TrimStringsStrategy::class,
|
||||
TrimStackFrameArgumentsStrategy::class,
|
||||
TrimContextItemsStrategy::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* @param array<int|string, mixed> $payload
|
||||
*
|
||||
* @return array<int|string, mixed>
|
||||
*/
|
||||
public function trim(array $payload): array
|
||||
{
|
||||
foreach ($this->strategies as $strategy) {
|
||||
if (! $this->needsToBeTrimmed($payload)) {
|
||||
break;
|
||||
}
|
||||
|
||||
$payload = (new $strategy($this))->execute($payload);
|
||||
}
|
||||
|
||||
return $payload;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int|string, mixed> $payload
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function needsToBeTrimmed(array $payload): bool
|
||||
{
|
||||
return strlen((string)json_encode($payload)) > self::getMaxPayloadSize();
|
||||
}
|
||||
|
||||
public static function getMaxPayloadSize(): int
|
||||
{
|
||||
return self::$maxPayloadSize;
|
||||
}
|
||||
|
||||
public static function setMaxPayloadSize(int $maxPayloadSize): void
|
||||
{
|
||||
self::$maxPayloadSize = $maxPayloadSize;
|
||||
}
|
||||
}
|
58
vendor/spatie/flare-client-php/src/Truncation/TrimContextItemsStrategy.php
vendored
Normal file
58
vendor/spatie/flare-client-php/src/Truncation/TrimContextItemsStrategy.php
vendored
Normal file
@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\FlareClient\Truncation;
|
||||
|
||||
class TrimContextItemsStrategy extends AbstractTruncationStrategy
|
||||
{
|
||||
/**
|
||||
* @return array<int, int>
|
||||
*/
|
||||
public static function thresholds(): array
|
||||
{
|
||||
return [100, 50, 25, 10];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int|string, mixed> $payload
|
||||
*
|
||||
* @return array<int|string, mixed>
|
||||
*/
|
||||
public function execute(array $payload): array
|
||||
{
|
||||
foreach (static::thresholds() as $threshold) {
|
||||
if (! $this->reportTrimmer->needsToBeTrimmed($payload)) {
|
||||
break;
|
||||
}
|
||||
|
||||
$payload['context'] = $this->iterateContextItems($payload['context'], $threshold);
|
||||
}
|
||||
|
||||
return $payload;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int|string, mixed> $contextItems
|
||||
* @param int $threshold
|
||||
*
|
||||
* @return array<int|string, mixed>
|
||||
*/
|
||||
protected function iterateContextItems(array $contextItems, int $threshold): array
|
||||
{
|
||||
array_walk($contextItems, [$this, 'trimContextItems'], $threshold);
|
||||
|
||||
return $contextItems;
|
||||
}
|
||||
|
||||
protected function trimContextItems(mixed &$value, mixed $key, int $threshold): mixed
|
||||
{
|
||||
if (is_array($value)) {
|
||||
if (count($value) > $threshold) {
|
||||
$value = array_slice($value, $threshold * -1, $threshold);
|
||||
}
|
||||
|
||||
array_walk($value, [$this, 'trimContextItems'], $threshold);
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
}
|
15
vendor/spatie/flare-client-php/src/Truncation/TrimStackFrameArgumentsStrategy.php
vendored
Normal file
15
vendor/spatie/flare-client-php/src/Truncation/TrimStackFrameArgumentsStrategy.php
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\FlareClient\Truncation;
|
||||
|
||||
class TrimStackFrameArgumentsStrategy implements TruncationStrategy
|
||||
{
|
||||
public function execute(array $payload): array
|
||||
{
|
||||
for ($i = 0; $i < count($payload['stacktrace']); $i++) {
|
||||
$payload['stacktrace'][$i]['arguments'] = null;
|
||||
}
|
||||
|
||||
return $payload;
|
||||
}
|
||||
}
|
49
vendor/spatie/flare-client-php/src/Truncation/TrimStringsStrategy.php
vendored
Normal file
49
vendor/spatie/flare-client-php/src/Truncation/TrimStringsStrategy.php
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\FlareClient\Truncation;
|
||||
|
||||
class TrimStringsStrategy extends AbstractTruncationStrategy
|
||||
{
|
||||
/**
|
||||
* @return array<int, int>
|
||||
*/
|
||||
public static function thresholds(): array
|
||||
{
|
||||
return [1024, 512, 256];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int|string, mixed> $payload
|
||||
*
|
||||
* @return array<int|string, mixed>
|
||||
*/
|
||||
public function execute(array $payload): array
|
||||
{
|
||||
foreach (static::thresholds() as $threshold) {
|
||||
if (! $this->reportTrimmer->needsToBeTrimmed($payload)) {
|
||||
break;
|
||||
}
|
||||
|
||||
$payload = $this->trimPayloadString($payload, $threshold);
|
||||
}
|
||||
|
||||
return $payload;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int|string, mixed> $payload
|
||||
* @param int $threshold
|
||||
*
|
||||
* @return array<int|string, mixed>
|
||||
*/
|
||||
protected function trimPayloadString(array $payload, int $threshold): array
|
||||
{
|
||||
array_walk_recursive($payload, function (&$value) use ($threshold) {
|
||||
if (is_string($value) && strlen($value) > $threshold) {
|
||||
$value = substr($value, 0, $threshold);
|
||||
}
|
||||
});
|
||||
|
||||
return $payload;
|
||||
}
|
||||
}
|
13
vendor/spatie/flare-client-php/src/Truncation/TruncationStrategy.php
vendored
Normal file
13
vendor/spatie/flare-client-php/src/Truncation/TruncationStrategy.php
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\FlareClient\Truncation;
|
||||
|
||||
interface TruncationStrategy
|
||||
{
|
||||
/**
|
||||
* @param array<int|string, mixed> $payload
|
||||
*
|
||||
* @return array<int|string, mixed>
|
||||
*/
|
||||
public function execute(array $payload): array;
|
||||
}
|
65
vendor/spatie/flare-client-php/src/View.php
vendored
Normal file
65
vendor/spatie/flare-client-php/src/View.php
vendored
Normal file
@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\FlareClient;
|
||||
|
||||
use Symfony\Component\VarDumper\Cloner\VarCloner;
|
||||
use Symfony\Component\VarDumper\Dumper\HtmlDumper;
|
||||
|
||||
class View
|
||||
{
|
||||
protected string $file;
|
||||
|
||||
/** @var array<string, mixed> */
|
||||
protected array $data = [];
|
||||
|
||||
/**
|
||||
* @param string $file
|
||||
* @param array<string, mixed> $data
|
||||
*/
|
||||
public function __construct(string $file, array $data = [])
|
||||
{
|
||||
$this->file = $file;
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $file
|
||||
* @param array<string, mixed> $data
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public static function create(string $file, array $data = []): self
|
||||
{
|
||||
return new self($file, $data);
|
||||
}
|
||||
|
||||
protected function dumpViewData(mixed $variable): string
|
||||
{
|
||||
$cloner = new VarCloner();
|
||||
|
||||
$dumper = new HtmlDumper();
|
||||
$dumper->setDumpHeader('');
|
||||
|
||||
$output = fopen('php://memory', 'r+b');
|
||||
|
||||
if (! $output) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$dumper->dump($cloner->cloneVar($variable)->withMaxDepth(1), $output, [
|
||||
'maxDepth' => 1,
|
||||
'maxStringLength' => 160,
|
||||
]);
|
||||
|
||||
return (string)stream_get_contents($output, -1, 0);
|
||||
}
|
||||
|
||||
/** @return array<string, mixed> */
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'file' => $this->file,
|
||||
'data' => array_map([$this, 'dumpViewData'], $this->data),
|
||||
];
|
||||
}
|
||||
}
|
23
vendor/spatie/flare-client-php/src/helpers.php
vendored
Normal file
23
vendor/spatie/flare-client-php/src/helpers.php
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
if (! function_exists('array_merge_recursive_distinct')) {
|
||||
/**
|
||||
* @param array<int|string, mixed> $array1
|
||||
* @param array<int|string, mixed> $array2
|
||||
*
|
||||
* @return array<int|string, mixed>
|
||||
*/
|
||||
function array_merge_recursive_distinct(array &$array1, array &$array2): array
|
||||
{
|
||||
$merged = $array1;
|
||||
foreach ($array2 as $key => &$value) {
|
||||
if (is_array($value) && isset($merged[$key]) && is_array($merged[$key])) {
|
||||
$merged[$key] = array_merge_recursive_distinct($merged[$key], $value);
|
||||
} else {
|
||||
$merged[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $merged;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user