Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updating Dumper code with PHP 8.1 features #623

Merged
merged 3 commits into from
Apr 1, 2022
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
115 changes: 45 additions & 70 deletions src/Dumper/src/Dumper.php
Original file line number Diff line number Diff line change
@@ -1,12 +1,5 @@
<?php

/**
* Spiral Framework.
*
* @license MIT
* @author Anton Titov (Wolfy-J)
*/

declare(strict_types=1);

namespace Spiral\Debug;
Expand Down Expand Up @@ -37,15 +30,14 @@ class Dumper implements LoggerAwareInterface
public const OUTPUT_CLI_COLORS = 5;
public const ROADRUNNER = 6;

/** @var int */
private $maxLevel = 12;
private int $maxLevel = 12;

/**
* Default render associations.
*
* @var array|RendererInterface[]
*/
private $targets = [
private array $targets = [
self::OUTPUT => HtmlRenderer::class,
self::OUTPUT_CLI => PlainRenderer::class,
self::OUTPUT_CLI_COLORS => ConsoleRenderer::class,
Expand All @@ -67,18 +59,17 @@ public function __construct(LoggerInterface $logger = null)
*/
public function setMaxLevel(int $maxLevel): void
{
$this->maxLevel = max($maxLevel, 1);
$this->maxLevel = \max($maxLevel, 1);
}

/**
* Dump given value into target output.
*
* @param mixed $value
* @param int $target Possible options: OUTPUT, RETURN, ERROR_LOG, LOGGER.
* @return string
* @param int $target Possible options: OUTPUT, RETURN, ERROR_LOG, LOGGER.
*
* @throws DumperException
*/
public function dump($value, int $target = self::OUTPUT): ?string
public function dump(mixed $value, int $target = self::OUTPUT): ?string
{
$r = $this->getRenderer($target);
$dump = $r->wrapContent($this->renderValue($r, $value));
Expand All @@ -100,7 +91,7 @@ public function dump($value, int $target = self::OUTPUT): ?string
break;

case self::ERROR_LOG:
error_log($dump, 0);
\error_log($dump, 0);
break;
}

Expand All @@ -115,7 +106,7 @@ public function dump($value, int $target = self::OUTPUT): ?string
public function setRenderer(int $target, RendererInterface $renderer): Dumper
{
if (!isset($this->targets[$target])) {
throw new DumperException(sprintf('Undefined dump target %d', $target));
throw new DumperException(\sprintf('Undefined dump target %d', $target));
}

$this->targets[$target] = $renderer;
Expand All @@ -140,10 +131,10 @@ private function getRenderer(int $target): RendererInterface
}

if (!isset($this->targets[$target])) {
throw new DumperException(sprintf('Undefined dump target %d', $target));
throw new DumperException(\sprintf('Undefined dump target %d', $target));
}

if (is_string($this->targets[$target])) {
if (\is_string($this->targets[$target])) {
$this->targets[$target] = new $this->targets[$target]();
}

Expand All @@ -154,14 +145,13 @@ private function getRenderer(int $target): RendererInterface
* Variable dumper. This is the oldest spiral function originally written in 2007. :).
*
* @param RendererInterface $r Render to style value content.
* @param mixed $value
* @param string $name Variable name, internal.
* @param int $level Dumping level, internal.
* @param bool $hideHeader Hide array/object header, internal.
*/
private function renderValue(
RendererInterface $r,
$value,
mixed $value,
string $name = '',
int $level = 0,
bool $hideHeader = false
Expand All @@ -177,33 +167,33 @@ private function renderValue(
return $r->indent($level) . $r->apply('-too deep-', 'maxLevel') . "\n";
}

$type = strtolower(gettype($value));
$type = \strtolower(\gettype($value));

if ($type == 'array') {
if ($type === 'array') {
return $header . $this->renderArray($r, $value, $level, $hideHeader);
}

if ($type == 'object') {
if ($type === 'object') {
return $header . $this->renderObject($r, $value, $level, $hideHeader);
}

if ($type == 'resource') {
if ($type === 'resource') {
//No need to dump resource value
$element = get_resource_type($value) . ' resource ';
$element = \get_resource_type($value) . ' resource ';

return $header . $r->apply($element, 'type', 'resource') . "\n";
}

//Value length
$length = strlen((string)$value);
$length = \strlen((string)$value);

//Including type size
$header .= $r->apply("{$type}({$length})", 'type', $type);
$header .= $r->apply(\sprintf('%s(%d)', $type, $length), 'type', $type);

$element = null;
switch ($type) {
case 'string':
$element = $r->escapeStrings() ? htmlspecialchars($value) : $value;
$element = $r->escapeStrings() ? \htmlspecialchars($value) : $value;
break;

case 'boolean':
Expand All @@ -213,7 +203,7 @@ private function renderValue(
default:
if ($value !== null) {
//Not showing null value, type is enough
$element = var_export($value, true);
$element = \var_export($value, true);
}
}

Expand All @@ -224,25 +214,25 @@ private function renderValue(
private function renderArray(RendererInterface $r, array $array, int $level, bool $hideHeader = false): string
{
if (!$hideHeader) {
$count = count($array);
$count = \count($array);

//Array size and scope
$output = $r->apply("array({$count})", 'type', 'array') . "\n";
$output = $r->apply(\sprintf('array(%d)', $count), 'type', 'array') . "\n";
$output .= $r->indent($level) . $r->apply('[', 'syntax', '[') . "\n";
} else {
$output = '';
}

foreach ($array as $key => $value) {
if (!is_numeric($key)) {
if (is_string($key) && $r->escapeStrings()) {
$key = htmlspecialchars($key);
if (!\is_numeric($key)) {
if (\is_string($key) && $r->escapeStrings()) {
$key = \htmlspecialchars($key);
}

$key = "'{$key}'";
$key = \sprintf("'%s'", $key);
}

$output .= $this->renderValue($r, $value, "[{$key}]", $level + 1);
$output .= $this->renderValue($r, $value, \sprintf('[%s]', $key), $level + 1);
}

if (!$hideHeader) {
Expand All @@ -253,19 +243,15 @@ private function renderArray(RendererInterface $r, array $array, int $level, boo
return $output;
}

/**
* @param object $value
*
*/
private function renderObject(
RendererInterface $r,
$value,
object $value,
int $level,
bool $hideHeader = false,
string $class = ''
): string {
if (!$hideHeader) {
$type = ($class ?: get_class($value)) . ' object ';
$type = ($class ?: $value::class) . ' object ';

$header = $r->apply($type, 'type', 'object') . "\n";
$header .= $r->indent($level) . $r->apply('(', 'syntax', '(') . "\n";
Expand All @@ -274,25 +260,25 @@ private function renderObject(
}

//Let's use method specifically created for dumping
if (method_exists($value, '__debugInfo') || $value instanceof \Closure) {
if (\method_exists($value, '__debugInfo') || $value instanceof \Closure) {
if ($value instanceof \Closure) {
$debugInfo = $this->describeClosure($value);
} else {
$debugInfo = $value->__debugInfo();
}

if (is_array($debugInfo)) {
if (\is_array($debugInfo)) {
//Pretty view
$debugInfo = (object)$debugInfo;
}

if (is_object($debugInfo)) {
if (\is_object($debugInfo)) {
//We are not including syntax elements here
return $this->renderObject($r, $debugInfo, $level, false, get_class($value));
return $this->renderObject($r, $debugInfo, $level, false, $value::class);
}

return $header
. $this->renderValue($r, $debugInfo, '', $level + (is_scalar($value)), true)
. $this->renderValue($r, $debugInfo, '', $level + (\is_scalar($value)), true)
. $r->indent($level) . $r->apply(')', 'syntax', ')') . "\n";
}

Expand All @@ -307,20 +293,16 @@ private function renderObject(
return $header . $output . $r->indent($level) . $r->apply(')', 'syntax', ')') . "\n";
}

/**
* @param object $value
*
*/
private function renderProperty(RendererInterface $r, $value, \ReflectionProperty $p, int $level): string
private function renderProperty(RendererInterface $r, object $value, \ReflectionProperty $p, int $level): string
{
if ($p->isStatic()) {
return '';
}

if (
!($value instanceof \stdClass)
&& is_string($p->getDocComment())
&& strpos($p->getDocComment(), '@internal') !== false
!($value instanceof \stdClass) &&
\is_string($p->getDocComment()) &&
str_contains($p->getDocComment(), '@internal')
) {
// Memory loop while reading doc comment for stdClass variables?
// Report a PHP bug about treating comment INSIDE property declaration as doc comment.
Expand All @@ -330,9 +312,6 @@ private function renderProperty(RendererInterface $r, $value, \ReflectionPropert
//Property access level
$access = $this->getAccess($p);

//To read private and protected properties
$p->setAccessible(true);

if ($value instanceof \stdClass) {
$name = $r->apply($p->getName(), 'dynamic');
} else {
Expand All @@ -350,30 +329,26 @@ private function describeClosure(\Closure $closure): array
{
try {
$r = new \ReflectionFunction($closure);
} catch (\ReflectionException $e) {
} catch (\ReflectionException) {
return ['closure' => 'unable to resolve'];
}

return [
'name' => $r->getName() . " (lines {$r->getStartLine()}:{$r->getEndLine()})",
'name' => $r->getName() . \sprintf(' (lines %s:%s)', $r->getStartLine(), $r->getEndLine()),
'file' => $r->getFileName(),
'this' => $r->getClosureThis(),
];
}

/**
* Property access level label.
*
*
*/
private function getAccess(\ReflectionProperty $p): string
{
if ($p->isPrivate()) {
return 'private';
} elseif ($p->isProtected()) {
return 'protected';
}

return 'public';
return match (true) {
$p->isPrivate() => 'private',
$p->isProtected() => 'protected',
default => 'public'
};
}
}
7 changes: 0 additions & 7 deletions src/Dumper/src/Exception/DumperException.php
Original file line number Diff line number Diff line change
@@ -1,12 +1,5 @@
<?php

/**
* Spiral Framework.
*
* @license MIT
* @author Anton Titov (Wolfy-J)
*/

declare(strict_types=1);

namespace Spiral\Debug\Exception;
Expand Down
30 changes: 5 additions & 25 deletions src/Dumper/src/Renderer/AbstractRenderer.php
Original file line number Diff line number Diff line change
@@ -1,12 +1,5 @@
<?php

/**
* Spiral Framework.
*
* @license MIT
* @author Anton Titov (Wolfy-J)
*/

declare(strict_types=1);

namespace Spiral\Debug\Renderer;
Expand All @@ -17,41 +10,28 @@ abstract class AbstractRenderer implements RendererInterface
{
/**
* Container element used to inject dump into, usually pre elemnt with some styling.
*
* @var string
*/
protected $body = '%s';
protected string $body = '%s';

/**
* Default indent string.
*
* @var string
*/
protected $indent = ' ';
protected string $indent = ' ';

/**
* @inheritdoc
*/
public function wrapContent(string $body): string
{
return sprintf($this->body, $body);
return \sprintf($this->body, $body);
}

/**
* @inheritdoc
*/
public function indent(int $level): string
{
if ($level == 0) {
if ($level === 0) {
return '';
}

return $this->apply(str_repeat($this->indent, $level), 'indent');
return $this->apply(\str_repeat($this->indent, $level), 'indent');
}

/**
* @inheritdoc
*/
public function escapeStrings(): bool
{
return true;
Expand Down
Loading