Initial commit
This commit is contained in:
317
vendor/psy/psysh/src/Formatter/CodeFormatter.php
vendored
Normal file
317
vendor/psy/psysh/src/Formatter/CodeFormatter.php
vendored
Normal file
@@ -0,0 +1,317 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Psy Shell.
|
||||
*
|
||||
* (c) 2012-2023 Justin Hileman
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Psy\Formatter;
|
||||
|
||||
use Psy\Exception\RuntimeException;
|
||||
use Symfony\Component\Console\Formatter\OutputFormatter;
|
||||
|
||||
/**
|
||||
* A pretty-printer for code.
|
||||
*/
|
||||
class CodeFormatter implements ReflectorFormatter
|
||||
{
|
||||
const LINE_MARKER = ' <urgent>></urgent> ';
|
||||
const NO_LINE_MARKER = ' ';
|
||||
|
||||
const HIGHLIGHT_DEFAULT = 'default';
|
||||
const HIGHLIGHT_KEYWORD = 'keyword';
|
||||
|
||||
const HIGHLIGHT_PUBLIC = 'public';
|
||||
const HIGHLIGHT_PROTECTED = 'protected';
|
||||
const HIGHLIGHT_PRIVATE = 'private';
|
||||
|
||||
const HIGHLIGHT_CONST = 'const';
|
||||
const HIGHLIGHT_NUMBER = 'number';
|
||||
const HIGHLIGHT_STRING = 'string';
|
||||
const HIGHLIGHT_COMMENT = 'code_comment';
|
||||
const HIGHLIGHT_INLINE_HTML = 'inline_html';
|
||||
|
||||
private const TOKEN_MAP = [
|
||||
// Not highlighted
|
||||
\T_OPEN_TAG => self::HIGHLIGHT_DEFAULT,
|
||||
\T_OPEN_TAG_WITH_ECHO => self::HIGHLIGHT_DEFAULT,
|
||||
\T_CLOSE_TAG => self::HIGHLIGHT_DEFAULT,
|
||||
\T_STRING => self::HIGHLIGHT_DEFAULT,
|
||||
\T_VARIABLE => self::HIGHLIGHT_DEFAULT,
|
||||
\T_NS_SEPARATOR => self::HIGHLIGHT_DEFAULT,
|
||||
|
||||
// Visibility
|
||||
\T_PUBLIC => self::HIGHLIGHT_PUBLIC,
|
||||
\T_PROTECTED => self::HIGHLIGHT_PROTECTED,
|
||||
\T_PRIVATE => self::HIGHLIGHT_PRIVATE,
|
||||
|
||||
// Constants
|
||||
\T_DIR => self::HIGHLIGHT_CONST,
|
||||
\T_FILE => self::HIGHLIGHT_CONST,
|
||||
\T_METHOD_C => self::HIGHLIGHT_CONST,
|
||||
\T_NS_C => self::HIGHLIGHT_CONST,
|
||||
\T_LINE => self::HIGHLIGHT_CONST,
|
||||
\T_CLASS_C => self::HIGHLIGHT_CONST,
|
||||
\T_FUNC_C => self::HIGHLIGHT_CONST,
|
||||
\T_TRAIT_C => self::HIGHLIGHT_CONST,
|
||||
|
||||
// Types
|
||||
\T_DNUMBER => self::HIGHLIGHT_NUMBER,
|
||||
\T_LNUMBER => self::HIGHLIGHT_NUMBER,
|
||||
\T_ENCAPSED_AND_WHITESPACE => self::HIGHLIGHT_STRING,
|
||||
\T_CONSTANT_ENCAPSED_STRING => self::HIGHLIGHT_STRING,
|
||||
|
||||
// Comments
|
||||
\T_COMMENT => self::HIGHLIGHT_COMMENT,
|
||||
\T_DOC_COMMENT => self::HIGHLIGHT_COMMENT,
|
||||
|
||||
// @todo something better here?
|
||||
\T_INLINE_HTML => self::HIGHLIGHT_INLINE_HTML,
|
||||
];
|
||||
|
||||
/**
|
||||
* Format the code represented by $reflector for shell output.
|
||||
*
|
||||
* @param \Reflector $reflector
|
||||
*
|
||||
* @return string formatted code
|
||||
*/
|
||||
public static function format(\Reflector $reflector): string
|
||||
{
|
||||
if (self::isReflectable($reflector)) {
|
||||
if ($code = @\file_get_contents($reflector->getFileName())) {
|
||||
return self::formatCode($code, self::getStartLine($reflector), $reflector->getEndLine());
|
||||
}
|
||||
}
|
||||
|
||||
throw new RuntimeException('Source code unavailable');
|
||||
}
|
||||
|
||||
/**
|
||||
* Format code for shell output.
|
||||
*
|
||||
* Optionally, restrict by $startLine and $endLine line numbers, or pass $markLine to add a line marker.
|
||||
*
|
||||
* @param string $code
|
||||
* @param int $startLine
|
||||
* @param int|null $endLine
|
||||
* @param int|null $markLine
|
||||
*
|
||||
* @return string formatted code
|
||||
*/
|
||||
public static function formatCode(string $code, int $startLine = 1, ?int $endLine = null, ?int $markLine = null): string
|
||||
{
|
||||
$spans = self::tokenizeSpans($code);
|
||||
$lines = self::splitLines($spans, $startLine, $endLine);
|
||||
$lines = self::formatLines($lines);
|
||||
$lines = self::numberLines($lines, $markLine);
|
||||
|
||||
return \implode('', \iterator_to_array($lines));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the start line for a given Reflector.
|
||||
*
|
||||
* Tries to incorporate doc comments if possible.
|
||||
*
|
||||
* This is typehinted as \Reflector but we've narrowed the input via self::isReflectable already.
|
||||
*
|
||||
* @param \ReflectionClass|\ReflectionFunctionAbstract $reflector
|
||||
*/
|
||||
private static function getStartLine(\Reflector $reflector): int
|
||||
{
|
||||
$startLine = $reflector->getStartLine();
|
||||
|
||||
if ($docComment = $reflector->getDocComment()) {
|
||||
$startLine -= \preg_match_all('/(\r\n?|\n)/', $docComment) + 1;
|
||||
}
|
||||
|
||||
return \max($startLine, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Split code into highlight spans.
|
||||
*
|
||||
* Tokenize via \token_get_all, then map these tokens to internal highlight types, combining
|
||||
* adjacent spans of the same highlight type.
|
||||
*
|
||||
* @todo consider switching \token_get_all() out for PHP-Parser-based formatting at some point.
|
||||
*
|
||||
* @param string $code
|
||||
*
|
||||
* @return \Generator [$spanType, $spanText] highlight spans
|
||||
*/
|
||||
private static function tokenizeSpans(string $code): \Generator
|
||||
{
|
||||
$spanType = null;
|
||||
$buffer = '';
|
||||
|
||||
foreach (\token_get_all($code) as $token) {
|
||||
$nextType = self::nextHighlightType($token, $spanType);
|
||||
$spanType = $spanType ?: $nextType;
|
||||
|
||||
if ($spanType !== $nextType) {
|
||||
yield [$spanType, $buffer];
|
||||
$spanType = $nextType;
|
||||
$buffer = '';
|
||||
}
|
||||
|
||||
$buffer .= \is_array($token) ? $token[1] : $token;
|
||||
}
|
||||
|
||||
if ($spanType !== null && $buffer !== '') {
|
||||
yield [$spanType, $buffer];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a token and the current highlight span type, compute the next type.
|
||||
*
|
||||
* @param array|string $token \token_get_all token
|
||||
* @param string|null $currentType
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
private static function nextHighlightType($token, $currentType)
|
||||
{
|
||||
if ($token === '"') {
|
||||
return self::HIGHLIGHT_STRING;
|
||||
}
|
||||
|
||||
if (\is_array($token)) {
|
||||
if ($token[0] === \T_WHITESPACE) {
|
||||
return $currentType;
|
||||
}
|
||||
|
||||
if (\array_key_exists($token[0], self::TOKEN_MAP)) {
|
||||
return self::TOKEN_MAP[$token[0]];
|
||||
}
|
||||
}
|
||||
|
||||
return self::HIGHLIGHT_KEYWORD;
|
||||
}
|
||||
|
||||
/**
|
||||
* Group highlight spans into an array of lines.
|
||||
*
|
||||
* Optionally, restrict by start and end line numbers.
|
||||
*
|
||||
* @param \Generator $spans as [$spanType, $spanText] pairs
|
||||
* @param int $startLine
|
||||
* @param int|null $endLine
|
||||
*
|
||||
* @return \Generator lines, each an array of [$spanType, $spanText] pairs
|
||||
*/
|
||||
private static function splitLines(\Generator $spans, int $startLine = 1, ?int $endLine = null): \Generator
|
||||
{
|
||||
$lineNum = 1;
|
||||
$buffer = [];
|
||||
|
||||
foreach ($spans as list($spanType, $spanText)) {
|
||||
foreach (\preg_split('/(\r\n?|\n)/', $spanText) as $index => $spanLine) {
|
||||
if ($index > 0) {
|
||||
if ($lineNum >= $startLine) {
|
||||
yield $lineNum => $buffer;
|
||||
}
|
||||
|
||||
$lineNum++;
|
||||
$buffer = [];
|
||||
|
||||
if ($endLine !== null && $lineNum > $endLine) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ($spanLine !== '') {
|
||||
$buffer[] = [$spanType, $spanLine];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($buffer)) {
|
||||
yield $lineNum => $buffer;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format lines of highlight spans for shell output.
|
||||
*
|
||||
* @param \Generator $spanLines lines, each an array of [$spanType, $spanText] pairs
|
||||
*
|
||||
* @return \Generator Formatted lines
|
||||
*/
|
||||
private static function formatLines(\Generator $spanLines): \Generator
|
||||
{
|
||||
foreach ($spanLines as $lineNum => $spanLine) {
|
||||
$line = '';
|
||||
|
||||
foreach ($spanLine as list($spanType, $spanText)) {
|
||||
if ($spanType === self::HIGHLIGHT_DEFAULT) {
|
||||
$line .= OutputFormatter::escape($spanText);
|
||||
} else {
|
||||
$line .= \sprintf('<%s>%s</%s>', $spanType, OutputFormatter::escape($spanText), $spanType);
|
||||
}
|
||||
}
|
||||
|
||||
yield $lineNum => $line.\PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepend line numbers to formatted lines.
|
||||
*
|
||||
* Lines must be in an associative array with the correct keys in order to be numbered properly.
|
||||
*
|
||||
* Optionally, pass $markLine to add a line marker.
|
||||
*
|
||||
* @param \Generator $lines Formatted lines
|
||||
* @param int|null $markLine
|
||||
*
|
||||
* @return \Generator Numbered, formatted lines
|
||||
*/
|
||||
private static function numberLines(\Generator $lines, ?int $markLine = null): \Generator
|
||||
{
|
||||
$lines = \iterator_to_array($lines);
|
||||
|
||||
// Figure out how much space to reserve for line numbers.
|
||||
\end($lines);
|
||||
$pad = \strlen(\key($lines));
|
||||
|
||||
// If $markLine is before or after our line range, don't bother reserving space for the marker.
|
||||
if ($markLine !== null) {
|
||||
if ($markLine > \key($lines)) {
|
||||
$markLine = null;
|
||||
}
|
||||
|
||||
\reset($lines);
|
||||
if ($markLine < \key($lines)) {
|
||||
$markLine = null;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($lines as $lineNum => $line) {
|
||||
$mark = '';
|
||||
if ($markLine !== null) {
|
||||
$mark = ($markLine === $lineNum) ? self::LINE_MARKER : self::NO_LINE_MARKER;
|
||||
}
|
||||
|
||||
yield \sprintf("%s<aside>%{$pad}s</aside>: %s", $mark, $lineNum, $line);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a Reflector instance is reflectable by this formatter.
|
||||
*
|
||||
* @phpstan-assert-if-true \ReflectionClass|\ReflectionFunctionAbstract $reflector
|
||||
*
|
||||
* @param \Reflector $reflector
|
||||
*/
|
||||
private static function isReflectable(\Reflector $reflector): bool
|
||||
{
|
||||
return ($reflector instanceof \ReflectionClass || $reflector instanceof \ReflectionFunctionAbstract) && \is_file($reflector->getFileName());
|
||||
}
|
||||
}
|
||||
166
vendor/psy/psysh/src/Formatter/DocblockFormatter.php
vendored
Normal file
166
vendor/psy/psysh/src/Formatter/DocblockFormatter.php
vendored
Normal file
@@ -0,0 +1,166 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Psy Shell.
|
||||
*
|
||||
* (c) 2012-2023 Justin Hileman
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Psy\Formatter;
|
||||
|
||||
use Psy\Util\Docblock;
|
||||
use Symfony\Component\Console\Formatter\OutputFormatter;
|
||||
|
||||
/**
|
||||
* A pretty-printer for docblocks.
|
||||
*/
|
||||
class DocblockFormatter implements ReflectorFormatter
|
||||
{
|
||||
private const VECTOR_PARAM_TEMPLATES = [
|
||||
'type' => 'info',
|
||||
'var' => 'strong',
|
||||
];
|
||||
|
||||
/**
|
||||
* Format a docblock.
|
||||
*
|
||||
* @param \Reflector $reflector
|
||||
*
|
||||
* @return string Formatted docblock
|
||||
*/
|
||||
public static function format(\Reflector $reflector): string
|
||||
{
|
||||
$docblock = new Docblock($reflector);
|
||||
$chunks = [];
|
||||
|
||||
if (!empty($docblock->desc)) {
|
||||
$chunks[] = '<comment>Description:</comment>';
|
||||
$chunks[] = self::indent(OutputFormatter::escape($docblock->desc), ' ');
|
||||
$chunks[] = '';
|
||||
}
|
||||
|
||||
if (!empty($docblock->tags)) {
|
||||
foreach ($docblock::$vectors as $name => $vector) {
|
||||
if (isset($docblock->tags[$name])) {
|
||||
$chunks[] = \sprintf('<comment>%s:</comment>', self::inflect($name));
|
||||
$chunks[] = self::formatVector($vector, $docblock->tags[$name]);
|
||||
$chunks[] = '';
|
||||
}
|
||||
}
|
||||
|
||||
$tags = self::formatTags(\array_keys($docblock::$vectors), $docblock->tags);
|
||||
if (!empty($tags)) {
|
||||
$chunks[] = $tags;
|
||||
$chunks[] = '';
|
||||
}
|
||||
}
|
||||
|
||||
return \rtrim(\implode("\n", $chunks));
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a docblock vector, for example, `@throws`, `@param`, or `@return`.
|
||||
*
|
||||
* @see DocBlock::$vectors
|
||||
*
|
||||
* @param array $vector
|
||||
* @param array $lines
|
||||
*/
|
||||
private static function formatVector(array $vector, array $lines): string
|
||||
{
|
||||
$template = [' '];
|
||||
foreach ($vector as $type) {
|
||||
$max = 0;
|
||||
foreach ($lines as $line) {
|
||||
$chunk = $line[$type];
|
||||
$cur = empty($chunk) ? 0 : \strlen($chunk) + 1;
|
||||
if ($cur > $max) {
|
||||
$max = $cur;
|
||||
}
|
||||
}
|
||||
|
||||
$template[] = self::getVectorParamTemplate($type, $max);
|
||||
}
|
||||
$template = \implode(' ', $template);
|
||||
|
||||
return \implode("\n", \array_map(function ($line) use ($template) {
|
||||
$escaped = \array_map(function ($l) {
|
||||
if ($l === null) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return OutputFormatter::escape($l);
|
||||
}, $line);
|
||||
|
||||
return \rtrim(\vsprintf($template, $escaped));
|
||||
}, $lines));
|
||||
}
|
||||
|
||||
/**
|
||||
* Format docblock tags.
|
||||
*
|
||||
* @param array $skip Tags to exclude
|
||||
* @param array $tags Tags to format
|
||||
*
|
||||
* @return string formatted tags
|
||||
*/
|
||||
private static function formatTags(array $skip, array $tags): string
|
||||
{
|
||||
$chunks = [];
|
||||
|
||||
foreach ($tags as $name => $values) {
|
||||
if (\in_array($name, $skip)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($values as $value) {
|
||||
$chunks[] = \sprintf('<comment>%s%s</comment> %s', self::inflect($name), empty($value) ? '' : ':', OutputFormatter::escape($value));
|
||||
}
|
||||
|
||||
$chunks[] = '';
|
||||
}
|
||||
|
||||
return \implode("\n", $chunks);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a docblock vector template.
|
||||
*
|
||||
* @param string $type Vector type
|
||||
* @param int $max Pad width
|
||||
*/
|
||||
private static function getVectorParamTemplate(string $type, int $max): string
|
||||
{
|
||||
if (!isset(self::VECTOR_PARAM_TEMPLATES[$type])) {
|
||||
return \sprintf('%%-%ds', $max);
|
||||
}
|
||||
|
||||
return \sprintf('<%s>%%-%ds</%s>', self::VECTOR_PARAM_TEMPLATES[$type], $max, self::VECTOR_PARAM_TEMPLATES[$type]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indent a string.
|
||||
*
|
||||
* @param string $text String to indent
|
||||
* @param string $indent (default: ' ')
|
||||
*/
|
||||
private static function indent(string $text, string $indent = ' '): string
|
||||
{
|
||||
return $indent.\str_replace("\n", "\n".$indent, $text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert underscored or whitespace separated words into sentence case.
|
||||
*
|
||||
* @param string $text
|
||||
*/
|
||||
private static function inflect(string $text): string
|
||||
{
|
||||
$words = \trim(\preg_replace('/[\s_-]+/', ' ', \preg_replace('/([a-z])([A-Z])/', '$1 $2', $text)));
|
||||
|
||||
return \implode(' ', \array_map('ucfirst', \explode(' ', $words)));
|
||||
}
|
||||
}
|
||||
23
vendor/psy/psysh/src/Formatter/ReflectorFormatter.php
vendored
Normal file
23
vendor/psy/psysh/src/Formatter/ReflectorFormatter.php
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Psy Shell.
|
||||
*
|
||||
* (c) 2012-2023 Justin Hileman
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Psy\Formatter;
|
||||
|
||||
/**
|
||||
* Reflector formatter interface.
|
||||
*/
|
||||
interface ReflectorFormatter
|
||||
{
|
||||
/**
|
||||
* @param \Reflector $reflector
|
||||
*/
|
||||
public static function format(\Reflector $reflector): string;
|
||||
}
|
||||
358
vendor/psy/psysh/src/Formatter/SignatureFormatter.php
vendored
Normal file
358
vendor/psy/psysh/src/Formatter/SignatureFormatter.php
vendored
Normal file
@@ -0,0 +1,358 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Psy Shell.
|
||||
*
|
||||
* (c) 2012-2023 Justin Hileman
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Psy\Formatter;
|
||||
|
||||
use Psy\Reflection\ReflectionConstant;
|
||||
use Psy\Reflection\ReflectionLanguageConstruct;
|
||||
use Psy\Util\Json;
|
||||
use Symfony\Component\Console\Formatter\OutputFormatter;
|
||||
|
||||
/**
|
||||
* An abstract representation of a function, class or property signature.
|
||||
*/
|
||||
class SignatureFormatter implements ReflectorFormatter
|
||||
{
|
||||
/**
|
||||
* Format a signature for the given reflector.
|
||||
*
|
||||
* Defers to subclasses to do the actual formatting.
|
||||
*
|
||||
* @param \Reflector $reflector
|
||||
*
|
||||
* @return string Formatted signature
|
||||
*/
|
||||
public static function format(\Reflector $reflector): string
|
||||
{
|
||||
switch (true) {
|
||||
case $reflector instanceof \ReflectionFunction:
|
||||
case $reflector instanceof ReflectionLanguageConstruct:
|
||||
return self::formatFunction($reflector);
|
||||
|
||||
case $reflector instanceof \ReflectionClass:
|
||||
// this case also covers \ReflectionObject
|
||||
return self::formatClass($reflector);
|
||||
|
||||
case $reflector instanceof \ReflectionClassConstant:
|
||||
return self::formatClassConstant($reflector);
|
||||
|
||||
case $reflector instanceof \ReflectionMethod:
|
||||
return self::formatMethod($reflector);
|
||||
|
||||
case $reflector instanceof \ReflectionProperty:
|
||||
return self::formatProperty($reflector);
|
||||
|
||||
case $reflector instanceof ReflectionConstant:
|
||||
return self::formatConstant($reflector);
|
||||
|
||||
default:
|
||||
throw new \InvalidArgumentException('Unexpected Reflector class: '.\get_class($reflector));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Print the signature name.
|
||||
*
|
||||
* @param \ReflectionClass|\ReflectionClassConstant|\ReflectionFunctionAbstract $reflector
|
||||
*
|
||||
* @return string Formatted name
|
||||
*/
|
||||
public static function formatName(\Reflector $reflector): string
|
||||
{
|
||||
return $reflector->getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Print the method, property or class modifiers.
|
||||
*
|
||||
* @param \ReflectionMethod|\ReflectionProperty|\ReflectionClass $reflector
|
||||
*
|
||||
* @return string Formatted modifiers
|
||||
*/
|
||||
private static function formatModifiers(\Reflector $reflector): string
|
||||
{
|
||||
return \implode(' ', \array_map(function ($modifier) {
|
||||
return \sprintf('<keyword>%s</keyword>', $modifier);
|
||||
}, \Reflection::getModifierNames($reflector->getModifiers())));
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a class signature.
|
||||
*
|
||||
* @param \ReflectionClass $reflector
|
||||
*
|
||||
* @return string Formatted signature
|
||||
*/
|
||||
private static function formatClass(\ReflectionClass $reflector): string
|
||||
{
|
||||
$chunks = [];
|
||||
|
||||
if ($modifiers = self::formatModifiers($reflector)) {
|
||||
$chunks[] = $modifiers;
|
||||
}
|
||||
|
||||
if ($reflector->isTrait()) {
|
||||
$chunks[] = 'trait';
|
||||
} else {
|
||||
$chunks[] = $reflector->isInterface() ? 'interface' : 'class';
|
||||
}
|
||||
|
||||
$chunks[] = \sprintf('<class>%s</class>', self::formatName($reflector));
|
||||
|
||||
if ($parent = $reflector->getParentClass()) {
|
||||
$chunks[] = 'extends';
|
||||
$chunks[] = \sprintf('<class>%s</class>', $parent->getName());
|
||||
}
|
||||
|
||||
$interfaces = $reflector->getInterfaceNames();
|
||||
if (!empty($interfaces)) {
|
||||
\sort($interfaces);
|
||||
|
||||
$chunks[] = $reflector->isInterface() ? 'extends' : 'implements';
|
||||
$chunks[] = \implode(', ', \array_map(function ($name) {
|
||||
return \sprintf('<class>%s</class>', $name);
|
||||
}, $interfaces));
|
||||
}
|
||||
|
||||
return \implode(' ', $chunks);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a constant signature.
|
||||
*
|
||||
* @param \ReflectionClassConstant $reflector
|
||||
*
|
||||
* @return string Formatted signature
|
||||
*/
|
||||
private static function formatClassConstant($reflector): string
|
||||
{
|
||||
$value = $reflector->getValue();
|
||||
$style = self::getTypeStyle($value);
|
||||
|
||||
return \sprintf(
|
||||
'<keyword>const</keyword> <const>%s</const> = <%s>%s</%s>',
|
||||
self::formatName($reflector),
|
||||
$style,
|
||||
OutputFormatter::escape(Json::encode($value)),
|
||||
$style
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a constant signature.
|
||||
*
|
||||
* @param ReflectionConstant $reflector
|
||||
*
|
||||
* @return string Formatted signature
|
||||
*/
|
||||
private static function formatConstant(ReflectionConstant $reflector): string
|
||||
{
|
||||
$value = $reflector->getValue();
|
||||
$style = self::getTypeStyle($value);
|
||||
|
||||
return \sprintf(
|
||||
'<keyword>define</keyword>(<string>%s</string>, <%s>%s</%s>)',
|
||||
OutputFormatter::escape(Json::encode($reflector->getName())),
|
||||
$style,
|
||||
OutputFormatter::escape(Json::encode($value)),
|
||||
$style
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for getting output style for a given value's type.
|
||||
*
|
||||
* @param mixed $value
|
||||
*/
|
||||
private static function getTypeStyle($value): string
|
||||
{
|
||||
if (\is_int($value) || \is_float($value)) {
|
||||
return 'number';
|
||||
} elseif (\is_string($value)) {
|
||||
return 'string';
|
||||
} elseif (\is_bool($value) || $value === null) {
|
||||
return 'bool';
|
||||
} else {
|
||||
return 'strong'; // @codeCoverageIgnore
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a property signature.
|
||||
*
|
||||
* @param \ReflectionProperty $reflector
|
||||
*
|
||||
* @return string Formatted signature
|
||||
*/
|
||||
private static function formatProperty(\ReflectionProperty $reflector): string
|
||||
{
|
||||
return \sprintf(
|
||||
'%s <strong>$%s</strong>',
|
||||
self::formatModifiers($reflector),
|
||||
$reflector->getName()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a function signature.
|
||||
*
|
||||
* @param \ReflectionFunction $reflector
|
||||
*
|
||||
* @return string Formatted signature
|
||||
*/
|
||||
private static function formatFunction(\ReflectionFunctionAbstract $reflector): string
|
||||
{
|
||||
return \sprintf(
|
||||
'<keyword>function</keyword> %s<function>%s</function>(%s)%s',
|
||||
$reflector->returnsReference() ? '&' : '',
|
||||
self::formatName($reflector),
|
||||
\implode(', ', self::formatFunctionParams($reflector)),
|
||||
self::formatFunctionReturnType($reflector)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a function signature's return type (if available).
|
||||
*
|
||||
* @param \ReflectionFunctionAbstract $reflector
|
||||
*
|
||||
* @return string Formatted return type
|
||||
*/
|
||||
private static function formatFunctionReturnType(\ReflectionFunctionAbstract $reflector): string
|
||||
{
|
||||
if (!\method_exists($reflector, 'hasReturnType') || !$reflector->hasReturnType()) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return \sprintf(': %s', self::formatReflectionType($reflector->getReturnType(), true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a method signature.
|
||||
*
|
||||
* @param \ReflectionMethod $reflector
|
||||
*
|
||||
* @return string Formatted signature
|
||||
*/
|
||||
private static function formatMethod(\ReflectionMethod $reflector): string
|
||||
{
|
||||
return \sprintf(
|
||||
'%s %s',
|
||||
self::formatModifiers($reflector),
|
||||
self::formatFunction($reflector)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Print the function params.
|
||||
*
|
||||
* @param \ReflectionFunctionAbstract $reflector
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private static function formatFunctionParams(\ReflectionFunctionAbstract $reflector): array
|
||||
{
|
||||
$params = [];
|
||||
foreach ($reflector->getParameters() as $param) {
|
||||
$hint = '';
|
||||
try {
|
||||
if (\method_exists($param, 'getType')) {
|
||||
// Only include the inquisitive nullable type iff param default value is not null.
|
||||
$defaultIsNull = $param->isOptional() && $param->isDefaultValueAvailable() && $param->getDefaultValue() === null;
|
||||
$hint = self::formatReflectionType($param->getType(), !$defaultIsNull);
|
||||
} else {
|
||||
if ($param->isArray()) {
|
||||
$hint = '<keyword>array</keyword>';
|
||||
} elseif ($class = $param->getClass()) {
|
||||
$hint = \sprintf('<class>%s</class>', $class->getName());
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
// sometimes we just don't know...
|
||||
// bad class names, or autoloaded classes that haven't been loaded yet, or whathaveyou.
|
||||
// come to think of it, the only time I've seen this is with the intl extension.
|
||||
|
||||
// Hax: we'll try to extract it :P
|
||||
|
||||
// @codeCoverageIgnoreStart
|
||||
$chunks = \explode('$'.$param->getName(), (string) $param);
|
||||
$chunks = \explode(' ', \trim($chunks[0]));
|
||||
$guess = \end($chunks);
|
||||
|
||||
$hint = \sprintf('<urgent>%s</urgent>', OutputFormatter::escape($guess));
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
if ($param->isOptional()) {
|
||||
if (!$param->isDefaultValueAvailable()) {
|
||||
$value = 'unknown';
|
||||
$typeStyle = 'urgent';
|
||||
} else {
|
||||
$value = $param->getDefaultValue();
|
||||
$typeStyle = self::getTypeStyle($value);
|
||||
$value = \is_array($value) ? '[]' : ($value === null ? 'null' : \var_export($value, true));
|
||||
}
|
||||
$default = \sprintf(' = <%s>%s</%s>', $typeStyle, OutputFormatter::escape($value), $typeStyle);
|
||||
} else {
|
||||
$default = '';
|
||||
}
|
||||
|
||||
$params[] = \sprintf(
|
||||
'%s%s%s<strong>$%s</strong>%s',
|
||||
$param->isPassedByReference() ? '&' : '',
|
||||
$hint,
|
||||
$hint !== '' ? ' ' : '',
|
||||
$param->getName(),
|
||||
$default
|
||||
);
|
||||
}
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Print function param or return type(s).
|
||||
*
|
||||
* @param \ReflectionType $type
|
||||
*/
|
||||
private static function formatReflectionType(?\ReflectionType $type, bool $indicateNullable): string
|
||||
{
|
||||
if ($type === null) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if ($type instanceof \ReflectionUnionType) {
|
||||
$delimeter = '|';
|
||||
} elseif ($type instanceof \ReflectionIntersectionType) {
|
||||
$delimeter = '&';
|
||||
} else {
|
||||
return self::formatReflectionNamedType($type, $indicateNullable);
|
||||
}
|
||||
|
||||
$formattedTypes = [];
|
||||
foreach ($type->getTypes() as $namedType) {
|
||||
$formattedTypes[] = self::formatReflectionNamedType($namedType, $indicateNullable);
|
||||
}
|
||||
|
||||
return \implode($delimeter, $formattedTypes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Print a single named type.
|
||||
*/
|
||||
private static function formatReflectionNamedType(\ReflectionNamedType $type, bool $indicateNullable): string
|
||||
{
|
||||
$typeStyle = $type->isBuiltin() ? 'keyword' : 'class';
|
||||
$nullable = $indicateNullable && $type->allowsNull() ? '?' : '';
|
||||
|
||||
return \sprintf('<%s>%s%s</%s>', $typeStyle, $nullable, OutputFormatter::escape($type->getName()), $typeStyle);
|
||||
}
|
||||
}
|
||||
96
vendor/psy/psysh/src/Formatter/TraceFormatter.php
vendored
Normal file
96
vendor/psy/psysh/src/Formatter/TraceFormatter.php
vendored
Normal file
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Psy Shell.
|
||||
*
|
||||
* (c) 2012-2023 Justin Hileman
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Psy\Formatter;
|
||||
|
||||
use Psy\Input\FilterOptions;
|
||||
use Symfony\Component\Console\Formatter\OutputFormatter;
|
||||
|
||||
/**
|
||||
* Output formatter for exception traces.
|
||||
*/
|
||||
class TraceFormatter
|
||||
{
|
||||
/**
|
||||
* Format the trace of the given exception.
|
||||
*
|
||||
* @param \Throwable $throwable The error or exception with a backtrace
|
||||
* @param FilterOptions $filter (default: null)
|
||||
* @param int $count (default: PHP_INT_MAX)
|
||||
* @param bool $includePsy (default: true)
|
||||
*
|
||||
* @return string[] Formatted stacktrace lines
|
||||
*/
|
||||
public static function formatTrace(\Throwable $throwable, ?FilterOptions $filter = null, ?int $count = null, bool $includePsy = true): array
|
||||
{
|
||||
if ($cwd = \getcwd()) {
|
||||
$cwd = \rtrim($cwd, \DIRECTORY_SEPARATOR).\DIRECTORY_SEPARATOR;
|
||||
}
|
||||
|
||||
if ($count === null) {
|
||||
$count = \PHP_INT_MAX;
|
||||
}
|
||||
|
||||
$lines = [];
|
||||
|
||||
$trace = $throwable->getTrace();
|
||||
\array_unshift($trace, [
|
||||
'function' => '',
|
||||
'file' => $throwable->getFile() !== null ? $throwable->getFile() : 'n/a',
|
||||
'line' => $throwable->getLine() !== null ? $throwable->getLine() : 'n/a',
|
||||
'args' => [],
|
||||
]);
|
||||
|
||||
if (!$includePsy) {
|
||||
for ($i = \count($trace) - 1; $i >= 0; $i--) {
|
||||
$thing = isset($trace[$i]['class']) ? $trace[$i]['class'] : $trace[$i]['function'];
|
||||
if (\preg_match('/\\\\?Psy\\\\/', $thing)) {
|
||||
$trace = \array_slice($trace, $i + 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for ($i = 0, $count = \min($count, \count($trace)); $i < $count; $i++) {
|
||||
$class = isset($trace[$i]['class']) ? $trace[$i]['class'] : '';
|
||||
$type = isset($trace[$i]['type']) ? $trace[$i]['type'] : '';
|
||||
$function = $trace[$i]['function'];
|
||||
$file = isset($trace[$i]['file']) ? $trace[$i]['file'] : 'n/a';
|
||||
$line = isset($trace[$i]['line']) ? $trace[$i]['line'] : 'n/a';
|
||||
|
||||
// Make file paths relative to cwd
|
||||
if ($cwd !== false) {
|
||||
$file = \preg_replace('/^'.\preg_quote($cwd, '/').'/', '', $file);
|
||||
}
|
||||
|
||||
// Leave execution loop out of the `eval()'d code` lines
|
||||
if (\preg_match("#/src/Execution(?:Loop)?Closure.php\(\d+\) : eval\(\)'d code$#", \str_replace('\\', '/', $file))) {
|
||||
$file = "eval()'d code";
|
||||
}
|
||||
|
||||
// Skip any lines that don't match our filter options
|
||||
if ($filter !== null && !$filter->match(\sprintf('%s%s%s() at %s:%s', $class, $type, $function, $file, $line))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$lines[] = \sprintf(
|
||||
' <class>%s</class>%s%s() at <info>%s:%s</info>',
|
||||
OutputFormatter::escape($class),
|
||||
OutputFormatter::escape($type),
|
||||
OutputFormatter::escape($function),
|
||||
OutputFormatter::escape($file),
|
||||
OutputFormatter::escape($line)
|
||||
);
|
||||
}
|
||||
|
||||
return $lines;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user