Skip to content

Commit aa5e5c3

Browse files
authored
[fix] capture VarDumper output and enable auto-dump for AST expressions (#24)
1 parent 676a5e9 commit aa5e5c3

3 files changed

Lines changed: 324 additions & 75 deletions

File tree

src/Psy/Presenter.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,11 @@ public function present($value, ?int $depth = null, int $options = 0): string
4646
});
4747

4848
if (isset(Tinker::$statements[Tinker::$current])) {
49-
Tinker::$statements[Tinker::$current]['html'] = $output;
49+
if (isset(Tinker::$statements[Tinker::$current]['html']) && Tinker::$statements[Tinker::$current]['html'] !== '') {
50+
Tinker::$statements[Tinker::$current]['html'] .= \PHP_EOL.$output;
51+
} else {
52+
Tinker::$statements[Tinker::$current]['html'] = $output;
53+
}
5054
}
5155

5256
return parent::present($value, $depth, $options);

src/Tinker.php

Lines changed: 149 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,30 @@
22

33
namespace TweakPHP\Client;
44

5+
use PhpParser\Node\Stmt\Expression;
56
use PhpParser\ParserFactory;
67
use PhpParser\PrettyPrinter\Standard;
8+
use Psy\CodeCleaner\NoReturnValue;
79
use Psy\Configuration;
810
use Psy\Exception\BreakException;
911
use Psy\Shell;
1012
use Symfony\Component\Console\Output\BufferedOutput;
1113
use Symfony\Component\Console\Output\OutputInterface;
14+
use Symfony\Component\VarDumper\VarDumper;
1215
use TweakPHP\Client\Database\QueryCollector;
1316
use TweakPHP\Client\Output\StreamingOutput;
1417
use TweakPHP\Client\OutputModifiers\OutputModifier;
1518

1619
class Tinker
1720
{
21+
public static bool $dumpOccurred = false;
22+
1823
protected OutputInterface $output;
1924

2025
protected Shell $shell;
2126

27+
protected Configuration $config;
28+
2229
protected OutputModifier $outputModifier;
2330

2431
public static array $statements = [];
@@ -29,6 +36,8 @@ public function __construct(OutputModifier $outputModifier, Configuration $confi
2936
{
3037
$this->output = new BufferedOutput;
3138

39+
$this->config = $config;
40+
3241
$this->shell = $this->createShell($this->output, $config);
3342

3443
$this->outputModifier = $outputModifier;
@@ -38,58 +47,68 @@ public function execute(string $rawPHPCode): array
3847
{
3948
self::$statements = [];
4049

41-
$rawPHPCode = $this->normalizePHPCode($rawPHPCode);
42-
43-
$parserFactory = new ParserFactory;
44-
$parser = method_exists($parserFactory, 'createForHostVersion')
45-
? $parserFactory->createForHostVersion()
46-
: $parserFactory->create(ParserFactory::PREFER_PHP7);
47-
$prettyPrinter = new Standard;
48-
foreach ($parser->parse($rawPHPCode) as $key => $stmt) {
49-
$code = $prettyPrinter->prettyPrint([$stmt]);
50-
self::$current = $key;
51-
self::$statements[] = [
52-
'line' => $stmt->getStartLine(),
53-
'code' => $code,
54-
];
50+
$this->registerVarDumperHandler($this->config);
5551

56-
QueryCollector::start();
57-
try {
58-
$output = $this->doExecute($code);
59-
} finally {
60-
$queries = QueryCollector::stop();
61-
self::$statements[$key]['queries'] = $queries;
52+
try {
53+
$rawPHPCode = $this->normalizePHPCode($rawPHPCode);
54+
55+
$parserFactory = new ParserFactory;
56+
$parser = method_exists($parserFactory, 'createForHostVersion')
57+
? $parserFactory->createForHostVersion()
58+
: $parserFactory->create(ParserFactory::PREFER_PHP7);
59+
$prettyPrinter = new Standard;
60+
foreach ($parser->parse($rawPHPCode) as $key => $stmt) {
61+
$code = $prettyPrinter->prettyPrint([$stmt]);
62+
$executableCode = $stmt instanceof Expression
63+
? $prettyPrinter->prettyPrintExpr($stmt->expr)
64+
: $code;
65+
66+
self::$current = $key;
67+
self::$statements[] = [
68+
'line' => $stmt->getStartLine(),
69+
'code' => $code,
70+
];
71+
72+
QueryCollector::start();
73+
try {
74+
$output = $this->doExecute($executableCode);
75+
} finally {
76+
$queries = QueryCollector::stop();
77+
self::$statements[$key]['queries'] = $queries;
78+
}
79+
80+
self::$statements[$key]['output'] = $output;
81+
82+
$queryErrors = QueryCollector::errors();
83+
if ($queryErrors !== []) {
84+
self::$statements[$key]['query_errors'] = $queryErrors;
85+
}
6286
}
6387

64-
self::$statements[$key]['output'] = $output;
65-
66-
$queryErrors = QueryCollector::errors();
67-
if ($queryErrors !== []) {
68-
self::$statements[$key]['query_errors'] = $queryErrors;
88+
$allQueries = [];
89+
$allQueryErrors = [];
90+
foreach (self::$statements as $stmt) {
91+
if (isset($stmt['queries'])) {
92+
$allQueries = array_merge($allQueries, $stmt['queries']);
93+
}
94+
if (isset($stmt['query_errors'])) {
95+
$allQueryErrors = array_merge($allQueryErrors, $stmt['query_errors']);
96+
}
6997
}
70-
}
7198

72-
$allQueries = [];
73-
$allQueryErrors = [];
74-
foreach (self::$statements as $stmt) {
75-
if (isset($stmt['queries'])) {
76-
$allQueries = array_merge($allQueries, $stmt['queries']);
77-
}
78-
if (isset($stmt['query_errors'])) {
79-
$allQueryErrors = array_merge($allQueryErrors, $stmt['query_errors']);
80-
}
81-
}
99+
$result = [
100+
'output' => self::$statements,
101+
'queries' => $allQueries,
102+
];
82103

83-
$result = [
84-
'output' => self::$statements,
85-
'queries' => $allQueries,
86-
];
104+
if ($allQueryErrors !== []) {
105+
$result['query_errors'] = $allQueryErrors;
106+
}
87107

88-
if ($allQueryErrors !== []) {
89-
$result['query_errors'] = $allQueryErrors;
108+
return $result;
109+
} finally {
110+
$this->restoreVarDumperHandler();
90111
}
91-
92-
return $result;
93112
}
94113

95114
/**
@@ -99,35 +118,45 @@ public function executeStreaming(string $rawPHPCode, callable $onEvent): void
99118
{
100119
self::$statements = [];
101120

102-
$rawPHPCode = $this->normalizePHPCode($rawPHPCode);
103-
104-
$parserFactory = new ParserFactory;
105-
$parser = method_exists($parserFactory, 'createForHostVersion')
106-
? $parserFactory->createForHostVersion()
107-
: $parserFactory->create(ParserFactory::PREFER_PHP7);
108-
$prettyPrinter = new Standard;
109-
110-
foreach ($parser->parse($rawPHPCode) as $key => $stmt) {
111-
$code = $prettyPrinter->prettyPrint([$stmt]);
112-
self::$current = $key;
113-
self::$statements[] = [
114-
'line' => $stmt->getStartLine(),
115-
'code' => $code,
116-
];
117-
118-
$onEvent([
119-
'type' => 'statement.started',
120-
'index' => $key,
121-
'line' => $stmt->getStartLine(),
122-
'code' => $code,
123-
]);
121+
$this->registerVarDumperHandler($this->config);
124122

125-
if (! $this->executeStreamingStatement($code, $key, $onEvent)) {
126-
return;
123+
try {
124+
$rawPHPCode = $this->normalizePHPCode($rawPHPCode);
125+
126+
$parserFactory = new ParserFactory;
127+
$parser = method_exists($parserFactory, 'createForHostVersion')
128+
? $parserFactory->createForHostVersion()
129+
: $parserFactory->create(ParserFactory::PREFER_PHP7);
130+
$prettyPrinter = new Standard;
131+
132+
foreach ($parser->parse($rawPHPCode) as $key => $stmt) {
133+
$code = $prettyPrinter->prettyPrint([$stmt]);
134+
$executableCode = $stmt instanceof Expression
135+
? $prettyPrinter->prettyPrintExpr($stmt->expr)
136+
: $code;
137+
138+
self::$current = $key;
139+
self::$statements[] = [
140+
'line' => $stmt->getStartLine(),
141+
'code' => $code,
142+
];
143+
144+
$onEvent([
145+
'type' => 'statement.started',
146+
'index' => $key,
147+
'line' => $stmt->getStartLine(),
148+
'code' => $code,
149+
]);
150+
151+
if (! $this->executeStreamingStatement($executableCode, $key, $onEvent)) {
152+
return;
153+
}
127154
}
128-
}
129155

130-
$onEvent(['type' => 'completed']);
156+
$onEvent(['type' => 'completed']);
157+
} finally {
158+
$this->restoreVarDumperHandler();
159+
}
131160
}
132161

133162
protected function executeStreamingStatement(string $code, int $key, callable $onEvent): bool
@@ -197,19 +226,30 @@ protected function executeStreamingStatement(string $code, int $key, callable $o
197226

198227
protected function doExecute(string $code): string
199228
{
229+
self::$dumpOccurred = false;
200230
$this->output = new BufferedOutput;
201231
$this->shell->setOutput($this->output);
202-
$this->shell->execute($code, true);
203-
$result = $this->outputModifier->modify($this->cleanOutput($this->output->fetch()));
232+
$return = $this->shell->execute($code, true);
233+
$output = $this->outputModifier->modify($this->cleanOutput($this->output->fetch()));
234+
235+
if (! self::$dumpOccurred && $return !== null && ! ($return instanceof NoReturnValue)) {
236+
$presented = $this->config->getPresenter()->present($return);
237+
if ($output !== '') {
238+
$output .= \PHP_EOL.$presented;
239+
} else {
240+
$output = $presented;
241+
}
242+
}
204243

205-
return trim($result);
244+
return trim($output);
206245
}
207246

208247
/**
209248
* @param callable(array): void $onEvent
210249
*/
211250
protected function doExecuteStreaming(string $code, int $index, callable $onEvent): void
212251
{
252+
self::$dumpOccurred = false;
213253
$this->output = new StreamingOutput(function (string $chunk) use ($index, $onEvent): void {
214254
if ($chunk === '') {
215255
return;
@@ -222,7 +262,18 @@ protected function doExecuteStreaming(string $code, int $index, callable $onEven
222262
]);
223263
});
224264
$this->shell->setOutput($this->output);
225-
$this->shell->execute($code, true);
265+
$return = $this->shell->execute($code, true);
266+
267+
if (! self::$dumpOccurred && $return !== null && ! ($return instanceof NoReturnValue)) {
268+
$output = $this->config->getPresenter()->present($return);
269+
if ($output !== '') {
270+
$onEvent([
271+
'type' => 'output',
272+
'index' => $index,
273+
'data' => $output,
274+
]);
275+
}
276+
}
226277
}
227278

228279
protected function createShell(OutputInterface $output, Configuration $config): Shell
@@ -252,6 +303,30 @@ protected function normalizePHPCode(string $rawPHPCode): string
252303
return $rawPHPCode;
253304
}
254305

306+
protected function registerVarDumperHandler(Configuration $config): void
307+
{
308+
if (! class_exists(VarDumper::class)) {
309+
return;
310+
}
311+
312+
VarDumper::setHandler(function ($var) use ($config) {
313+
self::$dumpOccurred = true;
314+
$output = $config->getPresenter()->present($var);
315+
$this->output->write($output, true);
316+
317+
return $output;
318+
});
319+
}
320+
321+
protected function restoreVarDumperHandler(): void
322+
{
323+
if (! class_exists(VarDumper::class)) {
324+
return;
325+
}
326+
327+
VarDumper::setHandler(null);
328+
}
329+
255330
public function getShell(): Shell
256331
{
257332
return $this->shell;

0 commit comments

Comments
 (0)