如何在Repository类所有公共方法执行前注入前置函数?
问题描述
我遇到一个问题,无法在所有方法执行前调用某个函数。我有一个父类——Repository类,代码如下:
class Repository { public function find(/*..*/){/*..*/} public function findAll(/*..*/){/*..*/} public function findBy(/*..*/){/*..*/} public function findA(/*..*/){/*..*/} public function findB(/*..*/){/*..*/} /* 还有100+个公共方法 */ }
我希望创建一个“Adapter类”,让所有公共方法执行前都能调用某个函数,但不想重写父类的所有方法。我试了下面的方案:
class OwnRepo extends Repository{ public function __call($methods, $args){/**/} }
但__call对公共方法不起作用。
补充说明
之前没说清楚!我的控制器里用了一个Model,不想修改/重写控制器里的函数:
class IndexController { public function index(){ $model = new Model(); // 这个会返回OwnRepo对象 $a = $model->findAll(); } }
解决方案
方案1:装饰器模式(推荐)
装饰器模式不用继承原类,而是通过包装原对象实现方法拦截,完全符合你的需求,而且不用改控制器代码(只需要调整Model返回的对象即可)。
步骤如下:
- 先给Repository加个接口(如果原类没有的话),这样更规范:
interface RepositoryInterface { public function find(/*..*/); public function findAll(/*..*/); // 把Repository里所有公共方法的声明都放这里 } // 让原Repository实现这个接口 class Repository implements RepositoryInterface { // 原方法逻辑保持不变 }
- 写装饰器类,实现同一个接口,内部持有原Repository实例:
class RepositoryDecorator implements RepositoryInterface { private $repo; public function __construct(RepositoryInterface $repo) { $this->repo = $repo; } // 定义你要在方法执行前跑的逻辑 private function beforeExecute() { // 这里写前置操作,比如日志、权限校验等 echo "方法执行前的操作\n"; } // 每个方法先调用前置逻辑,再委托给原对象 public function find(/*..*/) { $this->beforeExecute(); return $this->repo->find(...func_get_args()); } public function findAll(/*..*/) { $this->beforeExecute(); return $this->repo->findAll(...func_get_args()); } // 其他方法照这个格式写就行,IDE一般都有自动实现接口的功能,不用手动敲 }
- 修改Model类,让它返回装饰器实例:
class Model { public function __construct() { $originalRepo = new Repository(); return new RepositoryDecorator($originalRepo); } }
这样控制器代码完全不用改,调用任何方法都会先执行beforeExecute。
方案2:反射+__call(偷懒版)
如果不想写所有接口方法,可以用反射动态调用原类的公共方法,结合__call实现拦截:
class RepositoryDecorator { private $repo; private $reflection; public function __construct(Repository $repo) { $this->repo = $repo; $this->reflection = new ReflectionClass($repo); } private function beforeExecute() { // 前置逻辑 echo "方法执行前的操作\n"; } public function __call($method, $args) { // 检查原类是否有这个公共方法 if ($this->reflection->hasMethod($method) && $this->reflection->getMethod($method)->isPublic()) { $this->beforeExecute(); return $this->repo->$method(...$args); } throw new BadMethodCallException("方法 {$method} 不存在"); } }
这个方法不用写所有方法,只要原Repository的公共方法,都会被__call拦截(因为装饰器本身没有这些方法),执行前置逻辑后再调用原方法。
为什么你的继承方案没用?
__call只有在调用类中不存在的公共方法时才会触发,而OwnRepo继承了Repository的所有公共方法,调用这些方法时会直接执行父类的实现,根本不会走到__call。
内容的提问来源于stack exchange,提问作者Laszlo
相关产品推荐
相关产品推荐

