A little while ago, I had to build a .xls export function into a Yii application. I decided to use PHPExcel for this, since I had used it before. It’s also probably the best Excel importer/exporter for PHP. I soon figured that this didn’t go as smooth as I expected.
The problem
Yii kept throwing an Exception about it not being able to find the file to import. Of course it couldn’t find the file because PHPExcel’s importer was supposed to handle that. I searched around and found a couple of posts like this one. This failed for me because between the spl_autoload_unregister and spl_autoload_register calls, I had to get info from a Model through another Model. When you try to reach a related Model, Yii actually tries to autoload that Model. But since we just unregistered the yii autoloader by calling spl_autoload_unregister, the Yii import was never called and so autoloading the Model failed.
The solution
So I went on and figured out that the reason they unregistered the Yii autoloader, was because the Yii autoloader was called before the PHPExcel autoloader. Since the Yii autoloader throws an Exception when it can’t find a file and PHPExcel doesn’t, the solution was simple: switch the order in which the autoloaders get registered. I decided to do this by changing the PHPExcel Autoloader’s register method from this:
Click the link below to download the fixed Autoloader.php for PHPExcel:
Autoloader.php for PhpExcel 1.7.3c
Autoloader.php for PhpExcel 1.7.6
Valuable info. Exactly what I was looking for. i’ll check back soon again.
So, how does your solution affect the controller code (oposed to the solution in http://www.yiiframework.com/wiki/101/how-to-use-phpexcel-external-library-with-yii/)? Are the calls to the spl_load_unregister/register functions still needed in the controller?
I have tried your solution with and without calling to spl_load_* in my controller, but I still had no success
Any help will be appreciated.
Hi Angel,
If you use this solution, you don’t have to do anything in Yii except import PHPExcel.
If you do this and still have problems, feel free to let me know what is actually going wrong.
Thanks for your attention.
I can’t figure out what’s happening. This is is the fragment of source where I get the error:
[quote]
public function comprovaFitxerDeCarrega($fitxer) {
$errors = array();
$phpExcelPath = Yii::getPathOfAlias(‘ext.slt.phpexcel’);
include($phpExcelPath . DIRECTORY_SEPARATOR . ‘PHPExcel.php’);
$o = new PHPExcel();
CVarDumper::dump(“hello”, 1, true);
try {
// Identificar el tipus de fitxer
$inputFileType = PHPExcel_IOFactory::identify($fitxer);
CVarDumper::dump(“debug2″, 1, true);
} catch (Exception $e) {
CVarDumper::dump(“debug3″,1,true);
array_push($errors, “Error: ” . $e->getMessage() );
}
return $errors;
}
[/quote]
“comprovaFitxerDeCarrega” is a function called from my controller’s actionIndex() method. I get this error: http://i.imgur.com/ZfKIk.png
As you see, the line “$o = new PHPExcel();” (i included this line just for test; I don’t use the $o variable) executes successfully (so the PHPExcel class is found by the loader), but the line
$inputFileType = PHPExcel_IOFactory::identify($fitxer);
throws an error. I’m using PHP 5.3 and PHPExcel 1.7.6 (I haven’t tried with version 1.7.3c yet, the version you use). Any ideas about what steps could I follow to find the error?
Thanks in advance.
First you can replace this:
$phpExcelPath = Yii::getPathOfAlias(‘ext.slt.phpexcel’);
include($phpExcelPath . DIRECTORY_SEPARATOR . ‘PHPExcel.php’);
with this:
Yii:import(‘ext.slt.PHPExcel.PHPExcel’);
I also attached a new Autoloader.php file for PHPExcel 1.7.6 . Please make sure you use the Autoloader for the right version.
I really appreciate your help, MrSoundless, I finally got it working thanks to your advice.
BUT I have detected a really [i]strange[/i] behaviour: if I remove the line
$o = new PHPExcel();
from my function, I still get the same exception when calling to PHPExcel_IOfactory::identify(). So It seems to me that this sentence does some “magic” loading some classes or libraries which are not loaded if I call directly to PHPExcel_IOFactory::identify().
I am a PHP newbie, as you can see. Does this make any sense to you, or am I going crazy?
Anyway, thank you very much for your time!
I’m glad to hear that!
What you said there makes complete sense:
The reason that PHPExcel_IOFactory is not recognized is because the PHPExcel Autoloader is needed to import the PHPExcel_IOFActory class. At this time the autoloader is not loaded yet, which means it also can’t import anything. The PHPExcel Autoloader is loaded as soon as the PHPExcel.php file is included. If you check out that file, you’ll see that it includes the Autoloader in there.
The Yii::import function, includes a class ONLY when necessary. So until you call new PHPExcel, it won’t load in the PHPExcel file which means the PHPExcel autoloader is never called.
So to fix this, you could just create an instance of PHPExcel like you did. You could also use include instead of Yii::import (as you showed in an earlier comment)
So yeah this sounds like a good reason to use include instead of import.
it s magic! I love your patch ^)
@Angle
You could also use
Yii::import(‘blah.blah’, true);
which includes the files right away.
Thank you It is very useful.
Was looking for this.. thanks
Great share , Thanks
hi, thanks for share!
why you don`t use Yii::registerAutoloader()?
Yii::import(‘….PHPExcel’,true);
Yii::registerAutoloader(array(‘PHPExcel_Autoloader’, ‘Load’),true);
thanks a lot again
Oh wow, I didn’t know about registerAutoloader. That is indeed the best solution I’ve seen till now!
yep, but the problem is that we need to load
Autoloader.php (require or via yii::import() )
( or PHPExcel.php, which load it)
before call registerAutoloader.
And if it`s happened we`ll get error with
PHPExcel_Shared_ZipStreamWrapper::register();
I feel, that the another solution is near, but little bit tired to search it now ((
also, may be you clear with me, why does we need to unregister firstly Yii`s autoloader?
as saw it many times: in your example and in the http://www.yiiframework.com/wiki/37/integrating-with-other-frameworks/ and more results from yii`s forum
thanks in advance, good luck
When you add an autoloader it gets stacked.
So if you just have Yii your autoloader array will look like this:
element 0 => Yii’s Autoloader
And if you add PHPExcel’s Autoloader to it it’ll become this:
element 0 => Yii’s Autoloader
element 1 => PHPExcel’s Autoloader
So if php tries to autoload a class it first checks with Yii’s autoloader. Then PHPExcel’s autoloader. The problem with Yii’s autoloader is that it returns an exception if it cna’t find anything. This means it’ll never reach PHPExcel’s Autoloader..
By unregistering Yii’s Autoloader first, we clear the array, then add PHPExcel’s Autoloader, then add Yii’s Autoloader. After doing this the array will look like this:
0 => PHPExcel’s Autoloader
1 => Yii’s Autoloader
So now if PHP tries to autoload a class, it first tries with PHPExcel’s Autoloader. Since PHPExcel’s Autoloader does not throw an exception if it can’t find anything this will work fine. If it can’t find the class, it’ll just move on to Yii’s Autoloader.
That’s pretty much the reason why Yii’s Autoloader is unregistered before others are registered.
Hope this helps.
Thanks!! I definitely owe you one
one more comment from me only because you are counting! haha!
thanks man!
Haha, thanks. Awesome
Awesome. Thanks dude. Helps a lot!
Looking forward to see more. Thanks for sharing.
Greetings! Very helpful advice within this article! It’s the little changes that make the largest changes. Thanks for sharing!
Thanks man.
This helped me a lot. I tried a lot of solutions here and there, but this one worked great.
Also here is the link great for newbies like me on phpExcel Read/Write.
http://bayu.freelancer.web.id/2010/07/16/phpexcel-advanced-read-write-excel-made-simple/
Help it helps someone
Thank you so much. At last, my question has been answered after searching the web for hours! Really smart looking site by the way.
I will give it a try. Thx.