36

php 中是否有“朋友”或“内部”等价物?如果没有,是否有任何模式可以遵循来实现这种行为?

编辑: 对不起,但标准的 PHP 不是我要找的。我正在寻找类似于 ringmaster 所做的事情。

我有一些类在后端进行 C 风格的系统调用,而且杂耍已经开始变得很麻烦。我在对象 A 中有函数,这些函数将对象 B 作为参数,并且必须调用对象 B 中的一个方法,并将自身作为参数传递。最终用户可以调用 B 中的方法,系统就会崩溃。

4

5 回答 5

47

PHP 不支持任何类似朋友的声明。可以使用 PHP5 的 __get 和 __set 方法来模拟这一点,并仅检查允许的朋友类的回溯,尽管执行此操作的代码有点笨拙。

PHP 网站上有一些示例代码和关于该主题的讨论:

class HasFriends
{
    private $__friends = array('MyFriend', 'OtherFriend');

    public function __get($key)
    {
        $trace = debug_backtrace();
        if(isset($trace[1]['class']) && in_array($trace[1]['class'], $this->__friends)) {
            return $this->$key;
        }

        // normal __get() code here

        trigger_error('Cannot access private property ' . __CLASS__ . '::$' . $key, E_USER_ERROR);
    }

    public function __set($key, $value)
    {
        $trace = debug_backtrace();
        if(isset($trace[1]['class']) && in_array($trace[1]['class'], $this->__friends)) {
            return $this->$key = $value;
        }

        // normal __set() code here

        trigger_error('Cannot access private property ' . __CLASS__ . '::$' . $key, E_USER_ERROR);
    }
}

(代码由 nerdclub dot net在 bugs.php.net 上的 tsteiner 证明)

于 2008-11-25T16:14:08.107 回答
10

也可以提升权限,也就是选择性地泄漏数据,使用 php >=5.3.3 中的握手和闭包。

基本上,交互是这样的:A 类有一个接受 B 类对象的公共方法,并调用 B->grantAccess(或您的接口定义的任何内容),并传递给它一个闭包。$that=$this的闭包use($that,$anythingelseyouneed),以及确定允许访问哪些属性所需的任何其他内容。闭包有一个参数——要返回的属性;如果它是 $that 上的一个属性并且一切都很酷,则闭包返回该属性。否则,它返回 '',或者抛出异常,或者可能是默认值。

B->grantAccess 类接受一个可调用对象并将其存储,在其他方法中使用它来提取闭包允许泄漏的私有属性。将 B 类的默认构造函数设为私有。使用带有 A 类参数的静态工厂方法构造 B,以确保发生握手。

要点在这里:https ://gist.github.com/mcamiano/00592fb400e5043d8acd

于 2014-08-08T00:45:50.097 回答
0

也许这个?只有一个 hack 不是来自 C++ 的真正朋友关键字,在这种情况下仅适用于构造函数......

<?php

class FooWithFriend {

    private function guardOnlyFriend(): void 
    {
        if (( is_a($this, BarFriendOfFoo::class) ) === false ) {
         throw new Exception('Only class BarFriendOfFoo he can create me');
        }
    }

    protected function __construct() 
    {
        $this->guardOnlyFriend();
        echo 'Hello friend - ';
    }
}


final class BarFriendOfFoo extends FooWithFriend {
    public function __construct()
    {
        parent::__construct();
    }
}

final class Bar2FriendOfFoo extends FooWithFriend {
    public function __construct()
    {
        parent::__construct();
    }
}

$bar = new BarFriendOfFoo();
$bar = new Bar2FriendOfFoo();
$dummy = new FooWithFriend();
于 2020-03-31T22:20:42.897 回答
0

对于现在 PHP 有Class Friendship选项:

class Person
{
    friend HumanResourceReport;
 
    protected $id;
    protected $firstName;
    protected $lastName;
 
    public function __construct($id, $firstName, $lastName)
    {
        $this->id = $id;
        $this->firstName = $firstName;
        $this->lastName = $lastName;
    }
 
    public function makeReport()
    {
        return new HumanResourceReport($this);
    }
}

class HumanResourceReport
{
    private $person;
 
    public function __construct(Person $person)
    {
        $this->person = $person;
    }
 
    public function getFullName()
    {
        // HumanResourceReport would not have access to protected 
        // members of Person if not explicitly listed as a friend.
        return $this->person->firstName . ' ' . $this->person->lastName;
    }
 
    public function getReportIdentifier()
    {
        return "HR_REPORT_ID_{$this->person->id}";
    }
}

更多细节请查看:PHP RFC:Class Friendship

于 2021-10-08T10:11:47.660 回答
-19

我很确定您要查找的是“受保护”还是“私有”,具体取决于您的用例。

如果你在一个类中定义一个函数,并且你只希望它自己可用,你可以这样定义它:

private function foo($arg1, $arg2) { /*function stuff goes here */ }

如果您在一个类中定义一个函数,您希望从该类继承的类可以使用该函数,但不能公开使用,请以这种方式定义:

protected function foo($arg1, $arg2)

我很确定在 PHP5 中默认情况下,函数是公共的,这意味着您不必使用以下语法,但它是可选的:

public function foo($arg1, $arg2) { /*function stuff goes here */ }

在使用公共函数之前,您仍然必须实例化该对象。因此,我将彻底地告诉您,为了在类中使用函数而不实例化对象,请务必使用以下语法:

static function foo($arg1, $arg2) { /*function stuff goes here */ }

这将允许您仅通过引用类来使用该函数,如下所示:

MyClass::foo($a1, $a2);

否则,您需要执行以下操作:

$myObject = new MyClass();
$myObject->foo($a1, $a2);
于 2008-11-25T16:09:55.087 回答