天天看點

在 CakePHP 中使用 SimpleExcel 遇到的一個問題小記前言問題答案

前言

以前在公司做的一個項目中遇到的一個問題,還有意思的。

問題

大家有沒有在 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');
           
php