【问题标题】:Trigger an event when a function is called in a class在类中调用函数时触发事件
【发布时间】:2013-05-31 02:40:21
【问题描述】:

在 PHP 中是否可以在调用类中的函数时触发事件,而不将其添加到类中的每个函数中?

例子:

<?php
    class A {
        function xxx() {
            //this function will be called everytime I call another function in this class  
        }

        public static function b() {
            return 'Hello Stackoverflow!';
        }

        public static function c() {
            //I also want this function to trigger the event!
        }
    }

    echo A::b();
?>

【问题讨论】:

  • 您实际上想要执行Aspect-oriented Programming。一个通用的 AOP 解决方案对于您当前的目的可能有点(或非常)重量级,但 this article 谈到使用 FLOW 的 AOP 功能。不确定您是只需要一次还是在一百万个地方需要此功能,所以这对您来说可能是多余的。 耸耸肩 编辑:更好的是,@Oswald 对 Go! 的回答!可能是更好的选择框架。

标签: php class function events


【解决方案1】:

AFAIK 对此没有本地语言结构。如果您出于调试目的需要它,我建议您深入了解 xdebug 扩展,尤其是 function traces(太棒了!:)

另一个想法是在你的类中实现__call() 并包装所有公共方法。但这需要更改代码并有其他副作用:

(简化示例)

class Test {

    protected $listeners;

    public function __construct() {
        $this->listeners = array();
    }

    private function a() {
        echo 'something';
    }

    private function b() {
        echo 'something else';
    }

    public function __call($fname, $args) {
        call_user_func_array(array($this, $fname), $args);
        foreach($this->listeners as $listener) {
            $listener->notify('fname was called');
        }
    }

    public function addListener(Listener $listener) {
        $this->listeners[]= $listener;
    }
}

.

 class Listener {

     public function notify($message) {
         echo $message;
     }

 }

例子:

 $t = new Test();
 $l = new Listener();
 $t->addListener($l);

 $t->a();

【讨论】:

  • 太棒了!这正是我一直在寻找的。和不。这样做的意思不是调试。在我的课堂上,我存储了我的查询,为了避免无用的 MySQL 连接,我想添加一个 require MySQL 函数,以确保在执行查询时脚本连接到 MySQL 服务器。
  • OK :) 我稍微增强了一点。现在它可以触发事件(正如您在问题中所说)
  • 那么这基本上是什么:当调用私有函数时,它会重定向到 __call() ,但是 call_user_func_array() 会调用私有方法? @hek2mgl
  • 是的,如果您尝试调用对象的不可见或不存在的方法并且它实现了__call(),它将被调用。请注意,我将方法设为私有,未受保护以确保它们不能从子类中调用
【解决方案2】:

这是面向方面编程 (AOP) 的经典任务。 PHP 没有对 AOP 的原生支持,但是,有一些框架使 PHP 中的 AOP 成为可能。其中之一是GO! AOP PHP framework。你也可以使用runkit来实现AOP。

【讨论】:

  • +1 获取有趣的链接。听说过几次有关 AOP 的信息。但从来没有真正深入研究过。将查看您的链接(GO!)
  • 面向方面的编程不是解决这个问题的工具,因为数据库连接是查询的固有依赖关系,而不是它的一个方面。自动记录所有执行的查询是一个方面。
【解决方案3】:

PHP SplObserver 需要:From PHP Doc

【讨论】:

    【解决方案4】:

    这是依赖注入和延迟初始化的经典任务!依赖项是 MySQL 连接。因为它首先需要在执行第一个查询时可用,所以它不需要在“启动”时初始化,而只是在那时。这称为延迟初始化,其实现极其简单:

    class DbStuff {
      private $__conn = NULL;
    
      protected function _getConn() {
        if ( is_null( $this->__conn ) {
          $this->__conn = ... ;  // init (MySQL) DB connection here
          // throw on errors!
        }
        return $this->__conn;
      }
    
      public function someQuery($arg1, $arg2) {
        $conn = $this->_getConn();
        // MySQL query here:
        ...
      }
    }
    

    所需的所有“重构”都是在每个查询方法中调用$this-&gt;_getConn()

    面向方面的编程不是解决这个问题的工具,因为数据库连接是查询的固有依赖关系,而不是它的一个方面。自动记录所有执行的查询是一个方面。

    围绕 PHP 的 __call() 构建的触发器也不是一个好的选择;除了取消现代 IDE 的检查——这很容易快速查看模块是否正常——它会不必要地使测试复杂化:受保护的$this-&gt;_getWhatever() 可以很容易地在测试外观对象中被覆盖——从要测试的类派生——返回一个模拟对象或其他任何东西。使用__call(),需要更多代码来实现相同目的,这会导致代码中出现错误的风险,而这些代码仅用于测试(并且应该绝对没有错误)

    【讨论】:

      猜你喜欢
      • 2018-03-17
      • 2020-01-11
      • 2020-09-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-04-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多