As such, it's more of an aide memoire than anything else but since I thought it'd be useful to me, it might be useful to others too!

Composer supports autoloading classes where the code layout conforms to either the psr-0 or psr-4 standard. Coding to the PSR-n standards is much to be advised. Since PSR-4 is an updated and improved version of PSR-0, I'm only going to consider the former, newer PSR-4 in this article.

For the benefit of this article, I'm going to consider the following basic project structure

myapp
  \config (config and setup)
  \controllers
     \PageController.php
  \models
     \UserModel.php
  \views

  \public  (web site doc root)

  \vendor
    \composer
    \ ...(other packages)

Now the first thing to note is that we don't need to consider anything in the vendor dir - if they've been installed by composer then the autoloading is taken care of automatically.

However, how do we get our own app code to autoload without writing our own autoloader?

First of all, let's take a look at the PageController class.

<?php
namespace MyVendor\MyApplication;

class PageController
{
    ...
}

Namespacing is critically important here. In the snippet above, I've illustrated the namespace as being MyVendor\MyApplication. The first part of the namespace, MyVendor is mandated by the PSR-4 standard and it's certainly the habit that you want to get into.

The MyApplication part is optional but again, a good habit to develop (since you're likely to develop more than one application in your career!).

What then do we need to tell composer to be able to add this layout to its autoloader? The short answer is that we provide a namespace key and a filesystem path value. Like this:

{
    "require": {
        ... // required packages
    },
    "autoload": {
        "psr-4": {
            "MyVendor\\MyApplication\\" : "app/controllers/"
        }
    }
}

Then simply run composer dump-autoload to rebuild the autoloader.

Proof that this works can be seen with the following file

<?php  // ./public/index.php

require_once('../vendor/autoload.php');

$pageController = new MyVendor\MyApplication\PageController.php;

However, the eagle eyed amongst you might note that this will only look to the controllers folder, despite having used the rather generatic MyVendor\MyApplication namespace. What about autoloading the UserModel class?

The answer comes in using one of two particular options.

For very small applications and/or lazy programmers that don't want to type too much, you can provide an array of paths for the given namespace. Like this:

{
    "require": {
        ... // required packages
    },
    "autoload": {
        "psr-4": {
            "MyVendor\\MyApplication\\" : [
                "app/controllers/",
                "app/models/"
            ]
        }
    }
}

Note the use of the square brackers ( [ ] ) to enclose a comma separated array of paths for composer to search. Providing an array of paths to composer's autoloader means that it will now search each path in turn in order to find the requested class.

This last point is the reason why I qualified this approach as being suitable for small applications only. If you have hundreds of classes in hundreds of files, you'll want to be more specific in your composer.json to save thrashing your server's hard drive.

Here's how you do that.

First of all, you're going to need to provide an extra subnamespace in order to appropriate compartmentalise your code.

<?php
namespace MyVendor\MyApplication\Controller;

class PageController
{
    ...
}

Here, I've added the new subnamespace Controller as a suffix to MyVendor\MyApplication.

<?php
namespace MyVendor\MyApplication\Model;

class UserModel
{
    ...
}

Similarly, I've now specified a Model subnamespace in the UserModel.php file.

Finally, we need to update the autoload specification in composer.json.

{
    "require": {
        ... // required packages
    },
    "autoload": {
        "psr-4": {
            "MyVendor\\MyApplication\\Controller\\" : "myapp/controllers/",
            "MyVendor\\MyApplication\\Model\\" : "myapp/models/"
        }
    }
}

There. Job done. Breaking down the MyApplication namespace into further sub-namespaces allows us to provide more focus to the autoloader.

But wait, there's more. We know that PSR-4 also supports a directory->sub-namespace convention as well as the classname->filename convention.

Assuming we're just starting (or not too far long) the development path of our project, we can simplify the autoload specification in composer.json and avoid getting tangled in the future.

What I mean is this:

{
    "require": {
        ... // required packages
    },
    "autoload": {
        "psr-4": {
            "MyVendor\\MyApplication\\" : "myapp/"
        }
    }
}

There. That's much more succinct and comprehensible. What it means now though is that for every sub-namespace you specify in a file, you need a corresponding directory (or directories) underneath the "my app" one. As with the classname to filename convention, we need to match the spelling and capitalisation of each sub-namespace to a particular directory.

Like this:

myapp
  \config (config and setup)
  \Controller
     \PageController.php
  \Model
     \UserModel.php
  \View

  \public  (web site doc root)

  \vendor
    \composer
    \ ...(other packages)

One of the benefits of this approach is that it'll encourage you to aim for a coherent namespacing strategy and at the same time, help you keep your class files nicely organised on the filesystem. Why? Because it's much less effort than trying to manage a tree of fully qualified namespace to directory mappings in composer.json.

Instead, it just works!

Which method you use is up to you. You can even mix-and-match the choice of adding sub-namespaces or providing an array of paths. As long as you're following PSR-4 compliant coding practices, you're good to go.

FINAL NOTE: You can check the rules to follow on the PSR-4 web page but in brief, the key ones are:

  1. The top-level vendor namespace is mandatory
  2. sub-namespaces not specified as a key in the autoload segment must exactly match directory paths to the targeted classes
  3. One class per file
  4. The filename must exactly match the spelling and capitalisation of the class name inside the file.
  5. The file extension must be '.php'