Please note the position of the include_once
statement in all the following examples:
This works:
include_once __DIR__ . '/vendor/autoload.php';
use Symfony\Component\HttpFoundation\Request;
$request = Request::createFromGlobals();
echo $request->getMethod();
I thought this wouldn't work, but it does:
use Symfony\Component\HttpFoundation\Request;
include_once __DIR__ . '/vendor/autoload.php';
$request = Request::createFromGlobals();
echo $request->getMethod();
But this fails:
use Symfony\Component\HttpFoundation\Request;
$request = Request::createFromGlobals();
include_once __DIR__ . '/vendor/autoload.php';
echo $request->getMethod();
So does this:
include_once __DIR__ . '/vendor/autoload.php';
$request = Request::createFromGlobals();
use Symfony\Component\HttpFoundation\Request;
echo $request->getMethod();
I am guessing as long as use
and include_once
are there in code before the class is used/instantiated, the code would work fine.
Is that right?
A detailed explanation of the flow would be much appreciated.
The execution of a PHP script happens in two phases: first the script is compiled and, if there are no syntax errors, the compiler generates a sequence of so-called "opcodes". Then, the interpreter kicks in and starts the script execution; this means it takes the opcodes one by one, interprets them and performs the operations encoded by them.
Only the main script is compiled before starting the execution, the files included (using one of the include
functions) are compiled during the execution.
Let's see what happens with your script.
The include/include_once/require/require_once
statements generate code that tells the interpreter: "stop the execution of the current script here, load and compile this file and, if it doesn't contain syntax errors, execute its code before continuing this script".
That's it, the included files are not read during the compilation phase of the main script but only when (and if) they are needed during runtime. In this fragment of code:
if (false) {
include 'a.php';
}
the content of file a.php
is never read; even more, the interpreter doesn't even care if the file exists or not. It starts caring about a.php
only when the include
statement is about to be executed but this never happens because of the if (false)
test.
The use
statement is used to create an alias. Such an alias is only an agreement between the programmer and the compiler, the compiler doesn't generate any code for it.
By using the statement:
use Symfony\Component\HttpFoundation\Request;
the programmer tells the compiler: "hey, I want you to know that later in this file when you find the token Request
I mean \Symfony\Component\HttpFoundation\Request
but I prefer to use only Request
instead because it's shorter".
The use
statements don't let any trace in the generated code. They do not exist at runtime. The code:
use Symfony\Component\HttpFoundation\Request;
$request = new Request();
is the same as:
$request = new \Symfony\Component\HttpFoundation\Request();
The same code is generated for them; during the compilation, the first form is "transformed" into the second one.
The content of file vendor/autoload.php
is (probably) the autoloader generated by Composer. An autoloader is a callable (function or class method) registered using spl_autoload_register()
that is executed by the interpreter when the code reaches a reference to an unknown class. The autoloader receives the full class name (with namespace) and its purpose is to make the class available. It usually knows where to find the class (in a file). Multiple autoloaders can be registered and the interpreter calls them one by one until the class becomes available or all of them fail.
Let's replace the alias in your variants of code and see what we get.
Variants #1 and #2 produce the same code:
include_once __DIR__ . '/vendor/autoload.php';
$request = \Symfony\Component\HttpFoundation\Request::createFromGlobals();
echo $request->getMethod();
During runtime:
include_once
statement loads and registers the autoloader;$request = \Symfony\Component\HttpFoundation\Request::createFromGlobals()
- the class \Symfony\Component\HttpFoundation\Request
is not known, the interpreter invokes the autoloader that includes the file that declares the class; then the method createFromGlobals
of the class is executed and it returns an object of type \Symfony\Component\HttpFoundation\Request
;$request
is not null
and its class implements the method getMethod()
; the method is called, the value it returns is displayed by echo
, everybody is happy.Variant #3:
$request = \Symfony\Component\HttpFoundation\Request::createFromGlobals();
include_once __DIR__ . '/vendor/autoload.php';
echo $request->getMethod();
How it runs:
$request = \Symfony\Component\HttpFoundation\Request::createFromGlobals();
- the class \Symfony\Component\HttpFoundation\Request
was not declared yet and there is no autoloader registered - the interpreter cannot find the class, there is no way to continue.
PHP Fatal error: Class 'Symfony\Component\HttpFoundation\Request' not found.
Variant #4:
include_once __DIR__ . '/vendor/autoload.php';
$request = Request::createFromGlobals();
echo $request->getMethod();
The alias is not replaced because it is declared after it is used.
During runtime:
include_once
statement loads and registers the autoloader;$request = Request::createFromGlobals();
- the class Request
was not defined yet; the interpreter calls the autoloader but the autoloader fails finding it; the interpreter cannot continue.
Fatal error: Class 'Request' not found
The use
statement doesnt invoke the autoloader, or check if the class even exists, it just pulls that "name" into the current namespace. Until you try to use it, the autoloader is not invoked, nor does PHP look for the class to see if it even exists.