Skip to content

Commit

Permalink
Merge pull request #29 from yiisoft/extract-closure-exporter
Browse files Browse the repository at this point in the history
Extract closure exporter
  • Loading branch information
xepozz committed Nov 26, 2020
2 parents 55f627b + 577663a commit 47fe5a5
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 75 deletions.
86 changes: 86 additions & 0 deletions src/ClosureExporter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
<?php

declare(strict_types=1);

namespace Yiisoft\VarDumper;

final class ClosureExporter
{
private UseStatementParser $useStatementParser;

public function __construct()
{
$this->useStatementParser = new UseStatementParser();
}

public function export(\Closure $closure)
{
$reflection = new \ReflectionFunction($closure);

$fileName = $reflection->getFileName();
$start = $reflection->getStartLine();
$end = $reflection->getEndLine();

if ($fileName === false || $start === false || $end === false || ($fileContent = file($fileName)) === false) {
return 'function() {/* Error: unable to determine Closure source */}';
}

--$start;
$uses = $this->useStatementParser->fromFile($fileName);

$source = implode('', array_slice($fileContent, $start, $end - $start));
$tokens = token_get_all('<?php ' . $source);
array_shift($tokens);

$closureTokens = [];
$pendingParenthesisCount = 0;
$isShortClosure = false;
$buffer = '';
foreach ($tokens as $token) {
if (!isset($token[0])) {
continue;
}
if (in_array($token[0], [T_FUNCTION, T_FN, T_STATIC], true)) {
$closureTokens[] = $token[1];
if (!$isShortClosure && $token[0] === T_FN) {
$isShortClosure = true;
}
continue;
}
if ($closureTokens !== []) {
$readableToken = $token[1] ?? $token;
if ($this->isNextTokenIsPartOfNamespace($token)) {
$buffer .= $token[1];
if (!$this->isNextTokenIsPartOfNamespace(next($tokens)) && array_key_exists($buffer, $uses)) {
$readableToken = $uses[$buffer];
$buffer = '';
}
}
if ($token === '{' || $token === '[') {
$pendingParenthesisCount++;
} elseif ($token === '}' || $token === ']') {
if ($pendingParenthesisCount === 0) {
break;
}
$pendingParenthesisCount--;
} elseif ($token === ',' || $token === ';') {
if ($pendingParenthesisCount === 0) {
break;
}
}
$closureTokens[] = $readableToken;
}
}

return implode('', $closureTokens);
}

private function isNextTokenIsPartOfNamespace($token): bool
{
if (!is_array($token)) {
return false;
}

return $token[0] === T_STRING || $token[0] === T_NS_SEPARATOR;
}
}
79 changes: 4 additions & 75 deletions src/VarDumper.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ final class VarDumper
private $variable;
private static array $objects = [];

private ?UseStatementParser $useStatementParser = null;
private ?ClosureExporter $closureExporter = null;

private bool $beautify = true;

Expand Down Expand Up @@ -385,64 +385,11 @@ private function exportInternal($variable, int $level): string
*/
private function exportClosure(\Closure $closure): string
{
$reflection = new \ReflectionFunction($closure);

$fileName = $reflection->getFileName();
$start = $reflection->getStartLine();
$end = $reflection->getEndLine();

if ($fileName === false || $start === false || $end === false) {
return 'function() {/* Error: unable to determine Closure source */}';
if ($this->closureExporter === null) {
$this->closureExporter = new ClosureExporter();
}

--$start;
$uses = $this->getUsesParser()->fromFile($fileName);

$source = implode('', array_slice(file($fileName), $start, $end - $start));
$tokens = token_get_all('<?php ' . $source);
array_shift($tokens);

$closureTokens = [];
$pendingParenthesisCount = 0;
$isShortClosure = false;
$buffer = '';
foreach ($tokens as $token) {
if (!isset($token[0])) {
continue;
}
if (in_array($token[0], [T_FUNCTION, T_FN, T_STATIC], true)) {
$closureTokens[] = $token[1];
if (!$isShortClosure && $token[0] === T_FN) {
$isShortClosure = true;
}
continue;
}
if ($closureTokens !== []) {
$readableToken = $token[1] ?? $token;
if ($this->isNextTokenIsPartOfNamespace($token)) {
$buffer .= $token[1];
if (!$this->isNextTokenIsPartOfNamespace(next($tokens)) && array_key_exists($buffer, $uses)) {
$readableToken = $uses[$buffer];
$buffer = '';
}
}
if ($token === '{' || $token === '[') {
$pendingParenthesisCount++;
} elseif ($token === '}' || $token === ']') {
if ($pendingParenthesisCount === 0) {
break;
}
$pendingParenthesisCount--;
} elseif ($token === ',' || $token === ';') {
if ($pendingParenthesisCount === 0) {
break;
}
}
$closureTokens[] = $readableToken;
}
}

return implode('', $closureTokens);
return $this->closureExporter->export($closure);
}

public function asPhpString(): string
Expand All @@ -451,24 +398,6 @@ public function asPhpString(): string
return $this->export();
}

private function getUsesParser(): UseStatementParser
{
if ($this->useStatementParser === null) {
$this->useStatementParser = new UseStatementParser();
}

return $this->useStatementParser;
}

private function isNextTokenIsPartOfNamespace($token): bool
{
if (!is_array($token)) {
return false;
}

return $token[0] === T_STRING || $token[0] === T_NS_SEPARATOR;
}

private function getObjectDescription(object $object): string
{
return get_class($object) . '#' . spl_object_id($object);
Expand Down

0 comments on commit 47fe5a5

Please sign in to comment.