问题
Is is possible in PHP to trigger an event whenever a function in a class is called, without adding it to every function in the class?
Example:
<?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();
?>
回答1:
AFAIK there are no native language constructs for this. If you need it for debugging purposes I would advice you to have deeper look into the xdebug extension especially function traces (awesome! :)
Another idea would be to implement __call() in your class and wrap all public methods. But this requires to change the code and has other side effects:
(simplified example)
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;
}
}
Example:
$t = new Test();
$l = new Listener();
$t->addListener($l);
$t->a();
回答2:
This is a classic task for aspect oriented programming (AOP). PHP has no native support for AOP, however, there are some frameworks that make AOP in PHP possible. One of these is the GO! AOP PHP framework. You can also implement AOP using runkit.
回答3:
You need for PHP SplObserver: From PHP Doc
回答4:
This is a classic task for dependency injection and lazy initialization! The dependency is the MySQL connection. As it first needs to be available when the first query is executed, it need not be initialized at "startup", but only then. This is called lazy initialization, and its implementation is extremly simple:
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:
...
}
}
All "refactoring" required is calling $this->_getConn()
in every query method.
Aspect oriented programming is not the instrument to solve this, because the DB connection is an innate dependency of the query, and not an aspect of it. Automatic logging of all queries executed were an aspect.
A trigger built around PHP's __call()
isn't a good choice either; aside from knocking out modern IDE's inspections - which are great to see quickly whether a module is fine - it would unnecessarily complicate tests: a protected $this->_getWhatever()
can easily be overwritten in a test facade object - derived from the class to test - to return a mock object or whatever. With __call()
, more code is needed for the same purpose, which induces the risk of errors in code which is only there for testing (and should be absolutely free of errors)
来源:https://stackoverflow.com/questions/16922277/trigger-an-event-when-a-function-is-called-in-a-class