Можем ли мы добавить еще несколько строк в функцию, расширив ее с помощью 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()
звучит довольно плохо уже.
Мой вопрос
- Это возможно сделать с PHP?
- У него есть имя в Принципах ООП / ООП, чтобы я мог читать дальше?
- Это имеет какой-то смысл или это плохая идея?
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());
}
}
Это выглядит сложнее, но более гибко.