I may generally misunderstand something about setting up a custom classloader.
What happens is that once i initialize it:
use Doctrine\Common\ClassLoader;
require_once(DOCTRINE_PATH . '/Common/ClassLoader.php');
classLoader=new ClassLoader('Doctrine', DOCTRINE_PATH);
classLoader->register();
My previously defined function
function __autoload(){}
No longer works.
What am i missing here?
After diving a bit deeper into the autoload documentation, i found that in order to have multiple autoloaders at the same time, the use of
function __autoload(){}
is not a valid route to take. Instead, one has to define a custom autoload function, such as
function MyAutoLoader()
and then use
spl_autoload_register('MyAutoLoader');
to register it onto the autoload stack.
Finally, using this method, my autoloader is no longer overwritten by implementing the Doctrine classloader.
Related
In https://stackoverflow.com/a/3313137/569976 ircmaxell says the following:
I use a public static method init() on my static classes that require initialization (or at least need to execute some code). Then, in my autoloader, when it loads a class it checks is_callable($class, 'init'). If it is, it calls that method. Quick, simple and effective...
That post, however, is from 2010 and a lot has changed since then. My question is... does Composer support anything like this?
Composer's autoloader isn't going to automatically call init methods for you, but that doesn't stop you from using your own autoloader instead.
https://getcomposer.org/doc/01-basic-usage.md#autoloading
Note: Composer provides its own autoloader. If you don't want to use that one, you can just include vendor/composer/autoload_*.php files, which return associative arrays allowing you to configure your own autoloader.
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
When you manually include a PHP class you can do it while the current script is running, right? Then you can decide, if a condition matches you load it and if it doesn't you don't. Like this:
if ( $_SERVER['REQUEST_METHOD'] === 'POST' ) {
include '../../../Whatever/SanitizeUserInput.class.php';
SanitizeUserInput::sanitize($_POST['someFieldName']);
}
But let's say I use the autoload feature with this class. Will it be effectively loaded at the beginning or will it be loaded only if it's used?
I mean, should I add the __autoload function only in classes that I'm 100% sure I'm going to use in any script (e.g. database connection, session management, etc.)?
Thanks
Autoload is called only when you are trying to access desired class. And it would be better to use spl_autoload_register() instead of __autoload
Documentation:
You may define an __autoload() function which is automatically called
in case you are trying to use a class/interface which hasn't been
defined yet.
and
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.
Autoloading kicks in when you are trying to use a class that has not yet been loaded:
include 'foo.php';
new Foo; // autoload not used, because the class already exists
// Bar is not yet loaded here, auto or otherwise
new Bar; // Bar is being autoloaded, because it was not yet loaded
As such, autoloading can be very efficient. It's slightly less efficient than loading classes by hand at the time when you need them, because of the overhead of invoking the autoload function. But keeping track of loaded classes by hand is more work for very little return over autoload.
Try it out, you will see, that whenever PHP stumbles over a class it doesn't know yet, it will call your autoload function. When your autoload function tells PHP in which file the class is, it will load this file.
To make the answer short, PHP only loads the file when needed, that is true even for conditions, so following test class will never be loaded.
if (false)
{
$test = new CTest(); // never loaded with autoload.
}
i dont really get the docs for spl_autoload
bool spl_autoload_register ([ callback $autoload_function ] )
from my understanding, it will try to run functions registered when php comes across a class not loaded already. so example,
public function autoload() {
require ('nonLoadedClass.php');
}
spl_autoload_register(autoload);
$x = new nonLoadedClass();
will cause the require to run? so i can also register many autoload functions?
public function autoloadXXX() {...}
public function autoloadYYY() {...}
public function autoloadZZZ() {...}
spl_autoload_register('autoloadXXX');
spl_autoload_register('autoloadYYY');
spl_autoload_register('autoloadZZZ');
in the case of doctrine,
require_once(dirname(__FILE__) . '/lib/vendor/doctrine/Doctrine.php');
spl_autoload_register(array('Doctrine', 'autoload'));
an array is passed, so i guess it will try to run the autoload function within the Doctrine class (which was required)?
spl_autoloader_register registers a callback function/method, that will be called when your code is trying to use a not-known class.
A callback function can be described in several ways :
a simple function name : 'my_function'
a static method of a class : array('MyClass', 'myMethod')
a method of an instance of a class : array($myObject, 'myMethod')
In the case of Doctrine, it seems to be the second solution : if a class that PHP doesn't know is used, the autoloader will call Doctrine::autoload, passing it the class name as a parameter.
so i can also register many autoload
functions?
Yes, you can, with spl_autoload_register :
If there must be multiple autoload
functions, spl_autoload_register()
allows for this. It effectively
creates a queue of autoload functions,
and runs through each of them in the
order they are defined.
But we don't generally define one autoloading function/method for each class we have ; that would be quite inefficient, I suppose.
Instead, we use the class-name, which is received by the autoloading function, to decide which file should be included.
For instance, an autoload function might look like this :
function my_autoloader($className)
{
require LIBRARY_PATH . '/' . $className . '.php';
}
The trick being that if your files are named after the classes they contain, it immediatly becomes much easier ;-)
This is why classes are often named using the PEAR convention : My_Class_Name maps to the file My/Class/Name.php
Your entire statement is correct.
spl_autoload_register allows you to register multiple autoload functions. In the case that you try to create or use an object of a class that has not been loaded into the PHP environment, PHP will run all your autoload functions looking for that class.
The Doctrine example you give is using what is called a callback to register a method inside of a class. A callback is simply an array containing the class name (for static methods) or an instance of that class (for non-static methods), and the method name.
What say is correct. It's called when an unknown class is instantiated. You then get the chance to include/require the necessary file.
When the function resides in a class, you have to pass in an array. In this case it's a static function because they pass in the Doctrine class instead of an instance of the Doctrine class.
How can I disable the Zend_Loader_Autoloader?
You could manually force the Autoloader to unload, but this may lead to trouble with components depending on it being registered: make sure your other loader covers that.
spl_autoload_unregister(array('Zend_Loader_Autoloader','autoload'));
I stripped this from the constructor of Zend_Loader_Autoloader, and changed it to work outside of the class, and to unregister instead of register the loader.
If you're using the Zend_Application, in your index.php, after creating the instance of ZA, you can get/set the autoloader you want ZF to use:
$app = new Zend_Application(APPLICATION_ENV, APPLICATION_PATH . '/configs/config.ini');
$app->getAutoloader()->setDefaultAutoloader('yourAutoloader');
$app->bootstrap()->run();
HTH
I assume you're using Zend_Application, which automatically sets up the PHP environment, autoloading and bootstrapping. It's very handy. Sadly, setting up Zend_Autoloader is hard coded into the constructor, and I can't see any way to override it:
public function __construct($environment, $options = null)
{
$this->_environment = (string) $environment;
require_once 'Zend/Loader/Autoloader.php';
$this->_autoloader = Zend_Loader_Autoloader::getInstance();
//snip
}
My first suggestion would be to find a way to make Zend_Autoloader and your other autoloader work in harmony. I've been using Zend_Autoloader with the new Doctrine::IsolatedClassLoader with no problems. The advice about being explicit about autoloader namespaces or using pushAutoloader() is valid, and should work.
However, if that is not an option, you should probably abandon using Zend_Application, and handle environment setup and bootstrapping yourself. While an inconvenience, it's shouldn't be too difficult. Most ZF tutorials prior to version 1.8 (which is when Zend_Application was introduced) provided examples.
Here's a (now outdated) set of slides detailing some of this:
Getting Started With Zend Framework for v1.6
What about in your bootstrap.php
protected function _initAutoloader()
{
$this->getApplication()
->getAutoLoader()
->unregisterNamespace("Zend");
// or
$this->getApplication()
->getAutoloader()
->removeAutoloader();
}
Zend_Loader::registerAutoload('Zend_Loader', false);
Its in the framework documentation http://framework.zend.com/manual/en/zend.loader.html#zend.loader.load.autoload
However, I don't think you should have any problems leaving the zend autoloader enabled as long as you register your autoload callback using spl_autoload_register() so it gets added to the autoload stack.
Using spl_autoload_register, all callbacks will be called to try to satisfy the request. I don't know if the chain will be interrupted once a callback is successful. If it does then its probably controlled by returning true or false in the callbacks.
I'd suggest using "push autoloader" method of Autoloader, which can recieve other autoloader instance and call it when Namespaces match. Therefore you can use your autoloader along with the zend's one...