2009-05-20 36 views
11

Tôi cần một hàm tĩnh được thừa kế "gọi" để gọi một hàm tĩnh khác "bên trong" đã bị ghi đè. Tôi có thể làm điều này với ràng buộc tĩnh muộn, nhưng máy chủ của tôi không có php5.3 và vì vậy tôi cần phải làm việc xung quanh nó.Giả mạo ràng buộc tĩnh muộn trước php 5.3

class ClassA{ 
    static function call() 
    { 
     return self::inner(); 
    } 

    static function inner(){ 
     return "Class A"; 
    } 
} 

class ClassB extends ClassA{ 
    static function inner(){ 
     return "Class B"; 
    } 
} 

echo "<p>Class A = " . ClassA::call(); 
echo "<p>Class B = " . ClassB::call(); 

Tôi muốn đầu ra là:
Class A = Class A
Class B = Class B

Nhưng những gì nó là:
Class A = Class A
Class B = Lớp A

Đường ruột của tôi nói với tôi rằng tôi có thể viết một thứ gì đó trong cuộc gọi() để phát hiện đối tượng nào được tham chiếu khi "call()" được gọi là. Vì vậy, thay vì tự :: inner() nó sẽ như vậy một cái gì đó dọc theo dòng của gọi là :: inner(). Phát hiện phiên bản bên trong thích hợp() để gọi từ cuộc gọi phương thức gốc.

+0

Máy chủ của bạn có phải là FatCow không? Bây giờ là tháng 6 năm 2012 và họ vẫn chưa có PHP 5.3. -1 trên FatCow. – Ben

Trả lời

1

Bạn có thể sử dụng các cá thể đối tượng thay vì các lớp. Nếu bạn muốn có một biểu tượng toàn cầu, bạn có thể sử dụng một biến toàn cục. Vì chúng khá khó sử dụng trong PHP, một thủ thuật là quấn nó vào một hàm. Ví dụ:

class ClassA { 
    function call() { 
    return $this->inner(); 
    } 
    function inner() { 
    return "Class A"; 
    } 
} 
function ClassA() { 
    static $instance; 
    return $instance ? $instance : new ClassA(); 
} 

class ClassB extends ClassA { 
    function inner() { 
    return "Class B"; 
    } 
} 
function ClassB() { 
    static $instance; 
    return $instance ? $instance : new ClassB(); 
} 

echo "<p>Class A = " . ClassA()->call(); 
echo "<p>Class B = " . ClassB()->call(); 

Nhưng một ý tưởng tốt hơn có thể là tránh các biểu tượng toàn cầu; Lý do tại sao nó hoạt động tốt trong Ruby/Rails, là Ruby không thực sự có trạng thái tĩnh theo cùng cách mà PHP có. Một lớp có thể được phục hồi và được thêm vào trong thời gian chạy, cho phép mở rộng khung công tác dễ dàng. Trong PHP, các lớp luôn luôn là cuối cùng, do đó, đề cập đến chúng trong mã ứng dụng, là một mức độ rất mạnh của khớp nối.

+1

Bạn cũng có thể sử dụng các lớp đơn, bọc mã thể hiện trong hàm getInstance tĩnh() và có hàm classB() {return classB :: getInstance(); } –

1

Thật không may là không có cách nào tốt đẹp để làm điều đó (nếu không PHPers sẽ không cổ vũ rất nhiều cho tính năng đó).

Bạn phải chuyển tên lớp học. PHP < 5.3 cũng không có cú pháp tốt đẹp cho cuộc gọi tĩnh với tên lớp năng động mà làm cho toàn bộ điều thậm chí xấu xí:

static function call($class) 
{ 
    return call_user_func(array($class,"inner")); 
} 
… 
ClassA::call("ClassA"); 
ClassB::call("ClassB"); 

Nếu bạn có thể thay đổi mã (và không sử dụng static), sau đó độc thân làm cho nó có thể chịu đựng hơn . Bạn có thể làm helper chức năng để giảm bớt cú pháp:

X("ClassA")->call(); 
X("ClassB")->call(); 

Chức năng X nên nhìn lên, tạo ra và gửi lại thể hiện của một lớp học.

+1

Vì vậy, về cơ bản tránh vấn đề này hoàn toàn bằng cách cấu trúc mã để nó hoặc là, không cần thiết để làm điều này, hoặc chờ cho đến khi 5.3? hút. –

3

Nếu hiệu suất không phải là một vấn đề, bạn có thể sử dụng debug_backtrace() để tìm ra lớp được gọi là:

$bt = debug_backtrace(); 
return get_class($bt[1]['object']); 

http://php.net/manual/en/function.debug-backtrace.php

+0

Điều này không cung cấp cho lớp thực tế được gọi là trong PHP 5.2 –

1

Thông thường, cuối tĩnh ràng buộc là cần thiết khi một đứa trẻ gọi lại một phương pháp người cha, lần lượt, gọi một phương thức tĩnh trừu tượng của đứa trẻ. Tôi đã ở trong một postition như vậy, và không thể sử dụng tĩnh :: như PHP của tôi không phải là phiên bản 5.3 (hoặc mới hơn). Sau đây hoàn thành ràng buộc tĩnh muộn bằng cách debug_backtrace().

abstract class ParentClass 
{ 
    static function parent_method() 
    { 
     $child_class_str = self::get_child_class(); 
     eval("\$r = ".$child_class_str."::abstract_static();"); 
     return $r; 
    }// CHILD MUST OVERRIDE TO PUT ITSELF INTO TRACE 

    protected abstract static function abstract_static(); // THIS NEEDS LATE STATIC BINDING 

    private static function get_child_class() 
    { 
     $backtrace = debug_backtrace(); 
     $num = count($backtrace); 
     for($i = 0; $i < $num; $i++) 
     { 
      if($backtrace[$i]["class"] !== __CLASS__) 
        return $backtrace[$i]["class"]; 
     } 
     return null; 
    } 
} 

class ChildClass extends ParentClass 
{ 
    static function parent_method(){ return parent::parent_method(); } 

    protected static function abstract_static() 
    { 
     return __METHOD__."()"; 
    }// From ParentClass 
} 

print "The call was: ". ChildClass::parent_method(); 
0

Vì bạn không thể sử dụng tĩnh ::, hoặc get_called_class(), hoặc __callStatic, bạn sẽ phải gọi hàm trong() với một số dấu hiệu cho thấy với lớp gọi (như đã đề cập trong câu trả lời khác). Một thể hiện của lớp được gọi là sẽ ổn. Bạn có thể thêm các phương thức "Tĩnh giả" để bắt chước mọi phương thức tĩnh mà bạn cần ghi đè. Điều này tăng gấp đôi mã của bạn, nhưng bằng cách làm như sau, tôi hy vọng mã sẽ dễ dàng nâng cấp khi php5.3 xuất hiện: chỉ cần xóa tất cả các phương thức ps và tất cả các chức năng tham chiếu (và thay đổi "tự" để "tĩnh" khi cần ..)

class ClassA{ 
    static function call() 
    { 
     return self::inner(); 
    } 

    static function inner(){ 
     return "Class A"; 
    } 

    public function _ps_call() 
    { 
     return $this->_ps_inner(); 
    } 


} 

class ClassB extends ClassA{ 

    public static function getInstance() 
    { 
     return new self(); 
    } 

    public static function call() 
    { 
     return self::getInstance()->_ps_call(); 
    } 

    static function inner() 
    { 
     return "Class B"; 
    } 

    public function _ps_inner() 
    { 
     return self::inner(); 
    } 
} 

echo "<p>Class A = " . ClassA::call(); 
echo "<p>Class B = " . ClassB::call(); 
+0

.. nó trở nên phức tạp hơn nếu bạn thêm các vars tĩnh vào ví dụ. trong phương thức getInstance(), bạn có thể sao chép tất cả các get_class_vars thành các vars ví dụ như $ this -> _ ps_foo, mà bạn nên sử dụng thay vì các phiên bản tĩnh của chúng bên trong mỗi phương thức _ps_ dụ ... – commonpike

2

Đây là ví dụ nhanh.

<?php 

class ClassA{ 
    public function call(){ 
     return $this->inner(); 
    } 

    static function inner(){ 
     return "Class A"; 
    } 

    static function init($class){ 
     return new $class(); 
    } 
} 

class ClassB extends ClassA{ 
    static function inner(){ 
     return "Class B"; 
    } 
} 

echo "<p>Class A = " . ClassA::init("ClassA")->call(); 
echo "<p>Class B = " . ClassB::init("ClassB")->call(); 

?> 

Nếu bạn không thích chuyển tên lớp, bạn có thể thêm chức năng init tĩnh vào lớp con và chuyển nó một cách rõ ràng ở đó. Điều này sẽ cho phép bạn làm một cái gì đó như: ClassA :: init() -> call() và ClassB :: init() -> call() nhưng sẽ có một số mã trùng lặp nhỏ.

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