69
votes

I have been looking around the net with no luck on this issue. I am using composer's autoload with this code in my composer.json:

"autoload": {
    "psr-0": {"AppName": "src/"}
}

But I need to autoload at a higher level than the vendor folder.

Doing something like this does not work:

"autoload": {
    "psr-0": {"AppName": "../src/"}
}

Does anyone know a fix or how I can do this?

6

6 Answers

74
votes

Every package should be responsible for autoloading itself, what are you trying to achieve with autoloading classes that are out of the package you define?

One workaround if it's for your application itself is to add a namespace to the loader instance, something like this:

<?php

$loader = require 'vendor/autoload.php';
$loader->add('AppName', __DIR__.'/../src/');
54
votes

The composer documentation states that:

After adding the autoload field, you have to re-run install to re-generate the vendor/autoload.php file.

Assuming your "src" dir resides at the same level as "vendor" dir:

  • src
    • AppName
  • vendor
  • composer.json

the following config is absolutely correct:

{
    "autoload": {
        "psr-0": {"AppName": "src/"}
    }
}

but you must re-update/install dependencies to make it work for you, i.e. run:

php composer.phar update

This command will get the latest versions of the dependencies and update the file "vendor/composer/autoload_namespaces.php" to match your configuration.

Also as noted by @Dom, you can use composer dump-autoload to update the autoloader without having to go through an update.

25
votes

There also other ways to use the composer autoload features. Ways that can be useful to load packages without namespaces or packages that come with a custom autoload function.

For example if you want to include a single file that contains an autoload function as well you can use the "files" directive as follows:

"autoload": {
    "psr-0": {
        "": "src/",
        "SymfonyStandard": "app/"
    },
    "files": ["vendor/wordnik/wordnik-php/wordnik/Swagger.php"]
},

And inside the Swagger.php file we got:

function swagger_autoloader($className) {
    $currentDir = dirname(__FILE__);
    if (file_exists($currentDir . '/' . $className . '.php')) {
        include $currentDir . '/' . $className . '.php';
    } elseif (file_exists($currentDir . '/models/' . $className . '.php')) {
        include $currentDir . '/models/' . $className . '.php';
    }
}
spl_autoload_register('swagger_autoloader');

https://getcomposer.org/doc/04-schema.md#files

Otherwise you may want to use a classmap reference:

{
    "autoload": {
        "classmap": ["src/", "lib/", "Something.php"]
    }
}

https://getcomposer.org/doc/04-schema.md#classmap

Note: during your tests remember to launch the composer dump-autoload command or you won't see any change!

./composer.phar dump-autoload

Happy autoloading =)

12
votes

In my opinion, Sergiy's answer should be the selected answer for the given question. I'm sharing my understanding.

I was looking to autoload my package files using composer which I have under the dir structure given below.

<web-root>
    |--------src/
    |           |--------App/
    |           |
    |           |--------Test/
    |
    |---------library/
    |
    |---------vendor/
    |           |
    |           |---------composer/
    |           |           |---------autoload_psr4.php
    |           |           
    |           |----------autoload.php
    |
    |-----------composer.json
    |

I'm using psr-4 autoloading specification.

Had to add below lines to the project's composer.json. I intend to place my class files inside src/App , src/Test and library directory.

"autoload": {
        "psr-4": {
            "OrgName\\AppType\\AppName\\": ["src/App", "src/Test", "library/"]
        }
    } 

This is pretty much self explaining. OrgName\AppType\AppName is my intended namespace prefix. e.g for class User in src/App/Controller/Provider/User.php -

namespace OrgName\AppType\AppName\Controller\Provider; // namespace declaration

use OrgName\AppType\AppName\Controller\Provider\User; // when using the class

Also notice "src/App", "src/Test" .. are from your web-root that is where your composer.json is. Nothing to do with the vendor dir. take a look at vendor/autoload.php

Now if composer is installed properly all that is required is #composer update

After composer update my classes loaded successfully. What I observed is that composer is adding a line in vendor/composer/autoload_psr4.php

$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);

return array(
    'Monolog\\' => array($vendorDir . '/monolog/monolog/src/Monolog'),
    'OrgName\\AppType\\AppName\\' => array($baseDir . '/src/App', $baseDir . '/src/Test', $baseDir . '/library'),
);

This is how composer maps. For psr-0 mapping is in vendor/composer/autoload_classmap.php

3
votes

Just create a symlink in your src folder for the namespace pointing to the folder containing your classes...

ln -s ../src/AppName ./src/AppName

Your autoload in composer will look the same...

"autoload": {
    "psr-0": {"AppName": "src/"}
}

And your AppName namespaced classes will start a directory up from your current working directory in a src folder now... that should work.

3
votes

The autoload config does start below the vendor dir. So you might want change the vendor dir, e.g.

{
    "config": {
        "vendor-dir": "../vendor/"
    },
    "autoload": {
        "psr-0": {"AppName": "src/"}
    }
}

Or isn't this possible in your project?