Difference between Composer autload classmap and iclude-path - php

In composer documentation section about autoloading I found at least two means to load classes that not corresponds to psr-0/4. First one is to specify classmap property of composer.json file and second one is fill include-path property in my composer.json.
As I can see include-path is more plain feature while classmap caused scanning for classes in specified locations. Who can explain which one should I use in different cases?

Avoid using include-path. It is meant for old software that expects the include_path setting to contain some directories, so that require_once "Relative/Path/To/Class.php" works from ANY location (think of the way PEAR works). Using too many paths impacts performance, because PHP needs to scan starting from the first directory, until it finds the relative path requested.
Classmaps are an always working solution if the classes do not conform to PSR-0 or PSR-4. Autoloading works by knowing the name of the class and finding out the file this class is contained in. PSR-0/4 define a way to know the file name by using and splitting the class name. Classmaps however know every class name and their file name directly. The bad thing about classmaps is that if they grow too large, they also affect performance because loading a huge classmap and then only using about 1% of the contained classes has a big overhead.
include-path and classmap are not mutually exclusive. In fact, they might be both needed: To load the first class, you'd need the classmap (otherwise you'd be forced to explicitly use require_once), and if that file will load dependencies using relative paths inside require_once (and does not know about autoloading), a proper include path has to be set.
If you ever have the chance to change it, I highly recommend to avoid setting the include path, and only use the classmap feature to autoload classes (which means you should remove any include/require functions in that code base). It would be even better if your code can be transformed to be PSR-0 compliant, but this usually is a huge rewriting task for old code with barely any benefit. You'd have to worry about performance only if you really have a HUGE framework with many files and classes - which usually is not the case with older code bases.

Related

Converting to namespaces in PHP

I am currently working as a student on php project which has grown since the beginning of time and has about 1800 php-files.
The problem is: it is completely without namespaces, or any of the PSR-4, etc. recommendations. The technical debt is strong with this one :).
We want to use composer (and twig and some libraries more) and having problems including this (especially composer). I think it's because of the overwrite of __autoload() via spl_autoload_register() in the composer-autoloader?
Is there a good and fast way to start integrating namespaces without rewriting the whole project?
You can still use Composer with PSR-0 or classmap.
I'd probably go with the classmap first. Advantages: Can deal with multiple classes per file. Can deal with arbitrary file structures.
Once you achieved using Composer's autoloading, you can start removing either the existing autoloader or those require_once/include_once that are likely spread all over the place.
Once you've got rid of all that file loading legacy and have established Composer autoloading, you can try to organize the code according to PSR-0. This will probably require you to rename files and reposition them. It might also be the case that the classes don't have any identifiable prefixes - which is bad for your PSR-0 autoloading because all these files would belong into one single folder.
Note that until this point you haven't changed the name of any class, so the code should run without any changes.
Using namespaces does not have a very noticeable advantage. You'd be forced to rename all classes, rename all usages of that class name, and all in all it won't provide any noticeable benefit if used alone.
On the other hand, you sound like you do want to refactor everything else as well, so switching to namespaces can be used as a signal for "newer" code.
You can use PSR-4 and PSR-0 at the same time, so it won't affect your renaming of classes (besides the necessary class name changes in all the places).

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.

Import package or autoloading for PHP?

What solution would you recommend for including files in a PHP project?
There aren't manual calls of require/include functions - everything loads through autoload functions
Package importing, when needed.
Here is the package importing API:
import('util.html.HTMLParser');
import('template.arras.*');
In this function declaration you can explode the string with dots (package hierarchy delimeter), looping through files in particular package (folder) to include just one of them or all of them if the asterisk symbol is found at the end of the string, e.g. ('template.arras.*').
One of the benefits I can see in package importing method, is that it can force you to use better object decomposition and class grouping.
One of the drawbacks I can see in autoload method - is that autoload function can become very big and not very obvious/readable.
What do you think about it?
What benefits/drawbacks can you name in each of this methods?
How can I find the best solution for the project?
How can I know if there will be any performance problems if package management is used?
I use __autoload() extensively. The autload function that we use in our application has a few tweaks for backwards compatibility of older classes, but we generally follow a convention when creating new classes that allow the autoload() to work fairly seemlessly:
Consistent Class Naming: each class in its own file, each class is named with camel-case separated by an underscore. This maps to the class path. For example, Some_CoolClass maps to our class directory then 'Some/CoolClass.class.php'. I think some frameworks use this convention.
Explicitly Require External Classes: since we don't have control over the naming of any external libraries that we use, we load them using PHP's require_once() function.
The import method is an improvement but still loads up more than needed.
Either by using the asterisk or loading them up in the beginning of the script (because importing before every "new Classname" will become cumbersome)
I'm a fan of __autoload() or the even better spl_autoload_register()
Because it will include only the classes you're using and the extra benefit of not caring where the class is located. If your colleges moves a file to another directory you are not effected.
The downside is that it need additional logic
to make it work properly with directories.
I use require_once("../path-to-auto-load-script.php.inc") with auto load
I have a standard naming convention for all classes and inc files which makes it easier to programaticaly determine what class name is currently being requested.
for example, all classes have a certain extension like inc.php (so I know that they'll be in the /cls directory)
and
all inc files start with .ht (so they'll be in the /inc directory)
auto load accepts one parameter: className, which I then use to determine where the file is actually located. looping once I know what my target directory is, each time adding "../" to account for sub sub pages, (which seemed to break auto load for me) and finally require_once'ing the actual code file once found.
I strongly suggest doing the following instead:
Throw all your classes into a static array, className => filepath/classFile. The auto load function can use that to load classes.
This ensures that you always load the minimum amount of files. This also means you avoid completely silly class names, and parsing of said names.
If it's slow, you can throw on some accelerator, and that will gain you a whole lot more, if it still is slow, you can run things through a 'compile' process, where often used files are just dumped into common files, and the autoload references can be updated to point to the correct place.
If you start running into issues where your autoloading is too slow, which I find hard to believe, you can split that up according to packages, and have multiple autoloading functions, this way only subsets of the array are required, this works best if your packages are defined around modules of your software (login, admin, email, ...)
I'm not a fan of __autoload(). In a lot of libraries (some PEAR libraries, for instance), developersuse class_exists() without passing in the relatively new second parameter. Any legacy code you have could also have this issue. This can cause warnings and errors if you have an __autoload() defined.
If your libraries are clear though, and you don't have legacy code to deal with, it's a fantastic tool. I sometimes wish PHP had been a little smarter about how they managed the behavior of class_exists(), because I think the problem is with that functionality rather than __autoload().
Rolling your own packaging system is probably a bad idea. I would suggest that you go with explicit manual includes, or with autoload (or a combination for that matter).

Categories