This post wil help you understand the What, Why, and How of autoloading, namespaces, and the use
keyword in PHP.
If you’re already familiar with the What and Why of autoloading and want to skip right to the How, check out this post on Composer Autoloader: The Easiest Autoloading in PHP.
Before we get into PHP Namespaces, let’s look at Autoloading.
What is Autoloading?
Autoloading is a way to have PHP automatically include the PHP class files of a project.
Consider an OOP PHP project that has more than a hundred PHP classes. How might we make sure that all your classes are loaded before using them? Without autoloading, we might end up manually including every class, like this:
This is a bit tedious to say the least, and unmaintainable at worst.
What if instead, we could have PHP automatically load class files when we need it? Spoiler alert, we can, and it is called “Autoloading”.
PHP Autoloading 101
To create an autoloader only takes two steps:
- Write a function that looks for files that need to be included
- Register that function with the
spl_autoload_register()
core PHP function.
Let’s do this for the above example.
There we go. We have no longer need to manually require_once
every single class file in the project. Instead, with our autoloader, the system will automatically require the files as their classes are used. For a better understanding of what is going on here, let’s walk through the exact steps in the above code.
- We wrote a function named
my_custom_autoloader
which expects 1 parameter that we have called$class_name
. Given a class name, the function looks for a file with that name, and loads that file if found. spl_autoload_register()
is a function in PHP that expects 1 “callable” parameter. A “callable” parameter can be many things, such as a function name, class method, or even an anonymous function. In our case, we provided a function namedmy_custom_autoloader
- We then instantiate a class named
SomeClass1
without first having required its php file.
So what happens when this script is run?
- First, PHP realizes that there is not yet a class named
SomeClass1
loaded, so it begins executing registered autoloaders. - Then, PHP will execute the autoload function we wrote (named
my_custom_autoloader
), and it will pass in the stringSomeClass1
as the value for$class_name
. - Our function will define the file as
$file = __DIR__.'/includes/SomeClass1.php';
, look for its existence (file_exists()
), and willrequire_once __DIR__.'/includes/SomeClass1.php';
if found, resulting in our class’s PHP file being automatically loaded.
Huzzah! We now have a very simple autoloader that will automatically load class files as those classes are instantiated for the first time. In a decent sized project, we have just saved ourselves from writing hundreds of lines of code to include files.
What are PHP Namespaces?
Namespaces are a way to encapsulate like-functionality or properties. A very easy (and somewhat practical) way of thinking of this is like an operating system’s directory structure:
As a concrete example, the file foo.txt can exist in both directory /home/greg and in /home/other, but two copies of foo.txt cannot co-exist in the same directory.
In addition, to access the foo.txt file outside of the /home/greg directory, we must prepend the directory name to the file name using the directory separator to get /home/greg/foo.txt.
The way you define a namespace is at the top of a PHP file, using the namespace
keyword:
In the above example we have encapsulated the do_something()
function within the namespace
of Jonathan
. This implies a number of things, but most importantly it means that neither of those things will conflict with the other functions of the same name in the global scope.
For example, say we have the above code in its own file name “jonathan-stuff.php”, and in a separate file we have the following code:
See, no conflict there. We have 2 functions named do_something()
that are able to co-exist with each other. So now all we have to do is figure out how to access the namespaced function. This is done with a syntax that is very similar to a directory structure, with backslashes:
The above code is saying, “execute the function named do_something()
that resides within the Jonathan namespace”.
This also (and more commonly) is used with classes. For example:
Which can be instantiated like this:
With namespaces, very large projects can contain many classes that share the same name without any conflicts. Pretty sweet huh?
What is the point of all this? What problems do Namespaces solve?
To answer this question, we need to look back in time to a PHP without namespaces. Previous to PHP version 5.3, we could not encapsulate our classes, there was always a risk of conflicting with another class of the same name.
It was (and still is to some degree) not uncommon to prefix class names resulting in something more like this:
As you may imagine, the larger the code base, the more classes, the longer the prefixes. You shouldn’t be surprised to open an old PHP project and find a class name more than 60 characters long, like:
“Well, wait a second”, you say, “what’s the difference between writing that versus \Jonathan\SomeEntity\SomeBundle\SomeComponent\Validator
?”
That’s a great question! The answer lies in the ease of using that class more than once in a given context.
Imagine that we had to make use of a long class name multiple times within a single PHP file. Currently, we have two ways of doing this
Without namespaces:
Oof, that’s a lot of typing. Well, what about with namespaces?
… and elsewhere in the code:
Well, that certainly isn’t much better. Luckily, there is a third way, leveraging the use
keyword to pull a namespace.
Enter, the use
keyword
The use
keyword “imports” a given namespace into the current context, allowing you to make use of its contents without having to refer to its full path each time.
… now we can do this:
Aside from encapsulation, importing is the real power of namespaces.
Now that we have an idea of what both autoloading and namespaces are, let’s combine them together to create a reliable means of organizing our project files.
PSR-4: The Standard for PHP Autoloading & Namespaces
PHP Standard Recommendation (PSR) 4 is a commonly used pattern for organizing a PHP project so that the namespace for a class matches the relative file path to the file of that class.
For example, if you are working within a project that makes use of PSR-4 and you are dealing with a namespaced class named \Jonathan\SomeBundle\Validator();
, you can be sure that the file for that class can be found in this relative location in the file system: <relative root>/Jonathan/SomeBundle/Validator.php
.
Just to drive this point home, here are some more examples of where a PHP file exists for a class that is within a project making use of PSR-4:
Namespace & Class | File Location |
---|---|
\Project\Fields\Email\Validator() |
<relative root>/Project/Fields/Email/Validator.php |
\Acme\QueryBuilder\Where |
<relative root>/Acme/QueryBuilder/Where.php |
\MyFirstProject\Entity\EventEmitter |
<project root>/MyFirstProject/Entity/EventEmitter.php |
Note: this isn’t 100% accurate, as each component of a project has its own relative root, but don’t discount this information–
Knowing that PSR-4 implies the file location of a class will help you easily find any class within very large projects.
How does PSR-4 work?
Another great question! The answer to that question is simple, it’s done with an autoloader function!
Let’s take a look at one PSR-4 example autoloader function to get an idea of how it works:
Now let’s walk through what is going on here, assuming we have just instantiated the following class: new \Foo\Bar\Baz\Bug();
- PHP executes our autoloader with the
$class
parameter using a string value of$class = "\Foo\Bar\Baz\Bug"
- We use
str_replace()
to change all backslashes into forward slashes (like most directory structures use), essentially turning our namespaces into a directory path. - We look for the existence of that file in the location
<relative root>/src/Foo/Bar/Baz/Bug.php
- And finally, we load that file if it is found.
TL;DR – We changed Foo\Bar\Baz\Bug
to /src/Foo/Bar/Baz/Bug.php
and try to find that file.
Composer – PHP Package manager – Does autoloading for you
Composer is a command line PHP package manager. You may have seen a project before with a composer.json
file in its root directory. This file tells Composer about your project, including your project’s dependencies.
Example of a very simple composer.json
file:
This project is named “jonathan/example” and has 1 dependency of the Twig templating engine (at version 1.24 or higher).
With composer installed on my system I can use this json file to download my project’s dependencies, and in doing so, composer will generate an autoload.php
file that will automatically handle autoloading the classes in all your dependencies.
If I include this new file in my project, all my dependencies’ classes will be automatically be loaded as I need them in my project!
To re-iterate: Because of the PSR-4 standard and its wide-spread adoption, Composer can generate an autoloader that will automatically handle loading your dependencies as you instantiate them within your project.
Learn more about Creating your own PHP Namespaces and Autoloader with Composer.
Additional resources on used for this post:
Discussion
Muchas gracias por esta aportación. Me ha resultado muy útil y esclarecedor.
Heyo,
where you say
\”So now all we have to do is figure out how to access the namespaced variables and methods. This is done with a syntax that is very similar to a directory structure, with backslashes:\”
<?php
namespace Jonathan;
$a = \'The quick brown fox…\';
function do_something() {
echo \"this function does a thing\";
}
<?php
echo \\Jonathan\\$a;
// The quick brown fox…
From what I\'ve noticed,
echo \\Jonathan\\$a; <—- generates an error
Shouldn\'t it be:
<?php
namespace Jonathan;
const a = \'The quick brown fox…\';
This way we can access \"a\" in the namespace with that syntax:
<?php
echo \\Jonathan\\a;
// The quick brown fox…
I have found interesting comment on php.net by Lukasz Z.
\"
Well variables inside namespaces do not override others since variables are never affected by namespace but always global:
\"Although any valid PHP code can be contained within a namespace, only four types of code are affected by namespaces: classes, interfaces, functions and constants. \"
Source: \"Defining Namespaces\"
http://www.php.net/manual/en/language.namespaces.definition.php
\"
Maybe I\'m missing something? :)
Cheers,
Karol
You’re absolutely right. Thanks for pointing that out, it’s a pretty big mistake! I’ve updated the post to remove any mention of namespaced variables.