notes/src/ObjectReflection.php

143 lines
5.2 KiB
PHP

<?php
namespace Notes;
use Kash\HandleCacheTrait;
use Notes\Attribute\Ignore;
use Notes\Common\ReflectedClass;
use Notes\Common\ReflectedMethod;
use Notes\Common\ReflectedMethodProperty;
use Notes\Common\ReflectedProperty;
use Notes\Common\ReflectedPropertyType;
use Psr\SimpleCache\CacheInterface;
use Reflector, ReflectionClass, ReflectionProperty, ReflectionMethod, ReflectionUnionType, ReflectionNamedType, ReflectionParameter;
class ObjectReflection {
use HandleCacheTrait;
protected string $classname;
protected ReflectionClass $classReflection;
public function __construct(ReflectionClass|string $class, ? CacheInterface $cache = null) {
$this->classname = ltrim($class instanceof ReflectionClass ? $class->getName() : $class, '\\');
$this->cache = $cache;
#if ( ! $this->cache || ! $this->cache->has($class) ) {
$this->classReflection = $class instanceof ReflectionClass ? $class : new ReflectionClass($class);
# }
}
public static function fromClass(ReflectionClass|string $class, ? CacheInterface $cache = null) : self
{
return new static($class, $cache);
}
public function getClassReflection() : ReflectionClass
{
return $this->classReflection;
}
public function reflectClass() : ReflectedClass
{
$parentClass = $this->classReflection->getParentClass();
return new ReflectedClass(
name: $this->classReflection->getName(),
parent: $parentClass ? static::fromClass($parentClass)->reflectClass() : false,
methods: $this->reflectMethods(),
properties: $this->reflectProperties(),
attributes: AttributeReader::reflectAttributes($this->classReflection),
interfaces: array_map(fn($interface) => static::fromClass($interface)->reflectClass(), array_keys($this->classReflection->getInterfaces())),
traits: array_map(fn($trait) => static::fromClass($trait)->reflectClass(), array_keys($this->classReflection->getTraits())),
);
}
public function reflectProperties(int $filter =
ReflectionProperty::IS_PUBLIC |
ReflectionProperty::IS_PROTECTED |
ReflectionProperty::IS_PRIVATE
) : array
{
$defaultValues = $this->classReflection->getDefaultProperties();
$list = [];
foreach($this->classReflection->getProperties($filter) as $property) {
$reflected = new ReflectedProperty($property->getName());
$reflected->attributes = AttributeReader::reflectAttributes($property);
if ( $reflected->hasIgnoreAttribute() ) {
continue;
}
# Default value can be 'null', so isset() it not suitable here
if ( array_key_exists($reflected->name, $defaultValues) ) {
$reflected->value = $defaultValues[ $reflected->name ];
}
$reflected->typeFromReflection($property);
$list[ $reflected->name ] = $reflected;
}
return array_merge($properties ?? [], $list);
}
public function reflectMethods(int $filter =
ReflectionMethod::IS_PUBLIC |
ReflectionMethod::IS_PROTECTED |
ReflectionMethod::IS_PRIVATE |
ReflectionMethod::IS_STATIC |
ReflectionMethod::IS_FINAL
) : array
{
$list = [];
foreach($this->classReflection->getMethods($filter) as $method) {
# Skipping parent's methods, we'll retrieve them in its own reflection
if ( $method->class !== $this->classname ) {
continue;
}
$reflectedMethod = new ReflectedMethod(
name: $method->getName(),
classname: $method->class,
type: $method->hasReturnType() ? $method->getReturnType() : false,
isConstructor: $method->isConstructor(),
isDestructor: $method->isDestructor(),
isAbstract: $method->isAbstract(),
attributes: AttributeReader::reflectAttributes($method),
);
if ( $reflectedMethod->hasIgnoreAttribute() ) {
continue;
}
foreach($method->getParameters() as $parameter) {
$reflectedParameter = new ReflectedMethodProperty(
name: $parameter->getName(),
attributes: AttributeReader::reflectAttributes($parameter),
);
if ( $reflectedParameter->hasIgnoreAttribute() ) {
continue;
}
$reflectedParameter->position = $parameter->getPosition();
$reflectedParameter->typeFromReflection($parameter);
$reflectedParameter->byReference = $parameter->isPassedByReference();
$reflectedParameter->optional = $parameter->isOptional();
$reflectedMethod->parameters[$reflectedParameter->name] = $reflectedParameter;
}
$list[$reflectedMethod->name] = $reflectedMethod;
}
return $list;
}
}