<?php
    trait ReflectionHook
    {
        public static function __callStatic($name, $arguments): mixed
        {
            $formarly = "static" . ucfirst($name);
            if(method_exists(__CLASS__, $formarly))
            {
                return (new static)->{$formarly}(...$arguments);
            }
            else if(method_exists(__CLASS__, "call$name"))
            {
                return (new static)->{"call$name"}($name, ...$arguments);
            }
        }
        public function __call($name, $arguments)
        {
            $formarlyA = "call" . ucfirst($name);
            $formarlyB = "static" . ucfirst($name);
            if(method_exists($this, $formarlyA))
            {
                return $this->{$formarlyA}(...$arguments);
            }else if(method_exists(__CLASS__, $formarlyB))
            {
                return $this->{$formarlyB}(...$arguments);
            }
            else if(method_exists(__CLASS__, "call$name"))
            {
                return $this->{"call$name"}($name, ...$arguments);
            }
            else if(method_exists(__CLASS__, "callable"))
            {
                return $this->callable($name, ...$arguments);
            }
        }
        public function __get($name)
        {
            $formarly = ucfirst($name);
            if(method_exists(__CLASS__, "get" . $formarly . "Attribute"))
            {
                return $this->{"get" . $formarly . "Attribute"}();
            }
            else if(method_exists(__CLASS__, "getAttribute"))
            {
                return $this->getAttribute($name);
            }
            else if(property_exists(__CLASS__, "_attributes"))
            {
                return $this->_attributes[$name] ?? null;
            }
        }
        public function __set($name, $value)
        {
            $formarly = ucfirst($name);
            if(method_exists($this, "get" . $formarly . "Attribute"))
            {
                $this->{$formarly} = $value;
            }
            else if(method_exists($this, "setAttribute"))
            {
                return $this->setAttribute($name, $value);
            }
            else if(property_exists($this, "_attributes") && isset($this->_attributes[$name]))
            {
                $this->_attributes[$name] = $value;
            }
        }
        public function __isset($name)
        {
            $formarly = ucfirst($name);
            if(method_exists($this, "get" . $formarly . "Attribute"))
            {
                return true;
            }
            else
            {
                return isset($this->_attributes[$name]);
            }
        }
        public function __unset($name)
        {
            $formarly = ucfirst($name);
            if(method_exists($this, "set" . $formarly . "Attribute"))
            {
                $this->{"set" . $formarly . "Attribute"}();
            }
            else if(method_exists($this, "removeAttribute"))
            {
                return $this->removeAttribute($name);
            }
            else if(method_exists($this, "setAttribute"))
            {
                return $this->setAttribute($name, null);
            }
            else if(property_exists($this, "_attributes") && isset($this->_attributes[$name]))
            {
                unset($this->_attributes[$name]);
            }
        }
        public function __invoke(...$args)
        {
            if(method_exists($this, "reflectionCall"))
            {
                return $this->reflectionCall(...$args);
            }
        }
        public function __toString()
        {
            if(method_exists($this, "toString"))
            {
                return $this->toString();
            }
        }
        public function __serialize()
        {
            if(method_exists($this, "getProperties"))
            {
                return $this->getProperties();
            }
        }
    
        public function __unserialize($datas)
        {
            if(method_exists($this, "setProperties"))
            {
                $this->setProperties($datas);
            }
        }
    }