PHP-面向对象的访问控制为什么要基于Class而不是基于Object

发布于 2017-09-02 18:00:59 字数 301 浏览 1190 评论 5

今天发现自己之前对OOP权限控制的部分理解一直有个误区,一个简单的例子

class A {
private $resource = 1;

public function paste(A $a)
{
print_r($a->resource);
}
}

$a = new A();
$b = new A();

$a->paste($b); //1

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

晚风撩人 2017-10-13 07:24:03

如果$a, $b两个对象均为同一class的实例时,那么$a, $b为什么要访问对方的私有方法, 而不是在内部调用自身的私有方法。用户能看到的应该只有外部接口, 如果用户知道了私有方法, 并且需要调用私有方法才能实现用户需要的功能, 那只能说明这个Class的设计是失败的.

虐人心 2017-10-11 18:44:59

先顶下楼上两位的回答,不过我是从另一个角度理解的。
从程序的实现角度来说,调用方法和类属性的本质,是通过“特征”在对象所加载的堆区寻找合适的方法和属性。而“特征”则是由class模板定义的,所以无论根据同一个class模板new出多少个新对象,所包含的“特征”都是相同的。这就是LZ所说的访问控制是根据class来而不是Object来的。
我是Java码农,是从jvm角度来理解,PHP的原理应该也差不多。这么设计估计是底层实现起来容易。从使用角度来说是AOP的场景,比如:

class A {
private String str;

public A(String str) {
this.str = str;
}

public static void disp(A a) {
a.private_method();
}

public void print(A a) {
a.private_method();
}

private void private_method() {
System.out.println(str);
}
}

调用方法如下:

public static void main(String[] args) {
A a1 = new A("a1");
A a2 = new A("a2");
System.out.println("These are print by disp()");
A.disp(a1);
A.disp(a2);
System.out.println("These are print by print()");
a2.print(a1);
a1.print(a2);
}

调用的结果是:

These are print by disp()
a1
a2
These are print by print()
a1
a2

所以使用static方法,理解起来更清楚一点。

虐人心 2017-10-07 06:09:47

下边这个函数就是为验证某个属性能否被访问的验证方法:

 static int zend_verify_property_access(zend_property_info *property_info, zend_class_entry *ce TSRMLS_DC) /* {{{ */
{
switch (property_info->flags & ZEND_ACC_PPP_MASK) {
case ZEND_ACC_PUBLIC:
return 1;
case ZEND_ACC_PROTECTED:
return zend_check_protected(property_info->ce, EG(scope));
case ZEND_ACC_PRIVATE:
if ((ce==EG(scope) || property_info->ce == EG(scope)) && EG(scope)) {
return 1;
} else {
return 0;
}
break;
}
return 0;
}

主要看 case ZEND_ACC_PRIVATE: ,在 paste 方法中 $a->resource 时 EG(scope) 为 A类,ce 为 对象$b的类,也就是A类。ce==EG(scope) 成立,返回1,表示可以访问。

但是为什么这样设计就不清楚了。

参考 PHP内核探索:访问控制

偏爱自由 2017-09-28 09:14:33

虽然没碰到过这种情况,也不知道有什么意义,不过我提出一个理解,如果有错还请大家指出:
简单的说就是是否知道该类内部情况的问题。

类 类似于C语言中的struct,object实质就是内存中的一堆数据。
我们要操作这堆数据,必须知道他的结构。
每个类对外是隐藏结构里私有的部分的,所以在外部没法访问到该私有部分,因为不知道他这部分的结构。
而楼主说的这种情况,在类自身内部进行操作,自己是知道自身的结构的,包括私有部分。

比如说a在A单位工作,b在B单位工作,A单位新进了台设备,秘密地进行研究。a给b聊天时,说我单位有桌子有电脑(公开),但不会让他知道进了台新设备(私有),但a跟同在A单位工作的c聊天时,c表示,我也在A干活,你跟我藏什么掖什么。

浮生未歇 2017-09-12 23:40:25

好问题, 楼主的标题非常准确:
见:
http://docs.oracle.com/javase/specs/jls/se7/html/jls-6.html#jls-6.6.1

Otherwise, if the member or constructor is declared private, then access is permitted if and only if it occurs within the body of the top level class (§7.6) that encloses the declaration of the member or constructor.

我现在能想到的场景, 就是copy constructor:

public A(A a){
this.member1=a.member1;
this.member2=a.member2;
}

为什么这么设计, 我认为是这样: OO是为了把内部实现和外部接口分开, 即是 把类的生产者和类的使用者分开. 如, 我写了一个library, 里面有类A如下,

public class A implements AInf{
private int x=1;
public void paste(AInf a){
if(a instanceof A)
System.out.println(a.x); // 我能对a对象做任何事, 因为A就是我写的!!
}
}

实际上这个A.paste()方法只有我(类的生产者)才能控制, 而用户只能看到接口:

public interface AInf{
public void paste(AInf a);
}

用户写了代码:

AInf a=new A();
AInf b=new A();
b.paste(a); //我是用户, 我只能把a传给A的paste方法, 我不知道paste干了啥

答案补充
上面写了一堆, 其实只为了解释JLS里关于private限定词的描述.
其实我觉得容易让人困惑的是这里:

AInf a=new A();
AInf b=new A();
a.control(b);

看起来很像是 对象a调用了control方法, 而control方法中使用了b的private成员; 所以 a使用了 b的private成员.
事实上不应该这么理解, 在a.control(b)时, 首先去找a的类对象A, 找到A对象中的control方法, 传入两个对象参数this(a的引用), b.
如果control方法是这么定义的:

public void control(A para){
this.privatemember1=2;
para.privatemember1=2;
}

这里b的private成员被修改了, 可是this(a的引用)的private成员也被修改了啊! 在control方法看来, 这两个对象没什么不同, 都是由类的用户定义并传入.
所以结论是, 在类A的用户写语句a.control(b)时, a并没有"控制"b; 谁在控制? control方法; 谁控制control方法? 类的生产者(作者). => 访问控制基于Class而不是基于Object

最后, 用java反射来改写a.control(b), 可以清楚的看到我上面说的过程:

Method control = a.getClass.getDeclaredMethod("control"); //拿到control方法
control.invoke(a,b); //传入a,b(a会被传给this)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文