Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/Psy/Presenter.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,11 @@ public function present($value, ?int $depth = null, int $options = 0): string
});

if (isset(Tinker::$statements[Tinker::$current])) {
Tinker::$statements[Tinker::$current]['html'] = $output;
if (isset(Tinker::$statements[Tinker::$current]['html']) && Tinker::$statements[Tinker::$current]['html'] !== '') {
Tinker::$statements[Tinker::$current]['html'] .= \PHP_EOL.$output;
} else {
Tinker::$statements[Tinker::$current]['html'] = $output;
}
}

return parent::present($value, $depth, $options);
Expand Down
223 changes: 149 additions & 74 deletions src/Tinker.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,30 @@

namespace TweakPHP\Client;

use PhpParser\Node\Stmt\Expression;
use PhpParser\ParserFactory;
use PhpParser\PrettyPrinter\Standard;
use Psy\CodeCleaner\NoReturnValue;
use Psy\Configuration;
use Psy\Exception\BreakException;
use Psy\Shell;
use Symfony\Component\Console\Output\BufferedOutput;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\VarDumper\VarDumper;
use TweakPHP\Client\Database\QueryCollector;
use TweakPHP\Client\Output\StreamingOutput;
use TweakPHP\Client\OutputModifiers\OutputModifier;

class Tinker
{
public static bool $dumpOccurred = false;

protected OutputInterface $output;

protected Shell $shell;

protected Configuration $config;

protected OutputModifier $outputModifier;

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

$this->config = $config;

$this->shell = $this->createShell($this->output, $config);

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

$rawPHPCode = $this->normalizePHPCode($rawPHPCode);

$parserFactory = new ParserFactory;
$parser = method_exists($parserFactory, 'createForHostVersion')
? $parserFactory->createForHostVersion()
: $parserFactory->create(ParserFactory::PREFER_PHP7);
$prettyPrinter = new Standard;
foreach ($parser->parse($rawPHPCode) as $key => $stmt) {
$code = $prettyPrinter->prettyPrint([$stmt]);
self::$current = $key;
self::$statements[] = [
'line' => $stmt->getStartLine(),
'code' => $code,
];
$this->registerVarDumperHandler($this->config);

QueryCollector::start();
try {
$output = $this->doExecute($code);
} finally {
$queries = QueryCollector::stop();
self::$statements[$key]['queries'] = $queries;
try {
$rawPHPCode = $this->normalizePHPCode($rawPHPCode);

$parserFactory = new ParserFactory;
$parser = method_exists($parserFactory, 'createForHostVersion')
? $parserFactory->createForHostVersion()
: $parserFactory->create(ParserFactory::PREFER_PHP7);
$prettyPrinter = new Standard;
foreach ($parser->parse($rawPHPCode) as $key => $stmt) {
$code = $prettyPrinter->prettyPrint([$stmt]);
$executableCode = $stmt instanceof Expression
? $prettyPrinter->prettyPrintExpr($stmt->expr)
: $code;

self::$current = $key;
self::$statements[] = [
'line' => $stmt->getStartLine(),
'code' => $code,
];

QueryCollector::start();
try {
$output = $this->doExecute($executableCode);
} finally {
$queries = QueryCollector::stop();
self::$statements[$key]['queries'] = $queries;
}

self::$statements[$key]['output'] = $output;

$queryErrors = QueryCollector::errors();
if ($queryErrors !== []) {
self::$statements[$key]['query_errors'] = $queryErrors;
}
}

self::$statements[$key]['output'] = $output;

$queryErrors = QueryCollector::errors();
if ($queryErrors !== []) {
self::$statements[$key]['query_errors'] = $queryErrors;
$allQueries = [];
$allQueryErrors = [];
foreach (self::$statements as $stmt) {
if (isset($stmt['queries'])) {
$allQueries = array_merge($allQueries, $stmt['queries']);
}
if (isset($stmt['query_errors'])) {
$allQueryErrors = array_merge($allQueryErrors, $stmt['query_errors']);
}
}
}

$allQueries = [];
$allQueryErrors = [];
foreach (self::$statements as $stmt) {
if (isset($stmt['queries'])) {
$allQueries = array_merge($allQueries, $stmt['queries']);
}
if (isset($stmt['query_errors'])) {
$allQueryErrors = array_merge($allQueryErrors, $stmt['query_errors']);
}
}
$result = [
'output' => self::$statements,
'queries' => $allQueries,
];

$result = [
'output' => self::$statements,
'queries' => $allQueries,
];
if ($allQueryErrors !== []) {
$result['query_errors'] = $allQueryErrors;
}

if ($allQueryErrors !== []) {
$result['query_errors'] = $allQueryErrors;
return $result;
} finally {
$this->restoreVarDumperHandler();
}

return $result;
}

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

$rawPHPCode = $this->normalizePHPCode($rawPHPCode);

$parserFactory = new ParserFactory;
$parser = method_exists($parserFactory, 'createForHostVersion')
? $parserFactory->createForHostVersion()
: $parserFactory->create(ParserFactory::PREFER_PHP7);
$prettyPrinter = new Standard;

foreach ($parser->parse($rawPHPCode) as $key => $stmt) {
$code = $prettyPrinter->prettyPrint([$stmt]);
self::$current = $key;
self::$statements[] = [
'line' => $stmt->getStartLine(),
'code' => $code,
];

$onEvent([
'type' => 'statement.started',
'index' => $key,
'line' => $stmt->getStartLine(),
'code' => $code,
]);
$this->registerVarDumperHandler($this->config);

if (! $this->executeStreamingStatement($code, $key, $onEvent)) {
return;
try {
$rawPHPCode = $this->normalizePHPCode($rawPHPCode);

$parserFactory = new ParserFactory;
$parser = method_exists($parserFactory, 'createForHostVersion')
? $parserFactory->createForHostVersion()
: $parserFactory->create(ParserFactory::PREFER_PHP7);
$prettyPrinter = new Standard;

foreach ($parser->parse($rawPHPCode) as $key => $stmt) {
$code = $prettyPrinter->prettyPrint([$stmt]);
$executableCode = $stmt instanceof Expression
? $prettyPrinter->prettyPrintExpr($stmt->expr)
: $code;

self::$current = $key;
self::$statements[] = [
'line' => $stmt->getStartLine(),
'code' => $code,
];

$onEvent([
'type' => 'statement.started',
'index' => $key,
'line' => $stmt->getStartLine(),
'code' => $code,
]);

if (! $this->executeStreamingStatement($executableCode, $key, $onEvent)) {
return;
}
}
}

$onEvent(['type' => 'completed']);
$onEvent(['type' => 'completed']);
} finally {
$this->restoreVarDumperHandler();
}
}

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

protected function doExecute(string $code): string
{
self::$dumpOccurred = false;
$this->output = new BufferedOutput;
$this->shell->setOutput($this->output);
$this->shell->execute($code, true);
$result = $this->outputModifier->modify($this->cleanOutput($this->output->fetch()));
$return = $this->shell->execute($code, true);
$output = $this->outputModifier->modify($this->cleanOutput($this->output->fetch()));

if (! self::$dumpOccurred && $return !== null && ! ($return instanceof NoReturnValue)) {
$presented = $this->config->getPresenter()->present($return);
if ($output !== '') {
$output .= \PHP_EOL.$presented;
} else {
$output = $presented;
}
}

return trim($result);
return trim($output);
}

/**
* @param callable(array): void $onEvent
*/
protected function doExecuteStreaming(string $code, int $index, callable $onEvent): void
{
self::$dumpOccurred = false;
$this->output = new StreamingOutput(function (string $chunk) use ($index, $onEvent): void {
if ($chunk === '') {
return;
Expand All @@ -222,7 +262,18 @@ protected function doExecuteStreaming(string $code, int $index, callable $onEven
]);
});
$this->shell->setOutput($this->output);
$this->shell->execute($code, true);
$return = $this->shell->execute($code, true);

if (! self::$dumpOccurred && $return !== null && ! ($return instanceof NoReturnValue)) {
$output = $this->config->getPresenter()->present($return);
if ($output !== '') {
$onEvent([
'type' => 'output',
'index' => $index,
'data' => $output,
]);
}
}
}

protected function createShell(OutputInterface $output, Configuration $config): Shell
Expand Down Expand Up @@ -252,6 +303,30 @@ protected function normalizePHPCode(string $rawPHPCode): string
return $rawPHPCode;
}

protected function registerVarDumperHandler(Configuration $config): void
{
if (! class_exists(VarDumper::class)) {
return;
}

VarDumper::setHandler(function ($var) use ($config) {
self::$dumpOccurred = true;
$output = $config->getPresenter()->present($var);
$this->output->write($output, true);

return $output;
});
}

protected function restoreVarDumperHandler(): void
{
if (! class_exists(VarDumper::class)) {
return;
}

VarDumper::setHandler(null);
}

public function getShell(): Shell
{
return $this->shell;
Expand Down
Loading
Loading