2009-07-31 86 views
20

有沒有辦法在PHP中找出哪個對象稱爲另一個對象中的方法。找出在另一個類中調用哪個類的方法

〔實施例:

class Foo 
{ 
    public function __construct() 
    { 
    $bar = new Bar(); 
    $bar->test(); 
    } 
} 

class Bar 
{ 
    public function test() 
    { 
    } 
} 
$foo = new Foo(); 

會不會有一個辦法,我發現該測試方法是從Foo對象叫什麼?

回答

46

你可以使用debug_backtrace,有點像這樣:
BTW,看看手冊頁的評論:有給出了一些有用的功能和建議;-)

class Foo 
{ 
    public function __construct() 
    { 
    $bar = new Bar(); 
    $bar->test(); 
    } 
} 

class Bar 
{ 
    public function test() 
    { 
     $trace = debug_backtrace(); 
     if (isset($trace[1])) { 
      // $trace[0] is ourself 
      // $trace[1] is our caller 
      // and so on... 
      var_dump($trace[1]); 

      echo "called by {$trace[1]['class']} :: {$trace[1]['function']}"; 

     } 
    } 
} 
$foo = new Foo(); 

var_dump將輸出:

array 
    'file' => string '/home/squale/developpement/tests/temp/temp.php' (length=46) 
    'line' => int 29 
    'function' => string '__construct' (length=11) 
    'class' => string 'Foo' (length=3) 
    'object' => 
    object(Foo)[1] 
    'type' => string '->' (length=2) 
    'args' => 
    array 
     empty 

echo

called by Foo :: __construct 

但是,儘管它看起來很好,但我不確定它應該在應用程序中用作「正常的事情」......實際上似乎很奇怪:有了好的設計,一種方法不應該需要知道是什麼稱呼它,在我看來。

+2

工作例子總是很好。 +1。 :) – Randolpho 2009-07-31 18:21:58

+0

謝謝;-)(他們也是沒有得到第一個答案^^的原因,但至少,我也有一點樂趣,這樣;-)) – 2009-07-31 18:22:54

+1

謝謝你的例子。 是的,我同意在某種程度上,方法不需要知道,但我有一些其他人在我正在編寫的內容上編寫代碼,我想讓他們的生活更輕鬆,他。 我認爲使用調試有點冒失,但是謝謝你,你已經給了我一個很好的洞察調試功能,id以前從未真正做過的事情。 – Botto 2009-07-31 18:36:29

2

你或許可以用debug backtrace來達到這個目的,雖然這看起來有些ha。。

另一種選擇是將參數傳遞給該類,並在從另一個類中實例化類時告訴它它從哪裏調用。

+0

我已經研究過在此之前,它似乎在時間回溯是唯一的選擇。 – UnkwnTech 2009-07-31 18:21:51

2

至少,你可以使用debug_backtrace並分析,找到調用方法。

我想你應該也可以使用reflection API來做到這一點,但是我使用PHP的時間太長了,我不記得具體是怎麼做的。但是,鏈接至少應該讓你開始。

2

@Pascal MARTIN: 是的,在正常應用中可能不需要。但有時它可能會有用。 從我自己的應用程序中考慮一個示例:

有一個Controller子類可以使用Template對象來準備其輸出。每個模板都有一個名稱來引用它。當控制器需要一個模板時,它會通過將該名稱作爲參數向TemplateManager進行詢問。 但是,對於不同的控制器,可能會有許多具有該名稱的模板文件。控制者被用作插件,並且可以由不同的用戶編寫,所以他們使用的名稱不能被控制爲不相互衝突。模板的命名空間是必需的。 因此,模板對象的工廠TemplateManager需要模板名稱和名稱空間名稱來查找適當的模板源文件。該名稱空間與特定Controller的類名相關。

但是,在大多數情況下,每個控制器將使用來自其自己命名空間的模板,並且僅在極少數情況下使用來自其他命名空間的模板。因此,每次對TemplateManager :: getTemplate()的每個調用中指定名稱空間將會是一團糟。如果命名空間是可選的,並且默認爲... 調用TemplateManager :: getTemplate()的Controller!這裏是瞭解來電者的好地方。

當然,調用者控制器可以將自身或其名稱作爲參數傳遞,但與傳遞名稱空間名稱並無太大差別。無論如何,它都不是可選的。

但是,如果您可以知道調用者,則可以使用該信息在getTemplate()中自動默認命名空間,而不會影響調用者。它不必知道getTemplate()如何在其內部處理它,它如何知道正確的默認名稱空間。他只需要知道它的確如此,並且可以根據需要傳遞任何其他名稱空間。

5

您也可以讓呼叫對象將自己作爲參數傳遞自己

例如,在討論由Erich Gamma,等人,第278頁上的「調解員」結構模式:

class Foo 
{ 
    public function __construct() 
    { 
    $bar = new Bar(); 
    $bar->test($this); 
    } 
} 

class Bar 
{ 
    public function test() 
    { 
    } 
} 
$foo = new Foo(); 

我有這個想法從書「的可複用面向對象軟件的元素設計模式」。

模式的要點是減少一堆對象/類之間的多對多連接的數量。您創建一箇中介類,所有這些類都視爲中心。這樣的課程不需要了解彼此。中介處理交互。爲了讓調解員知道所跟蹤的類中的變化,他們可以將自己作爲參數傳遞,或者可以使用「觀察者」模式實現調解器。

2018編輯:

我有時使用的接口與上面的代碼,這樣的:

interface someInterface // many classes may implement this interface 
{ 
    public function giveMeBar(); 
} 

class Foo implements someInterface 
{ 
    public function __construct() 
    { 
    $bar = new Bar(); 
    $bar->test($this); 
    } 
    public function giveMeBar() { 
    return 'Bar'; 
    } 
} 
class Bar 
{ 
    public function test(someInterface $a) 
    { 
    echo $a->giveMeBar(); 
    } 
} 
$foo = new Foo(); // prints "Bar" 
14

這裏是一個襯裏溶液

list(, $caller) = debug_backtrace(false, 2); 

作爲PHP7的,這將不起作用根據文檔:http://php.net/manual/en/function.list.php,因爲我們不能有空的屬性,這裏是一個小的更新:

list($childClass, $caller) = debug_backtrace(false, 2); 
0

這個功能做這項工作沒有debug_backtrace:

/* 
usage : 
some code... 
getRealCallClass(__FUNCTION__); 
some code... 
*/ 

function getRealCallClass($functionName) //Parameter value must always be __FUNCTION__ 
{ 
    try 
    { 
    throw new exception(); 
    } 
    catch(exception $e) 
    { 
    $trace = $e->getTrace(); 
    $bInfunction = false; 
    foreach($trace as $trace_piece) 
     { 
      if ($trace_piece['function'] == $functionName) 
      { 
      if (!$bInfunction) 
       $bInfunction = true; 
      } 
      elseif($bInfunction) //found !!! 
      { 
      return $trace_piece['class']; 
      } 
     } 
    } 
}