I want my package to ship with a built-in composer-plugin.
I have a structure like this:
composer.json
src/
...
plugin/
composer.json
src/
...
The root composer.json
is configured like this:
{
"name": "foo/bar",
"type": "library",
"autoload": {
"psr-4": {
"Foo\\Bar\\": "src/"
}
},
"repositories": [
{
"type": "path",
"url": "./tools",
"options": {
"symlink": false
}
}
],
"require": {
"foo/bar-plugin": "*"
}
}
And the built-in composer-plugin's plugin/composer.json
like this:
{
"name": "foo/bar-plugin",
"type": "composer-plugin",
"require": {
"composer-plugin-api": "^1",
"composer/composer": "^1",
"foo/bar": "*"
},
"autoload": {
"psr-4": {
"Foo\\Bar\\Plugin\\": "src/"
}
},
"extra": {
"class": "Foo\\Bar\\Plugin\\MyComposerPlugin"
}
}
Notice how there's a two-way dependency here - the plugin depends on foo/bar
, and the project itself depends on foo/bar-plugin
.
Here's where it gets weird. During a fresh installation with e.g. composer install
or composer update
, everything is fine - the plugin does it's thing, which, right now, means just announcing itself on the console.
Now, after installation, if I type just composer
, I'd expect to see the plugin announce itself, same as before, right?
Instead, it generates a fatal "class not found error", as soon as it tries to reference any class belonging to the foo/bar
package.
It's as though composer lost track of the fact that foo/bar-plugin
requires foo/bar
, and for some reason it's classes aren't auto-loadable.
Is there any reason this shouldn't be possible? Why not?
Of course I can just package this stuff in separate external package, but that isn't going to make much sense, since these packages are just going to depend on each other - they're effectively one unit, a packaging them as two packages is going to result in a mess of major version increases with every small change, as basically every release of foo/bar
will break foo/bar-plugin
.
Ideally, I'd like to simply add the composer-plugin directly into the main package, but it appears that's not possible for some reason? Only a package with type composer-plugin
is allowed to add plug-ins, it seems?
If the plugin is essentially a part of your package, you should not use it as such. Composer offers alternatives.
As Jens mentioned in a comment to your question, there is 'scripts' key in composer.json
. You can invoke shell commands inside, but also call static class methods.
About plugin solution - composer explicitly mentions this on its site:
Composer makes no assumptions about the state of your dependencies prior to install or update. Therefore, you should not specify scripts that require Composer-managed dependencies in the pre-update-cmd or pre-install-cmd event hooks. If you need to execute scripts prior to install or update please make sure they are self-contained within your root package.
(my side note - this also roughly applies to plugins).
Anyway - to provide you with a solution: discard 'plugin' approach. Instead modify your composer.json
file so it looks as follows:
{
"name": "foo/bar",
"type": "library",
"autoload": {
"psr-4": {
"Foo\\Bar\\": "src/"
}
},
"require": {
},
"scripts": {
"post-install-cmd": [
"Foo\\Bar\\Composer\\Plugin::postInstall"
],
"post-update-cmd": [
"Foo\\Bar\\Composer\\Plugin::postUpdate"
]
}
}
Additionally, in src/Composer
folder create Plugin.php
:
<?php
namespace Foo\Bar\Composer;
use Foo\Bar\Test;
/**
* Composer scripts.
*/
class Plugin
{
public static function postInstall()
{
print_r("POST INSTALL\n");
print_r(Test::TEST_CONST);
print_r("\n");
}
public static function postUpdate()
{
print_r("POST UPDATE\n");
print_r(Test::TEST_CONST);
print_r("\n");
}
}
As you see, it prints constant from Test class. Create it in src/
:
<?php
namespace Foo\Bar;
/**
* Test class.
*/
class Test
{
const TEST_CONST = "HERE I AM";
}
Run this and check, how it plays out.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With