限制可以创建PHP类的内容

7
我有两个类,"A"和"B"。在应用逻辑中,除了类"A",任何人都不允许创建类"B"的对象。 但是,由于我不想把这两个类放在同一个文件中,所以无法使用“private”属性进行限制。
是否可能创建这种限制?如果除了"A"之外的其他人尝试创建类"B"的对象,您可以说“滚开!”吗?

13
为什么你想这样做,我很感兴趣? - rojoca
5个回答

7

这是非常不规范的做法,您不应该使用它。我只是发布它,因为我喜欢非常规的东西 ;) 此外,如果启用了E_STRICT错误报告,则会抛出错误:

class B
{
    private function __construct() {}

    public function getInstance() {
        if (!isset($this) || !$this instanceof A) {
            throw new LogicException('Construction of B from a class other than A is not permitted.');
        }

        return new self;
    }
}

class A
{
    function someMethod() {
        $b = B::getInstance(); // note that I'm calling the non-static method statically!
    }
}

这能够实现的原因是一种“特性”,可以在本手册页面的第二个示例中看到。

请记住,虽然这样做可以运行,但它会导致 PHP 报告一个 E_STRICT 错误。 - BoltClock
如果有人真的想使用这个hack,最好启用error_reporting(-1)并使用@$b = B::getInstance();,但我不确定@是否会抑制在getInstance方法中生成的所有警告/错误。 - Sudhi

5

您可以检查回溯:

class B
{
    public function __construct()
    {
        $chain = debug_backtrace();
        $caller = $chain[1]['class'];

        if ('A' != $caller) {
            throw new Exception('Illegal instantiation');
        }
    }
}

4
如果您选择这条路线,应该对其性能进行分析。这个函数被称为“调试回溯”有它的原因。 - Gordon
2
哦,超过了我们约定的范围,但这是一个解决方案! - Nev Stokes

0
在B的构造函数中,要求传入A。当您想从A获取B时,只需创建一个B并传入A即可。当调用new B时,它将要求传入A。
class A
{
    private $b;

    private function getB()
    {
        if (null === $this->b)
        {
            $this->b    = new B($this);
        }

        return $this->b;
    }
}

class B
{
    public function __construct(A $a)
    {

    }
}

其实仔细想想那是正确的。让我测试一下如果A遵循单例模式会发生什么。 - MANCHUCK
你也可以克隆一个现有的B实例。由于该类将由编写代码的人实例化,因此限制访问的意义非常小。这种方式还会使A和B类的测试变得困难,因为A具有硬编码的依赖项,而B始终需要一个A Mock。将B设置为Singleton将完全使您陷入困境。在我看来,限制访问所获得的价值不值得付出代价。 - Gordon
1
也许更好的解决方案是使用PHPcs。创建自己的嗅探器以检测B是否从A中调用。每次您想要部署代码时,只需运行嗅探器并查看它是否捕获到。 - MANCHUCK

0

也许你想使用像这样的东西:

class A
{
        protected function __construct ()
        {
        }
}

class B extends A
{
        public function __construct ()
        {
                $a = new A();
        }
}

$b = new B();

-1

使用get_called_class来查找试图实例化对象的类:

class B
{
        public function __construct ()
        {
                if(get_called_class() != 'A') {
                    //booboo
                }
        }
}

只有当 A 扩展 B 并且构造 B 而不是 A 时,这才能起作用。 - NikiC

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接