Upgrade framework
This commit is contained in:
1
vendor/nunomaduro/collision/.temp/.gitkeep
vendored
Normal file
1
vendor/nunomaduro/collision/.temp/.gitkeep
vendored
Normal file
@@ -0,0 +1 @@
|
||||
.gitkeep
|
||||
21
vendor/nunomaduro/collision/LICENSE.md
vendored
Executable file
21
vendor/nunomaduro/collision/LICENSE.md
vendored
Executable file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) Nuno Maduro <enunomaduro@gmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
81
vendor/nunomaduro/collision/README.md
vendored
Normal file
81
vendor/nunomaduro/collision/README.md
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
<a href="https://supportukrainenow.org/"><img src="https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner-direct.svg" width="100%"></a>
|
||||
|
||||
------
|
||||
|
||||
<p align="center">
|
||||
<img src="https://raw.githubusercontent.com/nunomaduro/collision/stable/docs/logo.png" alt="Collision logo" width="480">
|
||||
<br>
|
||||
<img src="https://raw.githubusercontent.com/nunomaduro/collision/stable/docs/example.png" alt="Collision code example" height="300">
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/nunomaduro/collision/actions"><img src="https://img.shields.io/github/workflow/status/nunomaduro/collision/Tests.svg" alt="Build Status"></img></a>
|
||||
<a href="https://scrutinizer-ci.com/g/nunomaduro/collision"><img src="https://img.shields.io/scrutinizer/g/nunomaduro/collision.svg" alt="Quality Score"></img></a>
|
||||
<a href="https://packagist.org/packages/nunomaduro/collision"><img src="https://poser.pugx.org/nunomaduro/collision/d/total.svg" alt="Total Downloads"></a>
|
||||
<a href="https://packagist.org/packages/nunomaduro/collision"><img src="https://poser.pugx.org/nunomaduro/collision/v/stable.svg" alt="Latest Stable Version"></a>
|
||||
<a href="https://packagist.org/packages/nunomaduro/collision"><img src="https://poser.pugx.org/nunomaduro/collision/license.svg" alt="License"></a>
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
Collision was created by, and is maintained by **[Nuno Maduro](https://github.com/nunomaduro)**, and is a package designed to give you beautiful error reporting when interacting with your app through the command line.
|
||||
|
||||
* It's included on **[Laravel](https://laravel.com)**, the most popular free, open-source PHP framework in the world.
|
||||
* Built on top of the **[Whoops](https://github.com/filp/whoops)** error handler.
|
||||
* Supports [Laravel](https://github.com/laravel/laravel), [Symfony](https://symfony.com), [PHPUnit](https://github.com/sebastianbergmann/phpunit), and many other frameworks.
|
||||
|
||||
## Installation & Usage
|
||||
|
||||
> **Requires [PHP 8.0+](https://php.net/releases/)**
|
||||
|
||||
Require Collision using [Composer](https://getcomposer.org):
|
||||
|
||||
```bash
|
||||
composer require nunomaduro/collision --dev
|
||||
```
|
||||
|
||||
## Laravel Version Compatibility
|
||||
|
||||
Laravel | Collision
|
||||
:---------|:----------
|
||||
6.x | 3.x
|
||||
7.x | 4.x
|
||||
8.x | 5.x
|
||||
9.x | 6.x
|
||||
|
||||
As an example, here is how to require Collision on Laravel 6.x:
|
||||
|
||||
```bash
|
||||
composer require nunomaduro/collision:^3.0 --dev
|
||||
```
|
||||
|
||||
## Phpunit adapter
|
||||
|
||||
Phpunit must be 9.0 or higher.
|
||||
|
||||
Add the Collision `printerClass` to your `phpunit.xml` in the `phpunit` section:
|
||||
|
||||
```xml
|
||||
<phpunit
|
||||
printerClass="NunoMaduro\Collision\Adapters\Phpunit\Printer">
|
||||
```
|
||||
|
||||
## No adapter
|
||||
|
||||
You need to register the handler in your code:
|
||||
|
||||
```php
|
||||
(new \NunoMaduro\Collision\Provider)->register();
|
||||
```
|
||||
|
||||
## Contributing
|
||||
|
||||
Thank you for considering to contribute to Collision. All the contribution guidelines are mentioned [here](CONTRIBUTING.md).
|
||||
|
||||
You can have a look at the [CHANGELOG](CHANGELOG.md) for constant updates & detailed information about the changes. You can also follow the twitter account for latest announcements or just come say hi!: [@enunomaduro](https://twitter.com/enunomaduro)
|
||||
|
||||
## License
|
||||
|
||||
Collision is an open-sourced software licensed under the [MIT license](LICENSE.md).
|
||||
|
||||
Logo by [Caneco](https://twitter.com/caneco).
|
||||
74
vendor/nunomaduro/collision/composer.json
vendored
Normal file
74
vendor/nunomaduro/collision/composer.json
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
{
|
||||
"name": "nunomaduro/collision",
|
||||
"description": "Cli error handling for console/command-line PHP applications.",
|
||||
"keywords": ["console", "command-line", "php", "cli", "error", "handling", "laravel-zero", "laravel", "artisan", "symfony"],
|
||||
"license": "MIT",
|
||||
"support": {
|
||||
"issues": "https://github.com/nunomaduro/collision/issues",
|
||||
"source": "https://github.com/nunomaduro/collision"
|
||||
},
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nuno Maduro",
|
||||
"email": "enunomaduro@gmail.com"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "^8.0.0",
|
||||
"filp/whoops": "^2.14.5",
|
||||
"symfony/console": "^6.0.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"brianium/paratest": "^6.4.1",
|
||||
"laravel/framework": "^9.26.1",
|
||||
"laravel/pint": "^1.1.1",
|
||||
"nunomaduro/larastan": "^1.0.3",
|
||||
"nunomaduro/mock-final-classes": "^1.1.0",
|
||||
"orchestra/testbench": "^7.7",
|
||||
"phpunit/phpunit": "^9.5.23",
|
||||
"spatie/ignition": "^1.4.1"
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Tests\\Unit\\": "tests/Unit",
|
||||
"Tests\\FakeProgram\\": "tests/FakeProgram",
|
||||
"Tests\\": "tests/LaravelApp/tests",
|
||||
"App\\": "tests/LaravelApp/app/"
|
||||
}
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"prefer-stable": true,
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"NunoMaduro\\Collision\\": "src/"
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"preferred-install": "dist",
|
||||
"sort-packages": true,
|
||||
"allow-plugins": {
|
||||
"pestphp/pest-plugin": true
|
||||
}
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-develop": "6.x-dev"
|
||||
},
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider"
|
||||
]
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "pint -v",
|
||||
"test:lint": "pint --test -v",
|
||||
"test:types": "phpstan analyse --ansi",
|
||||
"test:unit": "phpunit --colors=always",
|
||||
"test": [
|
||||
"@test:lint",
|
||||
"@test:types",
|
||||
"@test:unit"
|
||||
]
|
||||
}
|
||||
}
|
||||
84
vendor/nunomaduro/collision/src/Adapters/Laravel/CollisionServiceProvider.php
vendored
Normal file
84
vendor/nunomaduro/collision/src/Adapters/Laravel/CollisionServiceProvider.php
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace NunoMaduro\Collision\Adapters\Laravel;
|
||||
|
||||
use Illuminate\Contracts\Debug\ExceptionHandler as ExceptionHandlerContract;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use NunoMaduro\Collision\Adapters\Laravel\Commands\TestCommand;
|
||||
use NunoMaduro\Collision\Contracts\Provider as ProviderContract;
|
||||
use NunoMaduro\Collision\Handler;
|
||||
use NunoMaduro\Collision\Provider;
|
||||
use NunoMaduro\Collision\SolutionsRepositories\NullSolutionsRepository;
|
||||
use NunoMaduro\Collision\Writer;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
class CollisionServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $defer = true;
|
||||
|
||||
/**
|
||||
* Boots application services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
$this->commands([
|
||||
TestCommand::class,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
if ($this->app->runningInConsole() && ! $this->app->runningUnitTests()) {
|
||||
$this->app->bind(ProviderContract::class, function () {
|
||||
// @phpstan-ignore-next-line
|
||||
if ($this->app->has(\Spatie\Ignition\Contracts\SolutionProviderRepository::class)) {
|
||||
/** @var \Spatie\Ignition\Contracts\SolutionProviderRepository $solutionProviderRepository */
|
||||
$solutionProviderRepository = $this->app->get(\Spatie\Ignition\Contracts\SolutionProviderRepository::class);
|
||||
|
||||
$solutionsRepository = new IgnitionSolutionsRepository($solutionProviderRepository);
|
||||
} else {
|
||||
$solutionsRepository = new NullSolutionsRepository();
|
||||
}
|
||||
|
||||
$writer = new Writer($solutionsRepository);
|
||||
$handler = new Handler($writer);
|
||||
|
||||
return new Provider(null, $handler);
|
||||
});
|
||||
|
||||
/** @var \Illuminate\Contracts\Debug\ExceptionHandler $appExceptionHandler */
|
||||
$appExceptionHandler = $this->app->make(ExceptionHandlerContract::class);
|
||||
|
||||
$this->app->singleton(
|
||||
ExceptionHandlerContract::class,
|
||||
function ($app) use ($appExceptionHandler) {
|
||||
return new ExceptionHandler($app, $appExceptionHandler);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function provides()
|
||||
{
|
||||
return [ProviderContract::class];
|
||||
}
|
||||
}
|
||||
390
vendor/nunomaduro/collision/src/Adapters/Laravel/Commands/TestCommand.php
vendored
Normal file
390
vendor/nunomaduro/collision/src/Adapters/Laravel/Commands/TestCommand.php
vendored
Normal file
@@ -0,0 +1,390 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace NunoMaduro\Collision\Adapters\Laravel\Commands;
|
||||
|
||||
use Dotenv\Exception\InvalidPathException;
|
||||
use Dotenv\Parser\Parser;
|
||||
use Dotenv\Store\StoreBuilder;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Env;
|
||||
use Illuminate\Support\Str;
|
||||
use NunoMaduro\Collision\Adapters\Laravel\Exceptions\RequirementsException;
|
||||
use NunoMaduro\Collision\Coverage;
|
||||
use RuntimeException;
|
||||
use Symfony\Component\Process\Exception\ProcessSignaledException;
|
||||
use Symfony\Component\Process\Process;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
class TestCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'test
|
||||
{--without-tty : Disable output to TTY}
|
||||
{--coverage : Indicates whether code coverage information should be collected}
|
||||
{--min= : Indicates the minimum threshold enforcement for code coverage}
|
||||
{--p|parallel : Indicates if the tests should run in parallel}
|
||||
{--recreate-databases : Indicates if the test databases should be re-created}
|
||||
{--drop-databases : Indicates if the test databases should be dropped}
|
||||
';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Run the application tests';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->ignoreValidationErrors();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$phpunitVersion = \PHPUnit\Runner\Version::id();
|
||||
|
||||
if ((int) $phpunitVersion[0] === 1) {
|
||||
throw new RequirementsException('Running PHPUnit v10 or Pest v2 requires Collision ^7.0.');
|
||||
}
|
||||
|
||||
if ((int) $phpunitVersion[0] < 9) {
|
||||
throw new RequirementsException('Running Collision ^5.0 artisan test command requires at least PHPUnit ^9.0.');
|
||||
}
|
||||
|
||||
// @phpstan-ignore-next-line
|
||||
if ((int) \Illuminate\Foundation\Application::VERSION[0] < 8) {
|
||||
throw new RequirementsException('Running Collision ^5.0 artisan test command requires at least Laravel ^8.0.');
|
||||
}
|
||||
|
||||
if ($this->option('coverage') && ! Coverage::isAvailable()) {
|
||||
$this->output->writeln(sprintf(
|
||||
"\n <fg=white;bg=red;options=bold> ERROR </> Code coverage driver not available.%s</>",
|
||||
Coverage::usingXdebug()
|
||||
? " Did you set <href=https://xdebug.org/docs/code_coverage#mode>Xdebug's coverage mode</>?"
|
||||
: ''
|
||||
));
|
||||
|
||||
$this->newLine();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ($this->option('parallel') && ! $this->isParallelDependenciesInstalled()) {
|
||||
if (! $this->confirm('Running tests in parallel requires "brianium/paratest". Do you wish to install it as a dev dependency?')) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
$this->installParallelDependencies();
|
||||
}
|
||||
|
||||
$options = array_slice($_SERVER['argv'], $this->option('without-tty') ? 3 : 2);
|
||||
|
||||
$this->clearEnv();
|
||||
|
||||
$parallel = $this->option('parallel');
|
||||
|
||||
$process = (new Process(array_merge(
|
||||
// Binary ...
|
||||
$this->binary(),
|
||||
// Arguments ...
|
||||
$parallel ? $this->paratestArguments($options) : $this->phpunitArguments($options)
|
||||
),
|
||||
null,
|
||||
// Envs ...
|
||||
$parallel ? $this->paratestEnvironmentVariables() : $this->phpunitEnvironmentVariables(),
|
||||
))->setTimeout(null);
|
||||
|
||||
try {
|
||||
$process->setTty(! $this->option('without-tty'));
|
||||
} catch (RuntimeException $e) {
|
||||
$this->output->writeln('Warning: '.$e->getMessage());
|
||||
}
|
||||
|
||||
$exitCode = 1;
|
||||
|
||||
try {
|
||||
$exitCode = $process->run(function ($type, $line) {
|
||||
$this->output->write($line);
|
||||
});
|
||||
} catch (ProcessSignaledException $e) {
|
||||
if (extension_loaded('pcntl') && $e->getSignal() !== SIGINT) {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
if ($exitCode === 0 && $this->option('coverage')) {
|
||||
if (! $this->usingPest() && $this->option('parallel')) {
|
||||
$this->newLine();
|
||||
}
|
||||
|
||||
$coverage = Coverage::report($this->output);
|
||||
|
||||
$exitCode = (int) ($coverage < $this->option('min'));
|
||||
|
||||
if ($exitCode === 1) {
|
||||
$this->output->writeln(sprintf(
|
||||
"\n <fg=white;bg=red;options=bold> FAIL </> Code coverage below expected:<fg=red;options=bold> %s %%</>. Minimum:<fg=white;options=bold> %s %%</>.",
|
||||
number_format($coverage, 1),
|
||||
number_format((float) $this->option('min'), 1)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
$this->newLine();
|
||||
|
||||
return $exitCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the PHP binary to execute.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function binary()
|
||||
{
|
||||
if ($this->usingPest()) {
|
||||
$command = $this->option('parallel') ? ['vendor/pestphp/pest/bin/pest', '--parallel'] : ['vendor/pestphp/pest/bin/pest'];
|
||||
} else {
|
||||
$command = $this->option('parallel') ? ['vendor/brianium/paratest/bin/paratest'] : ['vendor/phpunit/phpunit/phpunit'];
|
||||
}
|
||||
|
||||
if ('phpdbg' === PHP_SAPI) {
|
||||
return array_merge([PHP_BINARY, '-qrr'], $command);
|
||||
}
|
||||
|
||||
return array_merge([PHP_BINARY], $command);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the common arguments of PHPUnit and Pest.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function commonArguments()
|
||||
{
|
||||
$arguments = [];
|
||||
|
||||
if ($this->option('coverage')) {
|
||||
$arguments[] = '--coverage-php';
|
||||
$arguments[] = Coverage::getPath();
|
||||
}
|
||||
|
||||
return $arguments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if Pest is being used.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function usingPest()
|
||||
{
|
||||
return class_exists(\Pest\Laravel\PestServiceProvider::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the array of arguments for running PHPUnit.
|
||||
*
|
||||
* @param array $options
|
||||
* @return array
|
||||
*/
|
||||
protected function phpunitArguments($options)
|
||||
{
|
||||
$options = array_merge(['--printer=NunoMaduro\\Collision\\Adapters\\Phpunit\\Printer'], $options);
|
||||
|
||||
$options = array_values(array_filter($options, function ($option) {
|
||||
return ! Str::startsWith($option, '--env=')
|
||||
&& $option != '-q'
|
||||
&& $option != '--quiet'
|
||||
&& $option != '--coverage'
|
||||
&& ! Str::startsWith($option, '--min');
|
||||
}));
|
||||
|
||||
if (! file_exists($file = base_path('phpunit.xml'))) {
|
||||
$file = base_path('phpunit.xml.dist');
|
||||
}
|
||||
|
||||
return array_merge($this->commonArguments(), ["--configuration=$file"], $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the array of arguments for running Paratest.
|
||||
*
|
||||
* @param array $options
|
||||
* @return array
|
||||
*/
|
||||
protected function paratestArguments($options)
|
||||
{
|
||||
$options = array_values(array_filter($options, function ($option) {
|
||||
return ! Str::startsWith($option, '--env=')
|
||||
&& $option != '--coverage'
|
||||
&& $option != '-q'
|
||||
&& $option != '--quiet'
|
||||
&& ! Str::startsWith($option, '--min')
|
||||
&& ! Str::startsWith($option, '-p')
|
||||
&& ! Str::startsWith($option, '--parallel')
|
||||
&& ! Str::startsWith($option, '--recreate-databases')
|
||||
&& ! Str::startsWith($option, '--drop-databases');
|
||||
}));
|
||||
|
||||
if (! file_exists($file = base_path('phpunit.xml'))) {
|
||||
$file = base_path('phpunit.xml.dist');
|
||||
}
|
||||
|
||||
return array_merge($this->commonArguments(), [
|
||||
"--configuration=$file",
|
||||
"--runner=\Illuminate\Testing\ParallelRunner",
|
||||
], $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the array of environment variables for running PHPUnit.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function phpunitEnvironmentVariables()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the array of environment variables for running Paratest.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function paratestEnvironmentVariables()
|
||||
{
|
||||
return [
|
||||
'LARAVEL_PARALLEL_TESTING' => 1,
|
||||
'LARAVEL_PARALLEL_TESTING_RECREATE_DATABASES' => $this->option('recreate-databases'),
|
||||
'LARAVEL_PARALLEL_TESTING_DROP_DATABASES' => $this->option('drop-databases'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears any set Environment variables set by Laravel if the --env option is empty.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function clearEnv()
|
||||
{
|
||||
if (! $this->option('env')) {
|
||||
$vars = self::getEnvironmentVariables(
|
||||
// @phpstan-ignore-next-line
|
||||
$this->laravel->environmentPath(),
|
||||
// @phpstan-ignore-next-line
|
||||
$this->laravel->environmentFile()
|
||||
);
|
||||
|
||||
$repository = Env::getRepository();
|
||||
|
||||
foreach ($vars as $name) {
|
||||
$repository->clear($name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
* @param string $file
|
||||
* @return array
|
||||
*/
|
||||
protected static function getEnvironmentVariables($path, $file)
|
||||
{
|
||||
try {
|
||||
$content = StoreBuilder::createWithNoNames()
|
||||
->addPath($path)
|
||||
->addName($file)
|
||||
->make()
|
||||
->read();
|
||||
} catch (InvalidPathException $e) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$vars = [];
|
||||
|
||||
foreach ((new Parser())->parse($content) as $entry) {
|
||||
$vars[] = $entry->getName();
|
||||
}
|
||||
|
||||
return $vars;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the parallel dependencies are installed.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function isParallelDependenciesInstalled()
|
||||
{
|
||||
return class_exists(\ParaTest\Console\Commands\ParaTestCommand::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Install parallel testing needed dependencies.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function installParallelDependencies()
|
||||
{
|
||||
$command = $this->findComposer().' require brianium/paratest --dev';
|
||||
|
||||
$process = Process::fromShellCommandline($command, null, null, null, null);
|
||||
|
||||
if ('\\' !== DIRECTORY_SEPARATOR && file_exists('/dev/tty') && is_readable('/dev/tty')) {
|
||||
try {
|
||||
$process->setTty(true);
|
||||
} catch (RuntimeException $e) {
|
||||
$this->output->writeln('Warning: '.$e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$process->run(function ($type, $line) {
|
||||
$this->output->write($line);
|
||||
});
|
||||
} catch (ProcessSignaledException $e) {
|
||||
if (extension_loaded('pcntl') && $e->getSignal() !== SIGINT) {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the composer command for the environment.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function findComposer()
|
||||
{
|
||||
$composerPath = getcwd().'/composer.phar';
|
||||
|
||||
if (file_exists($composerPath)) {
|
||||
return '"'.PHP_BINARY.'" '.$composerPath;
|
||||
}
|
||||
|
||||
return 'composer';
|
||||
}
|
||||
}
|
||||
87
vendor/nunomaduro/collision/src/Adapters/Laravel/ExceptionHandler.php
vendored
Normal file
87
vendor/nunomaduro/collision/src/Adapters/Laravel/ExceptionHandler.php
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace NunoMaduro\Collision\Adapters\Laravel;
|
||||
|
||||
use Illuminate\Contracts\Container\Container;
|
||||
use Illuminate\Contracts\Debug\ExceptionHandler as ExceptionHandlerContract;
|
||||
use NunoMaduro\Collision\Contracts\Provider as ProviderContract;
|
||||
use Symfony\Component\Console\Exception\ExceptionInterface as SymfonyConsoleExceptionInterface;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class ExceptionHandler implements ExceptionHandlerContract
|
||||
{
|
||||
/**
|
||||
* Holds an instance of the application exception handler.
|
||||
*
|
||||
* @var \Illuminate\Contracts\Debug\ExceptionHandler
|
||||
*/
|
||||
protected $appExceptionHandler;
|
||||
|
||||
/**
|
||||
* Holds an instance of the container.
|
||||
*
|
||||
* @var \Illuminate\Contracts\Container\Container
|
||||
*/
|
||||
protected $container;
|
||||
|
||||
/**
|
||||
* Creates a new instance of the ExceptionHandler.
|
||||
*/
|
||||
public function __construct(Container $container, ExceptionHandlerContract $appExceptionHandler)
|
||||
{
|
||||
$this->container = $container;
|
||||
$this->appExceptionHandler = $appExceptionHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function report(Throwable $e)
|
||||
{
|
||||
$this->appExceptionHandler->report($e);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function render($request, Throwable $e)
|
||||
{
|
||||
return $this->appExceptionHandler->render($request, $e);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function renderForConsole($output, Throwable $e)
|
||||
{
|
||||
if ($e instanceof SymfonyConsoleExceptionInterface) {
|
||||
$this->appExceptionHandler->renderForConsole($output, $e);
|
||||
} else {
|
||||
/** @var \NunoMaduro\Collision\Contracts\Provider $provider */
|
||||
$provider = $this->container->make(ProviderContract::class);
|
||||
|
||||
$handler = $provider->register()
|
||||
->getHandler()
|
||||
->setOutput($output);
|
||||
|
||||
$handler->setInspector((new Inspector($e)));
|
||||
|
||||
$handler->handle();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the exception should be reported.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function shouldReport(Throwable $e)
|
||||
{
|
||||
return $this->appExceptionHandler->shouldReport($e);
|
||||
}
|
||||
}
|
||||
16
vendor/nunomaduro/collision/src/Adapters/Laravel/Exceptions/RequirementsException.php
vendored
Normal file
16
vendor/nunomaduro/collision/src/Adapters/Laravel/Exceptions/RequirementsException.php
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace NunoMaduro\Collision\Adapters\Laravel\Exceptions;
|
||||
|
||||
use NunoMaduro\Collision\Contracts\RenderlessEditor;
|
||||
use NunoMaduro\Collision\Contracts\RenderlessTrace;
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class RequirementsException extends RuntimeException implements RenderlessEditor, RenderlessTrace
|
||||
{
|
||||
}
|
||||
38
vendor/nunomaduro/collision/src/Adapters/Laravel/IgnitionSolutionsRepository.php
vendored
Normal file
38
vendor/nunomaduro/collision/src/Adapters/Laravel/IgnitionSolutionsRepository.php
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace NunoMaduro\Collision\Adapters\Laravel;
|
||||
|
||||
use NunoMaduro\Collision\Contracts\SolutionsRepository;
|
||||
use Spatie\Ignition\Contracts\SolutionProviderRepository;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class IgnitionSolutionsRepository implements SolutionsRepository
|
||||
{
|
||||
/**
|
||||
* Holds an instance of ignition solutions provider repository.
|
||||
*
|
||||
* @var \Spatie\Ignition\Contracts\SolutionProviderRepository
|
||||
*/
|
||||
protected $solutionProviderRepository;
|
||||
|
||||
/**
|
||||
* IgnitionSolutionsRepository constructor.
|
||||
*/
|
||||
public function __construct(SolutionProviderRepository $solutionProviderRepository)
|
||||
{
|
||||
$this->solutionProviderRepository = $solutionProviderRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFromThrowable(Throwable $throwable): array
|
||||
{
|
||||
return $this->solutionProviderRepository->getSolutionsForThrowable($throwable);
|
||||
}
|
||||
}
|
||||
30
vendor/nunomaduro/collision/src/Adapters/Laravel/Inspector.php
vendored
Normal file
30
vendor/nunomaduro/collision/src/Adapters/Laravel/Inspector.php
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* This file is part of Collision.
|
||||
*
|
||||
* (c) Nuno Maduro <enunomaduro@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace NunoMaduro\Collision\Adapters\Laravel;
|
||||
|
||||
use Whoops\Exception\Inspector as BaseInspector;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class Inspector extends BaseInspector
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getTrace($e)
|
||||
{
|
||||
return $e->getTrace();
|
||||
}
|
||||
}
|
||||
40
vendor/nunomaduro/collision/src/Adapters/Phpunit/ConfigureIO.php
vendored
Normal file
40
vendor/nunomaduro/collision/src/Adapters/Phpunit/ConfigureIO.php
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* This file is part of Collision.
|
||||
*
|
||||
* (c) Nuno Maduro <enunomaduro@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace NunoMaduro\Collision\Adapters\Phpunit;
|
||||
|
||||
use ReflectionObject;
|
||||
use Symfony\Component\Console\Application;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\Output;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class ConfigureIO
|
||||
{
|
||||
/**
|
||||
* Configures both given input and output with
|
||||
* options from the environment.
|
||||
*
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
public static function of(InputInterface $input, Output $output): void
|
||||
{
|
||||
$application = new Application();
|
||||
$reflector = new ReflectionObject($application);
|
||||
$method = $reflector->getMethod('configureIO');
|
||||
$method->setAccessible(true);
|
||||
$method->invoke($application, $input, $output);
|
||||
}
|
||||
}
|
||||
246
vendor/nunomaduro/collision/src/Adapters/Phpunit/Printer.php
vendored
Normal file
246
vendor/nunomaduro/collision/src/Adapters/Phpunit/Printer.php
vendored
Normal file
@@ -0,0 +1,246 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace NunoMaduro\Collision\Adapters\Phpunit;
|
||||
|
||||
use NunoMaduro\Collision\Exceptions\ShouldNotHappen;
|
||||
use PHPUnit\Framework\AssertionFailedError;
|
||||
use PHPUnit\Framework\Test;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use PHPUnit\Framework\TestSuite;
|
||||
use PHPUnit\Framework\Warning;
|
||||
use ReflectionObject;
|
||||
use Symfony\Component\Console\Input\ArgvInput;
|
||||
use Symfony\Component\Console\Output\ConsoleOutput;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class Printer implements \PHPUnit\TextUI\ResultPrinter
|
||||
{
|
||||
/**
|
||||
* Holds an instance of the style.
|
||||
*
|
||||
* Style is a class we use to interact with output.
|
||||
*
|
||||
* @var Style
|
||||
*/
|
||||
private $style;
|
||||
|
||||
/**
|
||||
* Holds the duration time of the test suite.
|
||||
*
|
||||
* @var Timer
|
||||
*/
|
||||
private $timer;
|
||||
|
||||
/**
|
||||
* Holds the state of the test
|
||||
* suite. The number of tests, etc.
|
||||
*
|
||||
* @var State
|
||||
*/
|
||||
private $state;
|
||||
|
||||
/**
|
||||
* If the test suite has failed.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $failed = false;
|
||||
|
||||
/**
|
||||
* Creates a new instance of the listener.
|
||||
*
|
||||
* @param ConsoleOutput $output
|
||||
*
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
public function __construct(\Symfony\Component\Console\Output\ConsoleOutputInterface $output = null, bool $verbose = false, string $colors = 'always')
|
||||
{
|
||||
$this->timer = Timer::start();
|
||||
|
||||
$decorated = $colors === 'always' || $colors === 'auto';
|
||||
|
||||
$output = $output ?? new ConsoleOutput(ConsoleOutput::VERBOSITY_NORMAL, $decorated);
|
||||
|
||||
ConfigureIO::of(new ArgvInput(), $output);
|
||||
|
||||
$this->style = new Style($output);
|
||||
$dummyTest = new class() extends TestCase
|
||||
{
|
||||
};
|
||||
|
||||
$this->state = State::from($dummyTest);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addError(Test $testCase, Throwable $throwable, float $time): void
|
||||
{
|
||||
$this->failed = true;
|
||||
|
||||
$testCase = $this->testCaseFromTest($testCase);
|
||||
|
||||
$this->state->add(TestResult::fromTestCase($testCase, TestResult::FAIL, $throwable));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addWarning(Test $testCase, Warning $warning, float $time): void
|
||||
{
|
||||
$testCase = $this->testCaseFromTest($testCase);
|
||||
|
||||
$this->state->add(TestResult::fromTestCase($testCase, TestResult::WARN, $warning));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addFailure(Test $testCase, AssertionFailedError $error, float $time): void
|
||||
{
|
||||
$this->failed = true;
|
||||
|
||||
$testCase = $this->testCaseFromTest($testCase);
|
||||
|
||||
$reflector = new ReflectionObject($error);
|
||||
|
||||
if ($reflector->hasProperty('message')) {
|
||||
$message = trim((string) preg_replace("/\r|\n/", "\n ", $error->getMessage()));
|
||||
$property = $reflector->getProperty('message');
|
||||
$property->setAccessible(true);
|
||||
$property->setValue($error, $message);
|
||||
}
|
||||
|
||||
$this->state->add(TestResult::fromTestCase($testCase, TestResult::FAIL, $error));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addIncompleteTest(Test $testCase, Throwable $throwable, float $time): void
|
||||
{
|
||||
$testCase = $this->testCaseFromTest($testCase);
|
||||
|
||||
$this->state->add(TestResult::fromTestCase($testCase, TestResult::INCOMPLETE, $throwable));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addRiskyTest(Test $testCase, Throwable $throwable, float $time): void
|
||||
{
|
||||
$testCase = $this->testCaseFromTest($testCase);
|
||||
|
||||
$this->state->add(TestResult::fromTestCase($testCase, TestResult::RISKY, $throwable));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addSkippedTest(Test $testCase, Throwable $throwable, float $time): void
|
||||
{
|
||||
$testCase = $this->testCaseFromTest($testCase);
|
||||
|
||||
$this->state->add(TestResult::fromTestCase($testCase, TestResult::SKIPPED, $throwable));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function startTestSuite(TestSuite $suite): void
|
||||
{
|
||||
if ($this->state->suiteTotalTests === null) {
|
||||
$this->state->suiteTotalTests = $suite->count();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function endTestSuite(TestSuite $suite): void
|
||||
{
|
||||
// ..
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function startTest(Test $testCase): void
|
||||
{
|
||||
$testCase = $this->testCaseFromTest($testCase);
|
||||
|
||||
// Let's check first if the testCase is over.
|
||||
if ($this->state->testCaseHasChanged($testCase)) {
|
||||
$this->style->writeCurrentTestCaseSummary($this->state);
|
||||
|
||||
$this->state->moveTo($testCase);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function endTest(Test $testCase, float $time): void
|
||||
{
|
||||
$testCase = $this->testCaseFromTest($testCase);
|
||||
|
||||
if (! $this->state->existsInTestCase($testCase)) {
|
||||
$this->state->add(TestResult::fromTestCase($testCase, TestResult::PASS));
|
||||
}
|
||||
|
||||
if ($testCase instanceof TestCase
|
||||
&& $testCase->getTestResultObject() instanceof \PHPUnit\Framework\TestResult
|
||||
&& ! $testCase->getTestResultObject()->isStrictAboutOutputDuringTests()
|
||||
&& ! $testCase->hasExpectationOnOutput()) {
|
||||
$this->style->write($testCase->getActualOutput());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Intentionally left blank as we output things on events of the listener.
|
||||
*/
|
||||
public function write(string $content): void
|
||||
{
|
||||
// ..
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a test case from the given test.
|
||||
*
|
||||
* Note: This printer is do not work with normal Test classes - only
|
||||
* with Test Case classes. Please report an issue if you think
|
||||
* this should work any other way.
|
||||
*/
|
||||
private function testCaseFromTest(Test $test): TestCase
|
||||
{
|
||||
if (! $test instanceof TestCase) {
|
||||
throw new ShouldNotHappen();
|
||||
}
|
||||
|
||||
return $test;
|
||||
}
|
||||
|
||||
/**
|
||||
* Intentionally left blank as we output things on events of the listener.
|
||||
*/
|
||||
public function printResult(\PHPUnit\Framework\TestResult $result): void
|
||||
{
|
||||
if ($result->count() === 0) {
|
||||
$this->style->writeWarning('No tests executed!');
|
||||
}
|
||||
|
||||
$this->style->writeCurrentTestCaseSummary($this->state);
|
||||
|
||||
if ($this->failed) {
|
||||
$onFailure = $this->state->suiteTotalTests !== $this->state->testSuiteTestsCount();
|
||||
$this->style->writeErrorsSummary($this->state, $onFailure);
|
||||
}
|
||||
|
||||
$this->style->writeRecap($this->state, $this->timer);
|
||||
}
|
||||
}
|
||||
202
vendor/nunomaduro/collision/src/Adapters/Phpunit/State.php
vendored
Normal file
202
vendor/nunomaduro/collision/src/Adapters/Phpunit/State.php
vendored
Normal file
@@ -0,0 +1,202 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace NunoMaduro\Collision\Adapters\Phpunit;
|
||||
|
||||
use NunoMaduro\Collision\Contracts\Adapters\Phpunit\HasPrintableTestCaseName;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class State
|
||||
{
|
||||
/**
|
||||
* The complete test suite number of tests.
|
||||
*
|
||||
* @var int|null
|
||||
*/
|
||||
public $suiteTotalTests;
|
||||
|
||||
/**
|
||||
* The complete test suite tests.
|
||||
*
|
||||
* @var array<int, TestResult>
|
||||
*/
|
||||
public $suiteTests = [];
|
||||
|
||||
/**
|
||||
* The current test case class.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $testCaseName;
|
||||
|
||||
/**
|
||||
* The current test case tests.
|
||||
*
|
||||
* @var array<int, TestResult>
|
||||
*/
|
||||
public $testCaseTests = [];
|
||||
|
||||
/**
|
||||
* The current test case tests.
|
||||
*
|
||||
* @var array<int, TestResult>
|
||||
*/
|
||||
public $toBePrintedCaseTests = [];
|
||||
|
||||
/**
|
||||
* Header printed.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $headerPrinted = false;
|
||||
|
||||
/**
|
||||
* The state constructor.
|
||||
*/
|
||||
private function __construct(string $testCaseName)
|
||||
{
|
||||
$this->testCaseName = $testCaseName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new State starting from the given test case.
|
||||
*/
|
||||
public static function from(TestCase $test): self
|
||||
{
|
||||
return new self(self::getPrintableTestCaseName($test));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given test to the State.
|
||||
*/
|
||||
public function add(TestResult $test): void
|
||||
{
|
||||
$this->testCaseTests[] = $test;
|
||||
$this->toBePrintedCaseTests[] = $test;
|
||||
|
||||
$this->suiteTests[] = $test;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the test case title.
|
||||
*/
|
||||
public function getTestCaseTitle(): string
|
||||
{
|
||||
foreach ($this->testCaseTests as $test) {
|
||||
if ($test->type === TestResult::FAIL) {
|
||||
return 'FAIL';
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->testCaseTests as $test) {
|
||||
if ($test->type !== TestResult::PASS) {
|
||||
return 'WARN';
|
||||
}
|
||||
}
|
||||
|
||||
return 'PASS';
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the test case title color.
|
||||
*/
|
||||
public function getTestCaseTitleColor(): string
|
||||
{
|
||||
foreach ($this->testCaseTests as $test) {
|
||||
if ($test->type === TestResult::FAIL) {
|
||||
return 'red';
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->testCaseTests as $test) {
|
||||
if ($test->type !== TestResult::PASS) {
|
||||
return 'yellow';
|
||||
}
|
||||
}
|
||||
|
||||
return 'green';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of tests on the current test case.
|
||||
*/
|
||||
public function testCaseTestsCount(): int
|
||||
{
|
||||
return count($this->testCaseTests);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of tests on the complete test suite.
|
||||
*/
|
||||
public function testSuiteTestsCount(): int
|
||||
{
|
||||
return count($this->suiteTests);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given test case is different from the current one.
|
||||
*/
|
||||
public function testCaseHasChanged(TestCase $testCase): bool
|
||||
{
|
||||
return self::getPrintableTestCaseName($testCase) !== $this->testCaseName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the a new test case.
|
||||
*/
|
||||
public function moveTo(TestCase $testCase): void
|
||||
{
|
||||
$this->testCaseName = self::getPrintableTestCaseName($testCase);
|
||||
|
||||
$this->testCaseTests = [];
|
||||
|
||||
$this->headerPrinted = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Foreach test in the test case.
|
||||
*/
|
||||
public function eachTestCaseTests(callable $callback): void
|
||||
{
|
||||
foreach ($this->toBePrintedCaseTests as $test) {
|
||||
$callback($test);
|
||||
}
|
||||
|
||||
$this->toBePrintedCaseTests = [];
|
||||
}
|
||||
|
||||
public function countTestsInTestSuiteBy(string $type): int
|
||||
{
|
||||
return count(array_filter($this->suiteTests, function (TestResult $testResult) use ($type) {
|
||||
return $testResult->type === $type;
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given test already contains a result.
|
||||
*/
|
||||
public function existsInTestCase(TestCase $test): bool
|
||||
{
|
||||
foreach ($this->testCaseTests as $testResult) {
|
||||
if (TestResult::makeDescription($test) === $testResult->description) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the printable test case name from the given `TestCase`.
|
||||
*/
|
||||
public static function getPrintableTestCaseName(TestCase $test): string
|
||||
{
|
||||
return $test instanceof HasPrintableTestCaseName
|
||||
? $test->getPrintableTestCaseName()
|
||||
: get_class($test);
|
||||
}
|
||||
}
|
||||
269
vendor/nunomaduro/collision/src/Adapters/Phpunit/Style.php
vendored
Normal file
269
vendor/nunomaduro/collision/src/Adapters/Phpunit/Style.php
vendored
Normal file
@@ -0,0 +1,269 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace NunoMaduro\Collision\Adapters\Phpunit;
|
||||
|
||||
use NunoMaduro\Collision\Exceptions\ShouldNotHappen;
|
||||
use NunoMaduro\Collision\Writer;
|
||||
use PHPUnit\Framework\AssertionFailedError;
|
||||
use PHPUnit\Framework\ExceptionWrapper;
|
||||
use PHPUnit\Framework\ExpectationFailedException;
|
||||
use Symfony\Component\Console\Output\ConsoleOutput;
|
||||
use Symfony\Component\Console\Output\ConsoleOutputInterface;
|
||||
use Throwable;
|
||||
use Whoops\Exception\Inspector;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class Style
|
||||
{
|
||||
/**
|
||||
* @var ConsoleOutput
|
||||
*/
|
||||
private $output;
|
||||
|
||||
/**
|
||||
* Style constructor.
|
||||
*/
|
||||
public function __construct(ConsoleOutputInterface $output)
|
||||
{
|
||||
if (! $output instanceof ConsoleOutput) {
|
||||
throw new ShouldNotHappen();
|
||||
}
|
||||
|
||||
$this->output = $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints the content.
|
||||
*/
|
||||
public function write(string $content): void
|
||||
{
|
||||
$this->output->write($content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints the content similar too:.
|
||||
*
|
||||
* ```
|
||||
* PASS Unit\ExampleTest
|
||||
* ✓ basic test
|
||||
* ```
|
||||
*/
|
||||
public function writeCurrentTestCaseSummary(State $state): void
|
||||
{
|
||||
if ($state->testCaseTestsCount() === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (! $state->headerPrinted) {
|
||||
$this->output->writeln($this->titleLineFrom(
|
||||
$state->getTestCaseTitle() === 'FAIL' ? 'white' : 'black',
|
||||
$state->getTestCaseTitleColor(),
|
||||
$state->getTestCaseTitle(),
|
||||
$state->testCaseName
|
||||
));
|
||||
$state->headerPrinted = true;
|
||||
}
|
||||
|
||||
$state->eachTestCaseTests(function (TestResult $testResult) {
|
||||
$this->output->writeln($this->testLineFrom(
|
||||
$testResult->color,
|
||||
$testResult->icon,
|
||||
$testResult->description,
|
||||
$testResult->warning
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints the content similar too:.
|
||||
*
|
||||
* ```
|
||||
* PASS Unit\ExampleTest
|
||||
* ✓ basic test
|
||||
* ```
|
||||
*/
|
||||
public function writeErrorsSummary(State $state, bool $onFailure): void
|
||||
{
|
||||
$errors = array_filter($state->suiteTests, function (TestResult $testResult) {
|
||||
return $testResult->type === TestResult::FAIL;
|
||||
});
|
||||
|
||||
if (! $onFailure) {
|
||||
$this->output->writeln(['', " \e[2m---\e[22m", '']);
|
||||
}
|
||||
|
||||
array_map(function (TestResult $testResult) use ($onFailure) {
|
||||
if (! $onFailure) {
|
||||
$this->output->write(sprintf(
|
||||
' <fg=red;options=bold>• %s </>> <fg=red;options=bold>%s</>',
|
||||
$testResult->testCaseName,
|
||||
$testResult->description
|
||||
));
|
||||
}
|
||||
|
||||
if (! $testResult->throwable instanceof Throwable) {
|
||||
throw new ShouldNotHappen();
|
||||
}
|
||||
|
||||
$this->writeError($testResult->throwable);
|
||||
}, $errors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the final recap.
|
||||
*/
|
||||
public function writeRecap(State $state, Timer $timer = null): void
|
||||
{
|
||||
$types = [TestResult::FAIL, TestResult::WARN, TestResult::RISKY, TestResult::INCOMPLETE, TestResult::SKIPPED, TestResult::PASS];
|
||||
foreach ($types as $type) {
|
||||
if (($countTests = $state->countTestsInTestSuiteBy($type)) !== 0) {
|
||||
$color = TestResult::makeColor($type);
|
||||
$tests[] = "<fg=$color;options=bold>$countTests $type</>";
|
||||
}
|
||||
}
|
||||
|
||||
$pending = $state->suiteTotalTests - $state->testSuiteTestsCount();
|
||||
if ($pending !== 0) {
|
||||
$tests[] = "\e[2m$pending pending\e[22m";
|
||||
}
|
||||
|
||||
if (! empty($tests)) {
|
||||
$this->output->write([
|
||||
"\n",
|
||||
sprintf(
|
||||
' <fg=white;options=bold>Tests: </><fg=default>%s</>',
|
||||
implode(', ', $tests)
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
if ($timer !== null) {
|
||||
$timeElapsed = number_format($timer->result(), 2, '.', '');
|
||||
$this->output->writeln([
|
||||
'',
|
||||
sprintf(
|
||||
' <fg=white;options=bold>Time: </><fg=default>%ss</>',
|
||||
$timeElapsed
|
||||
),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
$this->output->writeln('');
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a warning message.
|
||||
*/
|
||||
public function writeWarning(string $message): void
|
||||
{
|
||||
$this->output->writeln($this->testLineFrom('yellow', $message, ''));
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the error using Collision's writer
|
||||
* and terminates with exit code === 1.
|
||||
*/
|
||||
public function writeError(Throwable $throwable): void
|
||||
{
|
||||
$writer = (new Writer())->setOutput($this->output);
|
||||
|
||||
if ($throwable instanceof AssertionFailedError) {
|
||||
$writer->showTitle(false);
|
||||
$this->output->write('', true);
|
||||
}
|
||||
|
||||
$writer->ignoreFilesIn([
|
||||
'/vendor\/bin\/pest/',
|
||||
'/bin\/pest/',
|
||||
'/vendor\/pestphp\/pest/',
|
||||
'/vendor\/phpspec\/prophecy-phpunit/',
|
||||
'/vendor\/phpspec\/prophecy/',
|
||||
'/vendor\/phpunit\/phpunit\/src/',
|
||||
'/vendor\/mockery\/mockery/',
|
||||
'/vendor\/laravel\/dusk/',
|
||||
'/vendor\/laravel\/framework\/src\/Illuminate\/Testing/',
|
||||
'/vendor\/laravel\/framework\/src\/Illuminate\/Foundation\/Testing/',
|
||||
'/vendor\/symfony\/framework-bundle\/Test/',
|
||||
'/vendor\/symfony\/phpunit-bridge/',
|
||||
'/vendor\/symfony\/dom-crawler/',
|
||||
'/vendor\/symfony\/browser-kit/',
|
||||
'/vendor\/symfony\/css-selector/',
|
||||
'/vendor\/bin\/.phpunit/',
|
||||
'/bin\/.phpunit/',
|
||||
'/vendor\/bin\/simple-phpunit/',
|
||||
'/bin\/phpunit/',
|
||||
'/vendor\/coduo\/php-matcher\/src\/PHPUnit/',
|
||||
'/vendor\/sulu\/sulu\/src\/Sulu\/Bundle\/TestBundle\/Testing/',
|
||||
'/vendor\/webmozart\/assert/',
|
||||
]);
|
||||
|
||||
if ($throwable instanceof ExceptionWrapper && $throwable->getOriginalException() !== null) {
|
||||
$throwable = $throwable->getOriginalException();
|
||||
}
|
||||
|
||||
$inspector = new Inspector($throwable);
|
||||
|
||||
$writer->write($inspector);
|
||||
|
||||
if ($throwable instanceof ExpectationFailedException && $comparisionFailure = $throwable->getComparisonFailure()) {
|
||||
$diff = $comparisionFailure->getDiff();
|
||||
$lines = explode(PHP_EOL, $diff);
|
||||
$diff = '';
|
||||
foreach ($lines as $line) {
|
||||
if (0 === strpos($line, '-')) {
|
||||
$line = '<fg=red>'.$line.'</>';
|
||||
} elseif (0 === strpos($line, '+')) {
|
||||
$line = '<fg=green>'.$line.'</>';
|
||||
}
|
||||
|
||||
$diff .= $line.PHP_EOL;
|
||||
}
|
||||
|
||||
$diff = trim((string) preg_replace("/\r|\n/", "\n ", $diff));
|
||||
|
||||
$this->output->write(" $diff");
|
||||
}
|
||||
|
||||
$this->output->writeln('');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the title contents.
|
||||
*/
|
||||
private function titleLineFrom(string $fg, string $bg, string $title, string $testCaseName): string
|
||||
{
|
||||
return sprintf(
|
||||
"\n <fg=%s;bg=%s;options=bold> %s </><fg=default> %s</>",
|
||||
$fg,
|
||||
$bg,
|
||||
$title,
|
||||
$testCaseName
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the test contents.
|
||||
*/
|
||||
private function testLineFrom(string $fg, string $icon, string $description, string $warning = null): string
|
||||
{
|
||||
if (! empty($warning)) {
|
||||
$warning = sprintf(
|
||||
' → %s',
|
||||
$warning
|
||||
);
|
||||
}
|
||||
|
||||
return sprintf(
|
||||
" <fg=%s;options=bold>%s</><fg=default> \e[2m%s\e[22m</><fg=yellow>%s</>",
|
||||
$fg,
|
||||
$icon,
|
||||
$description,
|
||||
$warning
|
||||
);
|
||||
}
|
||||
}
|
||||
196
vendor/nunomaduro/collision/src/Adapters/Phpunit/TestResult.php
vendored
Normal file
196
vendor/nunomaduro/collision/src/Adapters/Phpunit/TestResult.php
vendored
Normal file
@@ -0,0 +1,196 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace NunoMaduro\Collision\Adapters\Phpunit;
|
||||
|
||||
use NunoMaduro\Collision\Contracts\Adapters\Phpunit\HasPrintableTestCaseName;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class TestResult
|
||||
{
|
||||
public const FAIL = 'failed';
|
||||
|
||||
public const SKIPPED = 'skipped';
|
||||
|
||||
public const INCOMPLETE = 'incomplete';
|
||||
|
||||
public const RISKY = 'risky';
|
||||
|
||||
public const WARN = 'warnings';
|
||||
|
||||
public const RUNS = 'pending';
|
||||
|
||||
public const PASS = 'passed';
|
||||
|
||||
/**
|
||||
* @readonly
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $testCaseName;
|
||||
|
||||
/**
|
||||
* @readonly
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $description;
|
||||
|
||||
/**
|
||||
* @readonly
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $type;
|
||||
|
||||
/**
|
||||
* @readonly
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $icon;
|
||||
|
||||
/**
|
||||
* @readonly
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $color;
|
||||
|
||||
/**
|
||||
* @readonly
|
||||
*
|
||||
* @var Throwable|null
|
||||
*/
|
||||
public $throwable;
|
||||
|
||||
/**
|
||||
* @readonly
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $warning = '';
|
||||
|
||||
/**
|
||||
* Test constructor.
|
||||
*/
|
||||
private function __construct(string $testCaseName, string $description, string $type, string $icon, string $color, Throwable $throwable = null)
|
||||
{
|
||||
$this->testCaseName = $testCaseName;
|
||||
$this->description = $description;
|
||||
$this->type = $type;
|
||||
$this->icon = $icon;
|
||||
$this->color = $color;
|
||||
$this->throwable = $throwable;
|
||||
|
||||
$asWarning = $this->type === TestResult::WARN
|
||||
|| $this->type === TestResult::RISKY
|
||||
|| $this->type === TestResult::SKIPPED
|
||||
|| $this->type === TestResult::INCOMPLETE;
|
||||
|
||||
if ($throwable instanceof Throwable && $asWarning) {
|
||||
$this->warning = trim((string) preg_replace("/\r|\n/", ' ', $throwable->getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new test from the given test case.
|
||||
*/
|
||||
public static function fromTestCase(TestCase $testCase, string $type, Throwable $throwable = null): self
|
||||
{
|
||||
$testCaseName = State::getPrintableTestCaseName($testCase);
|
||||
|
||||
$description = self::makeDescription($testCase);
|
||||
|
||||
$icon = self::makeIcon($type);
|
||||
|
||||
$color = self::makeColor($type);
|
||||
|
||||
return new self($testCaseName, $description, $type, $icon, $color, $throwable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the test case description.
|
||||
*/
|
||||
public static function makeDescription(TestCase $testCase): string
|
||||
{
|
||||
$name = $testCase->getName(false);
|
||||
|
||||
if ($testCase instanceof HasPrintableTestCaseName) {
|
||||
return $name;
|
||||
}
|
||||
|
||||
// First, lets replace underscore by spaces.
|
||||
$name = str_replace('_', ' ', $name);
|
||||
|
||||
// Then, replace upper cases by spaces.
|
||||
$name = (string) preg_replace('/([A-Z])/', ' $1', $name);
|
||||
|
||||
// Finally, if it starts with `test`, we remove it.
|
||||
$name = (string) preg_replace('/^test/', '', $name);
|
||||
|
||||
// Removes spaces
|
||||
$name = trim($name);
|
||||
|
||||
// Lower case everything
|
||||
$name = mb_strtolower($name);
|
||||
|
||||
// Add the dataset name if it has one
|
||||
if ($dataName = $testCase->dataName()) {
|
||||
if (is_int($dataName)) {
|
||||
$name .= sprintf(' with data set #%d', $dataName);
|
||||
} else {
|
||||
$name .= sprintf(' with data set "%s"', $dataName);
|
||||
}
|
||||
}
|
||||
|
||||
return $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the test case icon.
|
||||
*/
|
||||
public static function makeIcon(string $type): string
|
||||
{
|
||||
switch ($type) {
|
||||
case self::FAIL:
|
||||
return '⨯';
|
||||
case self::SKIPPED:
|
||||
return '-';
|
||||
case self::RISKY:
|
||||
return '!';
|
||||
case self::INCOMPLETE:
|
||||
return '…';
|
||||
case self::WARN:
|
||||
return '!';
|
||||
case self::RUNS:
|
||||
return '•';
|
||||
default:
|
||||
return '✓';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the test case color.
|
||||
*/
|
||||
public static function makeColor(string $type): string
|
||||
{
|
||||
switch ($type) {
|
||||
case self::FAIL:
|
||||
return 'red';
|
||||
case self::SKIPPED:
|
||||
case self::INCOMPLETE:
|
||||
case self::RISKY:
|
||||
case self::WARN:
|
||||
case self::RUNS:
|
||||
return 'yellow';
|
||||
default:
|
||||
return 'green';
|
||||
}
|
||||
}
|
||||
}
|
||||
40
vendor/nunomaduro/collision/src/Adapters/Phpunit/Timer.php
vendored
Normal file
40
vendor/nunomaduro/collision/src/Adapters/Phpunit/Timer.php
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace NunoMaduro\Collision\Adapters\Phpunit;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class Timer
|
||||
{
|
||||
/**
|
||||
* @var float
|
||||
*/
|
||||
private $start;
|
||||
|
||||
/**
|
||||
* Timer constructor.
|
||||
*/
|
||||
private function __construct(float $start)
|
||||
{
|
||||
$this->start = $start;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the timer.
|
||||
*/
|
||||
public static function start(): Timer
|
||||
{
|
||||
return new self(microtime(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the elapsed time in microseconds.
|
||||
*/
|
||||
public function result(): float
|
||||
{
|
||||
return microtime(true) - $this->start;
|
||||
}
|
||||
}
|
||||
45
vendor/nunomaduro/collision/src/ArgumentFormatter.php
vendored
Normal file
45
vendor/nunomaduro/collision/src/ArgumentFormatter.php
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace NunoMaduro\Collision;
|
||||
|
||||
use NunoMaduro\Collision\Contracts\ArgumentFormatter as ArgumentFormatterContract;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @see \Tests\Unit\ArgumentFormatterTest
|
||||
*/
|
||||
final class ArgumentFormatter implements ArgumentFormatterContract
|
||||
{
|
||||
private const MAX_STRING_LENGTH = 1000;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function format(array $arguments, bool $recursive = true): string
|
||||
{
|
||||
$result = [];
|
||||
|
||||
foreach ($arguments as $argument) {
|
||||
switch (true) {
|
||||
case is_string($argument):
|
||||
$result[] = '"'.(mb_strlen($argument) > self::MAX_STRING_LENGTH ? mb_substr($argument, 0, self::MAX_STRING_LENGTH).'...' : $argument).'"';
|
||||
break;
|
||||
case is_array($argument):
|
||||
$associative = array_keys($argument) !== range(0, count($argument) - 1);
|
||||
if ($recursive && $associative && count($argument) <= 5) {
|
||||
$result[] = '['.$this->format($argument, false).']';
|
||||
}
|
||||
break;
|
||||
case is_object($argument):
|
||||
$class = get_class($argument);
|
||||
$result[] = "Object($class)";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return implode(', ', $result);
|
||||
}
|
||||
}
|
||||
292
vendor/nunomaduro/collision/src/ConsoleColor.php
vendored
Normal file
292
vendor/nunomaduro/collision/src/ConsoleColor.php
vendored
Normal file
@@ -0,0 +1,292 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace NunoMaduro\Collision;
|
||||
|
||||
use NunoMaduro\Collision\Exceptions\InvalidStyleException;
|
||||
use NunoMaduro\Collision\Exceptions\ShouldNotHappen;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class ConsoleColor
|
||||
{
|
||||
public const FOREGROUND = 38;
|
||||
|
||||
public const BACKGROUND = 48;
|
||||
|
||||
public const COLOR256_REGEXP = '~^(bg_)?color_(\d{1,3})$~';
|
||||
|
||||
public const RESET_STYLE = 0;
|
||||
|
||||
/** @var bool */
|
||||
private $isSupported;
|
||||
|
||||
/** @var bool */
|
||||
private $forceStyle = false;
|
||||
|
||||
/** @var array */
|
||||
private const STYLES = [
|
||||
'none' => null,
|
||||
'bold' => '1',
|
||||
'dark' => '2',
|
||||
'italic' => '3',
|
||||
'underline' => '4',
|
||||
'blink' => '5',
|
||||
'reverse' => '7',
|
||||
'concealed' => '8',
|
||||
|
||||
'default' => '39',
|
||||
'black' => '30',
|
||||
'red' => '31',
|
||||
'green' => '32',
|
||||
'yellow' => '33',
|
||||
'blue' => '34',
|
||||
'magenta' => '35',
|
||||
'cyan' => '36',
|
||||
'light_gray' => '37',
|
||||
|
||||
'dark_gray' => '90',
|
||||
'light_red' => '91',
|
||||
'light_green' => '92',
|
||||
'light_yellow' => '93',
|
||||
'light_blue' => '94',
|
||||
'light_magenta' => '95',
|
||||
'light_cyan' => '96',
|
||||
'white' => '97',
|
||||
|
||||
'bg_default' => '49',
|
||||
'bg_black' => '40',
|
||||
'bg_red' => '41',
|
||||
'bg_green' => '42',
|
||||
'bg_yellow' => '43',
|
||||
'bg_blue' => '44',
|
||||
'bg_magenta' => '45',
|
||||
'bg_cyan' => '46',
|
||||
'bg_light_gray' => '47',
|
||||
|
||||
'bg_dark_gray' => '100',
|
||||
'bg_light_red' => '101',
|
||||
'bg_light_green' => '102',
|
||||
'bg_light_yellow' => '103',
|
||||
'bg_light_blue' => '104',
|
||||
'bg_light_magenta' => '105',
|
||||
'bg_light_cyan' => '106',
|
||||
'bg_white' => '107',
|
||||
];
|
||||
|
||||
/** @var array */
|
||||
private $themes = [];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->isSupported = $this->isSupported();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|array $style
|
||||
* @param string $text
|
||||
* @return string
|
||||
*
|
||||
* @throws InvalidStyleException
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function apply($style, $text)
|
||||
{
|
||||
if (! $this->isStyleForced() && ! $this->isSupported()) {
|
||||
return $text;
|
||||
}
|
||||
|
||||
if (is_string($style)) {
|
||||
$style = [$style];
|
||||
}
|
||||
if (! is_array($style)) {
|
||||
throw new \InvalidArgumentException('Style must be string or array.');
|
||||
}
|
||||
|
||||
$sequences = [];
|
||||
|
||||
foreach ($style as $s) {
|
||||
if (isset($this->themes[$s])) {
|
||||
$sequences = array_merge($sequences, $this->themeSequence($s));
|
||||
} elseif ($this->isValidStyle($s)) {
|
||||
$sequences[] = $this->styleSequence($s);
|
||||
} else {
|
||||
throw new ShouldNotHappen();
|
||||
}
|
||||
}
|
||||
|
||||
$sequences = array_filter($sequences, function ($val) {
|
||||
return $val !== null;
|
||||
});
|
||||
|
||||
if (empty($sequences)) {
|
||||
return $text;
|
||||
}
|
||||
|
||||
return $this->escSequence(implode(';', $sequences)).$text.$this->escSequence(self::RESET_STYLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $forceStyle
|
||||
*/
|
||||
public function setForceStyle($forceStyle)
|
||||
{
|
||||
$this->forceStyle = $forceStyle;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isStyleForced()
|
||||
{
|
||||
return $this->forceStyle;
|
||||
}
|
||||
|
||||
public function setThemes(array $themes)
|
||||
{
|
||||
$this->themes = [];
|
||||
foreach ($themes as $name => $styles) {
|
||||
$this->addTheme($name, $styles);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param array|string $styles
|
||||
*/
|
||||
public function addTheme($name, $styles)
|
||||
{
|
||||
if (is_string($styles)) {
|
||||
$styles = [$styles];
|
||||
}
|
||||
if (! is_array($styles)) {
|
||||
throw new \InvalidArgumentException('Style must be string or array.');
|
||||
}
|
||||
|
||||
foreach ($styles as $style) {
|
||||
if (! $this->isValidStyle($style)) {
|
||||
throw new InvalidStyleException($style);
|
||||
}
|
||||
}
|
||||
|
||||
$this->themes[$name] = $styles;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getThemes()
|
||||
{
|
||||
return $this->themes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @return bool
|
||||
*/
|
||||
public function hasTheme($name)
|
||||
{
|
||||
return isset($this->themes[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
*/
|
||||
public function removeTheme($name)
|
||||
{
|
||||
unset($this->themes[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isSupported()
|
||||
{
|
||||
// The COLLISION_FORCE_COLORS variable is for internal purposes only
|
||||
if (getenv('COLLISION_FORCE_COLORS') !== false) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (DIRECTORY_SEPARATOR === '\\') {
|
||||
return getenv('ANSICON') !== false || getenv('ConEmuANSI') === 'ON';
|
||||
}
|
||||
|
||||
return function_exists('posix_isatty') && @posix_isatty(STDOUT);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function are256ColorsSupported()
|
||||
{
|
||||
if (DIRECTORY_SEPARATOR === '\\') {
|
||||
return function_exists('sapi_windows_vt100_support') && @sapi_windows_vt100_support(STDOUT);
|
||||
}
|
||||
|
||||
return strpos(getenv('TERM'), '256color') !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getPossibleStyles()
|
||||
{
|
||||
return array_keys(self::STYLES);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @return string[]
|
||||
*/
|
||||
private function themeSequence($name)
|
||||
{
|
||||
$sequences = [];
|
||||
foreach ($this->themes[$name] as $style) {
|
||||
$sequences[] = $this->styleSequence($style);
|
||||
}
|
||||
|
||||
return $sequences;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $style
|
||||
* @return string
|
||||
*/
|
||||
private function styleSequence($style)
|
||||
{
|
||||
if (array_key_exists($style, self::STYLES)) {
|
||||
return self::STYLES[$style];
|
||||
}
|
||||
|
||||
if (! $this->are256ColorsSupported()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
preg_match(self::COLOR256_REGEXP, $style, $matches);
|
||||
|
||||
$type = $matches[1] === 'bg_' ? self::BACKGROUND : self::FOREGROUND;
|
||||
$value = $matches[2];
|
||||
|
||||
return "$type;5;$value";
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $style
|
||||
* @return bool
|
||||
*/
|
||||
private function isValidStyle($style)
|
||||
{
|
||||
return array_key_exists($style, self::STYLES) || preg_match(self::COLOR256_REGEXP, $style);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|int $value
|
||||
* @return string
|
||||
*/
|
||||
private function escSequence($value)
|
||||
{
|
||||
return "\033[{$value}m";
|
||||
}
|
||||
}
|
||||
16
vendor/nunomaduro/collision/src/Contracts/Adapters/Phpunit/HasPrintableTestCaseName.php
vendored
Normal file
16
vendor/nunomaduro/collision/src/Contracts/Adapters/Phpunit/HasPrintableTestCaseName.php
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace NunoMaduro\Collision\Contracts\Adapters\Phpunit;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
interface HasPrintableTestCaseName
|
||||
{
|
||||
/**
|
||||
* Returns the test case name that should be used by the printer.
|
||||
*/
|
||||
public function getPrintableTestCaseName(): string;
|
||||
}
|
||||
22
vendor/nunomaduro/collision/src/Contracts/Adapters/Phpunit/Listener.php
vendored
Normal file
22
vendor/nunomaduro/collision/src/Contracts/Adapters/Phpunit/Listener.php
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace NunoMaduro\Collision\Contracts\Adapters\Phpunit;
|
||||
|
||||
use PHPUnit\Framework\Test;
|
||||
use PHPUnit\Framework\TestListener;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
interface Listener extends TestListener
|
||||
{
|
||||
/**
|
||||
* Renders the provided error
|
||||
* on the console.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function render(Test $test, \Throwable $t);
|
||||
}
|
||||
17
vendor/nunomaduro/collision/src/Contracts/ArgumentFormatter.php
vendored
Normal file
17
vendor/nunomaduro/collision/src/Contracts/ArgumentFormatter.php
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace NunoMaduro\Collision\Contracts;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
interface ArgumentFormatter
|
||||
{
|
||||
/**
|
||||
* Formats the provided array of arguments into
|
||||
* an understandable description.
|
||||
*/
|
||||
public function format(array $arguments, bool $recursive = true): string;
|
||||
}
|
||||
28
vendor/nunomaduro/collision/src/Contracts/Handler.php
vendored
Normal file
28
vendor/nunomaduro/collision/src/Contracts/Handler.php
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace NunoMaduro\Collision\Contracts;
|
||||
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Whoops\Handler\HandlerInterface;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
interface Handler extends HandlerInterface
|
||||
{
|
||||
/**
|
||||
* Sets the output.
|
||||
*
|
||||
* @return \NunoMaduro\Collision\Contracts\Handler
|
||||
*/
|
||||
public function setOutput(OutputInterface $output): Handler;
|
||||
|
||||
/**
|
||||
* Returns the writer.
|
||||
*
|
||||
* @return \NunoMaduro\Collision\Contracts\Writer
|
||||
*/
|
||||
public function getWriter(): Writer;
|
||||
}
|
||||
16
vendor/nunomaduro/collision/src/Contracts/Highlighter.php
vendored
Normal file
16
vendor/nunomaduro/collision/src/Contracts/Highlighter.php
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace NunoMaduro\Collision\Contracts;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
interface Highlighter
|
||||
{
|
||||
/**
|
||||
* Highlights the provided content.
|
||||
*/
|
||||
public function highlight(string $content, int $line): string;
|
||||
}
|
||||
25
vendor/nunomaduro/collision/src/Contracts/Provider.php
vendored
Normal file
25
vendor/nunomaduro/collision/src/Contracts/Provider.php
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace NunoMaduro\Collision\Contracts;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
interface Provider
|
||||
{
|
||||
/**
|
||||
* Registers the current Handler as Error Handler.
|
||||
*
|
||||
* @return \NunoMaduro\Collision\Contracts\Provider
|
||||
*/
|
||||
public function register(): Provider;
|
||||
|
||||
/**
|
||||
* Returns the handler.
|
||||
*
|
||||
* @return \NunoMaduro\Collision\Contracts\Handler
|
||||
*/
|
||||
public function getHandler(): Handler;
|
||||
}
|
||||
12
vendor/nunomaduro/collision/src/Contracts/RenderlessEditor.php
vendored
Normal file
12
vendor/nunomaduro/collision/src/Contracts/RenderlessEditor.php
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace NunoMaduro\Collision\Contracts;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
interface RenderlessEditor
|
||||
{
|
||||
}
|
||||
12
vendor/nunomaduro/collision/src/Contracts/RenderlessTrace.php
vendored
Normal file
12
vendor/nunomaduro/collision/src/Contracts/RenderlessTrace.php
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace NunoMaduro\Collision\Contracts;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
interface RenderlessTrace
|
||||
{
|
||||
}
|
||||
21
vendor/nunomaduro/collision/src/Contracts/SolutionsRepository.php
vendored
Normal file
21
vendor/nunomaduro/collision/src/Contracts/SolutionsRepository.php
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace NunoMaduro\Collision\Contracts;
|
||||
|
||||
use Spatie\Ignition\Contracts\Solution;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
interface SolutionsRepository
|
||||
{
|
||||
/**
|
||||
* Gets the solutions from the given `$throwable`.
|
||||
*
|
||||
* @return array<int, Solution>
|
||||
*/
|
||||
public function getFromThrowable(Throwable $throwable): array;
|
||||
}
|
||||
70
vendor/nunomaduro/collision/src/Contracts/Writer.php
vendored
Normal file
70
vendor/nunomaduro/collision/src/Contracts/Writer.php
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* This file is part of Collision.
|
||||
*
|
||||
* (c) Nuno Maduro <enunomaduro@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace NunoMaduro\Collision\Contracts;
|
||||
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Whoops\Exception\Inspector;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
interface Writer
|
||||
{
|
||||
/**
|
||||
* Ignores traces where the file string matches one
|
||||
* of the provided regex expressions.
|
||||
*
|
||||
* @param string[] $ignore the regex expressions
|
||||
* @return \NunoMaduro\Collision\Contracts\Writer
|
||||
*/
|
||||
public function ignoreFilesIn(array $ignore): Writer;
|
||||
|
||||
/**
|
||||
* Declares whether or not the Writer should show the trace.
|
||||
*
|
||||
* @return \NunoMaduro\Collision\Contracts\Writer
|
||||
*/
|
||||
public function showTrace(bool $show): Writer;
|
||||
|
||||
/**
|
||||
* Declares whether or not the Writer should show the title.
|
||||
*
|
||||
* @return \NunoMaduro\Collision\Contracts\Writer
|
||||
*/
|
||||
public function showTitle(bool $show): Writer;
|
||||
|
||||
/**
|
||||
* Declares whether or not the Writer should show the editor.
|
||||
*
|
||||
* @return \NunoMaduro\Collision\Contracts\Writer
|
||||
*/
|
||||
public function showEditor(bool $show): Writer;
|
||||
|
||||
/**
|
||||
* Writes the details of the exception on the console.
|
||||
*/
|
||||
public function write(Inspector $inspector): void;
|
||||
|
||||
/**
|
||||
* Sets the output.
|
||||
*
|
||||
* @return \NunoMaduro\Collision\Contracts\Writer
|
||||
*/
|
||||
public function setOutput(OutputInterface $output): Writer;
|
||||
|
||||
/**
|
||||
* Gets the output.
|
||||
*/
|
||||
public function getOutput(): OutputInterface;
|
||||
}
|
||||
201
vendor/nunomaduro/collision/src/Coverage.php
vendored
Normal file
201
vendor/nunomaduro/collision/src/Coverage.php
vendored
Normal file
@@ -0,0 +1,201 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace NunoMaduro\Collision;
|
||||
|
||||
use SebastianBergmann\CodeCoverage\CodeCoverage;
|
||||
use SebastianBergmann\CodeCoverage\Node\Directory;
|
||||
use SebastianBergmann\CodeCoverage\Node\File;
|
||||
use SebastianBergmann\Environment\Runtime;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Terminal;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class Coverage
|
||||
{
|
||||
/**
|
||||
* Returns the coverage path.
|
||||
*/
|
||||
public static function getPath(): string
|
||||
{
|
||||
return implode(DIRECTORY_SEPARATOR, [
|
||||
dirname(__DIR__),
|
||||
'.temp',
|
||||
'coverage',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs true there is any code coverage driver available.
|
||||
*/
|
||||
public static function isAvailable(): bool
|
||||
{
|
||||
if (! (new Runtime())->canCollectCodeCoverage()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (static::usingXdebug()) {
|
||||
$mode = getenv('XDEBUG_MODE') ?: ini_get('xdebug.mode');
|
||||
|
||||
return $mode && in_array('coverage', explode(',', $mode), true);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the user is using Xdebug.
|
||||
*/
|
||||
public static function usingXdebug(): bool
|
||||
{
|
||||
return (new Runtime())->hasXdebug();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports the code coverage report to the
|
||||
* console and returns the result in float.
|
||||
*/
|
||||
public static function report(OutputInterface $output): float
|
||||
{
|
||||
if (! file_exists($reportPath = self::getPath())) {
|
||||
if (self::usingXdebug()) {
|
||||
$output->writeln(
|
||||
" <fg=black;bg=yellow;options=bold> WARN </> Unable to get coverage using Xdebug. Did you set <href=https://xdebug.org/docs/code_coverage#mode>Xdebug's coverage mode</>?</>",
|
||||
);
|
||||
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
$output->writeln(
|
||||
' <fg=black;bg=yellow;options=bold> WARN </> No coverage driver detected.</>',
|
||||
);
|
||||
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
/** @var CodeCoverage $codeCoverage */
|
||||
$codeCoverage = require $reportPath;
|
||||
unlink($reportPath);
|
||||
|
||||
$totalCoverage = $codeCoverage->getReport()->percentageOfExecutedLines();
|
||||
|
||||
$totalWidth = (new Terminal())->getWidth();
|
||||
|
||||
$dottedLineLength = $totalWidth;
|
||||
|
||||
/** @var Directory<File|Directory> $report */
|
||||
$report = $codeCoverage->getReport();
|
||||
|
||||
foreach ($report->getIterator() as $file) {
|
||||
if (! $file instanceof File) {
|
||||
continue;
|
||||
}
|
||||
$dirname = dirname($file->id());
|
||||
$basename = basename($file->id(), '.php');
|
||||
|
||||
$name = $dirname === '.' ? $basename : implode(DIRECTORY_SEPARATOR, [
|
||||
$dirname,
|
||||
$basename,
|
||||
]);
|
||||
$rawName = $dirname === '.' ? $basename : implode(DIRECTORY_SEPARATOR, [
|
||||
$dirname,
|
||||
$basename,
|
||||
]);
|
||||
|
||||
$linesExecutedTakenSize = 0;
|
||||
|
||||
if ($file->percentageOfExecutedLines()->asString() != '0.00%') {
|
||||
$linesExecutedTakenSize = strlen($uncoveredLines = trim(implode(', ', self::getMissingCoverage($file)))) + 1;
|
||||
$name .= sprintf(' <fg=red>%s</>', $uncoveredLines);
|
||||
}
|
||||
|
||||
$percentage = $file->numberOfExecutableLines() === 0
|
||||
? '100.0'
|
||||
: number_format($file->percentageOfExecutedLines()->asFloat(), 1, '.', '');
|
||||
|
||||
$takenSize = strlen($rawName.$percentage) + 8 + $linesExecutedTakenSize; // adding 3 space and percent sign
|
||||
|
||||
$percentage = sprintf(
|
||||
'<fg=%s%s>%s</>',
|
||||
$percentage === '100.0' ? 'green' : ($percentage === '0.0' ? 'red' : 'yellow'),
|
||||
$percentage === '100.0' ? ';options=bold' : '',
|
||||
$percentage
|
||||
);
|
||||
|
||||
$output->writeln(sprintf(
|
||||
' <fg=white>%s</> <fg=#6C7280>%s</> %s <fg=#6C7280>%%</>',
|
||||
$name,
|
||||
str_repeat('.', max($dottedLineLength - $takenSize, 1)),
|
||||
$percentage
|
||||
));
|
||||
}
|
||||
|
||||
$output->writeln('');
|
||||
|
||||
$rawName = 'Total Coverage';
|
||||
|
||||
$takenSize = strlen($rawName.$totalCoverage->asString()) + 6;
|
||||
|
||||
$output->writeln(sprintf(
|
||||
' <fg=white;options=bold>%s</> <fg=#6C7280>%s</> %s <fg=#6C7280>%%</>',
|
||||
$rawName,
|
||||
str_repeat('.', max($dottedLineLength - $takenSize, 1)),
|
||||
number_format($totalCoverage->asFloat(), 1, '.', '')
|
||||
));
|
||||
|
||||
return $totalCoverage->asFloat();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an array of missing coverage on the following format:.
|
||||
*
|
||||
* ```
|
||||
* ['11', '20..25', '50', '60..80'];
|
||||
* ```
|
||||
*
|
||||
* @param File $file
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public static function getMissingCoverage($file): array
|
||||
{
|
||||
$shouldBeNewLine = true;
|
||||
|
||||
$eachLine = function (array $array, array $tests, int $line) use (&$shouldBeNewLine): array {
|
||||
if (count($tests) > 0) {
|
||||
$shouldBeNewLine = true;
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
if ($shouldBeNewLine) {
|
||||
$array[] = (string) $line;
|
||||
$shouldBeNewLine = false;
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
$lastKey = count($array) - 1;
|
||||
|
||||
if (array_key_exists($lastKey, $array) && str_contains($array[$lastKey], '..')) {
|
||||
[$from] = explode('..', $array[$lastKey]);
|
||||
$array[$lastKey] = $line > $from ? sprintf('%s..%s', $from, $line) : sprintf('%s..%s', $line, $from);
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
$array[$lastKey] = sprintf('%s..%s', $array[$lastKey], $line);
|
||||
|
||||
return $array;
|
||||
};
|
||||
|
||||
$array = [];
|
||||
foreach (array_filter($file->lineCoverageData(), 'is_array') as $line => $tests) {
|
||||
$array = $eachLine($array, $tests, $line);
|
||||
}
|
||||
|
||||
return $array;
|
||||
}
|
||||
}
|
||||
14
vendor/nunomaduro/collision/src/Exceptions/InvalidStyleException.php
vendored
Normal file
14
vendor/nunomaduro/collision/src/Exceptions/InvalidStyleException.php
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace NunoMaduro\Collision\Exceptions;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class InvalidStyleException extends RuntimeException
|
||||
{
|
||||
}
|
||||
23
vendor/nunomaduro/collision/src/Exceptions/ShouldNotHappen.php
vendored
Normal file
23
vendor/nunomaduro/collision/src/Exceptions/ShouldNotHappen.php
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace NunoMaduro\Collision\Exceptions;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class ShouldNotHappen extends RuntimeException
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private const MESSAGE = 'This should not happen, please open an issue on collision repository: %s';
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct(sprintf(self::MESSAGE, 'https://github.com/nunomaduro/collision/issues/new'));
|
||||
}
|
||||
}
|
||||
61
vendor/nunomaduro/collision/src/Handler.php
vendored
Normal file
61
vendor/nunomaduro/collision/src/Handler.php
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace NunoMaduro\Collision;
|
||||
|
||||
use NunoMaduro\Collision\Contracts\Handler as HandlerContract;
|
||||
use NunoMaduro\Collision\Contracts\Writer as WriterContract;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Whoops\Handler\Handler as AbstractHandler;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @see \Tests\Unit\HandlerTest
|
||||
*/
|
||||
final class Handler extends AbstractHandler implements HandlerContract
|
||||
{
|
||||
/**
|
||||
* Holds an instance of the writer.
|
||||
*
|
||||
* @var \NunoMaduro\Collision\Contracts\Writer
|
||||
*/
|
||||
protected $writer;
|
||||
|
||||
/**
|
||||
* Creates an instance of the Handler.
|
||||
*/
|
||||
public function __construct(WriterContract $writer = null)
|
||||
{
|
||||
$this->writer = $writer ?: new Writer();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->writer->write($this->getInspector());
|
||||
|
||||
return static::QUIT;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setOutput(OutputInterface $output): HandlerContract
|
||||
{
|
||||
$this->writer->setOutput($output);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getWriter(): WriterContract
|
||||
{
|
||||
return $this->writer;
|
||||
}
|
||||
}
|
||||
316
vendor/nunomaduro/collision/src/Highlighter.php
vendored
Normal file
316
vendor/nunomaduro/collision/src/Highlighter.php
vendored
Normal file
@@ -0,0 +1,316 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace NunoMaduro\Collision;
|
||||
|
||||
use NunoMaduro\Collision\Contracts\Highlighter as HighlighterContract;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class Highlighter implements HighlighterContract
|
||||
{
|
||||
public const TOKEN_DEFAULT = 'token_default';
|
||||
|
||||
public const TOKEN_COMMENT = 'token_comment';
|
||||
|
||||
public const TOKEN_STRING = 'token_string';
|
||||
|
||||
public const TOKEN_HTML = 'token_html';
|
||||
|
||||
public const TOKEN_KEYWORD = 'token_keyword';
|
||||
|
||||
public const ACTUAL_LINE_MARK = 'actual_line_mark';
|
||||
|
||||
public const LINE_NUMBER = 'line_number';
|
||||
|
||||
private const ARROW_SYMBOL = '>';
|
||||
|
||||
private const DELIMITER = '|';
|
||||
|
||||
private const ARROW_SYMBOL_UTF8 = '➜';
|
||||
|
||||
private const DELIMITER_UTF8 = '▕'; // '▶';
|
||||
|
||||
private const LINE_NUMBER_DIVIDER = 'line_divider';
|
||||
|
||||
private const MARKED_LINE_NUMBER = 'marked_line';
|
||||
|
||||
private const WIDTH = 3;
|
||||
|
||||
/**
|
||||
* Holds the theme.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private const THEME = [
|
||||
self::TOKEN_STRING => ['light_gray'],
|
||||
self::TOKEN_COMMENT => ['dark_gray', 'italic'],
|
||||
self::TOKEN_KEYWORD => ['magenta', 'bold'],
|
||||
self::TOKEN_DEFAULT => ['default', 'bold'],
|
||||
self::TOKEN_HTML => ['blue', 'bold'],
|
||||
|
||||
self::ACTUAL_LINE_MARK => ['red', 'bold'],
|
||||
self::LINE_NUMBER => ['dark_gray'],
|
||||
self::MARKED_LINE_NUMBER => ['italic', 'bold'],
|
||||
self::LINE_NUMBER_DIVIDER => ['dark_gray'],
|
||||
];
|
||||
|
||||
/** @var ConsoleColor */
|
||||
private $color;
|
||||
|
||||
/** @var array */
|
||||
private const DEFAULT_THEME = [
|
||||
self::TOKEN_STRING => 'red',
|
||||
self::TOKEN_COMMENT => 'yellow',
|
||||
self::TOKEN_KEYWORD => 'green',
|
||||
self::TOKEN_DEFAULT => 'default',
|
||||
self::TOKEN_HTML => 'cyan',
|
||||
|
||||
self::ACTUAL_LINE_MARK => 'dark_gray',
|
||||
self::LINE_NUMBER => 'dark_gray',
|
||||
self::MARKED_LINE_NUMBER => 'dark_gray',
|
||||
self::LINE_NUMBER_DIVIDER => 'dark_gray',
|
||||
];
|
||||
|
||||
/** @var string */
|
||||
private $delimiter = self::DELIMITER_UTF8;
|
||||
|
||||
/** @var string */
|
||||
private $arrow = self::ARROW_SYMBOL_UTF8;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private const NO_MARK = ' ';
|
||||
|
||||
/**
|
||||
* Creates an instance of the Highlighter.
|
||||
*/
|
||||
public function __construct(ConsoleColor $color = null, bool $UTF8 = true)
|
||||
{
|
||||
$this->color = $color ?: new ConsoleColor();
|
||||
|
||||
foreach (self::DEFAULT_THEME as $name => $styles) {
|
||||
if (! $this->color->hasTheme($name)) {
|
||||
$this->color->addTheme($name, $styles);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (self::THEME as $name => $styles) {
|
||||
$this->color->addTheme($name, $styles);
|
||||
}
|
||||
if (! $UTF8) {
|
||||
$this->delimiter = self::DELIMITER;
|
||||
$this->arrow = self::ARROW_SYMBOL;
|
||||
}
|
||||
$this->delimiter .= ' ';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function highlight(string $content, int $line): string
|
||||
{
|
||||
return rtrim($this->getCodeSnippet($content, $line, 4, 4));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $source
|
||||
* @param int $lineNumber
|
||||
* @param int $linesBefore
|
||||
* @param int $linesAfter
|
||||
*/
|
||||
public function getCodeSnippet($source, $lineNumber, $linesBefore = 2, $linesAfter = 2): string
|
||||
{
|
||||
$tokenLines = $this->getHighlightedLines($source);
|
||||
|
||||
$offset = $lineNumber - $linesBefore - 1;
|
||||
$offset = max($offset, 0);
|
||||
$length = $linesAfter + $linesBefore + 1;
|
||||
$tokenLines = array_slice($tokenLines, $offset, $length, $preserveKeys = true);
|
||||
|
||||
$lines = $this->colorLines($tokenLines);
|
||||
|
||||
return $this->lineNumbers($lines, $lineNumber);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $source
|
||||
*/
|
||||
private function getHighlightedLines($source): array
|
||||
{
|
||||
$source = str_replace(["\r\n", "\r"], "\n", $source);
|
||||
$tokens = $this->tokenize($source);
|
||||
|
||||
return $this->splitToLines($tokens);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $source
|
||||
*/
|
||||
private function tokenize($source): array
|
||||
{
|
||||
$tokens = token_get_all($source);
|
||||
|
||||
$output = [];
|
||||
$currentType = null;
|
||||
$buffer = '';
|
||||
|
||||
foreach ($tokens as $token) {
|
||||
if (is_array($token)) {
|
||||
switch ($token[0]) {
|
||||
case T_WHITESPACE:
|
||||
break;
|
||||
|
||||
case T_OPEN_TAG:
|
||||
case T_OPEN_TAG_WITH_ECHO:
|
||||
case T_CLOSE_TAG:
|
||||
case T_STRING:
|
||||
case T_VARIABLE:
|
||||
// Constants
|
||||
case T_DIR:
|
||||
case T_FILE:
|
||||
case T_METHOD_C:
|
||||
case T_DNUMBER:
|
||||
case T_LNUMBER:
|
||||
case T_NS_C:
|
||||
case T_LINE:
|
||||
case T_CLASS_C:
|
||||
case T_FUNC_C:
|
||||
case T_TRAIT_C:
|
||||
$newType = self::TOKEN_DEFAULT;
|
||||
break;
|
||||
|
||||
case T_COMMENT:
|
||||
case T_DOC_COMMENT:
|
||||
$newType = self::TOKEN_COMMENT;
|
||||
break;
|
||||
|
||||
case T_ENCAPSED_AND_WHITESPACE:
|
||||
case T_CONSTANT_ENCAPSED_STRING:
|
||||
$newType = self::TOKEN_STRING;
|
||||
break;
|
||||
|
||||
case T_INLINE_HTML:
|
||||
$newType = self::TOKEN_HTML;
|
||||
break;
|
||||
|
||||
default:
|
||||
$newType = self::TOKEN_KEYWORD;
|
||||
}
|
||||
} else {
|
||||
$newType = $token === '"' ? self::TOKEN_STRING : self::TOKEN_KEYWORD;
|
||||
}
|
||||
|
||||
if ($currentType === null) {
|
||||
$currentType = $newType;
|
||||
}
|
||||
|
||||
if ($currentType !== $newType) {
|
||||
$output[] = [$currentType, $buffer];
|
||||
$buffer = '';
|
||||
$currentType = $newType;
|
||||
}
|
||||
|
||||
$buffer .= is_array($token) ? $token[1] : $token;
|
||||
}
|
||||
|
||||
if (isset($newType)) {
|
||||
$output[] = [$newType, $buffer];
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
private function splitToLines(array $tokens): array
|
||||
{
|
||||
$lines = [];
|
||||
|
||||
$line = [];
|
||||
foreach ($tokens as $token) {
|
||||
foreach (explode("\n", $token[1]) as $count => $tokenLine) {
|
||||
if ($count > 0) {
|
||||
$lines[] = $line;
|
||||
$line = [];
|
||||
}
|
||||
|
||||
if ($tokenLine === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$line[] = [$token[0], $tokenLine];
|
||||
}
|
||||
}
|
||||
|
||||
$lines[] = $line;
|
||||
|
||||
return $lines;
|
||||
}
|
||||
|
||||
private function colorLines(array $tokenLines): array
|
||||
{
|
||||
$lines = [];
|
||||
foreach ($tokenLines as $lineCount => $tokenLine) {
|
||||
$line = '';
|
||||
foreach ($tokenLine as $token) {
|
||||
[$tokenType, $tokenValue] = $token;
|
||||
if ($this->color->hasTheme($tokenType)) {
|
||||
$line .= $this->color->apply($tokenType, $tokenValue);
|
||||
} else {
|
||||
$line .= $tokenValue;
|
||||
}
|
||||
}
|
||||
$lines[$lineCount] = $line;
|
||||
}
|
||||
|
||||
return $lines;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int|null $markLine
|
||||
*/
|
||||
private function lineNumbers(array $lines, $markLine = null): string
|
||||
{
|
||||
$lineStrlen = strlen((string) (array_key_last($lines) + 1));
|
||||
$lineStrlen = $lineStrlen < self::WIDTH ? self::WIDTH : $lineStrlen;
|
||||
$snippet = '';
|
||||
$mark = ' '.$this->arrow.' ';
|
||||
foreach ($lines as $i => $line) {
|
||||
$coloredLineNumber = $this->coloredLineNumber(self::LINE_NUMBER, $i, $lineStrlen);
|
||||
|
||||
if (null !== $markLine) {
|
||||
$snippet .=
|
||||
($markLine === $i + 1
|
||||
? $this->color->apply(self::ACTUAL_LINE_MARK, $mark)
|
||||
: self::NO_MARK
|
||||
);
|
||||
|
||||
$coloredLineNumber =
|
||||
($markLine === $i + 1 ?
|
||||
$this->coloredLineNumber(self::MARKED_LINE_NUMBER, $i, $lineStrlen) :
|
||||
$coloredLineNumber
|
||||
);
|
||||
}
|
||||
$snippet .= $coloredLineNumber;
|
||||
|
||||
$snippet .=
|
||||
$this->color->apply(self::LINE_NUMBER_DIVIDER, $this->delimiter);
|
||||
|
||||
$snippet .= $line.PHP_EOL;
|
||||
}
|
||||
|
||||
return $snippet;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $style
|
||||
* @param int $i
|
||||
* @param int $lineStrlen
|
||||
*/
|
||||
private function coloredLineNumber($style, $i, $lineStrlen): string
|
||||
{
|
||||
return $this->color->apply($style, str_pad((string) ($i + 1), $lineStrlen, ' ', STR_PAD_LEFT));
|
||||
}
|
||||
}
|
||||
60
vendor/nunomaduro/collision/src/Provider.php
vendored
Normal file
60
vendor/nunomaduro/collision/src/Provider.php
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace NunoMaduro\Collision;
|
||||
|
||||
use NunoMaduro\Collision\Contracts\Handler as HandlerContract;
|
||||
use NunoMaduro\Collision\Contracts\Provider as ProviderContract;
|
||||
use Whoops\Run;
|
||||
use Whoops\RunInterface;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @see \Tests\Unit\ProviderTest
|
||||
*/
|
||||
final class Provider implements ProviderContract
|
||||
{
|
||||
/**
|
||||
* Holds an instance of the Run.
|
||||
*
|
||||
* @var \Whoops\RunInterface
|
||||
*/
|
||||
protected $run;
|
||||
|
||||
/**
|
||||
* Holds an instance of the handler.
|
||||
*
|
||||
* @var \NunoMaduro\Collision\Contracts\Handler
|
||||
*/
|
||||
protected $handler;
|
||||
|
||||
/**
|
||||
* Creates a new instance of the Provider.
|
||||
*/
|
||||
public function __construct(RunInterface $run = null, HandlerContract $handler = null)
|
||||
{
|
||||
$this->run = $run ?: new Run();
|
||||
$this->handler = $handler ?: new Handler();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function register(): ProviderContract
|
||||
{
|
||||
$this->run->pushHandler($this->handler)
|
||||
->register();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getHandler(): HandlerContract
|
||||
{
|
||||
return $this->handler;
|
||||
}
|
||||
}
|
||||
22
vendor/nunomaduro/collision/src/SolutionsRepositories/NullSolutionsRepository.php
vendored
Normal file
22
vendor/nunomaduro/collision/src/SolutionsRepositories/NullSolutionsRepository.php
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace NunoMaduro\Collision\SolutionsRepositories;
|
||||
|
||||
use NunoMaduro\Collision\Contracts\SolutionsRepository;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class NullSolutionsRepository implements SolutionsRepository
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFromThrowable(Throwable $throwable): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
358
vendor/nunomaduro/collision/src/Writer.php
vendored
Normal file
358
vendor/nunomaduro/collision/src/Writer.php
vendored
Normal file
@@ -0,0 +1,358 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace NunoMaduro\Collision;
|
||||
|
||||
use NunoMaduro\Collision\Contracts\ArgumentFormatter as ArgumentFormatterContract;
|
||||
use NunoMaduro\Collision\Contracts\Highlighter as HighlighterContract;
|
||||
use NunoMaduro\Collision\Contracts\RenderlessEditor;
|
||||
use NunoMaduro\Collision\Contracts\RenderlessTrace;
|
||||
use NunoMaduro\Collision\Contracts\SolutionsRepository;
|
||||
use NunoMaduro\Collision\Contracts\Writer as WriterContract;
|
||||
use NunoMaduro\Collision\SolutionsRepositories\NullSolutionsRepository;
|
||||
use Symfony\Component\Console\Output\ConsoleOutput;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Whoops\Exception\Frame;
|
||||
use Whoops\Exception\Inspector;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @see \Tests\Unit\WriterTest
|
||||
*/
|
||||
final class Writer implements WriterContract
|
||||
{
|
||||
/**
|
||||
* The number of frames if no verbosity is specified.
|
||||
*/
|
||||
public const VERBOSITY_NORMAL_FRAMES = 1;
|
||||
|
||||
/**
|
||||
* Holds an instance of the solutions repository.
|
||||
*
|
||||
* @var \NunoMaduro\Collision\Contracts\SolutionsRepository
|
||||
*/
|
||||
private $solutionsRepository;
|
||||
|
||||
/**
|
||||
* Holds an instance of the Output.
|
||||
*
|
||||
* @var \Symfony\Component\Console\Output\OutputInterface
|
||||
*/
|
||||
protected $output;
|
||||
|
||||
/**
|
||||
* Holds an instance of the Argument Formatter.
|
||||
*
|
||||
* @var \NunoMaduro\Collision\Contracts\ArgumentFormatter
|
||||
*/
|
||||
protected $argumentFormatter;
|
||||
|
||||
/**
|
||||
* Holds an instance of the Highlighter.
|
||||
*
|
||||
* @var \NunoMaduro\Collision\Contracts\Highlighter
|
||||
*/
|
||||
protected $highlighter;
|
||||
|
||||
/**
|
||||
* Ignores traces where the file string matches one
|
||||
* of the provided regex expressions.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $ignore = [];
|
||||
|
||||
/**
|
||||
* Declares whether or not the trace should appear.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $showTrace = true;
|
||||
|
||||
/**
|
||||
* Declares whether or not the title should appear.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $showTitle = true;
|
||||
|
||||
/**
|
||||
* Declares whether or not the editor should appear.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $showEditor = true;
|
||||
|
||||
/**
|
||||
* Creates an instance of the writer.
|
||||
*/
|
||||
public function __construct(
|
||||
SolutionsRepository $solutionsRepository = null,
|
||||
OutputInterface $output = null,
|
||||
ArgumentFormatterContract $argumentFormatter = null,
|
||||
HighlighterContract $highlighter = null
|
||||
) {
|
||||
$this->solutionsRepository = $solutionsRepository ?: new NullSolutionsRepository();
|
||||
$this->output = $output ?: new ConsoleOutput();
|
||||
$this->argumentFormatter = $argumentFormatter ?: new ArgumentFormatter();
|
||||
$this->highlighter = $highlighter ?: new Highlighter();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function write(Inspector $inspector): void
|
||||
{
|
||||
$this->renderTitleAndDescription($inspector);
|
||||
|
||||
$frames = $this->getFrames($inspector);
|
||||
|
||||
$editorFrame = array_shift($frames);
|
||||
|
||||
$exception = $inspector->getException();
|
||||
|
||||
if ($this->showEditor
|
||||
&& $editorFrame !== null
|
||||
&& ! $exception instanceof RenderlessEditor
|
||||
) {
|
||||
$this->renderEditor($editorFrame);
|
||||
}
|
||||
|
||||
$this->renderSolution($inspector);
|
||||
|
||||
if ($this->showTrace && ! empty($frames) && ! $exception instanceof RenderlessTrace) {
|
||||
$this->renderTrace($frames);
|
||||
} elseif (! $exception instanceof RenderlessEditor) {
|
||||
$this->output->writeln('');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function ignoreFilesIn(array $ignore): WriterContract
|
||||
{
|
||||
$this->ignore = $ignore;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function showTrace(bool $show): WriterContract
|
||||
{
|
||||
$this->showTrace = $show;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function showTitle(bool $show): WriterContract
|
||||
{
|
||||
$this->showTitle = $show;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function showEditor(bool $show): WriterContract
|
||||
{
|
||||
$this->showEditor = $show;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setOutput(OutputInterface $output): WriterContract
|
||||
{
|
||||
$this->output = $output;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getOutput(): OutputInterface
|
||||
{
|
||||
return $this->output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns pertinent frames.
|
||||
*/
|
||||
protected function getFrames(Inspector $inspector): array
|
||||
{
|
||||
return $inspector->getFrames()
|
||||
->filter(
|
||||
function ($frame) {
|
||||
// If we are in verbose mode, we always
|
||||
// display the full stack trace.
|
||||
if ($this->output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach ($this->ignore as $ignore) {
|
||||
// Ensure paths are linux-style (like the ones on $this->ignore)
|
||||
// @phpstan-ignore-next-line
|
||||
$sanitizedPath = (string) str_replace('\\', '/', $frame->getFile());
|
||||
if (preg_match($ignore, $sanitizedPath)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
)
|
||||
->getArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the title of the exception.
|
||||
*/
|
||||
protected function renderTitleAndDescription(Inspector $inspector): WriterContract
|
||||
{
|
||||
$exception = $inspector->getException();
|
||||
$message = rtrim($exception->getMessage());
|
||||
$class = $inspector->getExceptionName();
|
||||
|
||||
if ($this->showTitle) {
|
||||
$this->render("<bg=red;options=bold> $class </>");
|
||||
$this->output->writeln('');
|
||||
}
|
||||
|
||||
$this->output->writeln("<fg=default;options=bold> $message</>");
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the solution of the exception, if any.
|
||||
*/
|
||||
protected function renderSolution(Inspector $inspector): WriterContract
|
||||
{
|
||||
$throwable = $inspector->getException();
|
||||
$solutions = $this->solutionsRepository->getFromThrowable($throwable);
|
||||
|
||||
foreach ($solutions as $solution) {
|
||||
/** @var \Spatie\Ignition\Contracts\Solution $solution */
|
||||
$title = $solution->getSolutionTitle();
|
||||
$description = $solution->getSolutionDescription();
|
||||
$links = $solution->getDocumentationLinks();
|
||||
|
||||
$description = trim((string) preg_replace("/\n/", "\n ", $description));
|
||||
|
||||
$this->render(sprintf(
|
||||
'<fg=cyan;options=bold>i</> <fg=default;options=bold>%s</>: %s %s',
|
||||
rtrim($title, '.'),
|
||||
$description,
|
||||
implode(', ', array_map(function (string $link) {
|
||||
return sprintf("\n <fg=gray>%s</>", $link);
|
||||
}, $links))
|
||||
));
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the editor containing the code that was the
|
||||
* origin of the exception.
|
||||
*/
|
||||
protected function renderEditor(Frame $frame): WriterContract
|
||||
{
|
||||
if ($frame->getFile() !== 'Unknown') {
|
||||
$file = $this->getFileRelativePath((string) $frame->getFile());
|
||||
|
||||
// getLine() might return null so cast to int to get 0 instead
|
||||
$line = (int) $frame->getLine();
|
||||
$this->render('at <fg=green>'.$file.'</>'.':<fg=green>'.$line.'</>');
|
||||
|
||||
$content = $this->highlighter->highlight((string) $frame->getFileContents(), (int) $frame->getLine());
|
||||
|
||||
$this->output->writeln($content);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the trace of the exception.
|
||||
*/
|
||||
protected function renderTrace(array $frames): WriterContract
|
||||
{
|
||||
$vendorFrames = 0;
|
||||
$userFrames = 0;
|
||||
foreach ($frames as $i => $frame) {
|
||||
if ($this->output->getVerbosity() < OutputInterface::VERBOSITY_VERBOSE && strpos($frame->getFile(), '/vendor/') !== false) {
|
||||
$vendorFrames++;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($userFrames > static::VERBOSITY_NORMAL_FRAMES && $this->output->getVerbosity() < OutputInterface::VERBOSITY_VERBOSE) {
|
||||
break;
|
||||
}
|
||||
|
||||
$userFrames++;
|
||||
|
||||
$file = $this->getFileRelativePath($frame->getFile());
|
||||
$line = $frame->getLine();
|
||||
$class = empty($frame->getClass()) ? '' : $frame->getClass().'::';
|
||||
$function = $frame->getFunction();
|
||||
$args = $this->argumentFormatter->format($frame->getArgs());
|
||||
$pos = str_pad((string) ((int) $i + 1), 4, ' ');
|
||||
|
||||
if ($vendorFrames > 0) {
|
||||
$this->output->write(
|
||||
sprintf("\n \e[2m+%s vendor frames \e[22m", $vendorFrames)
|
||||
);
|
||||
$vendorFrames = 0;
|
||||
}
|
||||
|
||||
$this->render("<fg=yellow>$pos</><fg=default;options=bold>$file</>:<fg=default;options=bold>$line</>");
|
||||
$this->render("<fg=gray> $class$function($args)</>", false);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders an message into the console.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
protected function render(string $message, bool $break = true): WriterContract
|
||||
{
|
||||
if ($break) {
|
||||
$this->output->writeln('');
|
||||
}
|
||||
|
||||
$this->output->writeln(" $message");
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the relative path of the given file path.
|
||||
*/
|
||||
protected function getFileRelativePath(string $filePath): string
|
||||
{
|
||||
$cwd = (string) getcwd();
|
||||
|
||||
if (! empty($cwd)) {
|
||||
return str_replace("$cwd/", '', $filePath);
|
||||
}
|
||||
|
||||
return $filePath;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user