You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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返回的对象即可)。

步骤如下:

  1. 先给Repository加个接口(如果原类没有的话),这样更规范:
interface RepositoryInterface {
    public function find(/*..*/);
    public function findAll(/*..*/);
    // 把Repository里所有公共方法的声明都放这里
}

// 让原Repository实现这个接口
class Repository implements RepositoryInterface {
    // 原方法逻辑保持不变
}
  1. 写装饰器类,实现同一个接口,内部持有原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一般都有自动实现接口的功能,不用手动敲
}
  1. 修改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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 21:10:52