diff --git a/src/Attrs.php b/src/Attrs.php index 827b67e..aa3c4cb 100644 --- a/src/Attrs.php +++ b/src/Attrs.php @@ -2,11 +2,16 @@ namespace spaf\simputils; +use Attribute; use Closure; use Exception; +use ReflectionAttribute; use ReflectionClass; +use ReflectionClassConstant; +use ReflectionException; use ReflectionMethod; use ReflectionObject; +use Reflector; use spaf\simputils\models\Box; use TypeError; use function class_exists; @@ -47,7 +52,7 @@ static function collectMethodReflections(mixed $instance, string $attr): Box|arr $reflection = new ReflectionClass($instance); } else { throw new TypeError( - '$instance is not Object or Class String or Class does not exist' + '$instance is not Object or Class String or Class does not exist', ); } if (!is_string($attr) || !class_exists($attr)) { @@ -65,4 +70,119 @@ static function collectMethodReflections(mixed $instance, string $attr): Box|arr return $res; } + /** + * @param string|object $instance + * @param null|Box|array $attrs + * @param int $target + * @param null|callable $callback Must return true, false or null. If null returned, then + * no further attributes are checked. + * params ( + * $attr_instance + * ReflectionAttribute $attr, + * Reflector $item, + * string|object $instance, + * $all_attrs + * ) + * @param bool $find_first Find first matching Spell + * + * @return array|Box + * @throws ReflectionException + */ + static function findSpells( + string|object $instance, + null|Box|array $attrs = null, + int $target = Attribute::TARGET_ALL, + ?callable $callback = null, + bool $find_first = false, + ) { + + $target = Attribute::TARGET_ALL | $target; + + $r = new ReflectionClass($instance); + $res = PHP::box(); + + $attrs = PHP::box($attrs); + + if (Boolean::isBitFlagOn($target, $t = Attribute::TARGET_CLASS)) { + static::_processAttributes($res, $r, $t, $attrs, $callback, $instance, $find_first); + if ($find_first && $res->size > 0) { + return $res; + } + } + + if (Boolean::isBitFlagOn($target, $t = Attribute::TARGET_PROPERTY)) { + foreach ($r->getProperties() as $ref_item) { + static::_processAttributes($res, $ref_item, $t, $attrs, $callback, $instance, $find_first); + if ($find_first && $res->size > 0) { + return $res; + } + } + } + + if (Boolean::isBitFlagOn($target, $t = Attribute::TARGET_METHOD)) { + foreach ($r->getMethods() as $ref_item) { + static::_processAttributes($res, $ref_item, $t, $attrs, $callback, $instance, $find_first); + if ($find_first && $res->size > 0) { + return $res; + } + } + } + + if (Boolean::isBitFlagOn($target, $t = Attribute::TARGET_CLASS_CONSTANT)) { + foreach ($r->getConstants() as $key => $val) { + $ref_item = new ReflectionClassConstant($instance, $key); + static::_processAttributes($res, $ref_item, $t, $attrs, $callback, $instance, $find_first); + } + } + + return $res; + } + + static protected function _processAttributes( + &$res, + Reflector $ref_item, + int $target, + Box $attrs, + ?callable $callback, + $instance, + $find_first, + ) { + foreach ($sub_attrs = $ref_item->getAttributes() as $at) { + /** @var ReflectionAttribute $at */ + if (!$attrs->size || $attrs->containsValue($at->getName())) { + if (!$callback) { + $res->append([ + 'instance' => $instance, + 'target' => $target, + 'item_reflection' => $ref_item, + 'attr' => $at->newInstance(), + 'attr_reflection' => $at, + ]); + if ($find_first) { + return; + } + } else { + $at_instance = $at->newInstance(); + $cbk_res = $callback($at_instance, $at, $ref_item, $instance, $sub_attrs); + if ($cbk_res === false) { + continue; + } + $res->append([ + 'instance' => $instance, + 'target' => $target, + 'item_reflection' => $ref_item, + 'attr' => $at_instance, + 'attr_reflection' => $at, + ]); + if ($find_first) { + return; + } + if ($cbk_res === null) { + break; + } + } + } + } + } + } diff --git a/src/Boolean.php b/src/Boolean.php index b3ce493..fe3c4b0 100644 --- a/src/Boolean.php +++ b/src/Boolean.php @@ -53,4 +53,19 @@ static function from(mixed $val, bool $strict = false): ?bool { static function to(mixed $val): mixed { return static::from($val)?static::$to_yes:static::$to_no; } + + /** + * Checks whether a bit-flag is on + * + * Basically a shortcut for bit operation "AND" mask + * + * @param int $value + * @param int $flags + * + * @return bool + */ + static function isBitFlagOn(int $value, int $flags): bool { + $res = $value & $flags; + return $res; + } } diff --git a/src/attributes/Property.php b/src/attributes/Property.php index 85c12fa..ac8f0cc 100644 --- a/src/attributes/Property.php +++ b/src/attributes/Property.php @@ -184,4 +184,8 @@ public static function methodAccessType($ref, \ReflectionAttribute $attr, $args return $method_type; } + + function getFinalType() { + + } } diff --git a/src/attributes/Renderer.php b/src/attributes/Renderer.php index 06df7c1..4c8c017 100644 --- a/src/attributes/Renderer.php +++ b/src/attributes/Renderer.php @@ -4,6 +4,7 @@ use Attribute; use spaf\simputils\components\RenderedWrapper; +use spaf\simputils\generic\Spell; use spaf\simputils\Html; use spaf\simputils\traits\BaseHtmlTrait; use spaf\simputils\traits\StaticRendererTrait; @@ -17,12 +18,19 @@ * and methods must return either `null` or {@see RenderedWrapper}. * * @see RenderedWrapper Stringifiable object for - * {@see \spaf\simputils\traits\StaticRendererTrait::render()} + * {@see StaticRendererTrait::render()} * @see StaticRendererTrait Trait containing `render` method/functionality. * @see Html Really minimal HTML static class to create/render tags. * @see BaseHtmlTrait Trait containing minimal HTML create/render tag(s) */ #[Attribute(Attribute::TARGET_METHOD)] -class Renderer { +class Renderer extends Spell { + static function getName(): string { + return 'renderer'; + } + + static function invoke(callable $target, ...$spell): mixed { + // TODO: Implement invoke() method. + } } diff --git a/src/attributes/spells/Serialize.php b/src/attributes/spells/Serialize.php new file mode 100644 index 0000000..0fe1ba6 --- /dev/null +++ b/src/attributes/spells/Serialize.php @@ -0,0 +1,16 @@ +invoke($instance, ...$params); - } - catch (TypeError) { + } catch (TypeError) { $res = null; } @@ -69,6 +69,21 @@ static function render(mixed ...$params): string { return $res; } + /** + * Shortcut for render method + * + * @param mixed ...$params + * + * @return string + * @throws \Exception + * @see Renderer + * @see RenderedWrapper + */ + #[Shortcut('static::render()')] + static function r(mixed ...$params): string { + return static::render(...$params); + } + #[Renderer] static private function defaultRenderer($arg = null): RenderedWrapper { return new RenderedWrapper($arg);