2011-12-08 2 views
6

Preamble:Chụp lớp gọi lên hệ thống phân cấp trong PHP

Những gì tôi sau khi là; nếu phương thức gọi phương thức get_typed_ancestor(), tên lớp cần thiết để thực hiện các hoạt động được yêu cầu trong get_typed_ancestor() là tên của lớp trong đó phương thức gọi là được xác định.

Chuyển $this để trích xuất tên lớp không thành công vì nó sẽ chuyển sang lớp bê tông. Nếu phương thức gọi được định nghĩa trong một lớp trừu tượng cao hơn hệ thống phân cấp hơn lớp bê tông trong đó cá thể là, chúng ta có được tên lớp không chính xác.

Tìm kiếm instanceof static không thành công vì lý do tương tự như được mô tả ở trên. Như mô tả dưới đây, mục đích để ghi tên lớp trong đó phương thức được xác định, là để get_typed_ancestor() có thể tìm thấy một cá thể của bất kỳ lớp nào được lấy từ lớp mà phương thức gọi được định nghĩa, không chỉ là một thể hiện khác của lớp bê tông mà khởi xướng các cuộc gọi stack (do đó $thisstatic là không đạt yêu cầu)

cho đến nay, đi qua __CLASS__-get_typed_ancestor() dường như sự là giải pháp duy nhất cho đến nay, như __CLASS__ sẽ đúng cách giải quyết để tên lớp trong đó phương thức gọi được định nghĩa, không phải tên lớp của cá thể gọi phương thức gọi .


Note:

Tôi đã bao gồm ở phần cuối của câu hỏi này là một ví dụ cho thấy cách tiếp cận tham số làm việc __CLASS__, và thất bại static cách tiếp cận. Nếu bạn muốn có một đâm, có lẽ sử dụng đó như là một sự khởi đầu.


Câu hỏi:

Tôi đã nhìn thấy một số "giải pháp" nổi xung quanh đòn bẩy mà debug_backtrace() để nắm bắt những lớp gọi của một phương pháp hoặc chức năng nhất định; tuy nhiên, những điều này là (vì dấu ngoặc kép của tôi có thể đề xuất) không phải là giải pháp chính xác theo như tôi quan tâm, vì debug_backtrace() được sử dụng theo cách này là hack.

Rant sang một bên, nếu hack này là câu trả lời duy nhất, sau đó hack tôi sẽ.

Dù sao; Tôi đang làm việc trên một tập hợp các lớp hoạt động như các nút trong cây có thể di chuyển từ trên xuống dưới. Dưới đây là một hệ thống phân cấp lớp, đơn giản hóa cho ngắn gọn:

abstract class AbstractNode{} 

abstract class AbstractComplexNode extends AbstractNode{} 

class SimpleNode extends AbstractNode{} 

class ComplexNodeOne extends AbstractComplexNode{} 

class ComplexNodeTwo extends AbstractComplexNode{} 

Nodes có thể có bất kỳ nút bê tông (hoặc null) là phụ huynh. Nhìn vào AbstractNode:

abstract class AbstractNode{ 

    protected $_parent; 

    public function get_typed_ancestor(){ 
     // here's where I'm working 
    } 

    public function get_parent(){ 
     return $this->_parent; 
    } 

} 

Phương thức get_typed_ancestor() là nơi tôi đang ở.

Từ các phương pháp khác trong các lớp mở rộng, get_typed_ancestor() được gọi để tìm số _parent gần nhất của loại lớp mà phương pháp đó thuộc về. Điều này được minh họa tốt hơn với một ví dụ; trao trước AbstractNode định nghĩa:

abstract class AbstractComplexNode extends AbstractNode{ 

    public function get_something(){ 
     if(something_exists()){ 
      return $something; 
     } 
     $node = $this->get_typed_ancestor(); 
     if(null !== $node){ 
      return $node->get_something(); 
     } 
    } 

} 

Phương pháp get_typed_ancestor(), khi gọi từ bối cảnh AbstractComplexNode::get_something(), sẽ tìm kiếm một đối tượng kiểu (hoặc mở rộng loại) AbstractComplexNode - trong trường hợp hệ thống cấp bậc này, các lớp cụ thể có thể là ComplexNodeOneComplexNodeTwo.

Vì không thể khởi tạo AbstractComplexNode, ví dụ cụ thể như ComplexNodeOne sẽ gọi get_something().

Tôi cần làm nổi bật một điểm ở đây; tìm kiếm trong trường hợp trước đây phải là dành cho AbstractComplexNode để tìm trường hợp đầu tiên là ComplexNodeOne hoặc ComplexNodeTwo. Như sẽ được giải thích trong giây lát, tìm kiếm và instanceof static sẽ không thành công, vì nó có thể bỏ qua các trường hợp của các lớp anh chị em và/hoặc con cái của chúng.

Các vấn đề là, kể từ khi có những tình huống mà các lớp gọi là trừu tượng, và các phương pháp gọi là thừa kế (và do đó được gọi từ một thể hiện của) một lớp học như ComplexNodeOne, tìm kiếm cha mẹ đó là instanceofstatic không hoạt động, vì static bị ràng buộc muộn với bê tông ComplexNodeOne.

Bây giờ, tôi có một giải pháp, nhưng tôi không thích nó:

abstract class AbstractNode{ 

    public function get_typed_ancestor($class){ 
     $node = $this; 
     while(null !== $node->_parent){ 
      if($node->_parent instanceof $class){ 
       return $node->_parent; 
      } 
      $node = $node->_parent; 
     } 
     return null; 
    } 

} 

abstract class AbstractComplexNode extends AbstractNode{ 

    public function get_something(){ 
     if(something_exists()){ 
      return $something; 
     } 
     $node = $this->get_typed_ancestor(__CLASS__); 
     if(null !== $node){ 
      return $node->get_something(); 
     } 
    } 

} 

Điều này dường như làm việc, kể từ __CLASS__ giải quyết để tên lớp của định nghĩa. Thật không may, tôi đã cố gắng sử dụng __CLASS__ như một đối số mặc định để get_typed_ancestor() không thành công (rằng mặc dù đã được dự kiến ​​)

tôi đang xem xét lại lập luận $class như một tùy chọn không phân biệt, nhưng nếu nó là ở tất cả có thể để "ngầm "chuyển dữ liệu này cùng với phương thức (khi không có đối số tùy chọn) sẽ tuyệt vời.


Solutions/Thất bại:

  • Đi qua __CLASS__ từ phương pháp gọi điện thoại như một cuộc tranh cãi để get_typed_ancestor().
    Hoạt động, nhưng không lý tưởng vì tôi muốn get_typed_ancestor() giải quyết lớp gọi điện thoại mà không được thông báo rõ ràng về điều đó.

  • Trong vòng tìm kiếm, hãy kiểm tra if($node->_parent instanceof static).
    Không hoạt động khi lớp gọi điện kế thừa phương thức gọi. Nó quyết định đến lớp bê tông mà phương thức được gọi, không phải là phương thức được định nghĩa.Tất nhiên, lỗi này cũng áp dụng cho selfparent.

  • Sử dụng debug_backtrace() để chụp $trace[1]['class'] và sử dụng để kiểm tra.
    Hoạt động, nhưng không lý tưởng vì nó là một hack.

Thật khó khăn khi thảo luận về cấu trúc phân cấp và hỗ trợ phân cấp lớp mà không cảm thấy bạn đang bối rối đối tượng của mình.


Ví dụ:

abstract class AbstractNode 
{ 
    protected $_id; 

    protected $_parent; 

    public function __construct($id, self $parent = null) 
    { 
     $this->_id = $id; 
     if(null !== $parent) 
     { 
      $this->set_parent($parent); 
     } 
    } 

    protected function get_typed_ancestor_by_class($class) 
    { 
     $node = $this; 
     while(null !== $node->_parent) 
     { 
      if($node->_parent instanceof $class) 
      { 
       return $node->_parent; 
      } 
      $node = $node->_parent; 
     } 
     return null; 
    } 

    public function get_typed_ancestor_with_static() 
    { 
     $node = $this; 
     while(null !== $node->_parent) 
     { 
      if($node->_parent instanceof static) 
      { 
       return $node->_parent; 
      } 
      $node = $node->_parent; 
     } 
     return null; 
    } 

    public function set_parent(self $parent) 
    { 
     $this->_parent = $parent; 
    } 

} 

class SimpleNode extends AbstractNode 
{ 

} 

abstract class AbstractComplexNode extends AbstractNode 
{ 

    public function test_method_class() 
    { 
     var_dump($this->get_typed_ancestor_by_class(__CLASS__)); 
    } 

    public function test_method_static() 
    { 
     var_dump($this->get_typed_ancestor_with_static()); 
    } 

} 

class ComplexNodeOne extends AbstractComplexNode 
{ 

} 

class ComplexNodeTwo extends AbstractComplexNode 
{ 

} 

$node_1 = new SimpleNode(1); 
$node_2 = new ComplexNodeTwo(2, $node_1); 
$node_3 = new SimpleNode(3, $node_2); 
$node_4 = new ComplexNodeOne(4, $node_3); 
$node_5 = new SimpleNode(5, $node_4); 
$node_6 = new ComplexNodeTwo(6, $node_5); 

// this call incorrectly finds ComplexNodeTwo ($node_2), skipping 
// the instance of ComplexNodeOne ($node_4) 
$node_6->test_method_static(); 
// object(ComplexNodeTwo)#2 (2) { 
//  ["_id":protected]=> 
//  int(2) 
//  ["_parent":protected]=> 
//  object(SimpleNode)#1 (2) { 
//  ["_id":protected]=> 
//  int(1) 
//  ["_parent":protected]=> 
//  NULL 
//  } 
// } 

// this call correctly finds ComplexNodeOne ($node_4) since it's 
// looking for an instance of AbstractComplexNode, resolved from 
// the passed __CLASS__ 
$node_6->test_method_class(); 
// object(ComplexNodeOne)#4 (2) { 
//  ["_id":protected]=> 
//  int(4) 
//  ["_parent":protected]=> 
//  object(SimpleNode)#3 (2) { 
//  ["_id":protected]=> 
//  int(3) 
//  ["_parent":protected]=> 
//  object(ComplexNodeTwo)#2 (2) { 
//   ["_id":protected]=> 
//   int(2) 
//   ["_parent":protected]=> 
//   object(SimpleNode)#1 (2) { 
//   ["_id":protected]=> 
//   int(1) 
//   ["_parent":protected]=> 
//   NULL 
//   } 
//  } 
//  } 
// } 

Trả lời

1

Đối với việc giải quyết các vấn đề "để nắm bắt những lớp gọi của một phương pháp nhất định hoặc chức năng", chỉ cần vượt qua các đối tượng tạo ra các ví dụ trong các nhà xây dựng .

<?php 

class A { 
    public function caller() { 
    $b = new B ($this); 
    $b->bar(); 
    } 
} 

class B { 
    $whoClass = ''; 
    public function __construct($who) 
    { 
    $this->whoClass = get_class($who); 
    } 
    public function bar($who) { 
    echo get_class($this->whoClass); 
    } 
} 
+0

+1 Đây là giải pháp đúng đắn, không phải là sự phản ánh/sự phản cảm của sự điên rồ mà OP đang xem xét. Sử dụng những khẩu pháo hạng nặng như vậy là một cách để địa ngục của lập trình viên. –

+0

Cảm ơn @macjohn - Chuyển ngữ cảnh (* qua '$ this' *) sẽ không giải quyết được vấn đề chính xác. Như trong trường hợp của ví dụ của tôi, nếu một thể hiện của 'ComplexNodeOne' được thông qua, lớp được truy xuất sẽ chỉ là vậy. Những gì tôi đang sau là, nếu tôi gọi một phương thức đã được * kế thừa * bởi 'ComplexNodeOne' (* được định nghĩa trong' AbstractComplexNode' *) thì lớp được truy xuất phải là 'AbstractComplexNode', không phải lớp của cá thể' $ this '. Điều này sẽ cho phép tìm kiếm tìm bất kỳ cá thể nào xuất phát từ 'AbstractComplexNode' và không chỉ là một thể hiện khác của đối tượng bối cảnh cụ thể. – Dan