PHP spl_autoload - php

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.

Related

What's a good example for making use of an anonymous class in PHP7

As I looked for the new PHP7-features I stumbled upon anonymous classes.
I didn't understand when they should become useful, and looked for an example.
I read this article, but I don't see the benefits of this feature.
In the last section before the conclusion they wrote the following about the advantages:
One advantage is that we no longer need the named extension. Normally the named extension would be hidden away in some included file, if you ever needed to see how it is defined you have to start searching for it. With anonymous classes the definition is in the same place the object is created.
On the other hand, I see a big disadvantage because you can use this anonymous class only at the place it is defined.
Can someone please explain when this feature is useful?
Especially if it can help when building custom systems or extending a CMS like WordPress (preferably in German, although English is also welcome).
Anonymous classes could be useful in writing implementation classes for listener interfaces, so you don't need to create a file or a generic class just to implement once.
One of the most elegant things about anonymous classes is that they
allow you to define a one-shot class exactly where it is needed. In
addition, anonymous classes have a succinct syntax that reduces
clutter in your code. Java in a nutshell
So, you can have an anonymous implementation of an interface or even extend a class, with additional properties or overwritten methods.
Example:
return new class(10) extends SomeClass implements SomeInterface {
private $num;
public function __construct($num)
{
$this->num = $num;
}
};
Another situation:
Provide a simple implementation of an adapter class. An adapter class is one that defines code that is invoked by some other object. Take, for example, the list() method on a class called File. This method lists the files in a directory. Before it returns the list, though, it passes the name of each file to a FilenameFilter object you must supply. This FilenameFilter object accepts or rejects each file. When you implement the FilenameFilter interface, you are defining an adapter class for use with the $file->list() method. Since the body of such a class is typically quite short, it is easy to define an adapter class as an anonymous class.
$file = new File("/src");
// Now call the list() method with a single FilenameFilter argument
// Define and instantiate an anonymous implementation of FilenameFilter
// as part of the method invocation expression.
$filelist = $file->list(new class extends FilenameFilterClass {
public function accept(File $f, string $otherInfo) {
return pathinfo($f, PATHINFO_EXTENSION) === ".php";
}
});
Some nice basic understanding and use about anonymous classes could be found on Java (I know its not PHP, but it helps on understanding) examples at https://www.geeksforgeeks.org/anonymous-inner-class-java/
I should use a anonymous class only if this class is not used anywhere else and if these class isn't changed since the first coding.
So for example a database class could maybe a candidate for using a anonymous class because it should be use only once and could be included in any other php file. In most cases the data for accessing the database is in the config file. Therefore this file should be loaded for every php file that is using the database when you want to avoid the hard coding of the database params (which is a bad idea). If you coded these class as an anonymous class in the config file you have eliminated one include whithout losing the maintainance.

When does Yii:autoload($className) invoked?

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

Is it possible to autoload static libraries using spl_autoload_register?

I am wondering is it possible to auto load static class, as like creating object to dynamically autoload library ?
I have done most of the part of php autoloader but really need to tips to autoload static libraries for which I don't want to create object.
Is there anyone have solution ? Please post or else give me best idea to develop the same.
Thanks
Yes SPL Autoloader will load classes and interfaces. Once the autoloader is triggered you can use any reference to an auto-loadable asset to trigger the load
My_Special_Class::SOME_CONSTANT
will trigger a load as well as calling or referencing any visible static method or property.
In fact exploiting this is one way to trigger the auto_loading of namespaced functions. Define a class file like this.
namespace My\Namespace;
abstract class Functions{
const LOADED = true;
}
function func1(){}
function func2(){}
function func3(){}
And in your code when you need the functions defined in My\Namespace simply
if (\My\Namespace\Functions::LOADED){
func1();
func3();
}
The reference to the abstract class triggers the autoloader to include the file that defines the functions.

multiple spl_autoload_register

what is/are the benefit(s) of having multiple spl_autoload_register
example:
spl_autoload_register('autoload_systems');
spl_autoload_register('autoload_thirdparties');
spl_autoload_register('autoload_services');
vs:
using one
spl_autoload_register('autoload'); or __autoload();
and then do the logic inside the function.
eg:
$ftp = new systems_ftp();
$services = new services_cron_email();
If you have one __autoload() and you include a third party library which also had one, it would be clobbered.
Registering multiple autoloads with spl_autoload_register() ensures you can have your code dropped in with existing code (think libraries, etc) without clobbering or shadowing of existing/future autoloads.
In addition, the PHP manual states...
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.
I guess no need to add multiple but it's on your logic.
The way spl_autoload_register works is awesome.
Suppose one have 3rd parties directories and that is usually manages through namespace which also represents their path.
Consider this simple class as autoloader class
class MyAutoLoader {
public function __construct()
{
spl_autoload_register( array($this, 'load') );
}
function load( $class )
{
$filepath = 'classes/'.$class.'.php';
require_once( $filepath);
}
}
Then in index file include this loader file and create instance of this class:
$autoload = new MyAutoLoader();
Now you can directly create the instances of classes.
Suppose we have 'Person' class in directory classes\QMS\SERVICES\Person.php and same path is the namespace of that class
$person = new QMS\SERVICES\Person();
Now when when you will use new it will check if this class exists and value as a string will be passes to 'load' function of class 'MyAutoLoader'. and there it will get included. Or you can change the function 'load' to fix the path of your class files put your if conditions or whatever need to b done to fix the paths.

Can PHP __autoload() a class from within another __autoload() of the same class?

OK, this is a tough one... I think, and I have a feeling the answer is simply no, but in that case I would like some answers for alternatives.
I have a very complex __autoload() function in a framework which can dynamically create classes. There a three classes required in order for the dynamic creation of a class called AuthActions -- these three are: fRecordSet, RecordSet, and AuthAction (note there's no S on that one).
My autoloader will look for a static method "init" on any class it loads and try to run that. On my ActiveRecord class it attempts to use AuthActions in order to get a list of supported actions on a particular active record. AuthAction (no S) also calls AuthActions within it's init, so basically the Active Record is loaded, and tries to load AuthActions, triggering the loading of the other three, and then when it finishes loading AuthAction still within the original autoloader AuthAction tries to call AuthActions which triggers another autoload since the original one did not complete yet.
This causes the following which has some echo statements to clarify:
Attempting to load ActiveRecord
Attempting to load fActiveRecord
fActiveRecord loaded via /var/www/dotink.org/inkwelldemo/inc/lib/flourish
ActiveRecord loaded via /var/www/dotink.org/inkwelldemo/inc/lib
Attempting to load AuthActions
Attempting to load RecordSet
Attempting to load fRecordSet
fRecordSet loaded via /var/www/dotink.org/inkwelldemo/inc/lib/flourish
RecordSet loaded via /var/www/dotink.org/inkwelldemo/inc/lib
Attempting to load AuthAction
AuthAction loaded via /var/www/dotink.org/inkwelldemo/models
Fatal error: Class 'AuthActions' not found in /var/www/dotink.org/inkwelldemo/models/AuthAction.php on line 24
The problem here is that the subsequent call to __autoload('AuthActions') would succeed because the three classes it requires are now in place... but it seems to die on the premise alone that it's already trying to autoload 'AuthActions' -- this seems to be hardwrit into PHP.
In testing this I found the following will loop forever with no error:
function __autoload($class) {
__autoload($class);
}
$foo = new Bar();
While this one below will error similarly:
function __autoload($class) {
$test = new Bar();
}
$foo = new Bar();
This behavior seems inconsistent as essentially they should amount to the same thing (kinda). If PHP internally triggered autoloads acted like a user call to __autoload() I don't think I would have a problem (or if I did the then it would be a problem of it looping forever which would be a separate issue of determining why the class wasn't getting loaded to resolve the dependencies).
In short, I need a way to either recursively loop the autoloader like this based on PHP triggered autoloads... is this simply not possible? Is it a bug in my particular version possibly? It seems to be affecting 5.2.6 - 5.3.2 in my tests, so I can't imagine it's a bug overall.
Update:
The code for the init method() on AuthAction is below:
/**
* Initializes the AuthAction model
*
* #param array $config The configuration array
* #return void
*/
static public function init($config) {
// Establish permission definitions and supported actions
$every_permission = 0;
$supported_actions = array();
foreach (AuthActions::build() as $auth_action) {
$action_name = $auth_action->getName();
$action_value = intval($auth_action->getBitValue());
$every_permission = $every_permission | $action_value;
$supported_actions[] = $action_name;
define(self::makeDefinition($action_name), $action_value);
}
define('PERM_ALL', $every_permission);
}
You can see where it is calling AuthActions as a separate class, and mind you it is only because it is being loaded within the original attempt to load that it fails. I can obviously remove this code and it will work -- the original load of AuthActions will complete successfully as all the pre-requisite classes are then loaded.
That said, init() is the most appropriate place for this code, and even if I am unable to use inter-dependent classes which haven't been loaded yet within init() I would still prefer to keep that functionality. Any alternative way of implementing that functionality would be great... Ideally PHP would have events for such things where you could for example register a callback when say an "autoloaded" event is triggered. This would allow the code to run AFTER the original autoload and would resolve this seemingly meaningless restriction.
Now with that said, any suggestions how to automatically call init() on a class every time it is loaded are appreciated and welcome.
__autoload method only gets called when you are are trying to use a class/interface which hasn't been defined yet.
Therefore with your example
function __autoload($class) {
$test = new Bar();
}
$foo = new Bar();
You are attempting to load the class bar which has not been required/requiered_once therefore the class is not defined so it calls the __autoload method as a last resort then within the autoload you are again trying to load the same class which has not been defined.
The correct way to use the __autoload would be as shown in the php site.
<?php
function __autoload($class_name) {
require_once $class_name . '.php';
}
$obj = new MyClass1();
$obj2 = new MyClass2();
?>
And all your init stuff can be put in __constructor can't it?
unless i'm missing something from your question...
I think this
My autoloader will look for a static
method "init" on any class it loads
and try to run that.
is the key to your problem. Your autoloader shouldn't be running anything, it should be including (or otherwise defining) the class and nothing else.
What sort of code are you trying to run when a class is defined (ie. in your static "init" method)?

Categories