How does the PHP 'use' keyword work (in Symfony 2)? - php

In Symfony 2, all requests are getting routed through app_dev.php (or app.php).
The first few lines look like:
<?php
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Debug\Debug;
...
In theory, I get that this just imports the specified namespace (or class) to the current scope. What I don't understand is how PHP maps this to a file. For example, the Debug class is located in:
vendor/symfony/symfony/src/Symfony/Component/Debug/Debug.php
How does PHP know to look in vendor/symfony/symfony/src/?
I'm probably misunderstanding what is happening, but any clarification would be appreciated.

Knowing which file a class lives in isn't the job of the language, it's the job of the autoloader.
All the use keyword does is this instance is create an alias:
use Symfony\Component\HttpFoundation\Request;
This is saying in the following script, when I refer to Request I really mean Symfony\Component\HttpFoundation\Request. If I use Request object in some way (by either creating an instance or calling a static method on it) then the autoloader will go and try to load the file that class is defined in if it hasn't been loaded already.
The inner workings of the autoloader, though, is different from project to project. There has been a move to standardize autoloader behavior à la PSR-4, but there's nothing in the language saying that you have to adhere to that standard. You could, for instance, have a scheme where all class files reside in a single directory and your autoloader just loads all classes from there, regardless of what namespace they're in.
That scheme would suffer from the fact that you could only have a single class of every name, but there's nothing stopping you from doing it that way.
To answer your question: "How does it know that "Symfony\Component\Debug\Debug" is a valid namespace?"
It doesn't, because it's not actually going out and trying to load that class at this point. If in any random PHP script you put something like this at the top:
use \Non\Existant\ObjectClass;
But don't ever actually use ObjectClass anywhere, you will get no errors. If you try to say new ObjectClass, though, you will get a class not found error.

Simplistically speaking, you have to load all the files beforehand into memory for PHP. PHP does not inherently have any standards to where files are located, the rules for loading files has to be done by your code.
When PHP attempts to load a file, it will check its memory if the class definition exists, if not it will attempt to autoload the file that may contain your class definition; see PHP: Autoloading Classes and spl_autoload_register
Some common autoloading strategies have been defined by the PHP Framework Interop Group:
PSR-0 (Autoloading standard)
PSR-4 (Improved Autoloading standard)
In this case, autoloading is done by Composer, all you need to do is include vendor/autoload.php in any of your scripts and vendor code downloaded via composer can be autoloaded. You can manually add classes into the composer autoloader as well. See: Composer Autoloading

Related

How to define a private class path with PHP Composer?

Is it possible to define a separate PSR-0/4 path for classes/interfaces that can only be loaded by the package itself (not other packages that include it)?
The idea being it would stop other packages from mistakenly using classes that should be private to the package.
I realise that they can always include the file manually and the class loader is global. The goal is not to prevent them from using the class, but rather make it clear that they shouldn't be accessing it directly now or if the API of the package changes.
Use case:
Let say there is a Person class in the package (already released and used). When a new version of the package arrives we no longer want them to invoke new Person, but instead use a PersonFactory (as it has to setup some other stuff with the person that the caller does not need to worry about).
Yes, you could document this in the change log. However an IDE and static analysers would not be able to report on this. The bug would only be discovered when the improperly initialised Person is given to a provider and the program crashes or throws an exception at runtime.
Based on Alexander's response, this seems the most reasonable:
https://repl.it/repls/GrowlingInconsequentialFanworms
I can't see how this would be possible, because the autoloader loads them into their defined namespaces on their first usage. Once in a namespace, anything can use it by accessing that namespace.
PHP namespace to not have any way of limiting what other namespaces can access them, so your answer is probably no.
What you could do though is put your private classes in a namespace that tells developers they are private:
use yourpackage\private\SomeClass;
It won't stop them, but it could make it clear they should not do that.
I don't think it's possible. spl_autoload_register() takes only $class as argument. You can't register autoload only for a part of application. Moreover there is no way to know which class invokes or creates object of the other classes. Even if you wrote your own autoloader you won't get all information that you need.
If you look for securing your code look at PHP extensions.
Am not sure if this is what you talking about but the php keywords private and protected should help a bit when the classes and interfaces are in a namespace

Composer: Require a package with custom namespace

I've been progressively starting to use Composer on my PHP projects, however there's something that I always questioned.
For instance, let's say this is my composer.json file:
{
"require": {
"ramsey/uuid": "~2.8"
}
}
I'm telling composer I want the package ramsey/uuid and as expected it download and includes the package.
When I want to access the classes on the package, I'm forced to something like:
$uuid = \Rhumsaa\Uuid\Uuid::uuid4();
Is there a way I can require the package and force a simpler namespace like for eg. \Uuid::uuid4();, avoiding the need to write the full NS including package author?
What should I change on my composer.json to be able to do this? Thank you.
Note: I'm aware of PHP's use. I could use Rhumsaa\Uuid\Uuid;... However I need to do this in every single file, it's not practical. Even less if I'm putting together a small self-usage framework. I want for instante to have \Util\UUIDmapped to \Rhumsaa\Uuid\Uuid.
I also think the file autoload_psr4.php could be changed to accomplish this, however after an update all changes are discarded.
No, there is nothing you can do to shorten these namespaces.
Composer doesn't have to do anything with PHP namespaces. The name you use to identify the software package is unrelated to the PHP namespace. They don't have to match in any way. Composer only provides an autoloader that will include the source code of a class you are using in your code. The possible alteratives would be to either load that source code manually using include_once() or require_once(), or create an autoloader on your own that does this.
Now that Composer is out of the way, we can discuss namespaces and class names. Before PHP 5.3, there were no namespaces. Classes either were not generally used in multiple projects because they were created with short names that likely would conflict with other classes bearing the same name, or they were extended to contain a distinguishing component in their name, like Zend_Controller_Abstract or sfController (from Symfony 1).
These class names also tend to get very long, especially with the invention of PSR-0 autoloading (which was for a very long time the only PSR standard ever defined).
With namespaces you get at least a method to shorten those class name references in your code.
You have to either use the original, long form in every place you use, or you have to import it with a shorter alias. Yes, you have to repeat the import clause in every file - but you don't import EVERY class into EVERY file, you would only import the classes you actually use.
Using an IDE is very helpful with these tasks. They offer you a way to search in all available classes for the one you want to use but can't remember. They also deal with importing namespaced classes into your file. You'd rarely ever need to manually "add the imports everywhere".

Autoload with composer of classes inside a single file

I'm trying to use a library that uses namespaces but has a part of the code auto-generated so they generate more than one class in a single file.
We use composer and I tried to add the namespace define in psr-4 like this
"name\space\prefix\": "folder/where/the/file/is"
But there's only one file that contains all the classes inside the autoload doesn't find the classes as, I imagine, it searches a file with the same name as the class you are trying to load. Is there a way to make composer autoload aware of this situation and use autoload with the classes ?
You have two other options besides PSR-4 (or PSR-0):
classmap - this will scan directories or files for all classes that are contained, and the result is put into a PHP array in a file. This requires to dump the autoloader whenever there is a change being made to the files being scanned.
files - the mentioned file(s) will be included whenever the Composer autoloader is included.
So you could either add the file with the autogenerated classes to be scanned with the classmap autoloader, which would load that file on the first usage of ANY of the classes in there, or you could add it to the files autoloading, which will always be included, no matter if the classes are being used or not.
If considering performance, the first alternative is preferred unless the amount of classes is huge and the amount of code in the classes is tiny. Having plenty of tiny classes in a classmap probably is more overhead than always just loading them in the first place.
Having plenty of code in these classes, and they are not always used, the amount or memory saved by NOT always loading them might be faster.
If in doubt: Measure it. And consider to split the classes into single files and use PSR-4 if it is too much of a performance penalty.
The PSR4 spec says:
The terminating class name corresponds to a file name ending in .php. The file name MUST match the case of the terminating class name.
Since the autogenerated code isn't PSR4 compatible, it can't get autoloaded by a PSR4 autoloader. I would fix the code which generates the classes or, use require_once.. (The first is preferered.

When I auto-load a class/file with composer, what is actually happening behind the scenes?

I haven't been able to find a strait answer to this question yet elsewhere online and was wondering how exactly composer autoloading worked.
When I autoload a class using PSR-0 or classmap what is actually happening behind the scenes? Is it just calling include(or some include variant) on the the specified file in the specified path. Is it actually skimming the file for class definitions and constructing its own file to include? Is it doing something that isn't analogous to a file include?
Thanks in advance!
A PSR-0 autoloader is simply a function attached to the global PHP process with spl_autoload_register(). That registered function is called whenever PHP needs to instantiate a class that isn't yet known, so this is the last moment to make the classes code known before PHP fails.
And the implementation of that autoloading can be either pretty sophisticated, or pretty simple, but in every case it will use either include() or require() (possibly with _once, but this is not really needed) to make the class code known to PHP. You could also implement a call to eval() to dynamically add some code that declares the class needed, but this would just be for academic used - I haven't seen it being used in real cases.
The same applies to the classmap loading. The classmap array contains names of classes as keys, and the filename of the containing file as value. This is for cases where there is no PSR-0-compatible ruleset mapping between class name and file path.
If you want more details of how Composer does the autoloading, you should have a look at the generated files inside vendor/composer. Basic knowledge about how PHP autoloading works in general would help understand what happens there.
Behind the scenes composer use spl_autoload_register to register an autoloader function which include your class.
The registered function follows a standardized namespace/path resolution algorithm (basically consider all "\" or "_" in your class name as path separators from a specified base directory) to find the php file to include.
Also, when you run composer install it create a cached index of relation between paths and namespace to speed up the path resolution.
You can dig in the Github repository and see it for yourself.

PHP 5.4 vs 5.3: Autoloading multiple classes in the same file

Firstly, this is a bit of a long one, so thank you for reading.
My issue is similar to this one:
Class not found in the same file
I have a custom-built framework originally written in 2008 for PHP 5 and it's been upgraded over the years to work with PHP 5.3. I've been looking at 5.4 compatibility and have hit a serious issue.
The ORM layer automatically generates classes for each DB table. These classes all sit in one file per table and our autoloader loads that file when required.
For example, a 'customer' table in the 'public' schema (postgresql) would have the following classes:
PublicCustomer, PublicCustomerDBReader, PublicCustomerDBWriter.
Now this may not be the ideal set up, but it is what we currently have.
In PHP 5.3, if PublicCustomer was required, the file would be included, parsed and all of the above classes would become available. So if, for example, a static method is called on PublicCustomer, and that method calls something in PublicCustomerDBReader, that would work fine, since that class is in the same file.
In PHP 5.4, it looks like some optimisations have been done in the core. In the above scenario:
A static method gets called in PublicCustomer.
The autoloader finds and loads the correct file.
The PHP parser only parses up to where it needs; the PublicCustomer class. It has not parsed or instantiated the PublicCustomerDBReader class. I can confirm this by testing if the class exists and by seeing if the parser reaches the end of the file when it gets included, when the method is called (it doesn't).
The method in PublicCustomer then tries to call a method in PublicCustomerDBReader. This fails, since our autoloader has already required the file once.
It seems to me that I have two solutions:
Separate these classes out so that there is one file for each (this will produce a huge number of files)
Redesign the ORM layer so that multiple classes are not required.
Have I understood the issue above properly?
Does anyone know if an optimisation or change was made in PHP 5.4 that would cause this behaviour?
Are there any other potential solutions to the problem that I have not considered?
Place the reader/writer classes at the head of the file. Also you might consider filing a bug report, since the parser should only halt on errors.
Referring to the PHP Framework Interop Group and their autoloading standards PSR-0 and PSR-4 each class must have its own file with a name ending in .php. Even if you have thousands of classes, this should't be a problem for the file system.
With more than one class in a file you have to consider following aspects:
For each class the autoloader must decide which file to load. If you have multiple classes in a file, each used class causes a load. Class files should be loaded once and not more, because classes cannot be redeclared. I do not recommend it, but you could handle this with your own autoloader, which remembers loaded files or tests for loaded classes.
Classes placed within the same file and using each other is problematic. The problem is described in Derived class defined later in the same file “does not exist”? and answered by Jon.

Categories