feat: adds shell

This commit is contained in:
Nuno Maduro
2025-07-21 13:25:50 +01:00
parent 8f810bf2a2
commit e0695a13cb
3 changed files with 110 additions and 1 deletions

View File

@ -57,7 +57,8 @@
"require-dev": { "require-dev": {
"pestphp/pest-dev-tools": "^4.0.0", "pestphp/pest-dev-tools": "^4.0.0",
"pestphp/pest-plugin-browser": "^4.0.0", "pestphp/pest-plugin-browser": "^4.0.0",
"pestphp/pest-plugin-type-coverage": "^4.0.0" "pestphp/pest-plugin-type-coverage": "^4.0.0",
"psy/psysh": "^0.12.9"
}, },
"minimum-stability": "dev", "minimum-stability": "dev",
"prefer-stable": true, "prefer-stable": true,

View File

@ -10,6 +10,7 @@ use Pest\Preset;
use Pest\Support\ChainableClosure; use Pest\Support\ChainableClosure;
use Pest\Support\ExceptionTrace; use Pest\Support\ExceptionTrace;
use Pest\Support\Reflection; use Pest\Support\Reflection;
use Pest\Support\Shell;
use Pest\TestSuite; use Pest\TestSuite;
use PHPUnit\Framework\Attributes\PostCondition; use PHPUnit\Framework\Attributes\PostCondition;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
@ -477,4 +478,12 @@ trait Testable
'notes' => self::$__latestNotes, 'notes' => self::$__latestNotes,
]; ];
} }
/**
* Opens a shell for the test case.
*/
public function shell(): void
{
Shell::open();
}
} }

99
src/Support/Shell.php Normal file
View File

@ -0,0 +1,99 @@
<?php
declare(strict_types=1);
namespace Pest\Support;
use Composer\Autoload\ClassLoader;
use Illuminate\Support\Env;
use Laravel\Tinker\ClassAliasAutoloader;
use Psy\Configuration;
use Psy\Shell as PsyShell;
use Psy\VersionUpdater\Checker;
/**
* @internal
*/
final class Shell
{
/**
* Creates a new interactive shell.
*/
public static function open(): void
{
$config = new Configuration;
$config->setUpdateCheck(Checker::NEVER);
$config->getPresenter()->addCasters(self::casters());
$shell = new PsyShell($config);
$loader = self::tinkered($shell);
try {
$shell->run();
} finally {
$loader?->unregister();
}
}
/**
* Returns the casters for the Psy Shell.
*
* @return array<string, callable>
*/
private static function casters(): array
{
$casters = [
'Illuminate\Support\Collection' => 'Laravel\Tinker\TinkerCaster::castCollection',
'Illuminate\Support\HtmlString' => 'Laravel\Tinker\TinkerCaster::castHtmlString',
'Illuminate\Support\Stringable' => 'Laravel\Tinker\TinkerCaster::castStringable',
];
if (class_exists('Illuminate\Database\Eloquent\Model')) {
$casters['Illuminate\Database\Eloquent\Model'] = 'Laravel\Tinker\TinkerCaster::castModel';
}
if (class_exists('Illuminate\Process\ProcessResult')) {
$casters['Illuminate\Process\ProcessResult'] = 'Laravel\Tinker\TinkerCaster::castProcessResult';
}
if (class_exists('Illuminate\Foundation\Application')) {
$casters['Illuminate\Foundation\Application'] = 'Laravel\Tinker\TinkerCaster::castApplication';
}
if (function_exists('app') === false) {
return $casters; // @phpstan-ignore-line
}
$config = app()->make('config');
return array_merge($casters, (array) $config->get('tinker.casters', []));
}
/**
* Tinkers the current shell, if the Tinker package is available.
*/
private static function tinkered(PsyShell $shell): ?ClassLoader
{
if (function_exists('app') === false
|| ! class_exists(Env::class)
|| ! class_exists(ClassAliasAutoloader::class)
) {
return null;
}
$path = Env::get('COMPOSER_VENDOR_DIR', app()->basePath().DIRECTORY_SEPARATOR.'vendor');
$path .= '/composer/autoload_classmap.php';
$config = app()->make('config');
$loader = ClassAliasAutoloader::register(
$shell, $path, $config->get('tinker.alias', []), $config->get('tinker.dont_alias', [])
);
return $loader;
}
}