前言
以前在公司做的一個項目中遇到的一個問題,還有意思的。
問題
大家有沒有在 CakePHP 中使用過第三方庫?我現在在引入了一個名為 SimpleExcel 的第三方庫時遇到了一些問題。我覺得問題可能跟我把這個庫的庫檔案放在了不正确的目錄下有關。
正常情況下,該庫在解壓後,會有一個名為 SimpleExcel 的目錄,該目錄下的檔案結構如下:
[email protected]:~/Desktop/faisalman-simple-excel-php-8d9fabc/src/SimpleExcel$ ls
Exception Parser SimpleExcel.php Writer
使用的話,隻需要在相應的 PHP 檔案中引入該庫就可以了,如下所示:
// test.php
// 該檔案和 SimpleExcel 目錄在同一個目錄,是以下面的 require_once 中的路徑為 ./SimpleExcel/SimpleExcel.php
use SimpleExcel\SimpleExcel;
require_once('./SimpleExcel/SimpleExcel.php');
現在在 CakePHP 中,我是這樣做的,将 SimpleExcel 目錄放在了app/controllers 目錄下,然後在相應的控制器中加入了如下代碼:
use SimpleExcel\SimpleExcel;
require_once('SimpleExcel/SimpleExcel.php');
但在通路該控制器時,總是出現問題,錯誤資訊如下:
Warning (2): require_once(/opt/xplico/xi/app/controllers/SimpleExcel/ontroller.php) [function.require-once]: failed to open stream: No such file or directory [APP/controllers/SimpleExcel/SimpleExcel.php, line 43]
到底我哪裡做錯了?
答案
首先,請看下這篇文章《PHP autoload機制詳解》。因為之前遇到的問題的根本原因就在于 SimpleExcel 庫在使用 autoload 機制時不夠嚴謹。
現在,重新看下那個錯誤提示:
Warning (2): require_once(/opt/xplico/xi/app/controllers/SimpleExcel/ontroller.php) [function.require-once]: failed to open stream: No such file or directory [APP/controllers/SimpleExcel/SimpleExcel.php, line 43]
從提示中可以看到,SimpleExcel.php 這個檔案已經執行了,也就是說,之前我們将 SimpleExcel 庫放在了 app/controllers 目錄下,并在控制器中加入的如下代碼是沒有問題的。
use SimpleExcel\SimpleExcel;
require_once('SimpleExcel/SimpleExcel.php');
那為什麼 SimpleExcel.php 會出現問題呢?現在定位到該檔案出錯的地方,如下所示:
if (!class_exists('Composer\\Autoload\\ClassLoader', false)){
// autoload all interfaces & classes
spl_autoload_register(function($class_name){
// TODO(H): 錯誤就出在這裡的require_once語句上
if($class_name != 'SimpleExcel') require_once(dirname(__FILE__).DIRECTORY_SEPARATOR.str_replace('\\', DIRECTORY_SEPARATOR, substr($class_name, strlen('SimpleExcel\\'))).'.php');
});
}
為什麼這裡會出錯,結合上面的錯誤資訊,這裡 require_once 了一個不存在的檔案?那這個不存在的檔案時怎麼來的呢?這就涉及到 autoload 機制。這個不存在的檔案的路徑是 SimpleExcel.php 檔案自身所在目錄加上目前控制器需要的類的名字構成的。作為一個控制器,它當然需要 Controller 父類,但這個類并不在 /opt/xplico/xi/app/controllers/SimpleExcel 目錄下,當然就出錯了。
其實,SimpleExcel 庫的作者本意是利用 autoload 機制加載 SimpleExcel.php 自身所需要的一些類(位于Exception、Parser 和 Writer 目錄下),但這裡對要加載的類的過濾不夠嚴謹,就出現了上面我們遇到問題。
修改方式,注釋上面的代碼,加入如下代碼,解決問題。
$currentDir = dirname(__FILE__);
require_once($currentDir . '/Parser/IParser.php');
require_once($currentDir . '/Parser/BaseParser.php');
require_once($currentDir . '/Parser/CSVParser.php');
require_once($currentDir . '/Parser/HTMLParser.php');
require_once($currentDir . '/Parser/JSONParser.php');
require_once($currentDir . '/Parser/TSVParser.php');
require_once($currentDir . '/Parser/XLSXParser.php');
require_once($currentDir . '/Parser/XMLParser.php');
require_once($currentDir . '/Writer/IWriter.php');
require_once($currentDir . '/Writer/BaseWriter.php');
require_once($currentDir . '/Writer/CSVWriter.php');
require_once($currentDir . '/Writer/HTMLWriter.php');
require_once($currentDir . '/Writer/JSONWriter.php');
require_once($currentDir . '/Writer/TSVWriter.php');
require_once($currentDir . '/Writer/XLSXWriter.php');
require_once($currentDir . '/Writer/XMLWriter.php');
require_once($currentDir . '/Exception/SimpleExcelException.php');