How to check if Autoload loads only used classes? - php

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.

Related

PHP namespaces, requires and relative paths issue [duplicate]

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.

Autoload namespaces based on directory structure

According to the top comment on the PHP page spl_autoload_register( ) :
Good news for PHP 5.3 users with namespaced classes:
When you create a subfolder structure matching the namespaces of the >containing classes, you will never even have to define an autoloader.
<?php
spl_autoload_extensions(".php"); // comma-separated list
spl_autoload_register();
?>
However, when I have the following structure:
* classes/someclass.php
* index.php
Where someclass.php contains the following:
<?php
class someclass {
function __construct( ) {
echo 'It works!';
}
}
?>
and index.php contains:
<?php
spl_autoload_extensions(".php");
spl_autoload_register();
new classes\someclass;
?>
Then I get the following error:
Fatal error: spl_autoload(): Class classes\someclass could not be
loaded
Am I getting this wrong? How can I make this work?
From the comments
This doesn't work either for the class:
<?php
namespace classes;
class someclass {
function __construct( ) {
echo 'It works!';
}
}
?>
In your someclass.php file you must define the namespace at the begginning.
<?php
namespace classes;
TLDR; It works, but:
namespace classes; is missing in classes/someclass.php
set_include_path(__DIR__); is missing in index.php
(spl_autoload_extensions(".php") is not necessary)
Include Path
The SPL autoload implementation is include path based. Using a dot as include path is relative to the working directory (!) which is independent from script file location. __DIR__ names the exact directory that is needed if the classes folder lies next to the index.php file as in the scenario.
Directory Separator mapping
Next the autoloader implementation does map the class namespace separator properly on Unix systems. In case that got lost in the o/p, the PHP source code clearly has this.
Case Sensitivity of File Names
What the source code also shows is that file-names to load are made lowercase. That is, if your file system is case sensitive, the file and directory names must be in lower case.
Refernces:
https://lxr.room11.org/xref/php-src%407.1/ext/spl/php_spl.c#spl_autoload
https://lxr.room11.org/xref/php-src%407.1/ext/spl/php_spl.c#307
PHP - most lightweight psr-0 compliant autoloader
The user, SedaSoft, who had posted the Good news comment you which you refer, had subsequently posted another comment, due to a revision of ideas:
What I said here previously is only true on Windows. The built-in
default autoloader that is registered when you call
spl_autoload_register() without any arguments simply adds the
qualified class name plus the registered file extension (.php) to each
of the include paths and tries to include that file.
Example (on Windows):
include paths:
- "."
- "d:/projects/phplib"
qualified class name to load:
network\http\rest\Resource
Here's what happens:
PHP tries to load
'.\\network\\http\\rest\\Resource.php'
-> file not found
PHP tries to load
'd:/projects/phplib\\network\\http\\rest\\Resource.php'
-> file found and included
Note the slashes and backslashes in the file path. On Windows this
works perfectly, but on a Linux machine, the backslashes won't work
and additionally the file names are case-sensitive.
That's why on Linux the quick-and-easy way would be to convert these
qualified class names to slashes and to lowercase and pass them to the
built-in autoloader like so:
<?php
spl_autoload_register(
function ($pClassName) {
spl_autoload(strtolower(str_replace("\\", "/", $pClassName)));
}
);
?>
But this means, you have to save all your classes with lowercase file
names. Otherwise, if you omit the strtolower call, you have to use the
class names exactly as specified by the file name, which can be
annoying for class names that are defined with non-straightforward
case like e. g. XMLHttpRequest.
I prefer the lowercase approach, because it is easier to use and the
file name conversion can be done automatically on deploying.
Note that this commenter had actually posted an answer here on SO today, containing the above links (along with a short explantation), but it was subsequently deleted, whilst in the review queue - presumably due to its brevity. I have reinstated the commenter's answer, along with the content of the link. Their answer was as follows:
I wrote that comment on php.net some time ago when I was working on a
Windows system. Later, I had to partially revoke what I wrote in that
comment on the very same page in another comment, which also contains
a possible solution to the problem that is the easiest I could think
of (apart from using Composer).
Here is a screenshot of the original answer:
Do not roll your own autoloading, but use composer instead.
Create a composer.json in the root of your project:
{
"autoload": {
"psr-4": {
"classes\\": "classes/"
}
}
}
Install composer, then run
composer dump-autoload
In your index.php, require the autoloader:
require __DIR__ . '/vendor/autoload.php';
For reference, see
https://getcomposer.org/doc/00-intro.md#installation-linux-unix-osx
https://getcomposer.org/doc/01-basic-usage.md#autoloading
http://www.php-fig.org/psr/psr-0/
http://www.php-fig.org/psr/psr-4/
Do not use the spl_autoload_extensions () function if the files to load only have the php extension.
In my case, I create a class called autoload .. something similar to this:
<?php
class Autoload
{
private static $_extensions = array(
0 => '.inc',
1 => '.lib.php',
2 => '.class.php',
);
public function __construct()
{
self::set_include_path();
spl_autoload_extensions(implode(',', self::$_extensions));
spl_autoload_register(__CLASS__, 'load_class');
}
public static function set_include_paths()
{
set_include_path(implode(PATH_SEPARATOR, array(
realpath('classes');
realpath('system');
...
get_include_path();,
)));
}
public function load_class($class)
{
if (!empty($class)) {
spl_autoload($class);
}
return false;
}
}
The use of ?> At the end of the file is not necessary.

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.

Autoloader appears to be omniscient | Nested includes not needed

I use this simple autoloader code:
function __autoload( $class_name ) { include 'class.' . $class_name . '.php'; }
and I make this call new SDB();
SDB actually inherits from SDBOne which I never include...yet the auto-loader loads it.
This means it knows to load modules nested / included in other modules.
So from the looks of it I never need to require or include anything if I use the auto-loader.
However, if I use a stand alone module where there is no auto-loader, I then need to include the class it inherits from.
Should I use
require,
require_once,
include,
include_once.
I would guess to go with require_once because I want an error not a warning...plus when I use the code with the autoloader I don't want it loaded twice.
Basically I have 2 types of use for SDB : one with the autoloader present and one with out.
Reference
http://php.net/manual/en/language.oop5.autoload.php
You are correct, require_once would be the correct way to include a parent class or dependent class in one file. That way if its included multiple times, the require_once prevents errors that would arise from redeclaring a class.
The autoloader is autoloading SDBOne automatically because it needs that class defined before it can extend SDB from it. The same thing is happening to autoload the parent class on demand as happens when you try to load the inherited class.
Also, you should consider switching to spl_autoload_register so that your code will work well with other code that may use an autoloader. The SPL autoloader supports multiple autoloaders and creates a stack in the order they are registered. This way if the first autoloader doesn't satisfy the requirement, it keeps trying subsequently registered autoloaders until the class is loaded or cannot be found.
Another note on preventing errors, you may want to change your autoload function as follows:
function __autoload( $class_name ) {
$file = 'class.' . $class_name . '.php';
if (file_exists($file)) {
include $file;
}
}
Because if the class being autoloaded doesn't exist, you will get errors about including a non-existent file. This is especially important when using spl_autoload_register; you don't want your autoloader to emit unnecessary warnings about missing files if another autoloader will be ultimately responsible for loading the class in question.
Hope that helps.

Why namespace is passed to spl_autoload_register and how can I get rid of it?

I'm currently doing this but it doesn't look right to me:
spl_autoload_register(function ($class_name){
$class_name = str_replace('MyNameSpace\\', '', $class_name . '.php');
require $class_name;
});
Please advise.
The namespace is passed because it has to be. How else would the autoloader function know the difference between Foo\Bar and Baz\Bar? :-)
Your method looks okay if you're absolutely sure that you won't ever need to load classes with the same names as those found in MyNameSpace. The canonical method to autoloading classes involves using the parts of the namespace as file system structure, so that, for example, foo\bar\Baz can be found at path foo/bar/Baz.php.

Categories