upgraded dependencies
This commit is contained in:
5
vendor/nunomaduro/collision/.github/FUNDING.yml
vendored
Normal file
5
vendor/nunomaduro/collision/.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: nunomaduro
|
||||
patreon: nunomaduro
|
||||
custom: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=66BYDWAT92N6L
|
@@ -1,38 +0,0 @@
|
||||
name: Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
php: [7.2, 7.3, 7.4, 8.0]
|
||||
dependency-version: [prefer-stable]
|
||||
|
||||
name: PHP ${{ matrix.php }} ${{ matrix.dependency-version }}
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php }}
|
||||
tools: composer
|
||||
coverage: none
|
||||
|
||||
- name: Setup Problem Matchers
|
||||
run: |
|
||||
echo "::add-matcher::${{ runner.tool_cache }}/php.json"
|
||||
echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json"
|
||||
|
||||
- name: Install PHP dependencies
|
||||
run: composer update --${{ matrix.dependency-version }} --no-interaction --prefer-dist --no-progress --ansi
|
||||
|
||||
- name: Run Unit Tests
|
||||
run: vendor/bin/phpunit --colors=always
|
29
vendor/nunomaduro/collision/.php_cs
vendored
Normal file
29
vendor/nunomaduro/collision/.php_cs
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
$finder = PhpCsFixer\Finder::create()
|
||||
->in(__DIR__ . DIRECTORY_SEPARATOR . 'tests')
|
||||
->notPath(__DIR__ . DIRECTORY_SEPARATOR . 'tests' . DIRECTORY_SEPARATOR . 'laravel')
|
||||
->in(__DIR__ . DIRECTORY_SEPARATOR . 'src')
|
||||
->append(['.php_cs']);
|
||||
|
||||
$rules = [
|
||||
'@Symfony' => true,
|
||||
'phpdoc_no_empty_return' => false,
|
||||
'array_syntax' => ['syntax' => 'short'],
|
||||
'yoda_style' => false,
|
||||
'binary_operator_spaces' => [
|
||||
'operators' => [
|
||||
'=>' => 'align',
|
||||
'=' => 'align',
|
||||
],
|
||||
],
|
||||
'concat_space' => ['spacing' => 'one'],
|
||||
'not_operator_with_space' => false,
|
||||
];
|
||||
|
||||
$rules['increment_style'] = ['style' => 'post'];
|
||||
|
||||
return PhpCsFixer\Config::create()
|
||||
->setUsingCache(true)
|
||||
->setRules($rules)
|
||||
->setFinder($finder);
|
36
vendor/nunomaduro/collision/README.md
vendored
36
vendor/nunomaduro/collision/README.md
vendored
@@ -7,23 +7,22 @@
|
||||
<p align="center">
|
||||
<a href="https://travis-ci.org/nunomaduro/collision"><img src="https://img.shields.io/travis/nunomaduro/collision/stable.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://scrutinizer-ci.com/g/nunomaduro/collision"><img src="https://img.shields.io/scrutinizer/coverage/g/nunomaduro/collision.svg" alt="Coverage"></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>
|
||||
|
||||
## About Collision
|
||||
---
|
||||
|
||||
Collision was created by, and is maintained by [Nuno Maduro](https://github.com/nunomaduro), and is an error handler framework for console/command-line PHP applications.
|
||||
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.
|
||||
|
||||
- Build on top of [Whoops](https://github.com/filp/whoops).
|
||||
- Supports [Laravel](https://github.com/laravel/laravel) Artisan & [PHPUnit](https://github.com/sebastianbergmann/phpunit).
|
||||
- Built with [PHP 7](https://php.net) using modern coding standards.
|
||||
* 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 7.1+](https://php.net/releases/)**
|
||||
> **Requires [PHP 7.2.5+](https://php.net/releases/)**
|
||||
|
||||
Require Collision using [Composer](https://getcomposer.org):
|
||||
|
||||
@@ -31,12 +30,6 @@ Require Collision using [Composer](https://getcomposer.org):
|
||||
composer require nunomaduro/collision --dev
|
||||
```
|
||||
|
||||
If you are not using Laravel, you need to register the handler in your code:
|
||||
|
||||
```php
|
||||
(new \NunoMaduro\Collision\Provider)->register();
|
||||
```
|
||||
|
||||
## Lumen adapter
|
||||
|
||||
Configure the Collision service provider:
|
||||
@@ -47,14 +40,21 @@ $app->register(\NunoMaduro\Collision\Adapters\Laravel\CollisionServiceProvider::
|
||||
|
||||
## Phpunit adapter
|
||||
|
||||
Phpunit must be 7.0 or higher.
|
||||
Phpunit must be 8.5.1 or higher.
|
||||
|
||||
Add the following configuration to your `phpunit.xml`:
|
||||
Add the Collision `printerClass` to your `phpunit.xml` in the `phpunit` section:
|
||||
|
||||
```xml
|
||||
<listeners>
|
||||
<listener class="NunoMaduro\Collision\Adapters\Phpunit\Listener" />
|
||||
</listeners>
|
||||
<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
|
||||
|
34
vendor/nunomaduro/collision/composer.json
vendored
34
vendor/nunomaduro/collision/composer.json
vendored
@@ -15,17 +15,28 @@
|
||||
],
|
||||
"require": {
|
||||
"php": "^7.2.5 || ^8.0",
|
||||
"filp/whoops": "^2.1.4",
|
||||
"symfony/console": "~2.8|~3.3|~4.0",
|
||||
"php-parallel-lint/php-console-highlighter": "0.5.*"
|
||||
"facade/ignition-contracts": "^1.0",
|
||||
"filp/whoops": "^2.4",
|
||||
"symfony/console": "^5.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"laravel/framework": "^6.0",
|
||||
"phpunit/phpunit": "^8.0 || ^9.0"
|
||||
"friendsofphp/php-cs-fixer": "^2.16",
|
||||
"facade/ignition": "^2.0",
|
||||
"fideloper/proxy": "^4.2",
|
||||
"fruitcake/laravel-cors": "^1.0",
|
||||
"laravel/framework": "^7.0",
|
||||
"laravel/tinker": "^2.0",
|
||||
"nunomaduro/larastan": "^0.6",
|
||||
"orchestra/testbench": "^5.0",
|
||||
"phpstan/phpstan": "^0.12.3",
|
||||
"phpunit/phpunit": "^8.5.1 || ^9.0"
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Tests\\": "tests/"
|
||||
"Tests\\Unit\\": "tests/Unit",
|
||||
"Tests\\FakeProgram\\": "tests/FakeProgram",
|
||||
"Tests\\": "tests/LaravelApp/tests",
|
||||
"App\\": "tests/LaravelApp/app/"
|
||||
}
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
@@ -45,5 +56,16 @@
|
||||
"NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider"
|
||||
]
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "php-cs-fixer fix -v",
|
||||
"test:types": "phpstan analyse --ansi",
|
||||
"test:unit": "phpunit --colors=always",
|
||||
"test:lint": "php-cs-fixer fix -v --dry-run",
|
||||
"test": [
|
||||
"@test:lint",
|
||||
"@test:unit",
|
||||
"@test:types"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
19
vendor/nunomaduro/collision/phpstan.neon.dist
vendored
Normal file
19
vendor/nunomaduro/collision/phpstan.neon.dist
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
includes:
|
||||
- ./vendor/nunomaduro/larastan/extension.neon
|
||||
parameters:
|
||||
level: max
|
||||
paths:
|
||||
- src
|
||||
checkMissingIterableValueType: false
|
||||
reportUnmatchedIgnoredErrors: false
|
||||
excludes_analyse:
|
||||
- src/Adapters/Phpunit/Printer
|
||||
ignoreErrors:
|
||||
- '#Parameter \#1 \$input of function str_pad expects string, int given.#'
|
||||
- '#Cannot call method addTheme\(\) on array|JakubOnderka\\PhpConsoleColor\\ConsoleColor#'
|
||||
- '#Method NunoMaduro\\Collision\\Adapters\\Laravel\\IgnitionSolutionsRepository::getFromThrowable\(\) should return array<int,#'
|
||||
- '#Result of static method Dotenv\\Repository\\RepositoryBuilder::create\(\) \(void\) is used.#'
|
||||
- message: '#Cannot call method make\(\) on void.#'
|
||||
paths:
|
||||
- src/Adapters/Laravel/Commands/*
|
||||
|
2
vendor/nunomaduro/collision/phpunit.xml.dist
vendored
2
vendor/nunomaduro/collision/phpunit.xml.dist
vendored
@@ -17,7 +17,7 @@
|
||||
>
|
||||
<testsuites>
|
||||
<testsuite name="Collision Test Suite">
|
||||
<directory suffix="Test.php">./tests</directory>
|
||||
<directory suffix="Test.php">./tests/Unit</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<filter>
|
||||
|
@@ -11,12 +11,14 @@
|
||||
|
||||
namespace NunoMaduro\Collision\Adapters\Laravel;
|
||||
|
||||
use NunoMaduro\Collision\Provider;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use NunoMaduro\Collision\Adapters\Phpunit\Listener;
|
||||
use NunoMaduro\Collision\Contracts\Provider as ProviderContract;
|
||||
use Illuminate\Contracts\Debug\ExceptionHandler as ExceptionHandlerContract;
|
||||
use NunoMaduro\Collision\Contracts\Adapters\Phpunit\Listener as ListenerContract;
|
||||
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;
|
||||
|
||||
/**
|
||||
* This is an Collision Laravel Adapter Service Provider implementation.
|
||||
@@ -29,17 +31,43 @@ 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->singleton(ListenerContract::class, Listener::class);
|
||||
$this->app->bind(ProviderContract::class, Provider::class);
|
||||
if ($this->app->runningInConsole() && !$this->app->runningUnitTests()) {
|
||||
$this->app->bind(ProviderContract::class, function () {
|
||||
if ($this->app->has(\Facade\IgnitionContracts\SolutionProviderRepository::class)) {
|
||||
$solutionsRepository = new IgnitionSolutionsRepository(
|
||||
$this->app->get(\Facade\IgnitionContracts\SolutionProviderRepository::class)
|
||||
);
|
||||
} else {
|
||||
$solutionsRepository = new NullSolutionsRepository();
|
||||
}
|
||||
|
||||
$writer = new Writer($solutionsRepository);
|
||||
$handler = new Handler($writer);
|
||||
|
||||
return new Provider(null, $handler);
|
||||
});
|
||||
|
||||
$appExceptionHandler = $this->app->make(ExceptionHandlerContract::class);
|
||||
|
||||
|
143
vendor/nunomaduro/collision/src/Adapters/Laravel/Commands/TestCommand.php
vendored
Normal file
143
vendor/nunomaduro/collision/src/Adapters/Laravel/Commands/TestCommand.php
vendored
Normal file
@@ -0,0 +1,143 @@
|
||||
<?php
|
||||
|
||||
namespace NunoMaduro\Collision\Adapters\Laravel\Commands;
|
||||
|
||||
use Dotenv\Dotenv;
|
||||
use Dotenv\Repository\RepositoryBuilder;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Str;
|
||||
use RuntimeException;
|
||||
use Symfony\Component\Process\Exception\ProcessSignaledException;
|
||||
use Symfony\Component\Process\Process;
|
||||
|
||||
class TestCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'test {--without-tty : Disable output to TTY}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Run the application tests';
|
||||
|
||||
/**
|
||||
* The arguments to be used while calling phpunit.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $arguments = [
|
||||
'--printer',
|
||||
'NunoMaduro\Collision\Adapters\Phpunit\Printer',
|
||||
];
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->ignoreValidationErrors();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$options = array_slice($_SERVER['argv'], $this->option('without-tty') ? 3 : 2);
|
||||
|
||||
$this->clearEnv();
|
||||
|
||||
$process = (new Process(array_merge(
|
||||
$this->binary(),
|
||||
array_merge(
|
||||
$this->arguments,
|
||||
$this->phpunitArguments($options)
|
||||
)
|
||||
)))->setTimeout(null);
|
||||
|
||||
try {
|
||||
$process->setTty(!$this->option('without-tty'));
|
||||
} catch (RuntimeException $e) {
|
||||
$this->output->writeln('Warning: ' . $e->getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
return $process->run(function ($type, $line) {
|
||||
$this->output->write($line);
|
||||
});
|
||||
} catch (ProcessSignaledException $e) {
|
||||
if (extension_loaded('pcntl') && $e->getSignal() !== SIGINT) {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the PHP binary to execute.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function binary()
|
||||
{
|
||||
if ('phpdbg' === PHP_SAPI) {
|
||||
return [PHP_BINARY, '-qrr', 'vendor/phpunit/phpunit/phpunit'];
|
||||
}
|
||||
|
||||
return [PHP_BINARY, 'vendor/phpunit/phpunit/phpunit'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the array of arguments for running PHPUnit.
|
||||
*
|
||||
* @param array $options
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function phpunitArguments($options)
|
||||
{
|
||||
$options = array_values(array_filter($options, function ($option) {
|
||||
return !Str::startsWith($option, '--env=');
|
||||
}));
|
||||
|
||||
if (!file_exists($file = base_path('phpunit.xml'))) {
|
||||
$file = base_path('phpunit.xml.dist');
|
||||
}
|
||||
|
||||
return array_merge(['-c', $file], $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears any set Environment variables set by Laravel if the --env option is empty.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function clearEnv()
|
||||
{
|
||||
if (!$this->option('env')) {
|
||||
$repositories = RepositoryBuilder::create()
|
||||
->make();
|
||||
|
||||
$envs = Dotenv::create(
|
||||
$repositories,
|
||||
$this->laravel->environmentPath(),
|
||||
$this->laravel->environmentFile()
|
||||
)->safeLoad();
|
||||
|
||||
foreach ($envs as $name => $value) {
|
||||
$repositories->clear($name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -11,11 +11,11 @@
|
||||
|
||||
namespace NunoMaduro\Collision\Adapters\Laravel;
|
||||
|
||||
use Exception;
|
||||
use Illuminate\Contracts\Container\Container;
|
||||
use NunoMaduro\Collision\Contracts\Provider as ProviderContract;
|
||||
use Illuminate\Contracts\Debug\ExceptionHandler as ExceptionHandlerContract;
|
||||
use NunoMaduro\Collision\Contracts\Provider as ProviderContract;
|
||||
use Symfony\Component\Console\Exception\ExceptionInterface as SymfonyConsoleExceptionInterface;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* This is an Collision Laravel Adapter ExceptionHandler implementation.
|
||||
@@ -42,20 +42,17 @@ class ExceptionHandler implements ExceptionHandlerContract
|
||||
|
||||
/**
|
||||
* Creates a new instance of the ExceptionHandler.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Container\Container $container
|
||||
* @param \Illuminate\Contracts\Debug\ExceptionHandler $appExceptionHandler
|
||||
*/
|
||||
public function __construct(Container $container, ExceptionHandlerContract $appExceptionHandler)
|
||||
{
|
||||
$this->container = $container;
|
||||
$this->container = $container;
|
||||
$this->appExceptionHandler = $appExceptionHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function report(Exception $e)
|
||||
public function report(Throwable $e)
|
||||
{
|
||||
$this->appExceptionHandler->report($e);
|
||||
}
|
||||
@@ -63,7 +60,7 @@ class ExceptionHandler implements ExceptionHandlerContract
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function render($request, Exception $e)
|
||||
public function render($request, Throwable $e)
|
||||
{
|
||||
return $this->appExceptionHandler->render($request, $e);
|
||||
}
|
||||
@@ -71,7 +68,7 @@ class ExceptionHandler implements ExceptionHandlerContract
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function renderForConsole($output, Exception $e)
|
||||
public function renderForConsole($output, Throwable $e)
|
||||
{
|
||||
if ($e instanceof SymfonyConsoleExceptionInterface) {
|
||||
$this->appExceptionHandler->renderForConsole($output, $e);
|
||||
@@ -90,10 +87,9 @@ class ExceptionHandler implements ExceptionHandlerContract
|
||||
/**
|
||||
* Determine if the exception should be reported.
|
||||
*
|
||||
* @param \Exception $e
|
||||
* @return bool
|
||||
*/
|
||||
public function shouldReport(Exception $e)
|
||||
public function shouldReport(Throwable $e)
|
||||
{
|
||||
return $this->appExceptionHandler->shouldReport($e);
|
||||
}
|
||||
|
49
vendor/nunomaduro/collision/src/Adapters/Laravel/IgnitionSolutionsRepository.php
vendored
Normal file
49
vendor/nunomaduro/collision/src/Adapters/Laravel/IgnitionSolutionsRepository.php
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* 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 Facade\IgnitionContracts\SolutionProviderRepository;
|
||||
use NunoMaduro\Collision\Contracts\SolutionsRepository;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* This is an Collision Laravel Adapter Solutions Provider implementation.
|
||||
*
|
||||
* Registers the Error Handler on Laravel.
|
||||
*
|
||||
* @author Nuno Maduro <enunomaduro@gmail.com>
|
||||
*/
|
||||
class IgnitionSolutionsRepository implements SolutionsRepository
|
||||
{
|
||||
/**
|
||||
* Holds an instance of ignition solutions provider repository.
|
||||
*
|
||||
* @var \Facade\IgnitionContracts\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);
|
||||
}
|
||||
}
|
38
vendor/nunomaduro/collision/src/Adapters/Phpunit/ConfigureIO.php
vendored
Normal file
38
vendor/nunomaduro/collision/src/Adapters/Phpunit/ConfigureIO.php
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* 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 enviroment.
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
}
|
@@ -1,177 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* 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 PHPUnit\Framework\Test;
|
||||
use PHPUnit\Framework\Warning;
|
||||
use Whoops\Exception\Inspector;
|
||||
use NunoMaduro\Collision\Writer;
|
||||
use PHPUnit\Framework\TestSuite;
|
||||
use Symfony\Component\Console\Application;
|
||||
use PHPUnit\Framework\AssertionFailedError;
|
||||
use Symfony\Component\Console\Input\ArgvInput;
|
||||
use Symfony\Component\Console\Output\ConsoleOutput;
|
||||
use NunoMaduro\Collision\Contracts\Writer as WriterContract;
|
||||
use NunoMaduro\Collision\Contracts\Adapters\Phpunit\Listener as ListenerContract;
|
||||
|
||||
if (class_exists(\PHPUnit\Runner\Version::class) && intval(substr(\PHPUnit\Runner\Version::id(), 0, 1)) >= 7) {
|
||||
|
||||
/**
|
||||
* This is an Collision Phpunit Adapter implementation.
|
||||
*
|
||||
* @author Nuno Maduro <enunomaduro@gmail.com>
|
||||
*/
|
||||
class Listener implements ListenerContract
|
||||
{
|
||||
/**
|
||||
* Holds an instance of the writer.
|
||||
*
|
||||
* @var \NunoMaduro\Collision\Contracts\Writer
|
||||
*/
|
||||
protected $writer;
|
||||
|
||||
/**
|
||||
* Holds the exception found, if any.
|
||||
*
|
||||
* @var \Throwable|null
|
||||
*/
|
||||
protected $exceptionFound;
|
||||
|
||||
/**
|
||||
* Creates a new instance of the class.
|
||||
*
|
||||
* @param \NunoMaduro\Collision\Contracts\Writer|null $writer
|
||||
*/
|
||||
public function __construct(WriterContract $writer = null)
|
||||
{
|
||||
$this->writer = $writer ?: $this->buildWriter();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function render(\Throwable $t)
|
||||
{
|
||||
$inspector = new Inspector($t);
|
||||
|
||||
$this->writer->write($inspector);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addError(Test $test, \Throwable $t, float $time): void
|
||||
{
|
||||
if ($this->exceptionFound === null) {
|
||||
$this->exceptionFound = $t;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addWarning(Test $test, Warning $t, float $time): void
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addFailure(Test $test, AssertionFailedError $t, float $time): void
|
||||
{
|
||||
$this->writer->ignoreFilesIn(['/vendor/'])
|
||||
->showTrace(false);
|
||||
|
||||
if ($this->exceptionFound === null) {
|
||||
$this->exceptionFound = $t;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addIncompleteTest(Test $test, \Throwable $t, float $time): void
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addRiskyTest(Test $test, \Throwable $t, float $time): void
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addSkippedTest(Test $test, \Throwable $t, float $time): void
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function startTestSuite(TestSuite $suite): void
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function endTestSuite(TestSuite $suite): void
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function startTest(Test $test): void
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function endTest(Test $test, float $time): void
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
if ($this->exceptionFound !== null) {
|
||||
$this->render($this->exceptionFound);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds an Writer.
|
||||
*
|
||||
* @return \NunoMaduro\Collision\Contracts\Writer
|
||||
*/
|
||||
protected function buildWriter(): WriterContract
|
||||
{
|
||||
$writer = new Writer;
|
||||
|
||||
$application = new Application();
|
||||
$reflector = new ReflectionObject($application);
|
||||
$method = $reflector->getMethod('configureIO');
|
||||
$method->setAccessible(true);
|
||||
$method->invoke($application, new ArgvInput, $output = new ConsoleOutput);
|
||||
|
||||
return $writer->setOutput($output);
|
||||
}
|
||||
}
|
||||
}
|
56
vendor/nunomaduro/collision/src/Adapters/Phpunit/Printer.php
vendored
Normal file
56
vendor/nunomaduro/collision/src/Adapters/Phpunit/Printer.php
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/*
|
||||
* This `if` condition exists because phpunit
|
||||
* is not a direct dependency of Collision.
|
||||
*
|
||||
* This code bellow it's for phpunit@8
|
||||
*/
|
||||
if (class_exists(\PHPUnit\Runner\Version::class) && intval(substr(\PHPUnit\Runner\Version::id(), 0, 1)) === 8) {
|
||||
/**
|
||||
* This is an Collision Phpunit Adapter implementation.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class Printer extends \PHPUnit\Util\Printer implements \PHPUnit\Framework\TestListener
|
||||
{
|
||||
use PrinterContents;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This `if` condition exists because phpunit
|
||||
* is not a direct dependency of Collision.
|
||||
*
|
||||
* This code bellow it's for phpunit@9
|
||||
*/
|
||||
if (class_exists(\PHPUnit\Runner\Version::class) && intval(substr(\PHPUnit\Runner\Version::id(), 0, 1)) === 9) {
|
||||
/**
|
||||
* This is an Collision Phpunit Adapter implementation.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class Printer implements \PHPUnit\TextUI\ResultPrinter
|
||||
{
|
||||
use PrinterContents;
|
||||
|
||||
/**
|
||||
* Intentionally left blank as we output things on events of the listener.
|
||||
*/
|
||||
public function printResult(\PHPUnit\Framework\TestResult $result): void
|
||||
{
|
||||
// ..
|
||||
}
|
||||
}
|
||||
}
|
224
vendor/nunomaduro/collision/src/Adapters/Phpunit/PrinterContents.php
vendored
Normal file
224
vendor/nunomaduro/collision/src/Adapters/Phpunit/PrinterContents.php
vendored
Normal file
@@ -0,0 +1,224 @@
|
||||
<?php
|
||||
|
||||
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;
|
||||
|
||||
trait PrinterContents
|
||||
{
|
||||
/**
|
||||
* 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 ended before.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $ended = false;
|
||||
|
||||
/**
|
||||
* Creates a new instance of the listener.
|
||||
*
|
||||
* @param ConsoleOutput $output
|
||||
*
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
public function __construct(ConsoleOutput $output = null)
|
||||
{
|
||||
if (intval(substr(\PHPUnit\Runner\Version::id(), 0, 1)) === 8) {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
$this->timer = Timer::start();
|
||||
|
||||
$output = $output ?? new ConsoleOutput();
|
||||
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
|
||||
{
|
||||
$testCase = $this->testCaseFromTest($testCase);
|
||||
|
||||
$this->state->add(TestResult::fromTestCase($testCase, TestResult::FAIL));
|
||||
|
||||
$this->style->writeError($this->state, $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->getMessage()));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addFailure(Test $testCase, AssertionFailedError $error, float $time): void
|
||||
{
|
||||
$testCase = $this->testCaseFromTest($testCase);
|
||||
|
||||
$this->state->add(TestResult::fromTestCase($testCase, TestResult::FAIL));
|
||||
|
||||
$reflector = new ReflectionObject($error);
|
||||
|
||||
if ($reflector->hasProperty('message')) {
|
||||
$message = trim((string) preg_replace("/\r|\n/", ' ', $error->getMessage()));
|
||||
$property = $reflector->getProperty('message');
|
||||
$property->setAccessible(true);
|
||||
$property->setValue($error, $message);
|
||||
}
|
||||
|
||||
$this->style->writeError($this->state, $error);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addIncompleteTest(Test $testCase, Throwable $t, float $time): void
|
||||
{
|
||||
$testCase = $this->testCaseFromTest($testCase);
|
||||
|
||||
$this->state->add(TestResult::fromTestCase($testCase, TestResult::INCOMPLETE));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addRiskyTest(Test $testCase, Throwable $t, float $time): void
|
||||
{
|
||||
$testCase = $this->testCaseFromTest($testCase);
|
||||
|
||||
$this->state->add(TestResult::fromTestCase($testCase, TestResult::RISKY, $t->getMessage()));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addSkippedTest(Test $testCase, Throwable $t, float $time): void
|
||||
{
|
||||
$testCase = $this->testCaseFromTest($testCase);
|
||||
|
||||
$this->state->add(TestResult::fromTestCase($testCase, TestResult::SKIPPED, $t->getMessage()));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function startTestSuite(TestSuite $suite): void
|
||||
{
|
||||
if ($this->state->suiteTotalTests === null) {
|
||||
$this->state->suiteTotalTests = $suite->count();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function endTestSuite(TestSuite $suite): void
|
||||
{
|
||||
if (!$this->ended && $this->state->suiteTotalTests === $this->state->testSuiteTestsCount()) {
|
||||
$this->ended = true;
|
||||
|
||||
$this->style->writeCurrentRecap($this->state);
|
||||
|
||||
$this->style->updateFooter($this->state);
|
||||
$this->style->writeRecap($this->timer);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@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->writeCurrentRecap($this->state);
|
||||
|
||||
$this->state->moveTo($testCase);
|
||||
}
|
||||
|
||||
$this->style->updateFooter($this->state, $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));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
194
vendor/nunomaduro/collision/src/Adapters/Phpunit/State.php
vendored
Normal file
194
vendor/nunomaduro/collision/src/Adapters/Phpunit/State.php
vendored
Normal file
@@ -0,0 +1,194 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* 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 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 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->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 = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Foreach test in the test case.
|
||||
*/
|
||||
public function eachTestCaseTests(callable $callback): void
|
||||
{
|
||||
foreach ($this->testCaseTests as $test) {
|
||||
$callback($test);
|
||||
}
|
||||
}
|
||||
|
||||
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`.
|
||||
*/
|
||||
private static function getPrintableTestCaseName(TestCase $test): string
|
||||
{
|
||||
if ($test instanceof HasPrintableTestCaseName) {
|
||||
$name = $test->getPrintableTestCaseName();
|
||||
} else {
|
||||
$name = get_class($test);
|
||||
}
|
||||
|
||||
return $name;
|
||||
}
|
||||
}
|
239
vendor/nunomaduro/collision/src/Adapters/Phpunit/Style.php
vendored
Normal file
239
vendor/nunomaduro/collision/src/Adapters/Phpunit/Style.php
vendored
Normal file
@@ -0,0 +1,239 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* 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 NunoMaduro\Collision\Writer;
|
||||
use PHPUnit\Framework\AssertionFailedError;
|
||||
use PHPUnit\Framework\ExceptionWrapper;
|
||||
use PHPUnit\Framework\ExpectationFailedException;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\Console\Output\ConsoleOutput;
|
||||
use Symfony\Component\Console\Output\ConsoleSectionOutput;
|
||||
use Throwable;
|
||||
use Whoops\Exception\Inspector;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class Style
|
||||
{
|
||||
/**
|
||||
* @var ConsoleOutput
|
||||
*/
|
||||
private $output;
|
||||
|
||||
/**
|
||||
* @var ConsoleSectionOutput
|
||||
*/
|
||||
private $footer;
|
||||
|
||||
/**
|
||||
* Style constructor.
|
||||
*/
|
||||
public function __construct(ConsoleOutput $output)
|
||||
{
|
||||
$this->output = $output;
|
||||
|
||||
$this->footer = $output->section();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints the content similar too:.
|
||||
*
|
||||
* ```
|
||||
* PASS Unit\ExampleTest
|
||||
* ✓ basic test
|
||||
* ```
|
||||
*/
|
||||
public function writeCurrentRecap(State $state): void
|
||||
{
|
||||
if (!$state->testCaseTestsCount()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->footer->clear();
|
||||
|
||||
$this->output->writeln($this->titleLineFrom(
|
||||
$state->getTestCaseTitle() === 'FAIL' ? 'white' : 'black',
|
||||
$state->getTestCaseTitleColor(),
|
||||
$state->getTestCaseTitle(),
|
||||
$state->testCaseName
|
||||
));
|
||||
|
||||
$state->eachTestCaseTests(function (TestResult $testResult) {
|
||||
$this->output->writeln($this->testLineFrom(
|
||||
$testResult->color,
|
||||
$testResult->icon,
|
||||
$testResult->description,
|
||||
$testResult->warning
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints the content similar too on the footer. Where
|
||||
* we are updating the current test.
|
||||
*
|
||||
* ```
|
||||
* Runs Unit\ExampleTest
|
||||
* • basic test
|
||||
* ```
|
||||
*/
|
||||
public function updateFooter(State $state, TestCase $testCase = null): void
|
||||
{
|
||||
$runs = [];
|
||||
|
||||
if ($testCase) {
|
||||
$runs[] = $this->titleLineFrom(
|
||||
'black',
|
||||
'yellow',
|
||||
'RUNS',
|
||||
get_class($testCase)
|
||||
);
|
||||
|
||||
$testResult = TestResult::fromTestCase($testCase, TestResult::RUNS);
|
||||
$runs[] = $this->testLineFrom(
|
||||
$testResult->color,
|
||||
$testResult->icon,
|
||||
$testResult->description
|
||||
);
|
||||
}
|
||||
|
||||
$types = [TestResult::FAIL, TestResult::WARN, TestResult::RISKY, TestResult::INCOMPLETE, TestResult::SKIPPED, TestResult::PASS];
|
||||
|
||||
foreach ($types as $type) {
|
||||
if ($countTests = $state->countTestsInTestSuiteBy($type)) {
|
||||
$color = TestResult::makeColor($type);
|
||||
$tests[] = "<fg=$color;options=bold>$countTests $type</>";
|
||||
}
|
||||
}
|
||||
|
||||
$pending = $state->suiteTotalTests - $state->testSuiteTestsCount();
|
||||
if ($pending) {
|
||||
$tests[] = "\e[2m$pending pending\e[22m";
|
||||
}
|
||||
|
||||
if (!empty($tests)) {
|
||||
$this->footer->overwrite(array_merge($runs, [
|
||||
'',
|
||||
sprintf(
|
||||
' <fg=white;options=bold>Tests: </><fg=default>%s</>',
|
||||
implode(', ', $tests)
|
||||
),
|
||||
]));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the final recap.
|
||||
*/
|
||||
public function writeRecap(Timer $timer): void
|
||||
{
|
||||
$timeElapsed = number_format($timer->result(), 2, '.', '');
|
||||
$this->footer->writeln(
|
||||
sprintf(
|
||||
' <fg=white;options=bold>Time: </><fg=default>%ss</>',
|
||||
$timeElapsed
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the error using Collision's writer
|
||||
* and terminates with exit code === 1.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function writeError(State $state, Throwable $throwable)
|
||||
{
|
||||
$this->writeCurrentRecap($state);
|
||||
|
||||
$this->updateFooter($state);
|
||||
|
||||
$writer = (new Writer())->setOutput($this->output);
|
||||
|
||||
if ($throwable instanceof AssertionFailedError) {
|
||||
$writer->showTitle(false);
|
||||
$this->output->write('', true);
|
||||
}
|
||||
|
||||
$writer->ignoreFilesIn([
|
||||
'/vendor\/phpunit\/phpunit\/src/',
|
||||
'/vendor\/mockery\/mockery/',
|
||||
'/vendor\/laravel\/framework\/src\/Illuminate\/Testing/',
|
||||
'/vendor\/laravel\/framework\/src\/Illuminate\/Foundation\/Testing/',
|
||||
]);
|
||||
|
||||
if ($throwable instanceof ExceptionWrapper && $throwable->getOriginalException() !== null) {
|
||||
$throwable = $throwable->getOriginalException();
|
||||
}
|
||||
|
||||
$inspector = new Inspector($throwable);
|
||||
|
||||
$writer->write($inspector);
|
||||
|
||||
if ($throwable instanceof ExpectationFailedException && $comparisionFailure = $throwable->getComparisonFailure()) {
|
||||
$this->output->write($comparisionFailure->getDiff());
|
||||
}
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the title contents.
|
||||
*/
|
||||
private function titleLineFrom(string $fg, string $bg, string $title, string $testCaseName): string
|
||||
{
|
||||
if (class_exists($testCaseName)) {
|
||||
$nameParts = explode('\\', $testCaseName);
|
||||
$highlightedPart = array_pop($nameParts);
|
||||
$nonHighlightedPart = implode('\\', $nameParts);
|
||||
$testCaseName = sprintf("\e[2m%s\e[22m<fg=white;options=bold>%s</>", "$nonHighlightedPart\\", $highlightedPart);
|
||||
} elseif (file_exists($testCaseName)) {
|
||||
$testCaseName = substr($testCaseName, strlen((string) getcwd()) + 1);
|
||||
$nameParts = explode(DIRECTORY_SEPARATOR, $testCaseName);
|
||||
$highlightedPart = (string) array_pop($nameParts);
|
||||
$highlightedPart = substr($highlightedPart, 0, (int) strrpos($highlightedPart, '.'));
|
||||
$nonHighlightedPart = implode('\\', $nameParts);
|
||||
$testCaseName = sprintf("\e[2m%s\e[22m<fg=white;options=bold>%s</>", "$nonHighlightedPart\\", $highlightedPart);
|
||||
}
|
||||
|
||||
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
|
||||
);
|
||||
}
|
||||
}
|
156
vendor/nunomaduro/collision/src/Adapters/Phpunit/TestResult.php
vendored
Normal file
156
vendor/nunomaduro/collision/src/Adapters/Phpunit/TestResult.php
vendored
Normal file
@@ -0,0 +1,156 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* 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 PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class TestResult
|
||||
{
|
||||
public const FAIL = 'failed';
|
||||
public const SKIPPED = 'skipped';
|
||||
public const INCOMPLETE = 'incompleted';
|
||||
public const RISKY = 'risked';
|
||||
public const WARN = 'warnings';
|
||||
public const RUNS = 'pending';
|
||||
public const PASS = 'passed';
|
||||
|
||||
/**
|
||||
* @readonly
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $description;
|
||||
|
||||
/**
|
||||
* @readonly
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $type;
|
||||
|
||||
/**
|
||||
* @readonly
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $icon;
|
||||
|
||||
/**
|
||||
* @readonly
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $color;
|
||||
|
||||
/**
|
||||
* @readonly
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
public $warning;
|
||||
|
||||
/**
|
||||
* Test constructor.
|
||||
*
|
||||
* @param string $warning
|
||||
*/
|
||||
private function __construct(string $description, string $type, string $icon, string $color, string $warning = null)
|
||||
{
|
||||
$this->description = $description;
|
||||
$this->type = $type;
|
||||
$this->icon = $icon;
|
||||
$this->color = $color;
|
||||
$this->warning = trim((string) preg_replace("/\r|\n/", ' ', (string) $warning));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new test from the given test case.
|
||||
*/
|
||||
public static function fromTestCase(TestCase $testCase, string $type, string $warning = null): self
|
||||
{
|
||||
$description = self::makeDescription($testCase);
|
||||
|
||||
$icon = self::makeIcon($type);
|
||||
|
||||
$color = self::makeColor($type);
|
||||
|
||||
return new self($description, $type, $icon, $color, $warning);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the test case description.
|
||||
*/
|
||||
public static function makeDescription(TestCase $testCase): string
|
||||
{
|
||||
$name = $testCase->getName(true);
|
||||
|
||||
// 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 = (string) trim($name);
|
||||
|
||||
// Finally, lower case everything
|
||||
return (string) mb_strtolower($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the test case icon.
|
||||
*/
|
||||
public static function makeIcon(string $type): string
|
||||
{
|
||||
switch ($type) {
|
||||
case self::FAIL:
|
||||
return '✕';
|
||||
case self::SKIPPED:
|
||||
return 's';
|
||||
case self::RISKY:
|
||||
return 'r';
|
||||
case self::INCOMPLETE:
|
||||
return 'i';
|
||||
case self::WARN:
|
||||
return 'w';
|
||||
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';
|
||||
}
|
||||
}
|
||||
}
|
47
vendor/nunomaduro/collision/src/Adapters/Phpunit/Timer.php
vendored
Normal file
47
vendor/nunomaduro/collision/src/Adapters/Phpunit/Timer.php
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
}
|
@@ -30,16 +30,16 @@ class ArgumentFormatter implements ArgumentFormatterContract
|
||||
foreach ($arguments as $argument) {
|
||||
switch (true) {
|
||||
case is_string($argument):
|
||||
$result[] = '"'.$argument.'"';
|
||||
$result[] = '"' . $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).']';
|
||||
$result[] = '[' . $this->format($argument, false) . ']';
|
||||
}
|
||||
break;
|
||||
case is_object($argument):
|
||||
$class = get_class($argument);
|
||||
$class = get_class($argument);
|
||||
$result[] = "Object($class)";
|
||||
break;
|
||||
}
|
||||
|
310
vendor/nunomaduro/collision/src/ConsoleColor.php
vendored
Normal file
310
vendor/nunomaduro/collision/src/ConsoleColor.php
vendored
Normal file
@@ -0,0 +1,310 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
use NunoMaduro\Collision\Exceptions\ShouldNotHappen;
|
||||
|
||||
/**
|
||||
* This is an Collision Console Color implementation.
|
||||
*
|
||||
* Code originally from { JakubOnderka\\PhpConsoleColor }. But the package got deprecated.
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
class ConsoleColor
|
||||
{
|
||||
const FOREGROUND = 38;
|
||||
const BACKGROUND = 48;
|
||||
|
||||
const COLOR256_REGEXP = '~^(bg_)?color_([0-9]{1,3})$~';
|
||||
|
||||
const RESET_STYLE = 0;
|
||||
|
||||
/** @var bool */
|
||||
private $isSupported;
|
||||
|
||||
/** @var bool */
|
||||
private $forceStyle = false;
|
||||
|
||||
/** @var array */
|
||||
private $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 = (bool) $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()
|
||||
{
|
||||
if (DIRECTORY_SEPARATOR === '\\') {
|
||||
if (function_exists('sapi_windows_vt100_support') && @sapi_windows_vt100_support(STDOUT)) {
|
||||
return true;
|
||||
} elseif (getenv('ANSICON') !== false || getenv('ConEmuANSI') === 'ON') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
} else {
|
||||
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);
|
||||
} else {
|
||||
return strpos(getenv('TERM'), '256color') !== false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getPossibleStyles()
|
||||
{
|
||||
return array_keys($this->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, $this->styles)) {
|
||||
return $this->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, $this->styles) || preg_match(self::COLOR256_REGEXP, $style);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|int $value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function escSequence($value)
|
||||
{
|
||||
return "\033[{$value}m";
|
||||
}
|
||||
}
|
23
vendor/nunomaduro/collision/src/Contracts/Adapters/Phpunit/HasPrintableTestCaseName.php
vendored
Normal file
23
vendor/nunomaduro/collision/src/Contracts/Adapters/Phpunit/HasPrintableTestCaseName.php
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* 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\Adapters\Phpunit;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
interface HasPrintableTestCaseName
|
||||
{
|
||||
/**
|
||||
* Returns the test case name that should be used by the printer.
|
||||
*/
|
||||
public function getPrintableTestCaseName(): string;
|
||||
}
|
@@ -11,6 +11,7 @@
|
||||
|
||||
namespace NunoMaduro\Collision\Contracts\Adapters\Phpunit;
|
||||
|
||||
use PHPUnit\Framework\Test;
|
||||
use PHPUnit\Framework\TestListener;
|
||||
|
||||
/**
|
||||
@@ -24,9 +25,7 @@ interface Listener extends TestListener
|
||||
* Renders the provided error
|
||||
* on the console.
|
||||
*
|
||||
* @param \Throwable $t
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function render(\Throwable $t);
|
||||
public function render(Test $test, \Throwable $t);
|
||||
}
|
||||
|
@@ -21,11 +21,6 @@ interface ArgumentFormatter
|
||||
/**
|
||||
* Formats the provided array of arguments into
|
||||
* an understandable description.
|
||||
*
|
||||
* @param array $arguments
|
||||
* @param bool $recursive
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function format(array $arguments, bool $recursive = true): string;
|
||||
}
|
||||
|
@@ -11,8 +11,8 @@
|
||||
|
||||
namespace NunoMaduro\Collision\Contracts;
|
||||
|
||||
use Whoops\Handler\HandlerInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Whoops\Handler\HandlerInterface;
|
||||
|
||||
/**
|
||||
* This is an Collision Handler contract.
|
||||
@@ -24,8 +24,6 @@ interface Handler extends HandlerInterface
|
||||
/**
|
||||
* Sets the output.
|
||||
*
|
||||
* @param \Symfony\Component\Console\Output\OutputInterface $output
|
||||
*
|
||||
* @return \NunoMaduro\Collision\Contracts\Handler
|
||||
*/
|
||||
public function setOutput(OutputInterface $output): Handler;
|
||||
|
@@ -20,11 +20,6 @@ interface Highlighter
|
||||
{
|
||||
/**
|
||||
* Highlights the provided content.
|
||||
*
|
||||
* @param string $content
|
||||
* @param int $line
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function highlight(string $content, int $line): string;
|
||||
}
|
||||
|
30
vendor/nunomaduro/collision/src/Contracts/SolutionsRepository.php
vendored
Normal file
30
vendor/nunomaduro/collision/src/Contracts/SolutionsRepository.php
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* 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 Facade\IgnitionContracts\Solution;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* This is an Collision Solutions Repository contract.
|
||||
*
|
||||
* @author Nuno Maduro <enunomaduro@gmail.com>
|
||||
*/
|
||||
interface SolutionsRepository
|
||||
{
|
||||
/**
|
||||
* Gets the solutions from the given `$throwable`.
|
||||
*
|
||||
* @return array<int, Solution>
|
||||
*/
|
||||
public function getFromThrowable(Throwable $throwable): array;
|
||||
}
|
@@ -11,8 +11,8 @@
|
||||
|
||||
namespace NunoMaduro\Collision\Contracts;
|
||||
|
||||
use Whoops\Exception\Inspector;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Whoops\Exception\Inspector;
|
||||
|
||||
/**
|
||||
* This is the Collision Writer contract.
|
||||
@@ -25,7 +25,7 @@ interface Writer
|
||||
* Ignores traces where the file string matches one
|
||||
* of the provided regex expressions.
|
||||
*
|
||||
* @param string[] $ignore The regex expressions.
|
||||
* @param string[] $ignore the regex expressions
|
||||
*
|
||||
* @return \NunoMaduro\Collision\Contracts\Writer
|
||||
*/
|
||||
@@ -34,16 +34,19 @@ interface Writer
|
||||
/**
|
||||
* Declares whether or not the Writer should show the trace.
|
||||
*
|
||||
* @param bool $show
|
||||
*
|
||||
* @return \NunoMaduro\Collision\Contracts\Writer
|
||||
*/
|
||||
public function showTrace(bool $show): Writer;
|
||||
|
||||
/**
|
||||
* Declares whether or not the Writer should show the editor.
|
||||
* Declares whether or not the Writer should show the title.
|
||||
*
|
||||
* @param bool $show
|
||||
* @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
|
||||
*/
|
||||
@@ -51,24 +54,18 @@ interface Writer
|
||||
|
||||
/**
|
||||
* Writes the details of the exception on the console.
|
||||
*
|
||||
* @param \Whoops\Exception\Inspector $inspector
|
||||
*/
|
||||
public function write(Inspector $inspector): void;
|
||||
|
||||
/**
|
||||
* Sets the output.
|
||||
*
|
||||
* @param \Symfony\Component\Console\Output\OutputInterface $output
|
||||
*
|
||||
* @return \NunoMaduro\Collision\Contracts\Writer
|
||||
*/
|
||||
public function setOutput(OutputInterface $output): Writer;
|
||||
|
||||
/**
|
||||
* Gets the output.
|
||||
*
|
||||
* @return \Symfony\Component\Console\Output\OutputInterface
|
||||
*/
|
||||
public function getOutput(): OutputInterface;
|
||||
}
|
||||
|
27
vendor/nunomaduro/collision/src/Exceptions/ShouldNotHappen.php
vendored
Normal file
27
vendor/nunomaduro/collision/src/Exceptions/ShouldNotHappen.php
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* 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\Exceptions;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class ShouldNotHappen extends RuntimeException
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$message = 'This should not happen, please open an issue on collision repository: %s';
|
||||
|
||||
parent::__construct(sprintf($message, 'https://github.com/nunomaduro/collision/issues/new'));
|
||||
}
|
||||
}
|
10
vendor/nunomaduro/collision/src/Handler.php
vendored
10
vendor/nunomaduro/collision/src/Handler.php
vendored
@@ -11,10 +11,10 @@
|
||||
|
||||
namespace NunoMaduro\Collision;
|
||||
|
||||
use Whoops\Handler\Handler as AbstractHandler;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use NunoMaduro\Collision\Contracts\Writer as WriterContract;
|
||||
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;
|
||||
|
||||
/**
|
||||
* This is an Collision Handler implementation.
|
||||
@@ -32,12 +32,10 @@ class Handler extends AbstractHandler implements HandlerContract
|
||||
|
||||
/**
|
||||
* Creates an instance of the Handler.
|
||||
*
|
||||
* @param \NunoMaduro\Collision\Contracts\Writer|null $writer
|
||||
*/
|
||||
public function __construct(WriterContract $writer = null)
|
||||
{
|
||||
$this->writer = $writer ?: new Writer;
|
||||
$this->writer = $writer ?: new Writer();
|
||||
}
|
||||
|
||||
/**
|
||||
|
250
vendor/nunomaduro/collision/src/Highlighter.php
vendored
250
vendor/nunomaduro/collision/src/Highlighter.php
vendored
@@ -11,43 +11,73 @@
|
||||
|
||||
namespace NunoMaduro\Collision;
|
||||
|
||||
use JakubOnderka\PhpConsoleColor\ConsoleColor;
|
||||
use JakubOnderka\PhpConsoleHighlighter\Highlighter as BaseHighlighter;
|
||||
use NunoMaduro\Collision\Contracts\Highlighter as HighlighterContract;
|
||||
|
||||
/**
|
||||
* This is an Collision Highlighter implementation.
|
||||
*
|
||||
* @author Nuno Maduro <enunomaduro@gmail.com>
|
||||
* Code originally from { JakubOnderka\\PhpConsoleColor }. But the package got deprecated.
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
class Highlighter extends BaseHighlighter implements HighlighterContract
|
||||
class Highlighter implements HighlighterContract
|
||||
{
|
||||
/**
|
||||
* Holds the theme.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $theme = [
|
||||
BaseHighlighter::TOKEN_STRING => ['light_gray'],
|
||||
BaseHighlighter::TOKEN_COMMENT => ['dark_gray', 'italic'],
|
||||
BaseHighlighter::TOKEN_KEYWORD => ['yellow'],
|
||||
BaseHighlighter::TOKEN_DEFAULT => ['default', 'bold'],
|
||||
BaseHighlighter::TOKEN_HTML => ['blue', 'bold'],
|
||||
BaseHighlighter::ACTUAL_LINE_MARK => ['bg_red', 'bold'],
|
||||
BaseHighlighter::LINE_NUMBER => ['dark_gray', 'italic'],
|
||||
private $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'],
|
||||
];
|
||||
|
||||
const TOKEN_DEFAULT = 'token_default';
|
||||
const TOKEN_COMMENT = 'token_comment';
|
||||
const TOKEN_STRING = 'token_string';
|
||||
const TOKEN_HTML = 'token_html';
|
||||
const TOKEN_KEYWORD = 'token_keyword';
|
||||
|
||||
const ACTUAL_LINE_MARK = 'actual_line_mark';
|
||||
const LINE_NUMBER = 'line_number';
|
||||
|
||||
/** @var ConsoleColor */
|
||||
private $color;
|
||||
|
||||
/** @var array */
|
||||
private $defaultTheme = [
|
||||
self::TOKEN_STRING => 'red',
|
||||
self::TOKEN_COMMENT => 'yellow',
|
||||
self::TOKEN_KEYWORD => 'green',
|
||||
self::TOKEN_DEFAULT => 'default',
|
||||
self::TOKEN_HTML => 'cyan',
|
||||
|
||||
self::ACTUAL_LINE_MARK => 'red',
|
||||
self::LINE_NUMBER => 'dark_gray',
|
||||
];
|
||||
|
||||
/**
|
||||
* Creates an instance of the Highlighter.
|
||||
*
|
||||
* @param \JakubOnderka\PhpConsoleColor\ConsoleColor|null $color
|
||||
*/
|
||||
public function __construct(ConsoleColor $color = null)
|
||||
{
|
||||
parent::__construct($color = $color ?: new ConsoleColor);
|
||||
$this->color = $color ?: new ConsoleColor();
|
||||
|
||||
foreach ($this->defaultTheme as $name => $styles) {
|
||||
if (!$this->color->hasTheme($name)) {
|
||||
$this->color->addTheme($name, $styles);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->theme as $name => $styles) {
|
||||
$color->addTheme((string) $name, $styles);
|
||||
$this->color->addTheme((string) $name, $styles);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,4 +88,192 @@ class Highlighter extends BaseHighlighter implements HighlighterContract
|
||||
{
|
||||
return rtrim($this->getCodeSnippet($content, $line, 4, 4));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $source
|
||||
* @param int $lineNumber
|
||||
* @param int $linesBefore
|
||||
* @param int $linesAfter
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getCodeSnippet($source, $lineNumber, $linesBefore = 2, $linesAfter = 2)
|
||||
{
|
||||
$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
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getHighlightedLines($source)
|
||||
{
|
||||
$source = str_replace(["\r\n", "\r"], "\n", $source);
|
||||
$tokens = $this->tokenize($source);
|
||||
|
||||
return $this->splitToLines($tokens);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $source
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function tokenize($source)
|
||||
{
|
||||
$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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
private function splitToLines(array $tokens)
|
||||
{
|
||||
$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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
private function colorLines(array $tokenLines)
|
||||
{
|
||||
$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
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function lineNumbers(array $lines, $markLine = null)
|
||||
{
|
||||
end($lines);
|
||||
$lineStrlen = strlen(key($lines) + 1);
|
||||
|
||||
$snippet = '';
|
||||
foreach ($lines as $i => $line) {
|
||||
if ($markLine !== null) {
|
||||
$snippet .= ($markLine === $i + 1 ? $this->color->apply(self::ACTUAL_LINE_MARK, ' > ') : ' ');
|
||||
}
|
||||
|
||||
$snippet .= $this->color->apply(self::LINE_NUMBER, str_pad($i + 1, $lineStrlen, ' ', STR_PAD_LEFT) . '| ');
|
||||
$snippet .= $line . PHP_EOL;
|
||||
}
|
||||
|
||||
return $snippet;
|
||||
}
|
||||
}
|
||||
|
11
vendor/nunomaduro/collision/src/Provider.php
vendored
11
vendor/nunomaduro/collision/src/Provider.php
vendored
@@ -11,10 +11,10 @@
|
||||
|
||||
namespace NunoMaduro\Collision;
|
||||
|
||||
use Whoops\Run;
|
||||
use Whoops\RunInterface;
|
||||
use NunoMaduro\Collision\Contracts\Handler as HandlerContract;
|
||||
use NunoMaduro\Collision\Contracts\Provider as ProviderContract;
|
||||
use Whoops\Run;
|
||||
use Whoops\RunInterface;
|
||||
|
||||
/**
|
||||
* This is an Collision Provider implementation.
|
||||
@@ -39,14 +39,11 @@ class Provider implements ProviderContract
|
||||
|
||||
/**
|
||||
* Creates a new instance of the Provider.
|
||||
*
|
||||
* @param \Whoops\RunInterface|null $run
|
||||
* @param \NunoMaduro\Collision\Contracts\Handler|null $handler
|
||||
*/
|
||||
public function __construct(RunInterface $run = null, HandlerContract $handler = null)
|
||||
{
|
||||
$this->run = $run ?: new Run;
|
||||
$this->handler = $handler ?: new Handler;
|
||||
$this->run = $run ?: new Run();
|
||||
$this->handler = $handler ?: new Handler();
|
||||
}
|
||||
|
||||
/**
|
||||
|
31
vendor/nunomaduro/collision/src/SolutionsRepositories/NullSolutionsRepository.php
vendored
Normal file
31
vendor/nunomaduro/collision/src/SolutionsRepositories/NullSolutionsRepository.php
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* 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\SolutionsRepositories;
|
||||
|
||||
use NunoMaduro\Collision\Contracts\SolutionsRepository;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* This is an Collision Null Solutions Provider implementation.
|
||||
*
|
||||
* @author Nuno Maduro <enunomaduro@gmail.com>
|
||||
*/
|
||||
class NullSolutionsRepository implements SolutionsRepository
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFromThrowable(Throwable $throwable): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
188
vendor/nunomaduro/collision/src/Writer.php
vendored
188
vendor/nunomaduro/collision/src/Writer.php
vendored
@@ -11,13 +11,15 @@
|
||||
|
||||
namespace NunoMaduro\Collision;
|
||||
|
||||
use Whoops\Exception\Frame;
|
||||
use Whoops\Exception\Inspector;
|
||||
use NunoMaduro\Collision\Contracts\ArgumentFormatter as ArgumentFormatterContract;
|
||||
use NunoMaduro\Collision\Contracts\Highlighter as HighlighterContract;
|
||||
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 NunoMaduro\Collision\Contracts\Writer as WriterContract;
|
||||
use NunoMaduro\Collision\Contracts\Highlighter as HighlighterContract;
|
||||
use NunoMaduro\Collision\Contracts\ArgumentFormatter as ArgumentFormatterContract;
|
||||
use Whoops\Exception\Frame;
|
||||
use Whoops\Exception\Inspector;
|
||||
|
||||
/**
|
||||
* This is an Collision Writer implementation.
|
||||
@@ -31,6 +33,13 @@ class Writer implements WriterContract
|
||||
*/
|
||||
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.
|
||||
*
|
||||
@@ -67,6 +76,13 @@ class Writer implements WriterContract
|
||||
*/
|
||||
protected $showTrace = true;
|
||||
|
||||
/**
|
||||
* Declares whether or not the title should appear.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $showTitle = true;
|
||||
|
||||
/**
|
||||
* Declares whether or not the editor should appear.
|
||||
*
|
||||
@@ -76,19 +92,17 @@ class Writer implements WriterContract
|
||||
|
||||
/**
|
||||
* Creates an instance of the writer.
|
||||
*
|
||||
* @param \Symfony\Component\Console\Output\OutputInterface|null $output
|
||||
* @param \NunoMaduro\Collision\Contracts\ArgumentFormatter|null $argumentFormatter
|
||||
* @param \NunoMaduro\Collision\Contracts\Highlighter|null $highlighter
|
||||
*/
|
||||
public function __construct(
|
||||
SolutionsRepository $solutionsRepository = null,
|
||||
OutputInterface $output = null,
|
||||
ArgumentFormatterContract $argumentFormatter = null,
|
||||
HighlighterContract $highlighter = null
|
||||
) {
|
||||
$this->output = $output ?: new ConsoleOutput;
|
||||
$this->argumentFormatter = $argumentFormatter ?: new ArgumentFormatter;
|
||||
$this->highlighter = $highlighter ?: new Highlighter;
|
||||
$this->solutionsRepository = $solutionsRepository ?: new NullSolutionsRepository();
|
||||
$this->output = $output ?: new ConsoleOutput();
|
||||
$this->argumentFormatter = $argumentFormatter ?: new ArgumentFormatter();
|
||||
$this->highlighter = $highlighter ?: new Highlighter();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -96,16 +110,19 @@ class Writer implements WriterContract
|
||||
*/
|
||||
public function write(Inspector $inspector): void
|
||||
{
|
||||
$this->renderTitle($inspector);
|
||||
$this->renderTitleAndDescription($inspector);
|
||||
|
||||
$frames = $this->getFrames($inspector);
|
||||
|
||||
$editorFrame = array_shift($frames);
|
||||
|
||||
if ($this->showEditor && $editorFrame !== null) {
|
||||
$this->renderEditor($editorFrame);
|
||||
}
|
||||
|
||||
if ($this->showTrace && ! empty($frames)) {
|
||||
$this->renderSolution($inspector);
|
||||
|
||||
if ($this->showTrace && !empty($frames)) {
|
||||
$this->renderTrace($frames);
|
||||
} else {
|
||||
$this->output->writeln('');
|
||||
@@ -132,6 +149,16 @@ class Writer implements WriterContract
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function showTitle(bool $show): WriterContract
|
||||
{
|
||||
$this->showTitle = $show;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
@@ -162,16 +189,18 @@ class Writer implements WriterContract
|
||||
|
||||
/**
|
||||
* Returns pertinent frames.
|
||||
*
|
||||
* @param \Whoops\Exception\Inspector $inspector
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
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) {
|
||||
if (preg_match($ignore, $frame->getFile())) {
|
||||
return false;
|
||||
@@ -186,18 +215,48 @@ class Writer implements WriterContract
|
||||
|
||||
/**
|
||||
* Renders the title of the exception.
|
||||
*
|
||||
* @param \Whoops\Exception\Inspector $inspector
|
||||
*
|
||||
* @return \NunoMaduro\Collision\Contracts\Writer
|
||||
*/
|
||||
protected function renderTitle(Inspector $inspector): WriterContract
|
||||
protected function renderTitleAndDescription(Inspector $inspector): WriterContract
|
||||
{
|
||||
$exception = $inspector->getException();
|
||||
$message = $exception->getMessage();
|
||||
$class = $inspector->getExceptionName();
|
||||
$message = rtrim($exception->getMessage());
|
||||
$class = $inspector->getExceptionName();
|
||||
|
||||
$this->render("<bg=red;options=bold> $class </> : <comment>$message</>");
|
||||
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 \Facade\IgnitionContracts\Solution $solution */
|
||||
$title = $solution->getSolutionTitle();
|
||||
$description = $solution->getSolutionDescription();
|
||||
$links = $solution->getDocumentationLinks();
|
||||
|
||||
$description = trim((string) preg_replace("/\n/", "\n ", $description));
|
||||
|
||||
$this->render(sprintf(
|
||||
'<fg=blue;options=bold>• </><fg=default;options=bold>%s</>: %s %s',
|
||||
rtrim($title, '.'),
|
||||
$description,
|
||||
implode(', ', array_map(function (string $link) {
|
||||
return sprintf("\n <fg=blue>%s</>", $link);
|
||||
}, $links))
|
||||
));
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
@@ -205,14 +264,14 @@ class Writer implements WriterContract
|
||||
/**
|
||||
* Renders the editor containing the code that was the
|
||||
* origin of the exception.
|
||||
*
|
||||
* @param \Whoops\Exception\Frame $frame
|
||||
*
|
||||
* @return \NunoMaduro\Collision\Contracts\Writer
|
||||
*/
|
||||
protected function renderEditor(Frame $frame): WriterContract
|
||||
{
|
||||
$this->render('at <fg=green>'.$frame->getFile().'</>'.':<fg=green>'.$frame->getLine().'</>');
|
||||
$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());
|
||||
|
||||
@@ -223,41 +282,56 @@ class Writer implements WriterContract
|
||||
|
||||
/**
|
||||
* Renders the trace of the exception.
|
||||
*
|
||||
* @param array $frames
|
||||
*
|
||||
* @return \NunoMaduro\Collision\Contracts\Writer
|
||||
*/
|
||||
protected function renderTrace(array $frames): WriterContract
|
||||
{
|
||||
$this->render('<comment>Exception trace:</comment>');
|
||||
$vendorFrames = 0;
|
||||
$userFrames = 0;
|
||||
foreach ($frames as $i => $frame) {
|
||||
if ($i > static::VERBOSITY_NORMAL_FRAMES && $this->output->getVerbosity(
|
||||
) < OutputInterface::VERBOSITY_VERBOSE) {
|
||||
$this->render('<info>Please use the argument <fg=red>-v</> to see more details.</info>');
|
||||
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;
|
||||
}
|
||||
|
||||
$file = $frame->getFile();
|
||||
$line = $frame->getLine();
|
||||
$class = empty($frame->getClass()) ? '' : $frame->getClass().'::';
|
||||
$function = $frame->getFunction();
|
||||
$args = $this->argumentFormatter->format($frame->getArgs());
|
||||
$pos = str_pad((int) $i + 1, 4, ' ');
|
||||
$userFrames++;
|
||||
|
||||
$this->render("<comment><fg=cyan>$pos</>$class$function($args)</comment>");
|
||||
$this->render(" <fg=green>$file</>:<fg=green>$line</>", false);
|
||||
$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=white> $class$function($args)</>", false);
|
||||
}
|
||||
|
||||
/* Let's consider add this later...
|
||||
* if ($vendorFrames > 0) {
|
||||
* $this->output->write(
|
||||
* sprintf("\n \e[2m+%s vendor frames \e[22m\n", $vendorFrames)
|
||||
* );
|
||||
* $vendorFrames = 0;
|
||||
* }.
|
||||
*/
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders an message into the console.
|
||||
*
|
||||
* @param string $message
|
||||
* @param bool $break
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
protected function render(string $message, bool $break = true): WriterContract
|
||||
@@ -270,4 +344,18 @@ class Writer implements WriterContract
|
||||
|
||||
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