How can I make PHPUnit respect __autoload functions?
For example, I have these three files:
loader.php
function __autoload($name)
{
echo "foo\n";
require_once("$name.php");
}
test.php
require_once("loader.php");
class FooTest extends PHPUnit_Framework_TestCase
{
function testFoo()
{
new Foo();
}
}
foo.php
require_once("loader.php");
new Foo();
As expected php foo.php errors out, saying that file "Foo.php" doesn't
exist. The testFoo() function however errors out by saying that there is
no such class as Foo, and never echos the "foo\n" line.
This is expected behavior.
See this PHPUnit bugtracker entry: Upgrading to 3.5.10 has broken function of "autoload"
As of PHPUnit 3.5:
PHPUnit now uses an autoloader to load its classes. If the tested code requires an autoloader, use spl_autoload_register() to register it.
Quick fix:
The only change required is to add spl_autoload_register('__autoload') in your bootstrap script.
Longer fix:
If you can I'd suggest you just get rid of __autoload all together and use spl_autoload_register in your application as it is the way to go with PHP 5 code. (If you only have one autoloader you can't use the autoloader of your framework and so on)
Try using spl_autoload_register instead of __autoload. spl_autoload_register allows for multipe autoloaders to work together without clobbering each other.
PHPUnit uses spl_autoload_register which turns off __autoload.
Related
I'm looking for __autoload magic method inside the YiiBase class, but it isn't. In the official docs said that public static function autoload($className) is
Class autoload loader. This method is provided to be invoked within an
__autoload() magic method.
I'm confused. What class of yii framework contains __autoload magic method? Is it possible to get an example with a component class autoloading?
Yii uses the spl_autoload_register function to set the autoloader function. The spl_autoload_register was introduced in PHP 5.1.2 and makes it possible for us to register multiple autoload functions. It effectively creates a queue of autoload functions, and runs through each of them in the order they are defined. By contrast, __autoload() may only be defined once. In Yii you can find this in YiiBase.php, just after the definition of the YiiBase class, at the very end of the file:
spl_autoload_register(array('YiiBase','autoload'));
Where autoload is a static method definied in the YiiBase class.
Using spl_autoload_register instead of the __autoload() magic function is good for us, because this way Yii's autoloader does not conflict with our own or with another thrid-party's autoloader. The YiiBase class also defines a registerAutoloader function to register another autoloader function before or after Yii's autoloader in the chain. If you take a look at its code, it is pretty straightforward what it does:
public static function registerAutoloader($callback, $append=false)
{
if($append)
{
self::$enableIncludePath=false;
spl_autoload_register($callback);
}
else
{
spl_autoload_unregister(array('YiiBase','autoload'));
spl_autoload_register($callback);
spl_autoload_register(array('YiiBase','autoload'));
}
}
It really just uses spl_autoload_register and/or spl_autoload_unregister (yes, you can also unregister an autoload function!), to put the new autoloader before or after Yii's autoloader, because spl_autoload_register always registers the new autoloader at the end of the chain.
I think that Yii tries to autoload a class whenever you reference it. Yii is simply making use of PHP's __autoload() function. It's a magic method like __get() or __set().
For example, when you write $foo = new Foo(); and class Foo has not been loaded yet, you will get an Error.
But you can prevent that if you define an __autoload($classname) function before. This way, instead of throwing an Error, PHP will first try to run the __autoload($classname) function. In that function, you can do things like: include the variable $classname and so on.
There's more information here: http://www.php.net/manual/en/function.autoload.php
I am using _autoload in my system, and I need to import a library that has alot of classes, it uses _autoload function to auto-load its classes.
unfortunately once I call this library the code is calling my classes with a wrong path.
does anyone have any idea how can i solve this issue?
As mentioned in the comments section, you could just register your autoload function especially for your namespace. Taken from the php docs, here is one example:
<?php
namespace Foobar;
class Foo {
static public function test($name) {
print '[['. $name .']]';
}
}
spl_autoload_register(__NAMESPACE__ .'\Foo::test'); // As of PHP 5.3.0
new InexistentClass;
Read up on PHP Namespaces to fully understand how this works.
I seem to have some misconceptions on how autoloading works in PHP still, one I simply cannot explain.
I have a class called glue which has a spl_autoload_register within it's main function, here called run like so:
class glue{
public static function run(){
spl_autoload_register(array('glue','autoload'));
}
}
The autoload function works by loading via the PSR-0 standard and works from absolute paths. This is all tested as working etc. Note that glue is not namespaced.
The autoload function covers a namespace called glue. Within this namespace I have a error handler called \glue\ErrorHandler.
When I trigger an error the glue class will autoload \glue\ErrorHandler by PSR-0 notation from the root directory as defined by a stored ROOT constant. This has been tested as working as well in classes such as \glue\User and \glue\Session.
So now for the problem. I cause a Call-time pass-by-reference has been deprecated error within \glue\Validation and it doesn't seem to run my autoload function.
I can see how it is going into my autoload function for everything but when I call this error in that class it just seems to skip my autoloader and bail out saying it can't find my error handler class.
Normally I would say it is something with my programming but I have tried everything. I cannot explain how, for this one error. What compounds my confusion further is that if I cause a:
syntax error, unexpected T_ISSET in /media/server_ws/xxxxxxx/glue/Validation.php on line 47
Error it works. It seems to be for that one error it just will not autoload my error handler.
I thought this might be because my spl_autoload_register is not being binded to that namespace (since the error handler that works is actually called from within glue) and some how, maybe, it is randomly working. So from \glue\Validation I called a class I have never looked at: \glue\util\Crypt but that works and goes into the autoloader correctly.
When I call this error: Call-time pass-by-reference has been deprecated from within glue class it works perfectly.
Can anyone shed some light on this?
Edit
As requested here is a brievated version of Validation.php:
namespace glue;
use glue,
\glue\Exception,
\glue\Collection;
class Validation extends \glue\Component{
private function validateRule($rule){
// This is the line, notice the pass by reference down there?
$valid = $validator($field,$field_value,$params,&$this->model) && $valid;
}
}
The Call-time pass-by-reference has been deprecated error is thrown during script compilation, and auto-loading is disabled during compilation. It's disabled because the compiler cannot start compiling multiple scripts at the same time (i.e. it is not re-entrant), and auto-loading may load some script, which may require compiling it.
Source: https://github.com/php/php-src/blob/76ad52ccc501c02aeb068d2eb4f119ef6f0c2b6a/Zend/zend_execute_API.c#L1058
Suppose I have classes in same namespaces:
directory :
(folder) a
a.php
(folder) b
b.php
c.php
and we use namespace and __autoload as you see:
in folder b\b.php :
<?php
namespace b;
use b as x;
function __autoload($clsName){
$clsName='../'.str_replace('\\', '/', $clsName).'.php';
require_once $clsName;
}
class b{
function __construct(){
print("b file<hr/>");
}
}
$t=new x\c(); // line 13
?>
and in folder b\c.php :
<?php
namespace b;
class c{
function __construct(){
print("c file<hr/>");
}
}
?>
when we define $t=new x\c, __autoload doesn't call!
please help me :(
error message:
Fatal error: Class 'b\c' not found in C:\xampp\htdocs\project\TEST\b\b.php on line 13
You have not defined autoloader. PHP looks for __autoload (or \__autoload - function defined in global namespace) while you have defined only \b\__autoload (yes, functions are namespaced to!)
How to fix it: move __autoload declaration outside namespace
Better fix: you should use spl_autoload_register
It's hard to see exactly what is going wrong. By the looks of the error, it appears that the __autoload() function isn't being called. If it was I would expect the require_once statement to fail with an error saying file not found, or something like that.
You could try by putting some debugging statements in your __autoload() function to see what's going on, including a var_dump of your $clsName variable.
It might be worth noting the following message that appears in the PHP manual regarding autoloading:
spl_autoload_register() provides a more flexible alternative for
autoloading classes. For this reason, using __autoload() is
discouraged and may be deprecated or removed in the future.
You should also note, that there is a "standard" for PHP autoloading, called PSR-0. Here is a link to an article that gives a good explanation of it.
In both the article and the PSR-0 document mentioned above, are example autoloaders that you can use. I would suggest using one of these than trying to implement your own.
I need to load a class in Symfony 1.4 but the simple placing of the file in /apps/frontend/lib seems not to be enough.
class test
{ function foo ($foo) { echo $foo; }
}
and I tried to use it in an action:
public function executeTest(sfWebRequest $request)
{
$a = new test();
$a->foo('aaa');
}
I have refreshed the cache and still:
Fatal error: Class 'test' not found in ...
Do I have to declare it somewhere?
You have to place your file in your project lib folder:
myproject/lib
[Updating]
About loading a class from a file in myproject/apps/frontend/lib/ maybe the problem is the filename where is the class.
I found in this page A Gentle Introduction to symfony - Chapter 2 - Exploring Symfony's Code that:
Symfony will then look for a MyClass definition in all files ending
with class.php in one of the project's lib/ directories. If the class
definition is found, it will be included automatically.
So if you store all your classes in lib/ directories, you don't need
to include classes anymore. That's why symfony projects usually do not
contain any include_once or require_once statements.
Contrary to the other answer, classes in /apps/frontend/lib/ absolutely should be loaded by the autoloader if you are in a frontend action.
Something else is going on here. Is your class actually named test? If you're in production, have you cleared your cache?