PHP namespaces, requires and relative paths issue [duplicate] - php

I am learning advanced PHP standards and trying to implement new and useful methods. Earlier I was using __autoload just to escape including multiple files on each page, but recently I have seen a tip on __autoload manual
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.
but I really can't figure out how to implement spl_autoload and spl_autoload_register

spl_autoload_register() allows you to register multiple functions (or static methods from your own Autoload class) that PHP will put into a stack/queue and call sequentially when a "new Class" is declared.
So for example:
spl_autoload_register('myAutoloader');
function myAutoloader($className)
{
$path = '/path/to/class/';
include $path.$className.'.php';
}
//-------------------------------------
$myClass = new MyClass();
In the example above, "MyClass" is the name of the class that you are trying to instantiate, PHP passes this name as a string to spl_autoload_register(), which allows you to pick up the variable and use it to "include" the appropriate class/file. As a result you don't specifically need to include that class via an include/require statement...
Just simply call the class you want to instantiate like in the example above, and since you registered a function (via spl_autoload_register()) of your own that will figure out where all your class are located, PHP will use that function.
The benefit of using spl_autoload_register() is that unlike __autoload() you don't need to implement an autoload function in every file that you create. spl_autoload_register() also allows you to register multiple autoload functions to speed up autoloading and make it even easier.
Example:
spl_autoload_register('MyAutoloader::ClassLoader');
spl_autoload_register('MyAutoloader::LibraryLoader');
spl_autoload_register('MyAutoloader::HelperLoader');
spl_autoload_register('MyAutoloader::DatabaseLoader');
class MyAutoloader
{
public static function ClassLoader($className)
{
//your loading logic here
}
public static function LibraryLoader($className)
{
//your loading logic here
}
With regards to spl_autoload, the manual states:
This function is intended to be used as a default implementation for __autoload(). If nothing else is specified and spl_autoload_register() is called without any parameters then this functions will be used for any later call to __autoload().
In more practical terms, if all your files are located in a single directory and your application uses not only .php files, but custom configuration files with .inc extensions for example, then one strategy you could use would be to add your directory containing all files to PHP's include path (via set_include_path()).
And since you require your configuration files as well, you would use spl_autoload_extensions() to list the extensions that you want PHP to look for.
Example:
set_include_path(get_include_path().PATH_SEPARATOR.'path/to/my/directory/');
spl_autoload_extensions('.php, .inc');
spl_autoload_register();
Since spl_autoload is the default implementation of the __autoload() magic method, PHP will call spl_autoload when you try and instantiate a new class.

Since PHP 5.3, you can use spl_autoload_register() with namespaces, which means that you can organize your project and autoload your php classes without any require or include and without redefining an __autoload() function.
To demonstrate this behaviour, just create a file called index.php :
<?php
spl_autoload_register();
var_dump(new Main\Application);
Then create a folder named Main located right next to the index.php file. Finally, creates a file called Application.php located into Main and paste the following code into it :
<?php namespace Main;
class Application{}

Here is the way I do use Autoload.
In the given example I wanto to load classes form 3 diferent directories.
function namespaceAutoload($rawClass){
$class = str_replace('\\', DIRECTORY_SEPARATOR, $rawClass);
$possiblePaths[] = '..\sys\class\file.php';
$possiblePaths[] = '..\sys\class\lib\file.php';
$possiblePaths[] = '..\sys\class\class.file.inc.php';
foreach ($possiblePaths as $templatePath) {
$path = str_replace(["\\", "file"], [DIRECTORY_SEPARATOR, $class], $templatePath);
if (file_exists($path)) {
require_once "$path";
break;
}
} spl_autoload_register("namespaceAutoload");
I the given example, the PHP will look for the namespace\class in these three directories using these three different filename formats.

Working with OOP in php,
spl_autoload_register() lets you register multiple function, classes, trait etc to your php script.
Heres a use case on /test folder structure
/.htaccess
/index.php
/autoload.php
/controller/test.php
Inside autoload.php file:
spl_autoload_register(function ($classname) {
include_once dirname(__FILE__) . "/" . str_replace("\\", "/", $classname) . '.php';
});
Inside .htaccess
DirectoryIndex index.php
# php_value auto_prepend_file
php_value auto_prepend_file "/xampp/htdocs/test/autoload.php"
Inside controller/test.php
namespace controller;
class test {
public static function sayHello () {
echo "hello";
}
}
Inside index.php
use controller/test;
echo test::sayHello();
The autoload.php can be included or like the example above prepend on all script. It loads the class test and lets you use it directly without having to include the test.php script by pathname.

Related

How to check if Autoload loads only used classes?

Uisng spl_autoload_register for auto loading classes on my code. Example:
<?php
spl_autoload_register(function ($class_name) {
include 'my_class1.php';
include 'my_class2.php';
//...
include 'my_classN.php';
});
$obj = new MyClass1();
?>
What if I used just class MyClass1 in my code, does autoload loads all files or just my_class1.php?
Thanks in advance.
EDIT: DO NOT use above code. Now I using #Alex Howansky's code with PSR-4 autoloading specifications.
NOTE: Autoload requires to use namespaces if classes located in subdirectory relatively to basedir (see examples).
With this code, the first time you refer to any class, it will load every class. This will work, but almost certainly isn't what you want. In an autoloader, you usually just want to load only the one source file containing the one class referenced by $class_name. Something like this:
spl_autoload_register(function ($class_name) {
$filename = '/path/to/classes/' . $class_name . '.php';
require_once $filename;
});
This obviously becomes very difficult if your source file names don't match your class names or you otherwise can't determine the source file names based on the class names. This is why you should use PSR-4 naming conventions.
Just as an addition, you can use the PHP function get_declared_classes() which will list all defined classes in the current script.

What does everyone mean by: 'spl:_autoload() is default implementation of __autoload()'

I got confused with one thing about php autoloading stuff: the spl_autoload() function. In every answer I found that this function is default implementation of __autoload. Shouldn't PHP define default implementation of __autoload() in itself and then if I explicitly create __autoload() it will just override it?
If i don't explicitly define __autoload() function in my php file, will be there the default implementation? Is spl_autoload() some kind of internal function, if yes, why is it in php doc then?
(If it's not an internal function)In every spl_autoload() example there isn't any call to this function, only spl_autoload_register with no parameters, spl_autoload_extensions and so on. why so? What am I missing?
Quoting from: What is Autoloading; How do you use spl_autoload, __autoload and spl_autoload_register?
set_include_path(get_include_path().PATH_SEPARATOR.'path/to/my/directory/');
spl_autoload_extensions('.php, .inc');
spl_autoload_register();
Since spl_autoload is the default implementation of the __autoload()
magic method, PHP will call spl_autoload when you try and instantiate
a new class.
So if I won't call spl_autoload_register(), it won't register the default implementation? Does the spl_autoload look into extensions set by spl_autoload_extensions(); and then import all files with these extensions from include path? Repeating question mentioned earlier: is spl_autoload() internal function?
I know that __autoload() is deprecated and I should use spl_autoload_register(). I just want to make sure that I know how it all works.
Thanks.
Repeating question mentioned earlier: is spl_autoload() internal function?
That's just the "default value" for spl_autoload_register if you don't pass an parameter. You could also call this function standalone, if you want to. The behaviour of spl_autoload could be configured with spl_autoload_extensions and set_include_path.
Internally spl_autoload takes the full qualified class name(fqcn) as the path to look for a class implementation. (Maybe with string replacements for directory seperators). Afterwards, it searches in every element of the class_include_path after the given file.
spl_autoload_extensions(".php");
spl_autoload_register();
$foo = new \foo\Bar();
// now spl_autoload tries to load the file foo/Bar.php inside your class path.
If you need something more complicated, you have to create your own callback for the autoloader. I.e. something like this
spl_autoload_register(function($class) {
$path = 'classes' . DIRECTORY_SEPERATOR;
// dont care about case
$class = strtolower($class);
// replace _ with DIRECTORY_SEPERATOR
$name = str_replace('_', DIRECTORY_SEPERATOR, $class);
// don't care about windows/unix
$name = str_replace('/', DIRECTORY_SEPERATOR, $name);
$file = $path . $name . '.php';
if (file_exists($file)) {
include ($file);
}
});
Note: The example above doesn't care about the value of spl_autoload_extensions or set_include_path.

inheritance and external files. How do I go about it?

I have 3 files.
vehicleClass.php
motorbikeClass.php (extends vehicleClass)
index.php
My question is... How do I connect all 3. On my index page, do I have to have
include 'classes/vehicleClass.php';
Do I need to have an include for the motorbike class or will the extend (inheritence) cover this?
You can let php autoload your files, by registering your own autoload function. If you have, in example, all your class files in the directory DOCROOT . 'classes' (DOCROOT being your document root), you can use a function like this (example):
function class_loader($class) {
include DOCROOT . 'classes/' . $class . '.class.php';
}
spl_autoload_register('class_loader');
Now if you try to create an object of class 'foo', it will try to include the file DOCROOT . '/classes/foo.class.php' before creating the object. You might want to extend the function a bit, eg. to lowercase file names (include DOCROOT . 'classes/'. strtolower($class) .'.class.php';). This way you can place class Foo in foo.class.php.
Short answer: a class that is extending (or otherwise using) another class must already have defined the parent class before the definition of the child class. Your assumption is correct, your VehicleClass must be included (or better, require'd) prior to your definition of MotorBike class.
However, most frameworks don't go about and include every depedency before all class definitions. This would become unwieldy on any system that has any amount of complexity to it. Instead, the developers of PHP have provided methods for autoloading classes. Using spl_autoload_register will allow you to write a function that will attempt to load in the source file for a given class whenever it is referenced but a definition for it has not yet been found.
Furthermore, once you get a system together that becomes complex, you don't want to store all of your files in a single place. Most frameworks leverage the filesystem and namespaces to help better organize all of their classes. Because of this, the PSR-0 standard was developed in order to help facilitate autoloading between frameworks. Take a look at this question for examples of PSR-0 compliant autoloaders.
Example of PSR-0 compliant class:
<?php namespace Vendor\Package;
class ClassName { }
This file would live in the filesystem at /Vendor/Package/ClassName.php
What you have to do is include 2 files in the index.php.
For example, your index.php page could be something like this.
<?php
require 'classes/vehicleClass.php';
require 'classes/motorbikeClass.php';
// Assuming your class name is MotorBike
$motorBike = new MotorBike();
// And just call the method you want, for example If you have a method called bikeName
echo $motorBike->bikeName();
?>
I hope you get an idea now.
P/S: I prefer require over include. :) Include() should work fine too.

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.

How do I allow autoloading of custom libraries in CakePHP?

Hi I am using CakePHP and there are some Vendor files I need to add to the autoloading. Is this the correct method:
In the bootstrap.php, I have the following code:
function __autoload($className){
$classFile = '../vendors/phprtf/' . str_replace('_', '/', $className) . '.php';
// check if file exists
if(file_exists($classFile)) {
require $classFile;
}
}
The PHPRTFLite has a lot of class files under various sub directories. So listing all of them is not a good option.
It seems to be working for now.
You should use spl_autoload_register() for the flexibility it offers.
If your code has an existing __autoload function then this function
must be explicitly registered on the __autoload stack. This is because
spl_autoload_register() will effectively replace the engine cache for
the __autoload function by either spl_autoload() or
spl_autoload_call().
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. By
contrast, __autoload() may only be defined once.

Categories