Thursday, January 8, 2015

Zero to PSR-4 in 60 seconds

By and large PSR-0 and PSR-4 are identical. Comparing and contrasting the two doesn't illuminate the crucial, but small, surface area where they differ.  Sadly, most documentation takes this compare and contrast approach. In this article, I break with that documentation pattern and rather focus on the essence of PSR-4.


PSR-4 lets a developer assign an arbitrary namespace to an arbitrary directory path, then acts like PSR-0 for all sub-directories within that directory.

Example

In composer.json autoload, a developer maps a namespace prefix to a directory path.  Components of the namespace prefix do not have to map 1-to-1 with the directory path.  In this example, note how the namespace and directory bear little relation:
"autoload": {
    "psr-4": {
        "MyApplication\\": "app/",
        "Third\\Party\\Package\\": "package/",
        "Parser\\": "research/mario/parser/"
    }
}

Namespace components after the prefix must map 1:1 with sub-directories in a case-sensitive manner.  This is exactly PSR-0 behavior:
NamespaceCorresponding directory
MyApplication\Fooapp/Foo
Third\Party\Package\Foo\BARpackage/Foo/BAR
Parser\xml\htmlresearch/mario/parser/xml/html

The final namespace component maps 1:1 with a case-sensitive file name in that full path:
ClassCorresponding file
MyApplication\Foo\quuxapp/Foo/quux.php
Third\Party\Package\Foo\BAR\Bazpackage/Foo/BAR/Baz.php
Parser\xml\html\READERresearch/mario/parser/xml/html/READER.php

Miscellanea and current best practices

If the target file system doesn't care about case, PSR-4 won't either.  But, it's best practice to presume a case-sensitive file system.

One namespace prefix may map to many file system paths.  Best practice is to prefer more & longer prefixes over fewer & shorter prefixes.

Laravel

I would be remiss to ignore a Laravel-specific discussion, since I spend much of my PHP time there.

Laravel 5 natively supports PSR-4, so you can follow its conventions without further ado.  But if you're in Laravel 4 and want to adopt PSR-4, you may have:
    "autoload": {
        "classmap": [ "app" ],
        "psr-4": {
            "Muddler\\": "app/",
            "Muddler\\Command": "app/commands"
        }
    },

That autoload affords two ways to access app/commands/InstallCommand.php.
  1.  new Muddler\commands\InstallCommand because of the 1st mapping
  2.  new Muddler\Command\InstallCommand because of the 2nd mapping
The first is ugly and unnatural, the second is beautiful and elegant. That is the beauty of PSR-4.  You have the flexibility to root the namespace hierarchy wherever you want, then can use the one-to-one mapping feature of PSR-0 from that point on.

0 comments:

Post a Comment

Share your thoughts!