| 
<?php
 /*
 * This file is part of Chevere.
 *
 * (c) Rodolfo Berrios <[email protected]>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
 
 declare(strict_types=1);
 
 namespace Chevere\VarDump\Processors;
 
 use Chevere\Parameter\Interfaces\TypeInterface;
 use Chevere\VarDump\Interfaces\ProcessorInterface;
 use Chevere\VarDump\Interfaces\ProcessorNestedInterface;
 use Chevere\VarDump\Interfaces\VarDumperInterface;
 use Chevere\VarDump\Processors\Traits\HandleDepthTrait;
 use Chevere\VarDump\Processors\Traits\ProcessorTrait;
 use Reflection;
 use ReflectionObject;
 
 final class ObjectProcessor implements ProcessorInterface, ProcessorNestedInterface
 {
 use ProcessorTrait;
 use HandleDepthTrait;
 
 private object $var;
 
 private string $className;
 
 private int $objectId;
 
 public function __construct(
 private VarDumperInterface $varDumper
 ) {
 $this->assertType();
 /** @var object $object */
 $object = $this->varDumper->dumpable()->var();
 $this->var = $object;
 $this->depth = $this->varDumper->depth() + 1;
 $this->className = $this->var::class;
 $this->handleNormalizeClassName();
 $this->objectId = spl_object_id($this->var);
 $this->info = $this->className . '#' . $this->objectId;
 }
 
 public function type(): string
 {
 return TypeInterface::OBJECT;
 }
 
 public function write(): void
 {
 $this->varDumper->writer()->write(
 $this->varDumper->format()
 ->highlight(
 VarDumperInterface::CLASS_REG,
 $this->className
 )
 .
 $this->varDumper->format()
 ->highlight(
 VarDumperInterface::OPERATOR,
 '#' . strval($this->objectId)
 )
 );
 if ($this->varDumper->objectReferences()->has($this->objectId)) {
 $this->varDumper->writer()->write(
 <<<STRING
 {$this->highlightParentheses(
 $this->circularReference() . ' #' . $this->objectId
 )}
 STRING
 );
 
 return;
 }
 $this->varDumper->objectReferences()->push($this->objectId);
 if ($this->depth > self::MAX_DEPTH) {
 $this->varDumper->writer()->write(
 <<<STRING
 {$this->highlightParentheses($this->maxDepthReached())}
 STRING
 );
 
 return;
 }
 $this->setProperties($this->var);
 }
 
 private function setProperties(object $object): void
 {
 $reflection = new ReflectionObject($object);
 $properties = [];
 $properties = $this->getProperties($object, $reflection);
 // @codeCoverageIgnoreStart
 if ($properties === [] && $reflection->isInternal()) {
 $properties = $this->getPublicProperties();
 }
 // @codeCoverageIgnoreEnd
 $keys = array_keys($properties);
 $aux = 0;
 foreach ($keys as $name) {
 $aux++;
 $name = strval($name);
 /** @var string[] */
 $property = $properties[$name];
 $property[] = $aux;
 // @phpstan-ignore-next-line
 $this->processProperty($name, ...$property);
 }
 if ($aux > 0) {
 $this->varDumper->writer()->write(
 $this->varDumper->format()->detailsClose()
 );
 }
 }
 
 /**
 * @return array<string, array<mixed>>
 * @codeCoverageIgnore
 */
 private function getPublicProperties(): array
 {
 /** @var array<string, array<mixed>> $properties */
 $properties = json_decode(json_encode($this->var) ?: '', true) ?? [];
 foreach ($properties as $name => $value) {
 $name = strval($name);
 $properties[$name] = ['public', $value, false];
 }
 
 return $properties;
 }
 
 /**
 * @return array<string, array<mixed>>
 */
 private function getProperties(
 object $object,
 ReflectionObject $reflection
 ): array {
 $properties = [];
 do {
 foreach ($reflection->getProperties() as $property) {
 if ($property->isStatic()) {
 continue;
 }
 $isUnset = false;
 if (! $property->isInitialized($object)) {
 $value = '';
 $isUnset = true;
 } else {
 $value = $property->getValue($this->var);
 }
 $properties[$property->getName()] = [
 implode(
 ' ',
 Reflection::getModifierNames($property->getModifiers())
 ),
 $value ?? null,
 $isUnset,
 ];
 }
 } while ($reflection = $reflection->getParentClass());
 
 return $properties;
 }
 
 private function processProperty(
 string $name,
 string $modifier,
 mixed $value,
 bool $isUnset,
 int $aux
 ): void {
 if ($aux === 1) {
 $this->varDumper->writer()->write(
 $this->varDumper->format()->detailsOpen(
 $this->varDumper->depth() === 0
 )
 );
 if ($this->varDumper->format()->detailsClose() === '') {
 $this->varDumper->writer()->write("\n");
 }
 } else {
 $this->varDumper->writer()->write("\n");
 }
 $indentString = $this->varDumper->indentString();
 $modifier = $this->varDumper->format()->highlight(
 VarDumperInterface::MODIFIER,
 $modifier
 );
 $variable = $this->varDumper->format()->highlight(
 VarDumperInterface::VARIABLE,
 $this->varDumper->format()->filterEncodedChars($name)
 );
 $this->varDumper->writer()->write(
 " {$indentString}{$modifier} {$variable} "
 );
 if ($isUnset) {
 $unset = $this->varDumper->format()->highlight(
 VarDumperInterface::EMPHASIS,
 'uninitialized'
 );
 $this->varDumper->writer()->write($unset);
 } else {
 $this->handleDepth($value);
 }
 }
 
 private function handleNormalizeClassName(): void
 {
 if (str_starts_with($this->className, VarDumperInterface::CLASS_ANON)) {
 $this->className = VarDumperInterface::CLASS_ANON;
 }
 }
 }
 
 |