From 9f66fed89621b70da937ff4ff25bfca73b9df8d1 Mon Sep 17 00:00:00 2001 From: Saeed Vaziry Date: Mon, 20 Jan 2025 12:27:36 +0100 Subject: [PATCH 1/2] fix dd --- index.php | 9 --------- src/Loaders/BaseLoader.php | 5 +---- src/Tinker.php | 4 +--- 3 files changed, 2 insertions(+), 16 deletions(-) diff --git a/index.php b/index.php index 09a040c..71d8110 100644 --- a/index.php +++ b/index.php @@ -11,15 +11,6 @@ exit(1); } -function dd(...$args) -{ - if (count($args) === 1) { - return $args[0]; - } - - return $args; -} - $loader = Loader::load($arguments[1]); if ($loader === null) { diff --git a/src/Loaders/BaseLoader.php b/src/Loaders/BaseLoader.php index d58db38..35e992a 100644 --- a/src/Loaders/BaseLoader.php +++ b/src/Loaders/BaseLoader.php @@ -25,10 +25,7 @@ public function init(): void ]); $config->setVerbosity(Configuration::VERBOSITY_QUIET); $config->setHistoryFile(defined('PHP_WINDOWS_VERSION_BUILD') ? 'null' : '/dev/null'); - $config->setRawOutput(false); - if (getenv('KUBERNETES_SERVICE_HOST') || defined('PHP_WINDOWS_VERSION_BUILD')) { - $config->setUsePcntl(false); - } + $config->setUsePcntl(false); if (class_exists('Illuminate\Support\Collection') && class_exists('Laravel\Tinker\TinkerCaster')) { $config->getPresenter()->addCasters([ diff --git a/src/Tinker.php b/src/Tinker.php index 3f5474a..235a53c 100644 --- a/src/Tinker.php +++ b/src/Tinker.php @@ -31,9 +31,7 @@ public function execute(string $phpCode): string $this->shell->addInput($phpCode); - if (defined('PHP_WINDOWS_VERSION_BUILD')) { - $this->shell->addInput("\necho('TWEAKPHP_END'); exit();"); - } + $this->shell->addInput("\necho('TWEAKPHP_END'); exit();"); $closure = new ExecutionLoopClosure($this->shell); From 947f40f20b1624ade1a66d76c6322d38435f392a Mon Sep 17 00:00:00 2001 From: Saeed Vaziry Date: Sat, 25 Jan 2025 12:18:21 +0100 Subject: [PATCH 2/2] add stacked output --- composer.json | 5 ++- composer.lock | 28 +++++++-------- index.php | 3 +- src/Casters/LaravelCaster.php | 29 +++++++++++++++ src/Loaders/BaseLoader.php | 33 +++++------------ src/Loaders/LoaderInterface.php | 2 +- src/Psy/Configuration.php | 15 ++++++++ src/Psy/Presenter.php | 54 ++++++++++++++++++++++++++++ src/Tinker.php | 63 ++++++++++++++++++--------------- 9 files changed, 162 insertions(+), 70 deletions(-) create mode 100644 src/Casters/LaravelCaster.php create mode 100644 src/Psy/Configuration.php create mode 100644 src/Psy/Presenter.php diff --git a/composer.json b/composer.json index 90a932a..cde727d 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,10 @@ "require": { "php": ">=7.4", "ext-json": "*", - "psy/psysh": "*" + "psy/psysh": "*", + "nikic/php-parser": "*", + "symfony/var-dumper": "*", + "symfony/console": "*" }, "minimum-stability": "stable", "prefer-stable": true diff --git a/composer.lock b/composer.lock index 2a5a09e..138cbdb 100644 --- a/composer.lock +++ b/composer.lock @@ -8,16 +8,16 @@ "packages": [ { "name": "nikic/php-parser", - "version": "v5.3.1", + "version": "v5.4.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "8eea230464783aa9671db8eea6f8c6ac5285794b" + "reference": "447a020a1f875a434d62f2a401f53b82a396e494" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/8eea230464783aa9671db8eea6f8c6ac5285794b", - "reference": "8eea230464783aa9671db8eea6f8c6ac5285794b", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/447a020a1f875a434d62f2a401f53b82a396e494", + "reference": "447a020a1f875a434d62f2a401f53b82a396e494", "shasum": "" }, "require": { @@ -60,9 +60,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.3.1" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.4.0" }, - "time": "2024-10-08T18:51:32+00:00" + "time": "2024-12-30T11:07:19+00:00" }, { "name": "psr/container", @@ -308,12 +308,12 @@ }, "type": "library", "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, "branch-alias": { "dev-main": "3.5-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" } }, "autoload": { @@ -698,12 +698,12 @@ }, "type": "library", "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, "branch-alias": { "dev-main": "3.5-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" } }, "autoload": { diff --git a/index.php b/index.php index 71d8110..0675ddd 100644 --- a/index.php +++ b/index.php @@ -44,6 +44,7 @@ echo 'Invalid arguments'.PHP_EOL; exit(1); } - echo $loader->execute(base64_decode($arguments[3])).PHP_EOL; + $output = json_encode($loader->execute(base64_decode($arguments[3]))); + echo 'TWEAKPHP_RESULT:'.$output.PHP_EOL; break; } diff --git a/src/Casters/LaravelCaster.php b/src/Casters/LaravelCaster.php new file mode 100644 index 0000000..cce9d50 --- /dev/null +++ b/src/Casters/LaravelCaster.php @@ -0,0 +1,29 @@ + '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'; + } + + return $casters; + } +} diff --git a/src/Loaders/BaseLoader.php b/src/Loaders/BaseLoader.php index 35e992a..2f31717 100644 --- a/src/Loaders/BaseLoader.php +++ b/src/Loaders/BaseLoader.php @@ -2,9 +2,11 @@ namespace TweakPHP\Client\Loaders; -use Psy\Configuration; +use Psy\Configuration as ConfigurationAlias; use Psy\VersionUpdater\Checker; +use TweakPHP\Client\Casters\LaravelCaster; use TweakPHP\Client\OutputModifiers\CustomOutputModifier; +use TweakPHP\Client\Psy\Configuration; use TweakPHP\Client\Tinker; abstract class BaseLoader implements LoaderInterface @@ -17,39 +19,22 @@ public function init(): void 'configFile' => null, ]); $config->setUpdateCheck(Checker::NEVER); - $config->setRawOutput(true); - $config->setInteractiveMode(Configuration::INTERACTIVE_MODE_DISABLED); - $config->setColorMode(Configuration::COLOR_MODE_DISABLED); + $config->setInteractiveMode(ConfigurationAlias::INTERACTIVE_MODE_DISABLED); + $config->setColorMode(ConfigurationAlias::COLOR_MODE_DISABLED); + $config->setRawOutput(false); $config->setTheme([ 'prompt' => '', ]); - $config->setVerbosity(Configuration::VERBOSITY_QUIET); $config->setHistoryFile(defined('PHP_WINDOWS_VERSION_BUILD') ? 'null' : '/dev/null'); $config->setUsePcntl(false); - if (class_exists('Illuminate\Support\Collection') && class_exists('Laravel\Tinker\TinkerCaster')) { - $config->getPresenter()->addCasters([ - \Illuminate\Support\Collection::class => 'Laravel\Tinker\TinkerCaster::castCollection', - ]); - } - if (class_exists('Illuminate\Database\Eloquent\Model') && class_exists('Laravel\Tinker\TinkerCaster')) { - $config->getPresenter()->addCasters([ - \Illuminate\Database\Eloquent\Model::class => 'Laravel\Tinker\TinkerCaster::castModel', - ]); - } - if (class_exists('Illuminate\Foundation\Application') && class_exists('Laravel\Tinker\TinkerCaster')) { - $config->getPresenter()->addCasters([ - \Illuminate\Foundation\Application::class => 'Laravel\Tinker\TinkerCaster::castApplication', - ]); - } + $config->getPresenter()->addCasters(LaravelCaster::casters()); $this->tinker = new Tinker(new CustomOutputModifier, $config); } - public function execute(string $code): string + public function execute(string $code): array { - $output = $this->tinker->execute($code); - - return trim($output); + return $this->tinker->execute($code); } } diff --git a/src/Loaders/LoaderInterface.php b/src/Loaders/LoaderInterface.php index e445ea7..443f8e4 100644 --- a/src/Loaders/LoaderInterface.php +++ b/src/Loaders/LoaderInterface.php @@ -12,5 +12,5 @@ public function version(): string; public function init(): void; - public function execute(string $code): string; + public function execute(string $code): array; } diff --git a/src/Psy/Configuration.php b/src/Psy/Configuration.php new file mode 100644 index 0000000..2fe06b2 --- /dev/null +++ b/src/Psy/Configuration.php @@ -0,0 +1,15 @@ +presenter)) { + $this->presenter = new Presenter($this->getOutput()->getFormatter(), $this->forceArrayIndexes()); + } + + return $this->presenter; + } +} diff --git a/src/Psy/Presenter.php b/src/Psy/Presenter.php new file mode 100644 index 0000000..49a0611 --- /dev/null +++ b/src/Psy/Presenter.php @@ -0,0 +1,54 @@ +cloner = new Cloner; + } + + public function addCasters(array $casters) + { + parent::addCasters($casters); + + $this->cloner->addCasters($casters); + } + + public function present($value, ?int $depth = null, int $options = 0): string + { + $dumper = new HtmlDumper; + $dumper->setDumpHeader(''); + $data = $this->cloner->cloneVar($value, ! ($options & self::VERBOSE) ? Caster::EXCLUDE_VERBOSE : 0); + if ($depth !== null) { + $data = $data->withMaxDepth($depth); + } + + $output = ''; + $dumper->dump($data, function ($line, $depth) use (&$output) { + if ($depth >= 0) { + if ($output !== '') { + $output .= \PHP_EOL; + } + $output .= \str_repeat(' ', $depth).$line; + } + }); + + if (isset(Tinker::$statements[Tinker::$current])) { + Tinker::$statements[Tinker::$current]['html'] = $output; + } + + return parent::present($value, $depth, $options); + } +} diff --git a/src/Tinker.php b/src/Tinker.php index 235a53c..02d9cb7 100644 --- a/src/Tinker.php +++ b/src/Tinker.php @@ -2,6 +2,8 @@ namespace TweakPHP\Client; +use PhpParser\ParserFactory; +use PhpParser\PrettyPrinter\Standard; use Psy\Configuration; use Psy\ExecutionLoopClosure; use Psy\Shell; @@ -16,6 +18,10 @@ class Tinker protected OutputModifier $outputModifier; + public static array $statements = []; + + public static int $current = 0; + public function __construct(OutputModifier $outputModifier, Configuration $config) { $this->output = new BufferedOutput; @@ -25,21 +31,41 @@ public function __construct(OutputModifier $outputModifier, Configuration $confi $this->outputModifier = $outputModifier; } - public function execute(string $phpCode): string + public function execute(string $rawPHPCode): array { - $phpCode = $this->removeComments($phpCode); + if (strpos($rawPHPCode, 'createForHostVersion(); + $prettyPrinter = new Standard; + foreach ($parser->parse($rawPHPCode) as $key => $stmt) { + $code = $prettyPrinter->prettyPrint([$stmt]); + self::$current = $key; + self::$statements[] = [ + 'line' => $stmt->getStartLine(), + 'code' => $code, + ]; + $output = $this->doExecute($code); + self::$statements[$key]['output'] = $output; + } - $this->shell->addInput($phpCode); + return [ + 'output' => self::$statements, + ]; + } + protected function doExecute(string $code): string + { + $this->shell->addInput($code); $this->shell->addInput("\necho('TWEAKPHP_END'); exit();"); - + $this->output = new BufferedOutput; + $this->shell->setOutput($this->output); $closure = new ExecutionLoopClosure($this->shell); - $closure->execute(); + $result = $this->outputModifier->modify($this->cleanOutput($this->output->fetch())); - $output = $this->cleanOutput($this->output->fetch()); - - return $this->outputModifier->modify($output); + return trim($result); } protected function createShell(BufferedOutput $output, Configuration $config): Shell @@ -51,27 +77,6 @@ protected function createShell(BufferedOutput $output, Configuration $config): S return $shell; } - public function removeComments(string $code): string - { - $tokens = token_get_all("'); - $result = ''; - - foreach ($tokens as $token) { - if (is_array($token)) { - [$id, $text] = $token; - - if (in_array($id, [T_COMMENT, T_DOC_COMMENT, T_OPEN_TAG, T_CLOSE_TAG])) { - continue; - } - $result .= $text; - } else { - $result .= $token; - } - } - - return $result; - } - protected function cleanOutput(string $output): string { $output = preg_replace('/(?s)()|Exit: Ctrl\+D/ms', '$2', $output);