2008-08-23 25 views
3

我想知道最好的做法是如何處理這個問題,必須在我的PHP腳本中包含這麼多文件以確保我需要使用的所有類都是我的腳本可以訪問。如何處理在PHP中包括所需的類

目前,我只是使用include_once來包含我直接訪問的類。其中每個將include_once他們訪問的類。

我已經研究過使用__autoload函數,但是如果您打算將您的類文件組織在目錄樹中,那麼帽子似乎不能很好地工作。如果你這樣做了,似乎你最終會走目錄樹,直到找到你要找的課程。 此外,我不知道這是如何影響具有相同名稱的類在不同的命名空間。

有沒有更簡單的方法來處理這個問題?

還是PHP只是不適應「enterprisey」類型的應用,有很多不同的對象全部位於單獨的文件,可以在許多不同的目錄中。

回答

6

我的應用程序我通常有setup.php文件,其中包含所有核心類(即框架和附帶的庫)。我的自定義類是使用自動加載器在目錄佈局圖的幫助下加載的。

每次添加新類時,我運行命令行構建器腳本,掃描整個目錄樹以搜索模型類,然後使用類名作爲鍵和路徑作爲值構建關聯數組。然後,__autoload函數在該數組中查找類名並獲取包含路徑。下面的代碼:

autobuild.php

define('MAP', 'var/cache/autoload.map'); 
error_reporting(E_ALL); 
require 'setup.php'; 
print(buildAutoloaderMap() . " classes mapped\n"); 

function buildAutoloaderMap() { 
    $dirs = array('lib', 'view', 'model'); 
    $cache = array(); 
    $n = 0; 
    foreach ($dirs as $dir) { 
     foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir)) as $entry) { 
      $fn = $entry->getFilename(); 
      if (!preg_match('/\.class\.php$/', $fn)) 
       continue; 
      $c = str_replace('.class.php', '', $fn); 
      if (!class_exists($c)) { 
       $cache[$c] = ($pn = $entry->getPathname()); 
       ++$n; 
      } 
     } 
    } 
    ksort($cache); 
    file_put_contents(MAP, serialize($cache)); 
    return $n; 
} 

autoload.php

define('MAP', 'var/cache/autoload.map'); 

function __autoload($className) { 
    static $map; 
    $map or ($map = unserialize(file_get_contents(MAP))); 
    $fn = array_key_exists($className, $map) ? $map[$className] : null; 
    if ($fn and file_exists($fn)) { 
     include $fn; 
     unset($map[$className]); 
    } 
} 

注意,文件命名約定必須是[CLASS_NAME] .class.php。改變目錄類將在autobuild.php中查找。當沒有找到類時,您也可以從autoload函數運行自動編譯器,但這可能會導致程序陷入無限循環。

串行化陣列快速修補。

@JasonMichael:PHP 4已經死了。克服它。

0

__autoload如果您的類具有一致的命名約定,可以告訴函數在目錄樹中找到它們的位置,那麼這種方法很有效。 MVC特別適合這種事情,因爲您可以輕鬆地將類分爲模型,視圖和控制器。

或者,將名稱的關聯數組保存到您的類的文件位置,並讓__autoload查詢此數組。

2

您可以定義多個spl_autoload_register自動加載功能:

spl_autoload_register('load_controllers'); 
spl_autoload_register('load_models'); 

function load_models($class){ 
    if(!file_exists("models/$class.php")) 
     return false; 

    include "models/$class.php"; 
    return true; 
} 
function load_controllers($class){ 
    if(!file_exists("controllers/$class.php")) 
     return false; 

    include "controllers/$class.php"; 
    return true; 
} 
0

的建議,到目前爲止,我偏愛凱文的,但它不不需要絕對。我發現有幾個不同的選項可以與__autoload一起使用。

  1. 將所有類文件放到一個目錄中。在課後命名文件,即classes/User.phpclasses/User.class.php
  2. Kevin將模型放入一個目錄,將控制器放入另一個目錄等等的想法。如果所有的類都很好地適合MVC框架,可以工作得很好,但有時候,事情會變得混亂。
  3. 將該目錄包含在類名中。例如,名爲Model _的類實際上位於classes/Model/User.php。您的__autoload函數會知道將下劃線轉換爲目錄分隔符以查找該文件。
  4. 只需解析一次整個目錄結構即可。無論是在__autoload函數中,還是隻在它定義的同一個PHP文件中,循環訪問classes目錄的內容並緩存哪些文件在哪裏。因此,如果您嘗試加載User課程,那麼無論是在classes/User.php還是classes/Models/User.phpclasses/Utility/User.php之間。一旦它在classes目錄中的某處發現User.php,它將知道當User類需要自動加載時,包含哪個文件。
0

@Kevin:

我只是想指出,spl_autoload_register是一個更好的選擇,因爲__autoload可以定義多個裝載機,他們不會互相沖突。如果您必須包含定義__autoload函數的庫,則很方便。

確定嗎? documentation有不同的說法:

如果你的代碼有一個現有的__autoload函數,那麼這個函數必須顯式註冊在__autoload棧上。這是因爲spl_autoload_register()將通過spl_autoload()或spl_autoload_call()有效地替代__autoload函數的引擎緩存。

=>您必須明確註冊任何庫的__autoload。但除此之外,你當然是對的,這個功能是更好的選擇。

1

您還可以通過使用映射到物理目錄的結構化命名約定以編程方式確定類文件的位置。這就是Zend在Zend Framework中的做法。所以當你調用Zend_Loader::loadClass("Zend_Db_Table");時,它會將下劃線分割爲一個目錄數組,然後Zend_Loader類將加載所需的文件。

和所有的Zend模塊一樣,我希望你可以在你自己的類中使用它自己的加載器,但我只使用它作爲使用Zend的MVC的站點的一部分。

但是,當您使用任何類型的動態類加載時,一直存在關於負載性能的擔憂,例如,請參閱this blog post比較Zend_Loader與加載類文件的難度。

除了必須搜索PHP包含路徑的性能損失之外,它還會破壞操作碼緩存。從該帖子評論:

當使用任何動態類加載器APC不能完全緩存這些文件作爲其不能確定哪些文件可以在任何單一的請求負載。通過硬加載文件,APC可以完全緩存它們。