Можем ли мы добавить еще несколько строк в функцию, расширив ее с помощью PHP?

У меня есть идея для системы событий, которую я разрабатываю для моей пользовательской среды.

Представьте себе такую ​​псевдо-функцию.

class Test
{
    public function hi()
    {
        Event::add(__FUNCTION__ . 'is about to run.');
        return "hi";
    }
}

Представьте, что вам нужно сделать то же самое для некоторых других функций. (Возможно, вы хотите записать, какие функции выполнялись во время выполнения, и хотите записать их в отдельный файл.)

Вместо того, чтобы делать это и добавлять события в функции вручную, мы можем сделать что-то подобное?

class Test
{
    public function hi()
    {
        return "hi";
    }
}

// events.php (It's a pseudo code so may not work.)
// Imagine extend's purpose is to inject codes into target function

Event::bind('on', $className, $methodName, function() use ($className, $methodName) 
{
    return $className->$methodName->extend('before', Event::add(__FUNCTION__ . 'is about to run.'));
});

Идея состоит в том, чтобы ввести hi() функция, которая находится внутри Test class и впрыскивать все, что мы проходим в extend функционировать снаружи. 'before' означает, что инъекция должна быть в первой строке целевой функции.

Наконец, события и привязки событий полностью отделены от функций. Я хочу иметь возможность связывать пользовательские вещи без изменения функций.

У меня есть ощущение, что мы можем сделать это, взломав eval() или играть с call_user_func(), Я не уверен, хотя. С помощью eval() звучит довольно плохо уже.

Мой вопрос

  1. Это возможно сделать с PHP?
  2. У него есть имя в Принципах ООП / ООП, чтобы я мог читать дальше?
  3. Это имеет какой-то смысл или это плохая идея?

1 ответ

Решение

Да, ты можешь. Вы можете использовать AOP используя GO! AOP Framework, который работает с аннотациями.

Например, вы хотите регистрировать каждый публичный вызов метода. Вместо добавления к каждой строке функции, как это.

namespace Acme;

class Controller
{
    public function updateData($arg1, $arg2)
    {
        $this->logger->info("Executing method " . __METHOD__, func_get_args()); 
        // ...
    }    
}

Вы можете использовать один аспект для всех открытых методов всех классов пространства имен Acme, например:

use Go\Aop\Aspect;
use Go\Aop\Intercept\MethodInvocation;
use Go\Lang\Annotation\Before;

    class LoggingAspect implements Aspect
    {
        /** @var null|LoggerInterface */
        protected $logger = null;

        /** ... */
        public function __construct($logger) 
        {
            $this->logger = $logger;
        }

        /**
         * Method that should be called before real method
         *
         * @param MethodInvocation $invocation Invocation
         * @Before("execution(public Acme\*->*())")
         */
        public function beforeMethodExecution(MethodInvocation $invocation)
        {
            $obj    = $invocation->getThis();
            $class  = is_object($obj) ? get_class($obj) : $obj;
            $type   = $invocation->getMethod()->isStatic() ? '::' : '->';
            $name   = $invocation->getMethod()->getName();
            $method = $class . $type . $name;

            $this->logger->info("Executing method " . $method, $invocation->getArguments());
        }
    }    

Это выглядит сложнее, но более гибко.

Другие вопросы по тегам