I am new to composer
in the PHP world.
I am trying to create a package Foo
that has the package Bar
as a dependency. Bar
is available via packagist
. Foo
doesn’t need any of the PHP code of Bar
though, just some of the assets. These assets can change with every new release of Bar
, so it’s not really feasible to ship them with Foo
.
Foo
is not supposed to run on its own (although it could), but it’s supposed to be used as a dependency by other packages – for instance Yada
. That is where my trouble begins.
So, let’s say, we have
Bar
with some assets.Foo
usingBar
as a dependency, e.g. with acomposer.json
where the relevant pieces look like
{
"name": "foo/foo",
"require": {
"bar/bar": "<some-version>"
}
}
Yada
usingFoo
as a dependency, e.g. with acomposer.json
where the relevant pieces look like
{
"name": "yada/yada",
"require": {
"foo/foo": "<some-version>"
}
}
I tried installing Foo
on its own (in theory one could create a tool from that which could be useful standalone) and as a dependency of Bar
. I expected vendor
folder to always be created in the same spot (relative to my Foo
files).
But:
- If I run
composer install
onFoo
directly, thenvendor/Bar
will be put intoFoo
‘s directory. - If I run
composer install
onYada
, thenFoo
andBar
will both be installed, butvendor/Foo
andvendor/Bar
will be put intoYada
‘s folder, soBar
‘s files will be in a different spot compared to 1).
Please note that 1) does not mean I have runcomposer install
onYada
, changed into thevendor/Foo
and there rancomposer install
again.
That means, that I cannot know where the assets that I want to used will be placed in relation to the files of Foo
, can I? In other words: How would a PHP class of Foo
know where too look for the vendor files of Bar
that contain the assets if Foo
was installed as a dependency (or sub-depencency or sub-sub-…) of some other package?
I learned about the post-install-cmd
script that you can add to the composer.json
file. That would allow me to copy the assets to a pre-determined folder in relation to the files of Foo
, so they could be referenced. Alas, "Only scripts defined in the root package’s composer.json are executed. If a dependency of the root package specifies its own scripts, Composer does not execute those additional scripts.". In other words, when you install Foo
as a dependency of Yada
, then the post-install-cmd
script will not run.
Is there a recommended solution to this kind of problem in the "composer world"? Or would it mean that when you want to use Foo
in your package, you will not only have to add it using require
in your composer.json
, but to also add a tiny post-install-cmd
script to copy/move the asset files as required? Would this be considered acceptable for PHP projects?
2
Answers
I don't know whether this is recommended, but I was able to find a solution to my problem:
Given that I don't think I can determine where in the dependency chain
Foo
will be in relation toBar
- given that it could be used as a dependency or potentially standalone at some point - I created a simple function to traverse the file tree upwards starting at__DIR__
looking forvendor
.Bar will always be under
./vendor/bar
, relative to thecomposer.json
that required it. The only thing you need to determine is the project root, which you could require the caller to inject, or ask Composer to provide.For example, in Foo, you can use Composer itself to determine Bar’s install directory:
And then:
This will give you the correct directory regardless of whether Bar is running from Foo or from Yada. (Which you’ll then presumably have to manipulate further to get to the assets.)