I've seen a bunch of examples using PSR to do autoloading with namespaces. Maybe this is a dumb question, but why isn't this bad practice? Doesn't that defeat the purpose of having namespaces?
For example, say you have two libraries, FormBuilder
and MySweetForms
, and both have a Form
class. Locations are at:
lib/FormBuilder/Core/Form.php
and lib/MySweetForms/Form.php
If you autoload based on the namespaces those Form
classes are in, aren't you going to run into the exact problem that namespaces are meant to prevent: ambiguous identifiers of classes and methods?
Won't it fail when you successfully locate another class in the MySweetForms
namespace, say AjaxFileField
, that relies on \MySweetForms\Form
, but it finds the FormBuilder
's implementation of the Form
class?
Because of namespaces, the autoloading won't find the FormBuilder
's implementation of the Form
class when it's looking for the MySweetForms
implementation, provided the autoloader and the two libraries follow best (or close to best) practices and are implemented correctly.
If the MySweetForms/AjaxFileField.php
file defines a class inside the MySweetForms
namespace as shown:
namespace MySweetForms;
class AjaxFileField
{
public function doFormStuff()
{
$form = new Form();
}
}
Then the reference to the Form
class inside AjaxFileField
is really a short-hand for the fully qualified class name MySweetForms\Form
.
When the autoloader is invoked to load the class, it will be asked to load the class based on it's fully qualified name. A PSR-0 autoloader would translate the MySweetForms\Form
classname into the MySweetForms/Form.php
path, and (provided it was told to look in the lib
directory) it would find it's way to the correct file. If that file was missing, the autoloader would ultimately fail. Similarly, if that file defined a Form
class but neglected to supply a namespace the actual class (MySweetForms\Form
) would not exist (instead, a global class Form
would) -- that doesn't match the fully qualified name of the class referred to in the AjaxFileField
class, so an error would occur.
Note that if the code had included $form = new \FormBuilder\Core\Form();
instead the class name would already be fully qualified, and the autoloader would be asked to load the FormBuilder\Core\Form
class (and would expect it to be in the FormBuilder/Core/Form.php
file).
If we had used $form = new \Form();
we would be referring to the Form
class in the global namespace and the autoloader would be asked to find Form
(it would look in the Form.php
file directly inside the lib
folder at this point).
The key is to recognize that there are not, really, two Form
classes -- there is one class with a fully qualified name of FormBuilder\Core\Form
and one with a fully qualified name of MySweetForms\Form
-- and a properly implemented autoloader will expect them in completely different locations, and will not try to load one file in place of the other.
The combination of namespaces and the PSR style of organizing files into directories based on their namespaces makes it very easy to re-use common words without causing conflicts, and to predictably map fully qualified class names to the corresponding files.
The resolution of class names to fully qualified class names is key to this, and is discussed in the PHP manual (the namespace FAQ has a couple of points about it: http://www.php.net/manual/en/language.namespaces.faq.php). The PSR-0 standard itself is also a useful reference: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md