簡體   English   中英

如果存在,如何有條件地使類使用特征?

[英]How can I conditionally make a class use a trait if it exists?

如果可用,我希望能夠use該特性。

顯然我不能在類本身內部定義它(語法錯誤)

//fails
include_once('myTrait.php');

class foo
{ 
    var $bar; 

    if (trait_exists('myTrait')) {
        use myTrait;
    }
}

//also fails
foo use myTrait;

//also fails
$f = new foo();
$f use myTrait;

//also fails
$f = new foo() use myTrait;

理想的情況是這樣的:

class foo
{
    var $bar;
}

if (file_exists('myTrait.php')) {
   include_once('myTrait.php');
   //make class foo use myTrait;
}

$f=new foo();

很難找到文檔和特征似乎不是很受歡迎,但在我的特殊情況下,它們非常有用。 我還嘗試通過僅在需要時包含文件來盡可能降低資源。

像往常一樣歡迎提示、文檔和解釋。


我的搜索帶給我的最接近的是這篇文章http://brendan-bates.com/traits-the-right-way/

假設其中一些控制器(但不是全部)需要數據庫連接。 為了保持性能,我們不應該為每個控制器提供數據庫連接。 我們可以做的是編寫一個抽象類,它擴展了提供數據庫連接的 BaseController。 但是,將來,如果不是控制器的對象需要數據庫連接怎么辦? 我們可以使用水平重用,而不是復制這個邏輯。

可以創建一個簡單的特征:

 trait DatabaseAware { protected $db; public function setDatabase($db) { $this->db = $db; } protected function query($query) { $this->db->query($query); } }

此特征現在提供具有通用數據庫功能的類。 任何需要數據庫連接的類,無論是控制器還是管理器(或任何東西),都可以使用這個特性:

 class IndexController extends BaseController { use DatabaseAware; public function indexAction() { $this->query("SELECT * FROM `someTable`"); } }

當我根據不同對象的需要實現特征時。 數據庫連接、調試報告等。

簡單!

特征

可以使用或不使用的特征都可以使用,也可以不使用,但最終將有助於實現接口:

<?php

trait BarTrait
{
    public function bar()
    {
        return 'Hey, I am your friend!';
    }
}

接口

我們希望實現的接口:

<?php

interface BarInterface
{
    /**
     * @return string
     */
    public function bar();
}

使用特征類

一個FooUsingBarTrait類,它使用特征BarTrait來實現上述接口:

<?php

class FooUsingBarTrait implements BarInterface
{
    use BarTrait;
}

類不使用特征

FooNotUsingBarTrait類,它不使用特征BarTrait ,而是自己實現上述接口:

class FooNotUsingBarTrait implements BarInterface
{
    public function bar()
    {
        return 'Hey, I am one of your friends!';
    }
}

有條件地創建類定義

最后,根據特征BarTrait是否存在,有條件地定義一個Foo類:

<?php

if (trait_exists(BarTrait::class) {
    class Foo extends FooUsingBarTrait
    {
    }
} else {
    class Foo extends FooNotUsingBarTrait
    {
    }
}

創建您的實例

$foo = new Foo();

$foo->bar();

var_dump(
    get_class($foo),
    class_parents(Foo::class)
);

注意如果兩個類FooUsingBarTraitFooNotUsingBarTrait實現一個公共接口,這可能最有意義-畢竟,您可能想提供一些將在兩種實現之間共享的功能:一種使用特征,另一種通過其他方式(方法由該課)。

供參考,請參閱:

有關示例,請參見:

您的問題很有趣,並且eval()可能滿足您的需求。 這種使用代碼生成的樣式很難看,但是我知道它可以工作,因為我自己在自己的機器上進行了驗證。 您可以按照以下方法進行操作:

$src = '
class foo {
  var $bar; // and all of your other code goes here
';
if (file_exists('myTrait.php')) {
  include_once('myTrait.php');
  $src .= "use myTrait;\n";
}
$src .= "}";
eval ($src); // your class finally gets declared

我不經常使用eval(),但是它解決了原本無法解決的問題時很有趣。

不管它有多糟糕,你都可以通過擴展類來實現。


trait AutoPilot {
   function navigate() {
       echo 'navigating...';
   }
}

if (trait_exists('AutoPilot')) {
   class Machine {
       use AutoPilot;
   }
} else {
   class Machine {
   }
}

class Car extends Machine {
}


$car = new Car;
$car->navigate();

這就是我最終得到的:

eval("class myClass {" 
    . (trait_exists('myTrait') ? "use myTrait;" : "") 
    . str_replace(['class ', '<?php'], '//', file_get_contents(myClass.php"))
);

總懶惰:

  • 復制trait_exists行以添加更多特征
  • 注釋掉class關鍵字和<?php標記,因此您不必編輯類文件
  • 評估一長串臭代碼。

對我和“按原樣”都很好除了我粘貼此行的文件外,無需對任何文件進行任何修改。對您而言,情況可能並非如此。

考慮以下事實:

  • 不要使用關閉php標簽
  • 檔案僅一堂課
  • 您需要添加其他任何關鍵字(擴展,工具等)
  • 並可能根據您的代碼采取更多意外行為

感謝lacalheinz的指導,但是Steven用eval()瞄准了靶心。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM