2012-10-05 33 views
12

Tôi đã tìm thấy một cái gì đó dường như là một vấn đề thừa kế lạ trong PHP.PHP thừa kế và là thành viên tầm nhìn bảo vệ

From the PHP manual:

viên tuyên bố bảo vệ có thể được truy cập chỉ trong lớp chính nó và bởi lớp thừa hưởng và phụ huynh.

Đối với tôi, điều này có nghĩa là: Có thể truy cập các thành viên được bảo vệ B nếu A instanceof B hoặc B instanceof A. Tuy nhiên, nếu cả A và B mở rộng Foo, và Foo có một hàm tạo được bảo vệ không được ghi đè trong B, thì tôi có thể tạo một cá thể B từ bên trong A. Điều này không có ý nghĩa với tôi, bởi vì A là không phải là một thể hiện của B và B không phải là một thể hiện của A. tôi cũng có thể gọi phương thức bảo vệ $b->test() từ bên trong A, trong đó thực hiện phương pháp thực hiện trong B. (Nếu B không redeclare test() sau đó thực hiện trong Foo được thực thi.) Với tôi điều này thậm chí còn kỳ lạ hơn bởi vì tôi không thể tạo một cá thể B từ bên trong A nếu B trực tiếp triển khai một hàm tạo được bảo vệ. Nó có vẻ lạ rằng tôi không thể truy cập một constructor được bảo vệ (cũng được khai báo trong lớp cha) nhưng việc truy cập một phương thức được bảo vệ (cũng được khai báo trong lớp cha) là không có vấn đề gì.

Lưu ý rằng tôi làm được những hành vi mong đợi khi tôi sử dụng một lớp C mà không mở rộng Foo. Nếu tôi cố gắng để khởi tạo B từ bên trong C, tôi nhận được một lỗi nghiêm trọng bởi vì tôi đang cố gắng truy cập một nhà xây dựng được bảo vệ. Nếu tôi thêm một hàm tạo công khai vào B thì có thể khởi tạo nó (được mong đợi) và tôi vẫn không thể truy cập phương thức được bảo vệ test() (đây cũng là hành vi được mong đợi). Tôi hy vọng những hành vi tương tự khi sử dụng A thay vì C.

Mẫu mã này giải thích một lần nữa:

class Foo { 
    protected function __construct() { 
     echo('Constructing ' . get_called_class()); 
    } 

    protected function test() { 
     echo('Hello world ' . __METHOD__); 
    } 
} 

class A extends Foo { 
    public function __construct() { 
     parent::__construct(); 
    } 

    public function testB() { 
     // Both of these lines work 
     $b = new B(); 
     $b->test(); 
    } 
} 

class B extends Foo { 
    protected function test() { 
     echo('Hello world Again ' . __METHOD__); 
    } 
} 

class C { 
    public function __construct() { 
    } 

    public function testB() { 
     // Both of these lines cause fatal errors 
     $b = new B(); 
     $b->test(); 
    } 
} 

$a = new A(); 
$a->testB(); 

$c = new C(); 
$c->testB(); 

Tôi có thể không nhìn thấy một cái gì đó, nhưng tôi không thể tìm thấy những gì. Bất cứ ai có thể giải thích hành vi với tôi?

+0

hành vi Rất lạ thực sự. – Sherlock

+0

Bạn có muốn giải thích về lý do hoặc thực hiện đằng sau hành vi này không? Vì đây là PHP, có khả năng cao là có * là * không có lý do nào. – Jon

+0

Nếu có lý do, tôi muốn biết nó là gì. – Arjan

Trả lời

6

Bạn có thể truy cập các phương pháp đó vì có khai báo chúng như được bảo vệ trong Foo, là cha mẹ của bạn và cho phép bạn truy cập vào phương pháp đó. Nếu bạn loại bỏ các tuyên bố từ phụ huynh và khai báo các phương pháp được bảo vệ trong B bạn sẽ nhận được một lỗi nghiêm trọng.

này được báo cáo là một lỗi trong PHP https://bugs.php.net/bug.php?id=50892

+0

Bạn không thể gọi 'new B()' từ 'A' nếu' B' ghi đè '__construct' ngay cả khi' Foo' khai báo nó. –

+2

Cảm ơn bạn đã chỉ cho tôi tới liên kết bugs.php.net. Tôi đã tìm kiếm ở đó nhưng không thể tìm thấy báo cáo lỗi. – Arjan

1

Không có lý do về vấn đề này, nó đã được báo cáo 2 năm trước: https://bugs.php.net/bug.php?id=52120

+0

Cảm ơn bạn đã liên kết.Tôi nghĩ rằng nó lạ hơn là bạn có thể gọi các phương thức được thừa hưởng được bảo vệ hơn là không thể gọi các nhà xây dựng kế thừa (đó là hành vi được mong đợi, imo) – Arjan

Các vấn đề liên quan