18

I'm developing a PHP component called php-app-config using composer. This component, once required by another project, and installed using composer install, should look for config files inside the config folder of the root package, something like root_package/config/config.yml.

The ./config/config.yml should exists only in the root package and not inside the component imported by the "require:" in composer.json, as below:

▾ root-package/
  ▸ bin/
  ▸ build/
  ▾ config/
    ▸ locales/
      config.yml
  ▸ src/
  ▸ tests/
  ▾ vendor/
    ▸ composer/
    ▸ phpdocumentor/
    ▸ phpspec/
    ▸ phpunit/
    ▾ robotdance/
      ▾ php-app-config/
        ▾ src/                                                                                                                                                                                               
      Config.php -> how to get the root "config/config.yml" path from here?
        ▸ tests/
      composer.json
      composer.lock
      phpunit.xml
      README.md

The root package can be a web app or command line utility. Is there any way to get the root package path using composer? If not, what is the better way?

Menegazzo
  • 331
  • 1
  • 2
  • 7
  • I have tried a number of ways and it doesn't seem to be possible outside of the composer run-script context where composer loads into the application. The problem you are experiencing should be solvable using a relative path to your applications php include path. In the example you provided, you should be passing a path from a script I assume is in bin/ which can get the install path using `dirname(__DIR__)`. Same principal apply's when using public/index.php as the entry point. All bets are off when using phar as the path overrides are horrifically broken. – Alex Barker Feb 03 '20 at 22:25

3 Answers3

21

Using ReflectionClass:

$reflection = new \ReflectionClass(\Composer\Autoload\ClassLoader::class);
$vendorDir = dirname(dirname($reflection->getFileName()));
Andrey Izman
  • 1,807
  • 1
  • 24
  • 27
  • 8
    In new versions of PHP you can simple use `dirname($reflection->getFileName(), 2)` or `dirname($reflection->getFileName(), 3)` instead of `dirname(dirname(` – supersan Feb 25 '19 at 07:19
5

You can use composer's very own \Composer\Factory::getComposerFile(); to get to the project root directory:

$projectRootPath = dirname(\Composer\Factory::getComposerFile());

And with your case, you can access your root-package/config/config.yml by:

$configYmlPath = $projectRootPath . '/config/config.yml'

Don't forget to add composer to your dependencies for the \Composer\Factory::class to be available:

$ composer require composer/composer

Jim M
  • 4,261
  • 1
  • 18
  • 15
  • 2
    As I see it `\Composer\Factory::getComposerFile()` returns the name of the file that Composer will use, not the path to the actual file. When a webserver exposes the site from a subfolder like `public`, `dirname(\Composer\Factory::getComposerFile())` will include that subfolder even though `composer.json` is not stored there. – Kwebble Apr 07 '18 at 15:27
2
  • I would suggest "anchoring" your application (web or cli) by defining the root path as a constant.

    When you have for instance a root-package/src/application.php file, it should know where it lives, something like define('APP_ROOT_FOLDER', dirname(__DIR__)); could help. Once the constant is declared, it's available for dependencies, too.

    So, in /php-app-config/Config.php you would simply use the constant:

    $config = APP_ROOT_FOLDER . '/config/config.yml';

    (Or define a APP_CONFIG_ROOT_FOLDER constant which points directly to the config folder of the application.)

  • You could also try go some folder levels up from the dependency.

    In php-app-config/Config.php you would use __DIR__, which is root-package/vendor/robotdance/php-app-config/src. Now, you would need to go 4 levels up to reach root-package/.

    $config = __DIR__.'/../../../../config/config.yml';

    This will not work out, when your application gets packaged as a PHAR.


Is there any way to get the root package path using Composer?

If you have the Composer object, you can get the path of the vendor directory from the Config object:

$vendorPath = $composer->getConfig()->get('vendor-dir');

then, go one folder up $config = dirname($vendorPath) . '/config/config.yml';

Jens A. Koch
  • 39,862
  • 13
  • 113
  • 141
  • I tried [getcwd()](http://php.net/manual/en/function.getcwd.php) and it seems to work as intended using the autoloader provided by composer. – Menegazzo Jun 23 '16 at 13:02
  • 4
    How to get the composer Object? is it something like `use composer\Composer; $composer = Composer::getComposer();` ? – Menegazzo Jun 23 '16 at 13:04
  • 4
    The snippet that deals with `vendor-dir` only considers the case where it is a directory directly inside the root package. Something like `dist/php` for `vendor-dir` would break. It would be ideal to get the install path from the `InstallationManager`, but there is no special handling for the root package at this time and would return an install path inside the `vendor-dir` when requesting the install location for the root package. The best way I've found is to do `dirname(\Composer\Factory::getComposerFile());` which also works when executing outside of the directory of the package. – Steve Buzonas Dec 18 '16 at 03:35