dependencies-upgrade
This commit is contained in:
		| @@ -1,13 +1,6 @@ | ||||
| <?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. | ||||
|  */ | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace NunoMaduro\Collision\Adapters\Laravel; | ||||
|  | ||||
| @@ -21,11 +14,9 @@ use NunoMaduro\Collision\SolutionsRepositories\NullSolutionsRepository; | ||||
| use NunoMaduro\Collision\Writer; | ||||
|  | ||||
| /** | ||||
|  * This is an Collision Laravel Adapter Service Provider implementation. | ||||
|  * @internal | ||||
|  * | ||||
|  * Registers the Error Handler on Laravel. | ||||
|  * | ||||
|  * @author Nuno Maduro <enunomaduro@gmail.com> | ||||
|  * @final | ||||
|  */ | ||||
| class CollisionServiceProvider extends ServiceProvider | ||||
| { | ||||
|   | ||||
| @@ -1,15 +1,25 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace NunoMaduro\Collision\Adapters\Laravel\Commands; | ||||
|  | ||||
| use Dotenv\Dotenv; | ||||
| use Dotenv\Repository\RepositoryBuilder; | ||||
| 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 RuntimeException; | ||||
| use Symfony\Component\Process\Exception\ProcessSignaledException; | ||||
| use Symfony\Component\Process\Process; | ||||
|  | ||||
| /** | ||||
|  * @internal | ||||
|  * | ||||
|  * @final | ||||
|  */ | ||||
| class TestCommand extends Command | ||||
| { | ||||
|     /** | ||||
| @@ -17,7 +27,11 @@ class TestCommand extends Command | ||||
|      * | ||||
|      * @var string | ||||
|      */ | ||||
|     protected $signature = 'test {--without-tty : Disable output to TTY}'; | ||||
|     protected $signature = 'test | ||||
|         {--without-tty : Disable output to TTY} | ||||
|         {--p|parallel : Indicates if the tests should run in parallel} | ||||
|         {--recreate-databases : Indicates if the test databases should be re-created} | ||||
|     '; | ||||
|  | ||||
|     /** | ||||
|      * The console command description. | ||||
| @@ -26,16 +40,6 @@ class TestCommand extends Command | ||||
|      */ | ||||
|     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. | ||||
|      * | ||||
| @@ -55,17 +59,39 @@ class TestCommand extends Command | ||||
|      */ | ||||
|     public function handle() | ||||
|     { | ||||
|         if ((int) \PHPUnit\Runner\Version::id()[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('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( | ||||
|             $this->binary(), | ||||
|             array_merge( | ||||
|                 $this->arguments, | ||||
|                 $this->phpunitArguments($options) | ||||
|             ) | ||||
|         )))->setTimeout(null); | ||||
|                 // 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')); | ||||
| @@ -91,11 +117,17 @@ class TestCommand extends Command | ||||
|      */ | ||||
|     protected function binary() | ||||
|     { | ||||
|         if ('phpdbg' === PHP_SAPI) { | ||||
|             return [PHP_BINARY, '-qrr', 'vendor/phpunit/phpunit/phpunit']; | ||||
|         if (class_exists(\Pest\Laravel\PestServiceProvider::class)) { | ||||
|             $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']; | ||||
|         } | ||||
|  | ||||
|         return [PHP_BINARY, 'vendor/phpunit/phpunit/phpunit']; | ||||
|         if ('phpdbg' === PHP_SAPI) { | ||||
|             return array_merge([PHP_BINARY, '-qrr'], $command); | ||||
|         } | ||||
|  | ||||
|         return array_merge([PHP_BINARY], $command); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -107,6 +139,8 @@ class TestCommand extends Command | ||||
|      */ | ||||
|     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='); | ||||
|         })); | ||||
| @@ -115,7 +149,56 @@ class TestCommand extends Command | ||||
|             $file = base_path('phpunit.xml.dist'); | ||||
|         } | ||||
|  | ||||
|         return array_merge(['-c', $file], $options); | ||||
|         return array_merge(["--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=') | ||||
|                 && !Str::startsWith($option, '-p') | ||||
|                 && !Str::startsWith($option, '--parallel') | ||||
|                 && !Str::startsWith($option, '--recreate-databases'); | ||||
|         })); | ||||
|  | ||||
|         if (!file_exists($file = base_path('phpunit.xml'))) { | ||||
|             $file = base_path('phpunit.xml.dist'); | ||||
|         } | ||||
|  | ||||
|         return array_merge([ | ||||
|             "--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'), | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -126,18 +209,101 @@ class TestCommand extends Command | ||||
|     protected function clearEnv() | ||||
|     { | ||||
|         if (!$this->option('env')) { | ||||
|             $repositories = RepositoryBuilder::create() | ||||
|                 ->make(); | ||||
|  | ||||
|             $envs = Dotenv::create( | ||||
|                 $repositories, | ||||
|             $vars = self::getEnvironmentVariables( | ||||
|                 // @phpstan-ignore-next-line | ||||
|                 $this->laravel->environmentPath(), | ||||
|                 // @phpstan-ignore-next-line | ||||
|                 $this->laravel->environmentFile() | ||||
|             )->safeLoad(); | ||||
|             ); | ||||
|  | ||||
|             foreach ($envs as $name => $value) { | ||||
|                 $repositories->clear($name); | ||||
|             $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'; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,13 +1,6 @@ | ||||
| <?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. | ||||
|  */ | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace NunoMaduro\Collision\Adapters\Laravel; | ||||
|  | ||||
| @@ -18,13 +11,9 @@ use Symfony\Component\Console\Exception\ExceptionInterface as SymfonyConsoleExce | ||||
| use Throwable; | ||||
|  | ||||
| /** | ||||
|  * This is an Collision Laravel Adapter ExceptionHandler implementation. | ||||
|  * | ||||
|  * Registers the Error Handler on Laravel. | ||||
|  * | ||||
|  * @author Nuno Maduro <enunomaduro@gmail.com> | ||||
|  * @internal | ||||
|  */ | ||||
| class ExceptionHandler implements ExceptionHandlerContract | ||||
| final class ExceptionHandler implements ExceptionHandlerContract | ||||
| { | ||||
|     /** | ||||
|      * Holds an instance of the application exception handler. | ||||
|   | ||||
							
								
								
									
										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 | ||||
| { | ||||
| } | ||||
| @@ -1,13 +1,6 @@ | ||||
| <?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. | ||||
|  */ | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace NunoMaduro\Collision\Adapters\Laravel; | ||||
|  | ||||
| @@ -16,13 +9,9 @@ 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> | ||||
|  * @internal | ||||
|  */ | ||||
| class IgnitionSolutionsRepository implements SolutionsRepository | ||||
| final class IgnitionSolutionsRepository implements SolutionsRepository | ||||
| { | ||||
|     /** | ||||
|      * Holds an instance of ignition solutions provider repository. | ||||
|   | ||||
| @@ -1,5 +1,7 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| /** | ||||
|  * This file is part of Collision. | ||||
|  * | ||||
| @@ -14,11 +16,9 @@ namespace NunoMaduro\Collision\Adapters\Laravel; | ||||
| use Whoops\Exception\Inspector as BaseInspector; | ||||
|  | ||||
| /** | ||||
|  * This is an Collision Laravel Adapter Inspector implementation. | ||||
|  * | ||||
|  * @author Nuno Maduro <enunomaduro@gmail.com> | ||||
|  * @internal | ||||
|  */ | ||||
| class Inspector extends BaseInspector | ||||
| final class Inspector extends BaseInspector | ||||
| { | ||||
|     /** | ||||
|      * {@inheritdoc} | ||||
|   | ||||
| @@ -1,5 +1,7 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| /** | ||||
|  * This file is part of Collision. | ||||
|  * | ||||
|   | ||||
| @@ -1,56 +1,245 @@ | ||||
| <?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. | ||||
|  */ | ||||
| declare(strict_types=1); | ||||
|  | ||||
| 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 | ||||
| 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 | ||||
|  */ | ||||
| if (class_exists(\PHPUnit\Runner\Version::class) && intval(substr(\PHPUnit\Runner\Version::id(), 0, 1)) === 8) { | ||||
| final class Printer implements \PHPUnit\TextUI\ResultPrinter | ||||
| { | ||||
|     /** | ||||
|      * This is an Collision Phpunit Adapter implementation. | ||||
|      * Holds an instance of the style. | ||||
|      * | ||||
|      * @internal | ||||
|      * Style is a class we use to interact with output. | ||||
|      * | ||||
|      * @var Style | ||||
|      */ | ||||
|     final class Printer extends \PHPUnit\Util\Printer implements \PHPUnit\Framework\TestListener | ||||
|     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') | ||||
|     { | ||||
|         use PrinterContents; | ||||
|         $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); | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * 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 | ||||
|      * {@inheritdoc} | ||||
|      */ | ||||
|     final class Printer implements \PHPUnit\TextUI\ResultPrinter | ||||
|     public function addError(Test $testCase, Throwable $throwable, float $time): void | ||||
|     { | ||||
|         use PrinterContents; | ||||
|         $this->failed = true; | ||||
|  | ||||
|         /** | ||||
|          * Intentionally left blank as we output things on events of the listener. | ||||
|          */ | ||||
|         public function printResult(\PHPUnit\Framework\TestResult $result): void | ||||
|         { | ||||
|             // .. | ||||
|         $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); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,224 +0,0 @@ | ||||
| <?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; | ||||
|     } | ||||
| } | ||||
| @@ -1,13 +1,6 @@ | ||||
| <?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. | ||||
|  */ | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace NunoMaduro\Collision\Adapters\Phpunit; | ||||
|  | ||||
| @@ -47,6 +40,20 @@ final class State | ||||
|      */ | ||||
|     public $testCaseTests = []; | ||||
|  | ||||
|     /** | ||||
|      * The current test case tests. | ||||
|      * | ||||
|      * @var array<int, TestResult> | ||||
|      */ | ||||
|     public $toBePrintedCaseTests = []; | ||||
|  | ||||
|     /** | ||||
|      * Header printed. | ||||
|      * | ||||
|      * @var bool | ||||
|      */ | ||||
|     public $headerPrinted = false; | ||||
|  | ||||
|     /** | ||||
|      * The state constructor. | ||||
|      */ | ||||
| @@ -68,7 +75,8 @@ final class State | ||||
|      */ | ||||
|     public function add(TestResult $test): void | ||||
|     { | ||||
|         $this->testCaseTests[] = $test; | ||||
|         $this->testCaseTests[]        = $test; | ||||
|         $this->toBePrintedCaseTests[] = $test; | ||||
|  | ||||
|         $this->suiteTests[] = $test; | ||||
|     } | ||||
| @@ -145,6 +153,8 @@ final class State | ||||
|         $this->testCaseName = self::getPrintableTestCaseName($testCase); | ||||
|  | ||||
|         $this->testCaseTests = []; | ||||
|  | ||||
|         $this->headerPrinted = false; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -152,9 +162,11 @@ final class State | ||||
|      */ | ||||
|     public function eachTestCaseTests(callable $callback): void | ||||
|     { | ||||
|         foreach ($this->testCaseTests as $test) { | ||||
|         foreach ($this->toBePrintedCaseTests as $test) { | ||||
|             $callback($test); | ||||
|         } | ||||
|  | ||||
|         $this->toBePrintedCaseTests = []; | ||||
|     } | ||||
|  | ||||
|     public function countTestsInTestSuiteBy(string $type): int | ||||
| @@ -181,14 +193,10 @@ final class State | ||||
|     /** | ||||
|      * Returns the printable test case name from the given `TestCase`. | ||||
|      */ | ||||
|     private static function getPrintableTestCaseName(TestCase $test): string | ||||
|     public static function getPrintableTestCaseName(TestCase $test): string | ||||
|     { | ||||
|         if ($test instanceof HasPrintableTestCaseName) { | ||||
|             $name = $test->getPrintableTestCaseName(); | ||||
|         } else { | ||||
|             $name = get_class($test); | ||||
|         } | ||||
|  | ||||
|         return $name; | ||||
|         return $test instanceof HasPrintableTestCaseName | ||||
|             ? $test->getPrintableTestCaseName() | ||||
|             : get_class($test); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,23 +1,16 @@ | ||||
| <?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. | ||||
|  */ | ||||
| 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 PHPUnit\Framework\TestCase; | ||||
| use Symfony\Component\Console\Output\ConsoleOutput; | ||||
| use Symfony\Component\Console\Output\ConsoleSectionOutput; | ||||
| use Symfony\Component\Console\Output\ConsoleOutputInterface; | ||||
| use Throwable; | ||||
| use Whoops\Exception\Inspector; | ||||
|  | ||||
| @@ -31,19 +24,24 @@ final class Style | ||||
|      */ | ||||
|     private $output; | ||||
|  | ||||
|     /** | ||||
|      * @var ConsoleSectionOutput | ||||
|      */ | ||||
|     private $footer; | ||||
|  | ||||
|     /** | ||||
|      * Style constructor. | ||||
|      */ | ||||
|     public function __construct(ConsoleOutput $output) | ||||
|     public function __construct(ConsoleOutputInterface $output) | ||||
|     { | ||||
|         $this->output = $output; | ||||
|         if (!$output instanceof ConsoleOutput) { | ||||
|             throw new ShouldNotHappen(); | ||||
|         } | ||||
|  | ||||
|         $this->footer = $output->section(); | ||||
|         $this->output = $output; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Prints the content. | ||||
|      */ | ||||
|     public function write(string $content): void | ||||
|     { | ||||
|         $this->output->write($content); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -54,20 +52,21 @@ final class Style | ||||
|      *    ✓ basic test | ||||
|      * ``` | ||||
|      */ | ||||
|     public function writeCurrentRecap(State $state): void | ||||
|     public function writeCurrentTestCaseSummary(State $state): void | ||||
|     { | ||||
|         if (!$state->testCaseTestsCount()) { | ||||
|         if ($state->testCaseTestsCount() === 0) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         $this->footer->clear(); | ||||
|  | ||||
|         $this->output->writeln($this->titleLineFrom( | ||||
|             $state->getTestCaseTitle() === 'FAIL' ? 'white' : 'black', | ||||
|             $state->getTestCaseTitleColor(), | ||||
|             $state->getTestCaseTitle(), | ||||
|             $state->testCaseName | ||||
|         )); | ||||
|         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( | ||||
| @@ -80,85 +79,97 @@ final class Style | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Prints the content similar too on the footer. Where | ||||
|      * we are updating the current test. | ||||
|      * Prints the content similar too:. | ||||
|      * | ||||
|      * ``` | ||||
|      *    Runs  Unit\ExampleTest | ||||
|      *    • basic test | ||||
|      *    PASS  Unit\ExampleTest | ||||
|      *    ✓ basic test | ||||
|      * ``` | ||||
|      */ | ||||
|     public function updateFooter(State $state, TestCase $testCase = null): void | ||||
|     public function writeErrorsSummary(State $state, bool $onFailure): void | ||||
|     { | ||||
|         $runs = []; | ||||
|         $errors = array_filter($state->suiteTests, function (TestResult $testResult) { | ||||
|             return $testResult->type === TestResult::FAIL; | ||||
|         }); | ||||
|  | ||||
|         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 | ||||
|             ); | ||||
|         if (!$onFailure) { | ||||
|             $this->output->writeln(['', "  \e[2m---\e[22m", '']); | ||||
|         } | ||||
|  | ||||
|         $types = [TestResult::FAIL, TestResult::WARN, TestResult::RISKY, TestResult::INCOMPLETE, TestResult::SKIPPED, TestResult::PASS]; | ||||
|         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)) { | ||||
|             if (($countTests = $state->countTestsInTestSuiteBy($type)) !== 0) { | ||||
|                 $color   = TestResult::makeColor($type); | ||||
|                 $tests[] = "<fg=$color;options=bold>$countTests $type</>"; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         $pending = $state->suiteTotalTests - $state->testSuiteTestsCount(); | ||||
|         if ($pending) { | ||||
|         if ($pending !== 0) { | ||||
|             $tests[] = "\e[2m$pending pending\e[22m"; | ||||
|         } | ||||
|  | ||||
|         if (!empty($tests)) { | ||||
|             $this->footer->overwrite(array_merge($runs, [ | ||||
|                 '', | ||||
|             $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(''); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Writes the final recap. | ||||
|      * Displays a warning message. | ||||
|      */ | ||||
|     public function writeRecap(Timer $timer): void | ||||
|     public function writeWarning(string $message): void | ||||
|     { | ||||
|         $timeElapsed = number_format($timer->result(), 2, '.', ''); | ||||
|         $this->footer->writeln( | ||||
|             sprintf( | ||||
|                 '  <fg=white;options=bold>Time:   </><fg=default>%ss</>', | ||||
|                 $timeElapsed | ||||
|             ) | ||||
|         ); | ||||
|         $this->output->writeln($this->testLineFrom('yellow', $message, '')); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Displays the error using Collision's writer | ||||
|      * and terminates with exit code === 1. | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function writeError(State $state, Throwable $throwable) | ||||
|     public function writeError(Throwable $throwable): void | ||||
|     { | ||||
|         $this->writeCurrentRecap($state); | ||||
|  | ||||
|         $this->updateFooter($state); | ||||
|  | ||||
|         $writer = (new Writer())->setOutput($this->output); | ||||
|  | ||||
|         if ($throwable instanceof AssertionFailedError) { | ||||
| @@ -167,10 +178,24 @@ final class Style | ||||
|         } | ||||
|  | ||||
|         $writer->ignoreFilesIn([ | ||||
|             '/vendor\/pestphp\/pest/', | ||||
|             '/vendor\/phpspec\/prophecy-phpunit/', | ||||
|             '/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/', | ||||
|         ]); | ||||
|  | ||||
|         if ($throwable instanceof ExceptionWrapper && $throwable->getOriginalException() !== null) { | ||||
| @@ -182,10 +207,25 @@ final class Style | ||||
|         $writer->write($inspector); | ||||
|  | ||||
|         if ($throwable instanceof ExpectationFailedException && $comparisionFailure = $throwable->getComparisonFailure()) { | ||||
|             $this->output->write($comparisionFailure->getDiff()); | ||||
|             $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"); | ||||
|         } | ||||
|  | ||||
|         exit(1); | ||||
|         $this->output->writeln(''); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -193,20 +233,6 @@ final class Style | ||||
|      */ | ||||
|     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, | ||||
|   | ||||
| @@ -1,17 +1,12 @@ | ||||
| <?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. | ||||
|  */ | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace NunoMaduro\Collision\Adapters\Phpunit; | ||||
|  | ||||
| use NunoMaduro\Collision\Contracts\Adapters\Phpunit\HasPrintableTestCaseName; | ||||
| use PHPUnit\Framework\TestCase; | ||||
| use Throwable; | ||||
|  | ||||
| /** | ||||
|  * @internal | ||||
| @@ -26,6 +21,13 @@ final class TestResult | ||||
|     public const RUNS       = 'pending'; | ||||
|     public const PASS       = 'passed'; | ||||
|  | ||||
|     /** | ||||
|      * @readonly | ||||
|      * | ||||
|      * @var string | ||||
|      */ | ||||
|     public $testCaseName; | ||||
|  | ||||
|     /** | ||||
|      * @readonly | ||||
|      * | ||||
| @@ -57,36 +59,53 @@ final class TestResult | ||||
|     /** | ||||
|      * @readonly | ||||
|      * | ||||
|      * @var string|null | ||||
|      * @var Throwable|null | ||||
|      */ | ||||
|     public $warning; | ||||
|     public $throwable; | ||||
|  | ||||
|     /** | ||||
|      * @readonly | ||||
|      * | ||||
|      * @var string | ||||
|      */ | ||||
|     public $warning = ''; | ||||
|  | ||||
|     /** | ||||
|      * Test constructor. | ||||
|      * | ||||
|      * @param string $warning | ||||
|      */ | ||||
|     private function __construct(string $description, string $type, string $icon, string $color, string $warning = null) | ||||
|     private function __construct(string $testCaseName, string $description, string $type, string $icon, string $color, Throwable $throwable = null) | ||||
|     { | ||||
|         $this->description = $description; | ||||
|         $this->type        = $type; | ||||
|         $this->icon        = $icon; | ||||
|         $this->color       = $color; | ||||
|         $this->warning     = trim((string) preg_replace("/\r|\n/", ' ', (string) $warning)); | ||||
|         $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, string $warning = null): self | ||||
|     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($description, $type, $icon, $color, $warning); | ||||
|         return new self($testCaseName, $description, $type, $icon, $color, $throwable); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -94,7 +113,11 @@ final class TestResult | ||||
|      */ | ||||
|     public static function makeDescription(TestCase $testCase): string | ||||
|     { | ||||
|         $name = $testCase->getName(true); | ||||
|         $name = $testCase->getName(false); | ||||
|  | ||||
|         if ($testCase instanceof HasPrintableTestCaseName) { | ||||
|             return $name; | ||||
|         } | ||||
|  | ||||
|         // First, lets replace underscore by spaces. | ||||
|         $name = str_replace('_', ' ', $name); | ||||
| @@ -106,10 +129,21 @@ final class TestResult | ||||
|         $name = (string) preg_replace('/^test/', '', $name); | ||||
|  | ||||
|         // Removes spaces | ||||
|         $name = (string) trim($name); | ||||
|         $name = trim($name); | ||||
|  | ||||
|         // Finally, lower case everything | ||||
|         return (string) mb_strtolower($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; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -119,15 +153,15 @@ final class TestResult | ||||
|     { | ||||
|         switch ($type) { | ||||
|             case self::FAIL: | ||||
|                 return '✕'; | ||||
|                 return '⨯'; | ||||
|             case self::SKIPPED: | ||||
|                 return 's'; | ||||
|                 return '-'; | ||||
|             case self::RISKY: | ||||
|                 return 'r'; | ||||
|                 return '!'; | ||||
|             case self::INCOMPLETE: | ||||
|                 return 'i'; | ||||
|                 return '…'; | ||||
|             case self::WARN: | ||||
|                 return 'w'; | ||||
|                 return '!'; | ||||
|             case self::RUNS: | ||||
|                 return '•'; | ||||
|             default: | ||||
|   | ||||
| @@ -1,13 +1,6 @@ | ||||
| <?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. | ||||
|  */ | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace NunoMaduro\Collision\Adapters\Phpunit; | ||||
|  | ||||
|   | ||||
| @@ -1,25 +1,20 @@ | ||||
| <?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. | ||||
|  */ | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace NunoMaduro\Collision; | ||||
|  | ||||
| use NunoMaduro\Collision\Contracts\ArgumentFormatter as ArgumentFormatterContract; | ||||
|  | ||||
| /** | ||||
|  * This is an Collision Argument Formatter implementation. | ||||
|  * @internal | ||||
|  * | ||||
|  * @author Nuno Maduro <enunomaduro@gmail.com> | ||||
|  * @see \Tests\Unit\ArgumentFormatterTest | ||||
|  */ | ||||
| class ArgumentFormatter implements ArgumentFormatterContract | ||||
| final class ArgumentFormatter implements ArgumentFormatterContract | ||||
| { | ||||
|     private const MAX_STRING_LENGTH = 1000; | ||||
|  | ||||
|     /** | ||||
|      * {@inheritdoc} | ||||
|      */ | ||||
| @@ -30,7 +25,7 @@ class ArgumentFormatter implements ArgumentFormatterContract | ||||
|         foreach ($arguments as $argument) { | ||||
|             switch (true) { | ||||
|                 case is_string($argument): | ||||
|                     $result[] = '"' . $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); | ||||
|   | ||||
							
								
								
									
										61
									
								
								vendor/nunomaduro/collision/src/ConsoleColor.php
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										61
									
								
								vendor/nunomaduro/collision/src/ConsoleColor.php
									
									
									
									
										vendored
									
									
								
							| @@ -1,35 +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. | ||||
|  */ | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace NunoMaduro\Collision; | ||||
|  | ||||
| use NunoMaduro\Collision\Exceptions\InvalidStyleException; | ||||
| 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 | ||||
| final class ConsoleColor | ||||
| { | ||||
|     const FOREGROUND = 38; | ||||
|     const BACKGROUND = 48; | ||||
|     public const FOREGROUND = 38; | ||||
|     public const BACKGROUND = 48; | ||||
|  | ||||
|     const COLOR256_REGEXP = '~^(bg_)?color_([0-9]{1,3})$~'; | ||||
|     public const COLOR256_REGEXP = '~^(bg_)?color_(\d{1,3})$~'; | ||||
|  | ||||
|     const RESET_STYLE = 0; | ||||
|     public const RESET_STYLE = 0; | ||||
|  | ||||
|     /** @var bool */ | ||||
|     private $isSupported; | ||||
| @@ -38,7 +26,7 @@ class ConsoleColor | ||||
|     private $forceStyle = false; | ||||
|  | ||||
|     /** @var array */ | ||||
|     private $styles = [ | ||||
|     private const STYLES = [ | ||||
|         'none'      => null, | ||||
|         'bold'      => '1', | ||||
|         'dark'      => '2', | ||||
| @@ -145,7 +133,7 @@ class ConsoleColor | ||||
|      */ | ||||
|     public function setForceStyle($forceStyle) | ||||
|     { | ||||
|         $this->forceStyle = (bool) $forceStyle; | ||||
|         $this->forceStyle = $forceStyle; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -217,17 +205,16 @@ class ConsoleColor | ||||
|      */ | ||||
|     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); | ||||
|         // 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); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -237,9 +224,9 @@ class ConsoleColor | ||||
|     { | ||||
|         if (DIRECTORY_SEPARATOR === '\\') { | ||||
|             return function_exists('sapi_windows_vt100_support') && @sapi_windows_vt100_support(STDOUT); | ||||
|         } else { | ||||
|             return strpos(getenv('TERM'), '256color') !== false; | ||||
|         } | ||||
|  | ||||
|         return strpos(getenv('TERM'), '256color') !== false; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -247,7 +234,7 @@ class ConsoleColor | ||||
|      */ | ||||
|     public function getPossibleStyles() | ||||
|     { | ||||
|         return array_keys($this->styles); | ||||
|         return array_keys(self::STYLES); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -272,8 +259,8 @@ class ConsoleColor | ||||
|      */ | ||||
|     private function styleSequence($style) | ||||
|     { | ||||
|         if (array_key_exists($style, $this->styles)) { | ||||
|             return $this->styles[$style]; | ||||
|         if (array_key_exists($style, self::STYLES)) { | ||||
|             return self::STYLES[$style]; | ||||
|         } | ||||
|  | ||||
|         if (!$this->are256ColorsSupported()) { | ||||
| @@ -295,7 +282,7 @@ class ConsoleColor | ||||
|      */ | ||||
|     private function isValidStyle($style) | ||||
|     { | ||||
|         return array_key_exists($style, $this->styles) || preg_match(self::COLOR256_REGEXP, $style); | ||||
|         return array_key_exists($style, self::STYLES) || preg_match(self::COLOR256_REGEXP, $style); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|   | ||||
| @@ -1,13 +1,6 @@ | ||||
| <?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. | ||||
|  */ | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace NunoMaduro\Collision\Contracts\Adapters\Phpunit; | ||||
|  | ||||
|   | ||||
| @@ -1,13 +1,6 @@ | ||||
| <?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. | ||||
|  */ | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace NunoMaduro\Collision\Contracts\Adapters\Phpunit; | ||||
|  | ||||
| @@ -15,9 +8,7 @@ use PHPUnit\Framework\Test; | ||||
| use PHPUnit\Framework\TestListener; | ||||
|  | ||||
| /** | ||||
|  * This is an Collision Phpunit Adapter contract. | ||||
|  * | ||||
|  * @author Nuno Maduro <enunomaduro@gmail.com> | ||||
|  * @internal | ||||
|  */ | ||||
| interface Listener extends TestListener | ||||
| { | ||||
|   | ||||
| @@ -1,20 +1,11 @@ | ||||
| <?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. | ||||
|  */ | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace NunoMaduro\Collision\Contracts; | ||||
|  | ||||
| /** | ||||
|  * This is an Collision Argument Formatter contract. | ||||
|  * | ||||
|  * @author Nuno Maduro <enunomaduro@gmail.com> | ||||
|  * @internal | ||||
|  */ | ||||
| interface ArgumentFormatter | ||||
| { | ||||
|   | ||||
| @@ -1,13 +1,6 @@ | ||||
| <?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. | ||||
|  */ | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace NunoMaduro\Collision\Contracts; | ||||
|  | ||||
| @@ -15,9 +8,7 @@ use Symfony\Component\Console\Output\OutputInterface; | ||||
| use Whoops\Handler\HandlerInterface; | ||||
|  | ||||
| /** | ||||
|  * This is an Collision Handler contract. | ||||
|  * | ||||
|  * @author Nuno Maduro <enunomaduro@gmail.com> | ||||
|  * @internal | ||||
|  */ | ||||
| interface Handler extends HandlerInterface | ||||
| { | ||||
|   | ||||
| @@ -1,20 +1,11 @@ | ||||
| <?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. | ||||
|  */ | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace NunoMaduro\Collision\Contracts; | ||||
|  | ||||
| /** | ||||
|  * This is the Collision Highlighter contract. | ||||
|  * | ||||
|  * @author Nuno Maduro <enunomaduro@gmail.com> | ||||
|  * @internal | ||||
|  */ | ||||
| interface Highlighter | ||||
| { | ||||
|   | ||||
| @@ -1,20 +1,11 @@ | ||||
| <?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. | ||||
|  */ | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace NunoMaduro\Collision\Contracts; | ||||
|  | ||||
| /** | ||||
|  * This is an Collision Provider contract. | ||||
|  * | ||||
|  * @author Nuno Maduro <enunomaduro@gmail.com> | ||||
|  * @internal | ||||
|  */ | ||||
| interface Provider | ||||
| { | ||||
|   | ||||
							
								
								
									
										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 | ||||
| { | ||||
| } | ||||
| @@ -1,13 +1,6 @@ | ||||
| <?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. | ||||
|  */ | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace NunoMaduro\Collision\Contracts; | ||||
|  | ||||
| @@ -15,9 +8,7 @@ use Facade\IgnitionContracts\Solution; | ||||
| use Throwable; | ||||
|  | ||||
| /** | ||||
|  * This is an Collision Solutions Repository contract. | ||||
|  * | ||||
|  * @author Nuno Maduro <enunomaduro@gmail.com> | ||||
|  * @internal | ||||
|  */ | ||||
| interface SolutionsRepository | ||||
| { | ||||
|   | ||||
| @@ -1,5 +1,7 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| /** | ||||
|  * This file is part of Collision. | ||||
|  * | ||||
| @@ -15,9 +17,7 @@ use Symfony\Component\Console\Output\OutputInterface; | ||||
| use Whoops\Exception\Inspector; | ||||
|  | ||||
| /** | ||||
|  * This is the Collision Writer contract. | ||||
|  * | ||||
|  * @author Nuno Maduro <enunomaduro@gmail.com> | ||||
|  * @internal | ||||
|  */ | ||||
| interface Writer | ||||
| { | ||||
|   | ||||
							
								
								
									
										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 | ||||
| { | ||||
| } | ||||
| @@ -1,13 +1,6 @@ | ||||
| <?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. | ||||
|  */ | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace NunoMaduro\Collision\Exceptions; | ||||
|  | ||||
| @@ -18,10 +11,13 @@ use RuntimeException; | ||||
|  */ | ||||
| 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() | ||||
|     { | ||||
|         $message = 'This should not happen, please open an issue on collision repository: %s'; | ||||
|  | ||||
|         parent::__construct(sprintf($message, 'https://github.com/nunomaduro/collision/issues/new')); | ||||
|         parent::__construct(sprintf(self::MESSAGE, 'https://github.com/nunomaduro/collision/issues/new')); | ||||
|     } | ||||
| } | ||||
|   | ||||
							
								
								
									
										15
									
								
								vendor/nunomaduro/collision/src/Handler.php
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										15
									
								
								vendor/nunomaduro/collision/src/Handler.php
									
									
									
									
										vendored
									
									
								
							| @@ -1,13 +1,6 @@ | ||||
| <?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. | ||||
|  */ | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace NunoMaduro\Collision; | ||||
|  | ||||
| @@ -17,11 +10,11 @@ use Symfony\Component\Console\Output\OutputInterface; | ||||
| use Whoops\Handler\Handler as AbstractHandler; | ||||
|  | ||||
| /** | ||||
|  * This is an Collision Handler implementation. | ||||
|  * @internal | ||||
|  * | ||||
|  * @author Nuno Maduro <enunomaduro@gmail.com> | ||||
|  * @see \Tests\Unit\HandlerTest | ||||
|  */ | ||||
| class Handler extends AbstractHandler implements HandlerContract | ||||
| final class Handler extends AbstractHandler implements HandlerContract | ||||
| { | ||||
|     /** | ||||
|      * Holds an instance of the writer. | ||||
|   | ||||
							
								
								
									
										158
									
								
								vendor/nunomaduro/collision/src/Highlighter.php
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										158
									
								
								vendor/nunomaduro/collision/src/Highlighter.php
									
									
									
									
										vendored
									
									
								
							| @@ -1,84 +1,94 @@ | ||||
| <?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. | ||||
|  */ | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace NunoMaduro\Collision; | ||||
|  | ||||
| use NunoMaduro\Collision\Contracts\Highlighter as HighlighterContract; | ||||
|  | ||||
| /** | ||||
|  * This is an Collision Highlighter implementation. | ||||
|  * | ||||
|  * Code originally from { JakubOnderka\\PhpConsoleColor }. But the package got deprecated. | ||||
|  * | ||||
|  * @internal | ||||
|  * | ||||
|  * @final | ||||
|  */ | ||||
| class Highlighter implements HighlighterContract | ||||
| 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 $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'], | ||||
|     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'], | ||||
|     ]; | ||||
|  | ||||
|     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 = [ | ||||
|     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 => 'red', | ||||
|         self::LINE_NUMBER      => 'dark_gray', | ||||
|         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) | ||||
|     public function __construct(ConsoleColor $color = null, bool $UTF8 = true) | ||||
|     { | ||||
|         $this->color = $color ?: new ConsoleColor(); | ||||
|  | ||||
|         foreach ($this->defaultTheme as $name => $styles) { | ||||
|         foreach (self::DEFAULT_THEME as $name => $styles) { | ||||
|             if (!$this->color->hasTheme($name)) { | ||||
|                 $this->color->addTheme($name, $styles); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         foreach ($this->theme as $name => $styles) { | ||||
|             $this->color->addTheme((string) $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 .= ' '; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -94,10 +104,8 @@ class Highlighter implements HighlighterContract | ||||
|      * @param int    $lineNumber | ||||
|      * @param int    $linesBefore | ||||
|      * @param int    $linesAfter | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     public function getCodeSnippet($source, $lineNumber, $linesBefore = 2, $linesAfter = 2) | ||||
|     public function getCodeSnippet($source, $lineNumber, $linesBefore = 2, $linesAfter = 2): string | ||||
|     { | ||||
|         $tokenLines = $this->getHighlightedLines($source); | ||||
|  | ||||
| @@ -113,10 +121,8 @@ class Highlighter implements HighlighterContract | ||||
|  | ||||
|     /** | ||||
|      * @param string $source | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     private function getHighlightedLines($source) | ||||
|     private function getHighlightedLines($source): array | ||||
|     { | ||||
|         $source = str_replace(["\r\n", "\r"], "\n", $source); | ||||
|         $tokens = $this->tokenize($source); | ||||
| @@ -126,10 +132,8 @@ class Highlighter implements HighlighterContract | ||||
|  | ||||
|     /** | ||||
|      * @param string $source | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     private function tokenize($source) | ||||
|     private function tokenize($source): array | ||||
|     { | ||||
|         $tokens = token_get_all($source); | ||||
|  | ||||
| @@ -148,7 +152,6 @@ class Highlighter implements HighlighterContract | ||||
|                     case T_CLOSE_TAG: | ||||
|                     case T_STRING: | ||||
|                     case T_VARIABLE: | ||||
|  | ||||
|                         // Constants | ||||
|                     case T_DIR: | ||||
|                     case T_FILE: | ||||
| @@ -204,10 +207,7 @@ class Highlighter implements HighlighterContract | ||||
|         return $output; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return array | ||||
|      */ | ||||
|     private function splitToLines(array $tokens) | ||||
|     private function splitToLines(array $tokens): array | ||||
|     { | ||||
|         $lines = []; | ||||
|  | ||||
| @@ -232,10 +232,7 @@ class Highlighter implements HighlighterContract | ||||
|         return $lines; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return array | ||||
|      */ | ||||
|     private function colorLines(array $tokenLines) | ||||
|     private function colorLines(array $tokenLines): array | ||||
|     { | ||||
|         $lines = []; | ||||
|         foreach ($tokenLines as $lineCount => $tokenLine) { | ||||
| @@ -256,24 +253,47 @@ class Highlighter implements HighlighterContract | ||||
|  | ||||
|     /** | ||||
|      * @param int|null $markLine | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     private function lineNumbers(array $lines, $markLine = null) | ||||
|     private function lineNumbers(array $lines, $markLine = null): string | ||||
|     { | ||||
|         end($lines); | ||||
|         $lineStrlen = strlen(key($lines) + 1); | ||||
|  | ||||
|         $snippet = ''; | ||||
|         $lineStrlen = strlen((string) (array_key_last($lines) + 1)); | ||||
|         $lineStrlen = $lineStrlen < self::WIDTH ? self::WIDTH : $lineStrlen; | ||||
|         $snippet    = ''; | ||||
|         $mark       = '  ' . $this->arrow . ' '; | ||||
|         foreach ($lines as $i => $line) { | ||||
|             if ($markLine !== null) { | ||||
|                 $snippet .= ($markLine === $i + 1 ? $this->color->apply(self::ACTUAL_LINE_MARK, '  > ') : '    '); | ||||
|             } | ||||
|             $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 .= $this->color->apply(self::LINE_NUMBER, str_pad($i + 1, $lineStrlen, ' ', STR_PAD_LEFT) . '| '); | ||||
|             $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)); | ||||
|     } | ||||
| } | ||||
|   | ||||
							
								
								
									
										15
									
								
								vendor/nunomaduro/collision/src/Provider.php
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										15
									
								
								vendor/nunomaduro/collision/src/Provider.php
									
									
									
									
										vendored
									
									
								
							| @@ -1,13 +1,6 @@ | ||||
| <?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. | ||||
|  */ | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace NunoMaduro\Collision; | ||||
|  | ||||
| @@ -17,11 +10,11 @@ use Whoops\Run; | ||||
| use Whoops\RunInterface; | ||||
|  | ||||
| /** | ||||
|  * This is an Collision Provider implementation. | ||||
|  * @internal | ||||
|  * | ||||
|  * @author Nuno Maduro <enunomaduro@gmail.com> | ||||
|  * @see \Tests\Unit\ProviderTest | ||||
|  */ | ||||
| class Provider implements ProviderContract | ||||
| final class Provider implements ProviderContract | ||||
| { | ||||
|     /** | ||||
|      * Holds an instance of the Run. | ||||
|   | ||||
| @@ -1,13 +1,6 @@ | ||||
| <?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. | ||||
|  */ | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace NunoMaduro\Collision\SolutionsRepositories; | ||||
|  | ||||
| @@ -15,11 +8,9 @@ use NunoMaduro\Collision\Contracts\SolutionsRepository; | ||||
| use Throwable; | ||||
|  | ||||
| /** | ||||
|  * This is an Collision Null Solutions Provider implementation. | ||||
|  * | ||||
|  * @author Nuno Maduro <enunomaduro@gmail.com> | ||||
|  * @internal | ||||
|  */ | ||||
| class NullSolutionsRepository implements SolutionsRepository | ||||
| final class NullSolutionsRepository implements SolutionsRepository | ||||
| { | ||||
|     /** | ||||
|      * {@inheritdoc} | ||||
|   | ||||
							
								
								
									
										58
									
								
								vendor/nunomaduro/collision/src/Writer.php
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										58
									
								
								vendor/nunomaduro/collision/src/Writer.php
									
									
									
									
										vendored
									
									
								
							| @@ -1,18 +1,13 @@ | ||||
| <?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. | ||||
|  */ | ||||
| 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; | ||||
| @@ -22,16 +17,16 @@ use Whoops\Exception\Frame; | ||||
| use Whoops\Exception\Inspector; | ||||
|  | ||||
| /** | ||||
|  * This is an Collision Writer implementation. | ||||
|  * @internal | ||||
|  * | ||||
|  * @author Nuno Maduro <enunomaduro@gmail.com> | ||||
|  * @see \Tests\Unit\WriterTest | ||||
|  */ | ||||
| class Writer implements WriterContract | ||||
| final class Writer implements WriterContract | ||||
| { | ||||
|     /** | ||||
|      * The number of frames if no verbosity is specified. | ||||
|      */ | ||||
|     const VERBOSITY_NORMAL_FRAMES = 1; | ||||
|     public const VERBOSITY_NORMAL_FRAMES = 1; | ||||
|  | ||||
|     /** | ||||
|      * Holds an instance of the solutions repository. | ||||
| @@ -116,15 +111,20 @@ class Writer implements WriterContract | ||||
|  | ||||
|         $editorFrame = array_shift($frames); | ||||
|  | ||||
|         if ($this->showEditor && $editorFrame !== null) { | ||||
|         $exception = $inspector->getException(); | ||||
|  | ||||
|         if ($this->showEditor | ||||
|             && $editorFrame !== null | ||||
|             && !$exception instanceof RenderlessEditor | ||||
|         ) { | ||||
|             $this->renderEditor($editorFrame); | ||||
|         } | ||||
|  | ||||
|         $this->renderSolution($inspector); | ||||
|  | ||||
|         if ($this->showTrace && !empty($frames)) { | ||||
|         if ($this->showTrace && !empty($frames) && !$exception instanceof RenderlessTrace) { | ||||
|             $this->renderTrace($frames); | ||||
|         } else { | ||||
|         } elseif (!$exception instanceof RenderlessEditor) { | ||||
|             $this->output->writeln(''); | ||||
|         } | ||||
|     } | ||||
| @@ -202,7 +202,10 @@ class Writer implements WriterContract | ||||
|                     } | ||||
|  | ||||
|                     foreach ($this->ignore as $ignore) { | ||||
|                         if (preg_match($ignore, $frame->getFile())) { | ||||
|                         // 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; | ||||
|                         } | ||||
|                     } | ||||
| @@ -267,15 +270,17 @@ class Writer implements WriterContract | ||||
|      */ | ||||
|     protected function renderEditor(Frame $frame): WriterContract | ||||
|     { | ||||
|         $file = $this->getFileRelativePath((string) $frame->getFile()); | ||||
|         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 . '</>'); | ||||
|             // 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()); | ||||
|             $content = $this->highlighter->highlight((string) $frame->getFileContents(), (int) $frame->getLine()); | ||||
|  | ||||
|         $this->output->writeln($content); | ||||
|             $this->output->writeln($content); | ||||
|         } | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
| @@ -317,15 +322,6 @@ class Writer implements WriterContract | ||||
|             $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; | ||||
|     } | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 RafficMohammed
					RafficMohammed