如何在PHP中通过对象间接实例化并禁止直接实例化对象
问题
想要模拟Stripe库的调用逻辑,实现两个核心要求:
- 禁止直接通过
new Products()实例化Products对象,必须通过$someobject->products->retrieve()的方式获取实例 - 实现延迟实例化:仅调用
retrieve()方法时才创建Products对象,而非在Thing类的构造函数中提前初始化
目前已实现部分逻辑的代码如下:
class Thing { function __get($name) { if ($name === 'products') { $p = new Products(); return $p; } } } class Products { public $id; public $option; function retrieve($id, $option) { $this->id = $id; $this->option = $option; return $this; } }
可以通过$thing->products->retrieve('123', 'bob')获取实例,但仍允许直接new Products()实例化,不符合需求。
解决方案
要实现禁止直接实例化+延迟加载的效果,可通过以下方式修改代码:
核心思路
- 将Products类的构造函数设为私有,阻断外部直接实例化的路径
- 让Thing类的
products属性返回一个代理对象,仅当调用retrieve()时才触发Products实例的创建
最终实现代码
class Thing { public function __get($name) { if ($name === 'products') { // 返回代理对象,延迟Products实例化时机 return new class { public function retrieve($id, $option) { // 通过Products的静态方法创建实例 return Products::create($id, $option); } }; } throw new \Exception("Undefined property: Thing::$name"); } } class Products { public $id; public $option; // 私有构造函数,禁止外部直接new private function __construct($id, $option) { $this->id = $id; $this->option = $option; } // 静态工厂方法,唯一合法的实例创建入口 public static function create($id, $option) { return new self($id, $option); } // 禁止克隆,进一步阻断实例获取路径 private function __clone() {} }
验证效果
- 合法调用(正常工作):
$thing = new Thing(); $prod = $thing->products->retrieve('123', 'bob'); var_dump($prod instanceof Products); // 输出 bool(true)
- 非法调用(触发错误):
$prod = new Products('123', 'bob'); // 报错:Uncaught Error: Call to private Products::__construct() from global scope
补充说明
如果想要完全模拟Stripe"直接实例化提示类不存在"的效果,可以将Products类封装到Thing类的内部(PHP 7.4+支持嵌套类),或者通过命名空间+自动加载机制隐藏类的可见性。上述方案已满足核心需求,是最简洁的实现方式。
内容的提问来源于stack exchange,提问作者russ
相关产品推荐
相关产品推荐

