From 61b84bfd09c3be4f05ef1366758928892600ff64 Mon Sep 17 00:00:00 2001 From: Dave Mc Nicoll Date: Thu, 9 May 2024 19:43:08 +0000 Subject: [PATCH 01/10] - Totally reworked Notes to allievate most conceptions problems. Removed old annotations artifacts too --- composer.json | 4 +- src/AttributeReader.php | 59 +++++ src/CLI/CommandFetcher.php | 2 +- src/Common/Reflected.php | 59 +++++ src/Common/ReflectedAttribute.php | 12 + src/Common/ReflectedClass.php | 149 ++++++++++++ src/Common/ReflectedInterface.php | 8 + src/Common/ReflectedMethod.php | 22 ++ src/Common/ReflectedMethodProperty.php | 12 + src/Common/ReflectedMethodType.php | 8 + src/Common/ReflectedProperty.php | 47 ++++ src/Common/ReflectedPropertyType.php | 21 ++ src/Cronard/TaskFetcher.php | 4 +- src/Event/EventFetcher.php | 4 +- src/ObjectReflection.php | 304 ++++++------------------- src/ObjectResolver.php | 231 ++----------------- src/Route/RouteFetcher.php | 6 +- src/Security/SecurityHandler.php | 6 +- 18 files changed, 495 insertions(+), 463 deletions(-) create mode 100644 src/AttributeReader.php create mode 100644 src/Common/Reflected.php create mode 100644 src/Common/ReflectedAttribute.php create mode 100644 src/Common/ReflectedClass.php create mode 100644 src/Common/ReflectedInterface.php create mode 100644 src/Common/ReflectedMethod.php create mode 100644 src/Common/ReflectedMethodProperty.php create mode 100644 src/Common/ReflectedMethodType.php create mode 100644 src/Common/ReflectedProperty.php create mode 100644 src/Common/ReflectedPropertyType.php diff --git a/composer.json b/composer.json index 9fa3edb..f43d4b3 100644 --- a/composer.json +++ b/composer.json @@ -9,7 +9,9 @@ "email": "info@mcnd.ca" } ], - "require": {}, + "require": { + "mcnd/kash" : "dev-master" + }, "autoload": { "psr-4": { "Notes\\": "src/" diff --git a/src/AttributeReader.php b/src/AttributeReader.php new file mode 100644 index 0000000..acb0396 --- /dev/null +++ b/src/AttributeReader.php @@ -0,0 +1,59 @@ +getAttributes() as $attribute) { + try { + $list[] = new ReflectedAttribute( + tag: substr(strrchr($attribute->getName(), "\\"), 1), + object: $attribute->newInstance(), + arguments: $attribute->getArguments(), + ); + } + catch(\Throwable $e) { + throw new \Exception(sprintf("%s for %s in file '%s'", $e->getMessage(), $reflect, static::getObjectName($reflect))); + } + } + + return $list; + } + + protected static function getObjectName(Reflector $reflect) : string + { + switch(true) { + case $reflect instanceof ReflectionMethod : + case $reflect instanceof ReflectionProperty : + return $reflect->class . "::" . $reflect->name; + + case $reflect instanceof ReflectionClass : + return $reflect->name; + } + + throw new \InvalidArgumentException("Given reflector is not supported within getObjectName()."); + } + + protected static function getObjectNamespace(Reflector $reflect) : string + { + switch(true) { + case $reflect instanceof ReflectionMethod : + case $reflect instanceof ReflectionProperty : + return $reflect->getDeclaringClass()->getNamespaceName(); + + case $reflect instanceof ReflectionClass : + return $reflect->getNamespaceName(); + } + + throw new \InvalidArgumentException("Given reflector is not supported within getObjectNamespace()."); + } +} diff --git a/src/CLI/CommandFetcher.php b/src/CLI/CommandFetcher.php index b82fd6e..8737b3a 100644 --- a/src/CLI/CommandFetcher.php +++ b/src/CLI/CommandFetcher.php @@ -87,7 +87,7 @@ class CommandFetcher { $class = $this->generateClassname($basename, $namespace); - $objectResolver = new ObjectResolver($class, false, true, false, false); + $objectResolver = new ObjectResolver($class, true, false, false); $attributes = $objectResolver->getAttributeListFromClassname( $this->annotations['object'], false ) ; diff --git a/src/Common/Reflected.php b/src/Common/Reflected.php new file mode 100644 index 0000000..080aa0d --- /dev/null +++ b/src/Common/Reflected.php @@ -0,0 +1,59 @@ +attributes, fn($e) => $e->object instanceof Ignore); + } + + public function allowsNull() : bool + { + return empty($this->type) || $this->expectType("null"); + } + + public function expectType(string $type) : bool + { + $type = strtolower($type); + + foreach($this->getTypes() as $item) { + + if ($type === "null") { + if ($item->type === "null" || $item->nullable) { + return true; + } + } + elseif ($type === $item->type) { + return true; + } + } + + return false; + } + + public function getTypes() : array + { + return is_array($this->type) ? $this->type : [ $this->type ]; + } + + public function typeFromReflection(\ReflectionProperty|\ReflectionParameter $property) : void + { + if ( $property->hasType() ) { + $type = $property->getType(); + + if ($type instanceof \ReflectionUnionType ) { + foreach($type->getTypes() as $item) { + $this->type[] = new ReflectedPropertyType($item->getName(), $item->isBuiltIn(), $item->allowsNull()); + } + } + else { + $this->type = new ReflectedPropertyType($type->getName(), $type->isBuiltIn(), $type->allowsNull()); + } + } + } + +} \ No newline at end of file diff --git a/src/Common/ReflectedAttribute.php b/src/Common/ReflectedAttribute.php new file mode 100644 index 0000000..a14887a --- /dev/null +++ b/src/Common/ReflectedAttribute.php @@ -0,0 +1,12 @@ +parent ? $this->parent->getProperties(true) : [], + $this->parent ? $this->parent->getProperties(true) : [], + $this->properties + ) : $this->properties; + } + + public function getMethods(bool $deep = true) : array + { + $m = array_merge(...array_map(fn($e) => $e->getMethods(true), $this->traits)); + + return $deep ? array_replace( + $this->parent ? $this->parent->getMethods(true) : [], + array_merge(...array_map(fn($e) => $e->getMethods(true), $this->interfaces)), + array_merge(...array_map(fn($e) => $e->getMethods(true), $this->traits)), + $this->methods + ) : $this->methods; + } + + public function getAttributes(bool $deep = true) : array + { + return $deep ? array_merge( + $this->parent ? $this->parent->getAttributes(true) : [], + array_merge(...array_map(fn($e) => $e->getAttributes(true), $this->interfaces)), + array_merge(...array_map(fn($e) => $e->getAttributes(true), $this->traits)), + $this->attributes + ) : $this->attributes; + } + + # Get Attributes based on Native attributes's target + public function getClassAttributeList(array|string $className, bool $deepSearch, bool $throwOnError = true) : array + { + $list = []; + + foreach((array) $className as $class) { + $reflection = new \ReflectionClass($class); + $attributes = $reflection->getAttributes(); + + foreach($attributes as $attribute) { + # Native PHP Attribute + if ($attribute->getName() === 'Attribute') { + if ($attribute->getTarget() && \Attribute::TARGET_CLASS) { + $list = $this->getClassAttributeListFromClassname($class, $deepSearch, false); + } + + if ($attribute->getTarget() && \Attribute::TARGET_METHOD) { + $list = array_merge($list, $this->getMethodAttributeListFromClassname($class, $deepSearch, false)); + } + + if ($attribute->getTarget() && \Attribute::TARGET_PROPERTY) { + $list = array_merge($list, $this->getPropertyAttributeListFromClassname($class, $deepSearch, false)); + } + } + } + } + + if ( empty($list) && $throwOnError ) { + $className = implode(',', (array) $className); + throw new \InvalidArgumentException("Attribute(s) `$className` was not found within class {$this->name} (or it's parents / traits)"); + } + + return $list; + } + + + public function getClassAttributeListFromClassname(array|string $className, bool $deepSearch, bool $throwOnError = true) : array + { + $list = []; + + foreach((array) $className as $class) { + foreach ($this->getAttributes($deepSearch) as $item) { + if ($item->object instanceof $class) { + $list[] = $item->object; + } + } + } + + if ( empty($list) && $throwOnError ) { + $className = implode(',', (array) $className); + throw new \InvalidArgumentException("Attribute(s) `$className` was not found within class {$this->name} (or it's parents / traits)"); + } + + return $list; + } + + public function getPropertyAttributeListFromClassname(array|string $className, bool $deepSearch, bool $throwOnError = true) : array + { + $list = []; + + foreach((array) $className as $class) { + foreach ($this->getProperties($deepSearch) as $property) { + foreach ($property->attributes as $item) { + if ($item->object instanceof $class) { + $list[$property->name] ??= []; + $list[$property->name][] = $item->object; + } + } + } + } + + if ( empty($list) && $throwOnError ) { + $className = implode(',', (array) $className); + throw new \InvalidArgumentException("Attribute(s) `$className` was not found within properties of {$this->name} (or it's parents / traits)"); + } + + return $list; + } + + public function getMethodAttributeListFromClassname(array|string $className, bool $deepSearch, bool $throwOnError = true) : array + { + $list = []; + + foreach((array) $className as $class) { + foreach ($this->getMethods($deepSearch) as $method) { + foreach ($method->attributes as $item) { + if ($item->object instanceof $class) { + $list[$method->name] ??= []; + $list[$method->name][] = $item->object; + } + } + } + } + + if ( empty($list) && $throwOnError ) { + $className = implode(',', (array) $className); + throw new \InvalidArgumentException("Attribute(s) `$className` was not found within methods of {$this->name} (or it's parents / traits)"); + } + + return $list; + } +} \ No newline at end of file diff --git a/src/Common/ReflectedInterface.php b/src/Common/ReflectedInterface.php new file mode 100644 index 0000000..ae9dbcb --- /dev/null +++ b/src/Common/ReflectedInterface.php @@ -0,0 +1,8 @@ +attributes; + } +} \ No newline at end of file diff --git a/src/Common/ReflectedMethodProperty.php b/src/Common/ReflectedMethodProperty.php new file mode 100644 index 0000000..43aba6a --- /dev/null +++ b/src/Common/ReflectedMethodProperty.php @@ -0,0 +1,12 @@ +attributes; + } + + + ## BACKWARD COMPATIBILITY ONLY To be removed really soon ! + public function offsetExists(mixed $offset): bool + { + return isset($this->$offset); + } + + public function offsetGet(mixed $offset): mixed + { + if ($offset === 'tags') { + return $this->attributes; + } + + return $this->$offset; + } + + public function offsetSet(mixed $offset, mixed $value): void + { + $this->$offset = $value; + } + + public function offsetUnset(mixed $offset): void + { + unset($this->$offset); + } +} \ No newline at end of file diff --git a/src/Common/ReflectedPropertyType.php b/src/Common/ReflectedPropertyType.php new file mode 100644 index 0000000..7cc438b --- /dev/null +++ b/src/Common/ReflectedPropertyType.php @@ -0,0 +1,21 @@ +nullable) { + return true; + } + + return $this->type === $type; + } +} \ No newline at end of file diff --git a/src/Cronard/TaskFetcher.php b/src/Cronard/TaskFetcher.php index c876d5e..9a88568 100644 --- a/src/Cronard/TaskFetcher.php +++ b/src/Cronard/TaskFetcher.php @@ -78,9 +78,9 @@ class TaskFetcher { $class = $this->generateClassname($file->getBasename(".php"), $namespace); # Should generate an equivalent of Ulmus's object reflection here ! - $objectResolver = new ObjectResolver($class, true, true, false, true); + $objectResolver = new ObjectResolver($class, $this->cache); - foreach($objectResolver->getAttributeListFromClassname( $this->annotations['method'], false ) as $func => $cronard) { + foreach($objectResolver->reflectedClass->getMethodAttributeListFromClassname( $this->annotations['method'], false, false ) as $func => $cronard) { foreach($cronard as $task) { $list[] = [ 'class' => $class, 'method' => $func, 'annotation' => $task ]; } diff --git a/src/Event/EventFetcher.php b/src/Event/EventFetcher.php index 9c7c437..5bd013b 100644 --- a/src/Event/EventFetcher.php +++ b/src/Event/EventFetcher.php @@ -78,9 +78,9 @@ class EventFetcher { $class = $this->generateClassname($file->getBasename(".php"), $namespace); # Should generate an equivalent of Ulmus's object reflection here ! - $objectResolver = new ObjectResolver($class, true, true, false, true); + $objectResolver = new ObjectResolver($class, $this->cache ?? null); - foreach($objectResolver->getAttributeListFromClassname( $this->annotations['method'], false ) as $func => $events) { + foreach($objectResolver->reflectedClass->getMethodAttributeListFromClassname($this->annotations['method'], false, false ) as $func => $events) { foreach($events as $ev) { $list[] = new \Mcnd\Event\Event(name: $ev->name, class: $class, method: $func, attribute: $ev); } diff --git a/src/ObjectReflection.php b/src/ObjectReflection.php index b773b43..a871886 100644 --- a/src/ObjectReflection.php +++ b/src/ObjectReflection.php @@ -4,6 +4,11 @@ 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; @@ -14,81 +19,37 @@ class ObjectReflection { protected ReflectionClass $classReflection; - public AnnotationReader $annotationReader; + public function __construct(ReflectionClass|string $class, ? CacheInterface $cache = null) { + $this->classname = ltrim($class instanceof ReflectionClass ? $class->getName() : $class, '\\'); - public function __construct($class, AnnotationReader $annotationReader = null, ? CacheInterface $cache = null) { - $this->classname = ltrim($class, '\\'); $this->cache = $cache; #if ( ! $this->cache || ! $this->cache->has($class) ) { $this->classReflection = $class instanceof ReflectionClass ? $class : new ReflectionClass($class); - $this->annotationReader = $annotationReader ?: AnnotationReader::fromClass($class); # } } - public static function fromClass($class, ? CacheInterface $cache = null) : self + public static function fromClass(ReflectionClass|string $class, ? CacheInterface $cache = null) : self { return new static($class, null, $cache); } - public function read(bool $fullUses = true, bool $fullObject = true, $fullMethod = true, $fullProperty = true) : array + public function reflectClass() : ReflectedClass { - return $this->handleCaching(implode('', [ $this->classname, (int)$fullObject, (int) $fullMethod, (int) $fullProperty ]), fn() => [ - 'uses' => $this->gatherUses($fullUses), - 'class' => $this->gatherClass($fullObject), - 'method' => $this->gatherMethods($fullMethod), - 'property' => $this->gatherProperties($fullProperty), - ]); + $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 gatherUses(bool $full = true) : array - { - if ( $full ) { - if ( $parentClass = $this->classReflection->getParentClass() ) { - $list = static::fromClass($parentClass)->gatherUses(true); - } - - foreach($this->classReflection->getTraits() as $trait) { - $list = array_replace(static::fromClass($trait->name)->gatherUses(true), $list ?? []); - } - } - - return array_replace($list ?? [], $this->getUsesStatements()); - } - - public function gatherClass(bool $full = true) : array - { - if ( $full ) { - if ( $parentClass = $this->classReflection->getParentClass() ) { - $class = static::fromClass($parentClass)->gatherClass(true); - } - - if ( $traits = $this->classReflection->getTraits() ) { - foreach($traits as $key => $value) { - $traitTags = static::fromClass($key)->gatherClass(true); - } - } - - if ( $interfaces = $this->classReflection->getInterfaces() ) { - foreach($interfaces as $key => $value) { - $interfaceTags = static::fromClass($key)->gatherClass(true); - } - } - - $itemName = function($item) { - return $item->getName(); - }; - } - - return array_merge_recursive($class ?? [], $traitTags ?? [], $interfaceTags ?? [], [ - 'tags' => $this->annotationReader->getClass($this->classReflection) - ] + ( ! $full ? [] : [ - 'traits' => array_map($itemName, $traits), - 'interfaces' => array_map($itemName, $interfaces), - ] )); - } - - public function gatherProperties(bool $full = true, int $filter = + + public function reflectProperties(int $filter = ReflectionProperty::IS_PUBLIC | ReflectionProperty::IS_PROTECTED | ReflectionProperty::IS_PRIVATE @@ -96,48 +57,31 @@ class ObjectReflection { { $defaultValues = $this->classReflection->getDefaultProperties(); - if ( $full ) { - if ( $parentClass = $this->classReflection->getParentClass() ) { - $properties = static::fromClass($parentClass)->gatherProperties($full, $filter); - } - } - $list = []; foreach($this->classReflection->getProperties($filter) as $property) { - $current = [ - 'name' => $property->getName() - ]; + $reflected = new ReflectedProperty($property->getName()); - # Default value can be 'null', so isset() it not suitable here - if ( array_key_exists($current['name'], $defaultValues) ) { - $current['value'] = $defaultValues[ $current['name'] ]; - } + $reflected->attributes = AttributeReader::reflectAttributes($property); - - if ( $property->hasType() ) { - $type = $property->getType(); - - if (! $type instanceof \ReflectionUnionType ) { - $current['type'] = $type->getName(); - $current['builtin'] = $type->isBuiltIn(); - $current['nullable'] = $type->allowsNull(); - } - } - - $current['tags'] = $this->annotationReader->getProperty($property); - - if ( $this->ignoreElementAnnotation($current['tags']) ) { + if ( $reflected->hasIgnoreAttribute() ) { continue; } - $list[ $current['name'] ] = $current; + # 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 gatherMethods(bool $full = true, int $filter = + public function reflectMethods(int $filter = ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_PROTECTED | ReflectionMethod::IS_PRIVATE | @@ -147,163 +91,47 @@ class ObjectReflection { { $list = []; - if ( $full ) { - if ( $parentClass = $this->classReflection->getParentClass() ) { - $methods = static::fromClass($parentClass)->gatherMethods($full, $filter); - } - } - foreach($this->classReflection->getMethods($filter) as $method) { - if ( ! $full && ( $method->class !== $this->classname ) ) { + # Skipping parent's methods, we'll retrieve them in its own reflection + if ( $method->class !== $this->classname ) { continue; } - $parameters = []; + $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) { - $parameters[$parameter->getName()] = [ - 'null' => $parameter->allowsNull(), - 'position' => $parameter->getPosition(), - 'type' => $parameter->hasType() && $parameter->getType() instanceof \ReflectionNamedType ? $parameter->getType()->getName() : false, - 'array' => $this->isType('array', $parameter), - 'callable' => $this->isType('callable', $parameter), - 'optional' => $parameter->isOptional(), - 'byReference' => $parameter->isPassedByReference(), - ]; + $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; } - $current = [ - 'name' => $method->getName(), - 'classname' => $method->class, - 'type' => $method->hasReturnType() && $method->getReturnType() instanceof \ReflectionNamedType ? $method->getReturnType()->getName() : false, - 'constructor' => $method->isConstructor(), - 'destructor' => $method->isDestructor(), - 'parameters' => $parameters, - ]; - - $current['tags'] = $this->annotationReader->getMethod($method); - - if ( $this->ignoreElementAnnotation($current['tags']) ) { - continue; - } - - $list[ $current['name'] ] = $current; + $list[$reflectedMethod->name] = $reflectedMethod; } - return array_merge($methods ?? [], $list); - } - - protected function ignoreElementAnnotation($tags) : bool - { - return [] !== array_filter($tags, fn($e) => ( $e['object'] ?? null ) instanceof Ignore); - } - - - protected function readCode() : string - { - static $code = []; - $fileName = $this->classReflection->getFilename(); - return $code[$fileName] ?? $code[$fileName] = file_get_contents($fileName); - } - - # From https://www.php.net/manual/en/reflectionparameter.isarray.php - public static function isType(string $type, ReflectionParameter $reflectionParameter) : bool - { - if ( $reflectionType = $reflectionParameter->getType() ) { - $types = $reflectionType instanceof ReflectionUnionType ? $reflectionType->getTypes() : [$reflectionType]; - - return in_array($type, array_map(fn(ReflectionNamedType $t) => $t->getName(), $types)); - } - - return false; - } - - protected function getUsesStatements() : array - { - $uses = []; - $tokens = token_get_all( $c = $this->readCode() ); - - while ( $token = array_shift($tokens) ) { - - if ( is_array($token) ) { - list($token, $value) = $token; - } - - switch ($token) { - case T_CLASS: - case T_TRAIT: - case T_INTERFACE: - break 2; - - case T_USE: - $isUse = true; - break; - - case T_NS_SEPARATOR: - $isNamespace = $isUse; - break; - - case T_NAME_QUALIFIED: - case T_STRING: - if ( $isNamespace && $latestString ) { - $statement[] = $latestString; - } - - $latestString = $value; - break; - - case T_AS: - # My\Name\Space\aClassHere `as` ClassAlias; - $replacedClass = implode("\\", array_merge($statement, [ $latestString ])); - $latestString = null; - break; - - case T_WHITESPACE: - case T_COMMENT: - case T_DOC_COMMENT: - break; - - case '{': - # opening a sub-namespace -> \My\Name\Space\`{`OneItem, AnotherItem} - if ( $isNamespace ) { - $inNamespace = true; - } - break; - - case ';'; - case ',': - case '}': - if ( $isUse ) { - $clsName = ltrim(substr($latestString, strrpos($latestString, "\\") ), '\\'); - if ( $replacedClass ) { - $uses[$replacedClass] = $clsName; - $replacedClass = ""; - } - elseif ( $latestString ) { - $uses[implode("\\", array_merge($statement, [ $latestString ]))] = $clsName; - } - } - - if ( $inNamespace ) { - $latestString = ""; - - # \My\Name\Space\{OneItem, AnotherItem`}` <- closing a sub-namespace - if ( $token !== "}" ) { - break; - } - } - - case T_OPEN_TAG: - default: - $statement = []; - $latestString = ""; - $replacedClass = null; - $isNamespace = $inNamespace = false; - $isUse = ( $isUse ?? false ) && ( $token === ',' ); - break; - } - } - - return $uses; + return $list; } } diff --git a/src/ObjectResolver.php b/src/ObjectResolver.php index 9333adb..22ddfa3 100644 --- a/src/ObjectResolver.php +++ b/src/ObjectResolver.php @@ -2,16 +2,13 @@ namespace Notes; +use Notes\Common\ReflectedClass; use Psr\SimpleCache\CacheInterface; class ObjectResolver { - - const KEY_ENTITY_NAME = 01; - const KEY_COLUMN_NAME = 02; - public string $objectClass; - public array $uses; + public ReflectedClass $reflectedClass; public array $class; @@ -19,52 +16,11 @@ class ObjectResolver { public array $methods; - public function __construct(string $objectClass, bool $fullUses = true, bool $fullObject = true, $fullMethod = true, $fullProperty = true, ? CacheInterface $cache = null) + public function __construct(string $objectClass, ? CacheInterface $cache = null) { $this->objectClass = $objectClass; - list($this->uses, $this->class, $this->methods, $this->properties) = array_values( - ObjectReflection::fromClass($objectClass, $cache)->read($fullUses, $fullObject, $fullMethod, $fullProperty) - ); - - $this->resolveAnnotations(); - } - - - /** - * Transform an annotation into it's object's counterpart - */ - public function getAttributeFromClassname(array|string $className, bool $throwOnError = true) : object|bool - { - foreach((array) $className as $class) { - foreach (array_reverse($this->class['tags']) as $item) { - if ($item['object'] instanceof $class) { - return $this->instanciateAnnotationObject($item); - } - - foreach ($this->properties as $property) { - foreach ($property['tags'] as $tag) { - if ($item['object'] instanceof $class) { - return $this->instanciateAnnotationObject($tag); - } - } - } - - foreach ($this->methods as $method) { - foreach ($method['tags'] as $tag) { - if ($item['object'] instanceof $class) { - return $this->instanciateAnnotationObject($tag); - } - } - } - } - } - - if ($throwOnError) { - throw new \Exception(sprintf("Annotation `%s` could not be found within your object `%s`.", implode(', ', (array)$className), $this->objectClass)); - } - - return false; + $this->reflectedClass = ObjectReflection::fromClass($objectClass, $cache)->reflectClass(); } /** @@ -75,26 +31,26 @@ class ObjectResolver { $list = []; foreach((array) $className as $class) { - foreach ($this->class['tags'] as $item) { - if ($item['object'] instanceof $class) { - $list[] = $this->instanciateAnnotationObject($item); + foreach ($this->reflectedClass->getAttributes() as $item) { + if ($item->object instanceof $class) { + $list[] = $item->object; } } - foreach ($this->properties as $property) { - foreach ($property['tags'] as $item) { - if ($item['object'] instanceof $class) { - $list[$property['name']] ??= []; - $list[$property['name']][] = $this->instanciateAnnotationObject($item); + foreach ($this->reflectedClass->getProperties(true) as $property) { + foreach ($property->attributes as $item) { + if ($item->object instanceof $class) { + $list[$property->name] ??= []; + $list[$property->name][] = $item->object; } } } - foreach ($this->methods as $method) { - foreach ($method['tags'] as $item) { - if ($item['object'] instanceof $class) { - $list[$method['name']] ??= []; - $list[$method['name']][] = $this->instanciateAnnotationObject($item); + foreach ($this->reflectedClass->getMethods(true) as $method) { + foreach ($method->attributes as $item) { + if ($item->object instanceof $class) { + $list[$method->name] ??= []; + $list[$method->name][] = $item->object; } } } @@ -106,157 +62,4 @@ class ObjectResolver { return $list; } - - /** - * @deprecated Will soon be deprecated in favour of PHP's native attributes - * Transform an annotation into it's object's counterpart - */ - public function getAnnotationFromClassname(array|string $className, bool $throwOnError = true) : object|bool - { - foreach((array) $className as $class) { - - if ( $name = $this->uses[$class] ?? false) { - foreach (array_reverse($this->class['tags']) as $item) { - if ($item['tag'] === $name) { - return $this->instanciateAnnotationObject($item); - } - - foreach ($this->properties as $property) { - foreach ($property['tags'] as $item) { - if ($item['tag'] === $name) { - return $this->instanciateAnnotationObject($item); - } - } - } - - foreach ($this->methods as $method) { - foreach ($method['tags'] as $item) { - if ($item['tag'] === $name) { - return $this->instanciateAnnotationObject($item); - } - } - } - } - } - } - - if ($throwOnError) { - throw new \Exception(sprintf("Annotation `%s` could not be found within your object `%s`.", implode(', ', (array)$className), $this->objectClass)); - } - - return false; - } - - /** - * Transform an annotation into it's object's counterpart - */ - public function getAnnotationListFromClassname(array|string $className, bool $throwOnError = true) : array - { - $list = []; - - foreach((array) $className as $class) { - if ($name = $this->uses[$class] ?? false) { - foreach ($this->class['tags'] as $item) { - if ($item['tag'] === $name) { - $list[] = $this->instanciateAnnotationObject($item); - } - } - - foreach ($this->properties as $property) { - foreach ($property['tags'] as $item) { - if ($item['tag'] === $name) { - $list[$property['name']] ??= []; - - $list[$property['name']][] = $this->instanciateAnnotationObject($item); - } - } - } - - foreach ($this->methods as $method) { - foreach ($method['tags'] as $item) { - if ($item['tag'] === $name) { - $list[$method['name']] ??= []; - - $list[$method['name']][] = $this->instanciateAnnotationObject($item); - } - } - } - } - } - - if ( empty($list) ) { - if ($throwOnError) throw new \InvalidArgumentException("Class `$className` was not found within {$this->objectClass} uses statement (or it's children / traits)"); - } - - return $list; - } - - public function instanciateAnnotationObject(array $tagDefinition) : Annotation|\Notes\Attribute - { - if (isset($tagDefinition['object']) && $tagDefinition['object'] instanceof \Notes\Attribute) { - return $tagDefinition['object']; - } - - $arguments = $this->extractArguments($tagDefinition['arguments']); - - if ( false === $class = array_search($tagDefinition['tag'], $this->uses) ) { - return new class() implements Annotation {}; - # throw new \InvalidArgumentException("Annotation class `{$tagDefinition['tag']}` was not found within {$this->objectClass} uses statement (or it's children / traits)"); - } - try { - $obj = new $class(... $arguments['constructor']); - } - catch (\Throwable $e) { - $err = $e::class; - throw new $err(sprintf("An error occured trying to instanciate class definition '%s' given argument '%s' : %s", var_export($tagDefinition, true), var_export($arguments['constructor'], true), $e->getMessage())); - } - - foreach($arguments['setter'] as $key => $value) { - $obj->$key = $value; - } - - if ( ! $obj instanceof \Notes\Attribute ) { - throw new \RuntimeException(sprintf("An error occurred trying to retrieve an Attribute def:%s from class %s", json_encode($tagDefinition), $this->objectClass)); - } - - return $obj; - } - - /** - * Extracts arguments from an Annotation definition, easing object's declaration. - */ - protected function extractArguments(array $arguments) : array - { - $list = [ - 'setter' => [], - 'constructor' => [], - ]; - - ksort($arguments); - - foreach($arguments as $key => $value) { - $list[ is_int($key) ? 'constructor' : 'setter' ][$key] = $value; - } - - return $list; - } - - protected function resolveAnnotations() - { - foreach($this->class['tags'] as $key => &$tag) { - $tag['object'] ??= $this->instanciateAnnotationObject($tag); - } - - foreach($this->properties as &$property) { - foreach($property['tags'] as &$tag){ - $tag['object'] ??= $this->instanciateAnnotationObject($tag); - } - } - - foreach($this->methods as &$method) { - foreach($method['tags'] as &$tag){ - $tag['object'] ??= $this->instanciateAnnotationObject($tag); - } - } - } } diff --git a/src/Route/RouteFetcher.php b/src/Route/RouteFetcher.php index 8a5adff..5fbe254 100644 --- a/src/Route/RouteFetcher.php +++ b/src/Route/RouteFetcher.php @@ -93,11 +93,11 @@ class RouteFetcher { $class = $this->generateClassname($file->getBasename(".php"), $namespace); $methods = null; - $objectResolver = new ObjectResolver($class, true, true, false, true, $this->cache); + $objectResolver = new ObjectResolver($class, $this->cache); if ( isset($attributes['object']) ) { - $objects = $objectResolver->getAttributeListFromClassname($attributes['object'], false); + $objects = $objectResolver->reflectedClass->getClassAttributeListFromClassname($attributes['object'], true, false); foreach(array_reverse($objects) as $object) { if ($object->method ?? false ) { @@ -109,7 +109,7 @@ class RouteFetcher { } if ( isset($attributes['method']) ) { - $routeList = $objectResolver->getAttributeListFromClassname( $attributes['method'], false ); + $routeList = $objectResolver->reflectedClass->getMethodAttributeListFromClassname( $attributes['method'], false, false ); foreach($routeList as $func => $routes) { if (is_array($routes)) { diff --git a/src/Security/SecurityHandler.php b/src/Security/SecurityHandler.php index 81d2b02..e22e8c2 100644 --- a/src/Security/SecurityHandler.php +++ b/src/Security/SecurityHandler.php @@ -58,10 +58,12 @@ class SecurityHandler { protected function getClassAttributes(string $annotationClass, string $className, string $methodName)/* : \Notes\Attribute|array */ { - $objectResolver = new ObjectResolver($className, true, true, false, true); + $objectResolver = new ObjectResolver($className); try { - $method = $objectResolver->getAttributeListFromClassname( $annotationClass, false ); + $method = $objectResolver->reflectedClass->getClassAttributeList( $annotationClass, true, false ); + + # var_dump($method); } catch(\Exception $e) { } From 0cdc2dc3d8489ee17c00627dcae66e368a5e8a6e Mon Sep 17 00:00:00 2001 From: Dave Mc Nicoll Date: Mon, 27 May 2024 18:06:56 +0000 Subject: [PATCH 02/10] - Added some more method to filter properties --- src/Common/Reflected.php | 27 +++++++++++++++++++++++++ src/Common/ReflectedClass.php | 34 ++++++++++++++++++++++++++++++-- src/Common/ReflectedMethod.php | 4 ---- src/Common/ReflectedProperty.php | 6 ------ src/ObjectReflection.php | 2 +- 5 files changed, 60 insertions(+), 13 deletions(-) diff --git a/src/Common/Reflected.php b/src/Common/Reflected.php index 080aa0d..d776602 100644 --- a/src/Common/Reflected.php +++ b/src/Common/Reflected.php @@ -56,4 +56,31 @@ abstract class Reflected } } + public function getAttributes(?string $attributeType = null): array + { + if ($attributeType) { + $list = []; + + foreach($this->attributes as $attribute) { + if ($attribute->object instanceof $attributeType) { + $list[] = $attribute; + } + } + + return $list; + } + + return $this->attributes; + } + + public function getAttribute(string $attributeType): ?object + { + foreach($this->getAttributes($attributeType) as $attribute) { + if ($attribute->object instanceof $attributeType) { + return $attribute; + } + } + + return null; + } } \ No newline at end of file diff --git a/src/Common/ReflectedClass.php b/src/Common/ReflectedClass.php index cde1608..05a7842 100644 --- a/src/Common/ReflectedClass.php +++ b/src/Common/ReflectedClass.php @@ -14,6 +14,11 @@ class ReflectedClass implements ReflectedInterface public array $traits = [], ) {} + public function getClassName() : string + { + return end(explode('\\', $this->name)); + } + public function getProperties(bool $deep = true) : array { return $deep ? array_replace( @@ -35,14 +40,28 @@ class ReflectedClass implements ReflectedInterface ) : $this->methods; } - public function getAttributes(bool $deep = true) : array + public function getAttributes(bool $deep = true, ?string $attributeType = null) : array { - return $deep ? array_merge( + $attributes = $deep ? array_merge( $this->parent ? $this->parent->getAttributes(true) : [], array_merge(...array_map(fn($e) => $e->getAttributes(true), $this->interfaces)), array_merge(...array_map(fn($e) => $e->getAttributes(true), $this->traits)), $this->attributes ) : $this->attributes; + + if ($attributeType) { + $list = []; + + foreach($attributes as $attribute) { + if ($attribute->object instanceof $attributeType) { + $list[] = $attribute; + } + } + + return $list; + } + + return $attributes; } # Get Attributes based on Native attributes's target @@ -146,4 +165,15 @@ class ReflectedClass implements ReflectedInterface return $list; } + + public function getAttribute(string $attributeType): ?object + { + foreach($this->getAttributes($attributeType) as $attribute) { + if ($attribute->object instanceof $attributeType) { + return $attribute; + } + } + + return null; + } } \ No newline at end of file diff --git a/src/Common/ReflectedMethod.php b/src/Common/ReflectedMethod.php index 53d3919..5ba50ba 100644 --- a/src/Common/ReflectedMethod.php +++ b/src/Common/ReflectedMethod.php @@ -15,8 +15,4 @@ class ReflectedMethod extends Reflected implements ReflectedInterface public array $parameters = [], ) {} - public function getAttributes(): array - { - return $this->attributes; - } } \ No newline at end of file diff --git a/src/Common/ReflectedProperty.php b/src/Common/ReflectedProperty.php index be5b70a..9baf564 100644 --- a/src/Common/ReflectedProperty.php +++ b/src/Common/ReflectedProperty.php @@ -14,12 +14,6 @@ class ReflectedProperty extends Reflected implements ReflectedInterface, \ArrayA public array $attributes = [], ) {} - public function getAttributes(): array - { - return $this->attributes; - } - - ## BACKWARD COMPATIBILITY ONLY To be removed really soon ! public function offsetExists(mixed $offset): bool { diff --git a/src/ObjectReflection.php b/src/ObjectReflection.php index a871886..cb53edb 100644 --- a/src/ObjectReflection.php +++ b/src/ObjectReflection.php @@ -31,7 +31,7 @@ class ObjectReflection { public static function fromClass(ReflectionClass|string $class, ? CacheInterface $cache = null) : self { - return new static($class, null, $cache); + return new static($class, $cache); } public function reflectClass() : ReflectedClass From 19c79d037402b09418ea2361a9081e402be346e2 Mon Sep 17 00:00:00 2001 From: Dave Mc Nicoll Date: Fri, 31 May 2024 12:28:00 +0000 Subject: [PATCH 03/10] - getAttribute() can now query multiple types --- src/Common/Reflected.php | 18 +++++++----------- src/Common/ReflectedClass.php | 1 - 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/src/Common/Reflected.php b/src/Common/Reflected.php index d776602..f555268 100644 --- a/src/Common/Reflected.php +++ b/src/Common/Reflected.php @@ -56,14 +56,16 @@ abstract class Reflected } } - public function getAttributes(?string $attributeType = null): array + public function getAttributes(null|string|array $attributeType = null): array { if ($attributeType) { $list = []; foreach($this->attributes as $attribute) { - if ($attribute->object instanceof $attributeType) { - $list[] = $attribute; + foreach((array) $attributeType as $type) { + if ($attribute->object instanceof $type) { + $list[] = $attribute; + } } } @@ -73,14 +75,8 @@ abstract class Reflected return $this->attributes; } - public function getAttribute(string $attributeType): ?object + public function getAttribute(string|array $attributeType): ?object { - foreach($this->getAttributes($attributeType) as $attribute) { - if ($attribute->object instanceof $attributeType) { - return $attribute; - } - } - - return null; + return $this->getAttributes($attributeType)[0] ?? null; } } \ No newline at end of file diff --git a/src/Common/ReflectedClass.php b/src/Common/ReflectedClass.php index 05a7842..dff3e02 100644 --- a/src/Common/ReflectedClass.php +++ b/src/Common/ReflectedClass.php @@ -22,7 +22,6 @@ class ReflectedClass implements ReflectedInterface public function getProperties(bool $deep = true) : array { return $deep ? array_replace( - # $this->parent ? $this->parent->getProperties(true) : [], $this->parent ? $this->parent->getProperties(true) : [], $this->properties ) : $this->properties; From 4568427c6659ca4b7e4c1fcfc18c17642dca611f Mon Sep 17 00:00:00 2001 From: Dave Mc Nicoll Date: Fri, 31 May 2024 19:00:32 +0000 Subject: [PATCH 04/10] - Added HTTP methods to Taxus object --- src/Security/Attribute/Taxus.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Security/Attribute/Taxus.php b/src/Security/Attribute/Taxus.php index dc5076f..e0f3e72 100644 --- a/src/Security/Attribute/Taxus.php +++ b/src/Security/Attribute/Taxus.php @@ -7,5 +7,6 @@ class Taxus implements \Notes\Attribute { public function __construct( public null|string|\BackedEnum $privilege = null, public null|string $module = null, + public string|array $method = "*", ) {} } From 86afea9b4e75c93e24f1a528f204e481971a4eaf Mon Sep 17 00:00:00 2001 From: Dave Mc Nicoll Date: Sun, 2 Jun 2024 19:54:55 +0000 Subject: [PATCH 05/10] - Reworked the SecurityHandler granting part which now supports NULL as a 'skipping' value. This necessity appeared since a new property in the attribute was added. --- src/Security/SecurityHandler.php | 63 +++++++++++++++++++++----------- 1 file changed, 42 insertions(+), 21 deletions(-) diff --git a/src/Security/SecurityHandler.php b/src/Security/SecurityHandler.php index e22e8c2..4b95969 100644 --- a/src/Security/SecurityHandler.php +++ b/src/Security/SecurityHandler.php @@ -2,6 +2,7 @@ namespace Notes\Security; +use Notes\Common\ReflectedAttribute; use Taxus\Taxus; use Psr\Http\Message\ResponseInterface; @@ -27,10 +28,12 @@ class SecurityHandler { return $this->isLocked($className, $methodName) ? $this->redirectResponse : null; } + # @TODO Must HANDLE REALM ! public function isLocked(string $className, string $methodName) : bool { - if ( $security = $this->getClassAttributes(Attribute\Security::class, $className, $methodName) ) { - return array_pop($security)->locked; + # Searching method first and fallbacking on object if none found + if ( $list = $this->findAttributes(Attribute\Security::class, $className, $methodName) ) { + return array_pop($list)->locked; } return true; @@ -38,15 +41,16 @@ class SecurityHandler { public function taxus(string $className, string $methodName, object $user = null) : ? ResponseInterface { - if ($taxus = $this->getClassAttributes(Attribute\Taxus::class, $className, $methodName)) { - if ($this->unauthorizeResponse) { - foreach ($taxus as $item) { - if (!isset($item->privilege) || $this->taxus->granted($item->privilege, $user, $item)) { - return null; - } - } + $fromObject = $this->findAttributes(Attribute\Taxus::class, $className); + $fromMethod = $this->findAttributes(Attribute\Taxus::class, $className, $methodName); - return call_user_func_array($this->unauthorizeResponse, [ $user, $taxus, $className, $methodName ]); + if ($fromMethod || $fromObject) { + if ( $this->taxusGrantPermission($fromMethod, $user) || $this->taxusGrantPermission($fromObject, $user) ) { + return null; + } + + if ($this->unauthorizeResponse) { + return call_user_func_array($this->unauthorizeResponse, [ $user, ['method' => $fromMethod, 'object' => $fromObject ], $className, $methodName ]); } else { throw new \ErrorException("Unauthorized response given."); @@ -56,22 +60,39 @@ class SecurityHandler { return null; } - protected function getClassAttributes(string $annotationClass, string $className, string $methodName)/* : \Notes\Attribute|array */ + protected function findAttributes(string $attribute, string $class, ? string $method = null) : array { - $objectResolver = new ObjectResolver($className); + $objectResolver = new ObjectResolver($class); - try { - $method = $objectResolver->reflectedClass->getClassAttributeList( $annotationClass, true, false ); + if ($method) { + $fromMethod = $objectResolver->reflectedClass->getMethods( false ); - # var_dump($method); - } - catch(\Exception $e) { } + if (isset($fromMethod[$method])) { + $attributeList = $fromMethod[$method]->getAttributes($attribute); - if ( $method[$methodName] ?? false ) { - return $method[$methodName]; + if ($attributeList) { + return array_map(fn(ReflectedAttribute $ref) => $ref->object, $attributeList); + } + } } - else { - return array_filter($method, fn($e) => is_object($e)); + + $attributeList = $objectResolver->reflectedClass->getAttributes(false, $attribute) ?: $objectResolver->reflectedClass->getAttributes(true, $attribute); + + if ($attributeList) { + return array_map(fn(ReflectedAttribute $ref) => $ref->object, $attributeList); } + + return []; + } + + protected function taxusGrantPermission(array $attributeList, object $user = null) : bool + { + foreach ($attributeList as $item) { + if (! isset($item->privilege) || $this->taxus->granted($item->privilege, $user, $item)) { + return true; + } + } + + return false; } } From c92baf6cdaa84ec1e606f07fdd2a146e7f2bbd78 Mon Sep 17 00:00:00 2001 From: Dave Mc Nicoll Date: Sun, 2 Jun 2024 23:02:27 +0000 Subject: [PATCH 06/10] - Removed NULL and added a new Enum instead --- src/Security/SecurityHandler.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Security/SecurityHandler.php b/src/Security/SecurityHandler.php index 4b95969..5af6884 100644 --- a/src/Security/SecurityHandler.php +++ b/src/Security/SecurityHandler.php @@ -8,6 +8,7 @@ use Taxus\Taxus; use Psr\Http\Message\ResponseInterface; use Notes\ObjectResolver; +use Taxus\TaxusGrantEnum; class SecurityHandler { @@ -88,8 +89,10 @@ class SecurityHandler { protected function taxusGrantPermission(array $attributeList, object $user = null) : bool { foreach ($attributeList as $item) { - if (! isset($item->privilege) || $this->taxus->granted($item->privilege, $user, $item)) { - return true; + if ( $grant = $this->taxus->granted($item->privilege, $user, $item) ) { + if (is_bool($grant) ? $grant : $grant === TaxusGrantEnum::Authorized) { + return true; + } } } From dd680c8a9a2564cce373e483773b7b128bccbea2 Mon Sep 17 00:00:00 2001 From: Dave Mc Nicoll Date: Tue, 4 Jun 2024 13:33:11 +0000 Subject: [PATCH 07/10] v2.0.0b --- src/Common/Reflected.php | 4 ++-- src/Common/ReflectedClass.php | 15 ++++++++++----- src/Route/RouteFetcher.php | 2 +- src/Security/SecurityHandler.php | 2 +- 4 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/Common/Reflected.php b/src/Common/Reflected.php index f555268..a55893a 100644 --- a/src/Common/Reflected.php +++ b/src/Common/Reflected.php @@ -69,10 +69,10 @@ abstract class Reflected } } - return $list; + return array_reverse($list); } - return $this->attributes; + return array_reverse($this->attributes); } public function getAttribute(string|array $attributeType): ?object diff --git a/src/Common/ReflectedClass.php b/src/Common/ReflectedClass.php index dff3e02..9d84c61 100644 --- a/src/Common/ReflectedClass.php +++ b/src/Common/ReflectedClass.php @@ -39,12 +39,17 @@ class ReflectedClass implements ReflectedInterface ) : $this->methods; } - public function getAttributes(bool $deep = true, ?string $attributeType = null) : array + public function getAttributes(bool $deep = true, ? string $attributeType = null) : array + { + return array_reverse($this->getOrderedAttributes($deep, $attributeType)); + } + + protected function getOrderedAttributes(bool $deep = true, ? string $attributeType = null) : array { $attributes = $deep ? array_merge( - $this->parent ? $this->parent->getAttributes(true) : [], - array_merge(...array_map(fn($e) => $e->getAttributes(true), $this->interfaces)), - array_merge(...array_map(fn($e) => $e->getAttributes(true), $this->traits)), + $this->parent ? $this->parent->getOrderedAttributes(true) : [], + array_merge(...array_map(fn($e) => $e->getOrderedAttributes(true), $this->interfaces)), + array_merge(...array_map(fn($e) => $e->getOrderedAttributes(true), $this->traits)), $this->attributes ) : $this->attributes; @@ -167,7 +172,7 @@ class ReflectedClass implements ReflectedInterface public function getAttribute(string $attributeType): ?object { - foreach($this->getAttributes($attributeType) as $attribute) { + foreach($this->getAttributes(true, $attributeType) as $attribute) { if ($attribute->object instanceof $attributeType) { return $attribute; } diff --git a/src/Route/RouteFetcher.php b/src/Route/RouteFetcher.php index 5fbe254..a635b0e 100644 --- a/src/Route/RouteFetcher.php +++ b/src/Route/RouteFetcher.php @@ -99,7 +99,7 @@ class RouteFetcher { $objects = $objectResolver->reflectedClass->getClassAttributeListFromClassname($attributes['object'], true, false); - foreach(array_reverse($objects) as $object) { + foreach($objects as $object) { if ($object->method ?? false ) { $methods ??= (array) $object->method; } diff --git a/src/Security/SecurityHandler.php b/src/Security/SecurityHandler.php index 5af6884..b41d182 100644 --- a/src/Security/SecurityHandler.php +++ b/src/Security/SecurityHandler.php @@ -34,7 +34,7 @@ class SecurityHandler { { # Searching method first and fallbacking on object if none found if ( $list = $this->findAttributes(Attribute\Security::class, $className, $methodName) ) { - return array_pop($list)->locked; + return array_shift($list)->locked; } return true; From 34a3351ed560a7d87e581f9254c6c56b18976e90 Mon Sep 17 00:00:00 2001 From: Dave Mc Nicoll Date: Fri, 21 Jun 2024 14:17:57 +0000 Subject: [PATCH 08/10] - Added classReflection property --- src/ObjectReflection.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/ObjectReflection.php b/src/ObjectReflection.php index cb53edb..187d470 100644 --- a/src/ObjectReflection.php +++ b/src/ObjectReflection.php @@ -34,6 +34,11 @@ class ObjectReflection { return new static($class, $cache); } + public function getClassReflection() : ReflectionClass + { + return $this->classReflection; + } + public function reflectClass() : ReflectedClass { $parentClass = $this->classReflection->getParentClass(); From 81fa3d49c739628d4c8f16511e0a99ce77278c45 Mon Sep 17 00:00:00 2001 From: Dave Mc Nicoll Date: Mon, 14 Oct 2024 12:52:34 +0000 Subject: [PATCH 09/10] - Breadcrumb now shows up only if deep within at least two routes (if ignoreSoleRoute is true) --- src/AnnotationReader.php | 2 ++ src/Breadcrumb/Breadcrumb.php | 8 +++++++- src/ObjectReflection.php | 22 +++++++++++----------- 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/AnnotationReader.php b/src/AnnotationReader.php index 5d30232..5fdb920 100644 --- a/src/AnnotationReader.php +++ b/src/AnnotationReader.php @@ -99,6 +99,8 @@ class AnnotationReader case $reflect instanceof ReflectionClass : return $reflect->name; } + + return "#UNKNOWN#"; } protected function getObjectNamespace(Reflector $reflect) : string diff --git a/src/Breadcrumb/Breadcrumb.php b/src/Breadcrumb/Breadcrumb.php index d2011d5..ceb27fb 100644 --- a/src/Breadcrumb/Breadcrumb.php +++ b/src/Breadcrumb/Breadcrumb.php @@ -10,6 +10,8 @@ use RuntimeException, DirectoryIterator, Generator; class Breadcrumb extends RouteFetcher { + public bool $ignoreSoleRoute = true; + public function getRouteTree(\Notes\Route\Attribute\Method\Route $route) : array { $tree = []; @@ -36,7 +38,11 @@ class Breadcrumb extends RouteFetcher { else { $route = null; } - }; + } + + if ($this->ignoreSoleRoute && count($tree) === 1) { + return []; + } return array_reverse($tree); } diff --git a/src/ObjectReflection.php b/src/ObjectReflection.php index 187d470..56d972b 100644 --- a/src/ObjectReflection.php +++ b/src/ObjectReflection.php @@ -25,8 +25,8 @@ class ObjectReflection { $this->cache = $cache; #if ( ! $this->cache || ! $this->cache->has($class) ) { - $this->classReflection = $class instanceof ReflectionClass ? $class : new ReflectionClass($class); - # } + $this->classReflection = $class instanceof ReflectionClass ? $class : new ReflectionClass($class); + # } } public static function fromClass(ReflectionClass|string $class, ? CacheInterface $cache = null) : self @@ -53,11 +53,11 @@ class ObjectReflection { 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 + ReflectionProperty::IS_PUBLIC | + ReflectionProperty::IS_PROTECTED | + ReflectionProperty::IS_PRIVATE ) : array { $defaultValues = $this->classReflection->getDefaultProperties(); @@ -87,11 +87,11 @@ class ObjectReflection { } public function reflectMethods(int $filter = - ReflectionMethod::IS_PUBLIC | - ReflectionMethod::IS_PROTECTED | - ReflectionMethod::IS_PRIVATE | - ReflectionMethod::IS_STATIC | - ReflectionMethod::IS_FINAL + ReflectionMethod::IS_PUBLIC | + ReflectionMethod::IS_PROTECTED | + ReflectionMethod::IS_PRIVATE | + ReflectionMethod::IS_STATIC | + ReflectionMethod::IS_FINAL ) : array { $list = []; From 6ba66dcd377ceaaa21d94a6bb343046e93a69f61 Mon Sep 17 00:00:00 2001 From: Dave Mc Nicoll Date: Tue, 29 Oct 2024 16:00:12 +0000 Subject: [PATCH 10/10] - Adjusted for notes V2.x --- src/CLI/CommandFetcher.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CLI/CommandFetcher.php b/src/CLI/CommandFetcher.php index 8737b3a..b172962 100644 --- a/src/CLI/CommandFetcher.php +++ b/src/CLI/CommandFetcher.php @@ -87,7 +87,7 @@ class CommandFetcher { $class = $this->generateClassname($basename, $namespace); - $objectResolver = new ObjectResolver($class, true, false, false); + $objectResolver = new ObjectResolver($class, $this->cache); $attributes = $objectResolver->getAttributeListFromClassname( $this->annotations['object'], false ) ;