-3

I'm developing an app that will consist of multiple Composer packages.

First I have the "main" app (it will be a "project" in Composer) that will contain all the necessary files and folders:

app/
public/
    index.php
logs/
config.php
..etc..

This is not an issue. I just set the type as "project" in the composer file so it can be install with composer create-project foo/bar.

I will also build a few optional extensions for the main app. They will be their own Composer packages. This isn't either an issue. I just make them into type "library" and install them with composer install foo/the-extension.

The issue

The extensions will have their own namespaces and some of them will have their own dependencies. A couple of them will even have the same dependencies.
This is needed since all of them will are optional. You can install one or the other or all.

Currently, I've created a new folder called "/dev" in the main app where I have all my extensions while developing. Then, in the main app, I'm loading all the extensions auto loaders:

# Main apps autoloader
require_once __DIR__ . '/vendor/autoload.php';

# Extensions
require_once __DIR__ . '/dev/foo/vendor/autoload.php';
require_once __DIR__ . '/dev/bar/vendor/autoload.php';
...etc...

This works, but it comes with a few drawbacks:

  1. I need to change the code in the main app every time I'm going to make a commit to the main apps repo. This is a hassle and it's easy to miss something
  2. Potential versioning clashes. If two packages depends on the same package that gets a new update. If I miss to update both, there might be a version clash. (This have happened)

It's never good to have more than one auto loader since that can mess things up royally.

So, does anyone know of a proper way of handling this, or is it one of those "well, if it works for you, do it like that"-type of situations?

Been searching for a good solution for this a while now but haven't found any. If I missed some answer here on SO, please mark it as a duplicate and I'll remove this post.

Edit

As @sammitch points out in the answer below, I could add the extensions using Composers "repositories" key. This would remove the multiple auto loader problem. However, that will make the development flow pretty awkward:

  1. You make a change to an extension
  2. You commit and push that change to a git repo
  3. You do a composer update in the main app (to get the new commit)
  4. Now you can test if your changes work

I rather not need to go through all that every single time I make a change the extensions code just to see if the change worked or not.

Community
  • 1
  • 1
M. Eriksson
  • 13,450
  • 4
  • 29
  • 40

1 Answers1

9

Whoa whoa whoa, you should only ever have one composer autoloader, and it's a bad idea to just cram in external dependencies like that as it will complicate your dev and/or deployment pipelines later.

What you want to do is have the main project include the subpackages as actual packages. You can do this either by:

1. Pushing them to a git host

https://getcomposer.org/doc/05-repositories.md#loading-a-package-from-a-vcs-repository

{
    "repositories": [{
        "type": "vcs",
        "url": "https://github.com/youruser/yourrepo"}],
    "require": {
        "youruser/yourpackage": "^1.0.0"
    }
}

2. Specifying a local repo

Composer require local package

{
    "repositories": [{
        "type": "vcs",
        "url": "/home/youruser/src/yourrepo"
    }],
    "require": {
        "youruser/yourpackage": "^1.0.0"
    }
}

Now you can simply run composer install or composer update youruser/yourpackage and composer will pull in all the necessaries and build the relevant autoloader.

Note: Repository specifications are only effective in the root composer.json, so if your dependencies have specific repo config you'll need to either put that config into the root composer.json, or into your global composer config.

Sammitch
  • 30,782
  • 7
  • 50
  • 77
  • Yes, I know it's bad to have multiple auto loaders, that's why I stated _"It's never good to have more than one autoloader."_ :-) The problem with both these approaches are that it will make the development flow awkward. I will need to do a git push every single time I make a change, just to be able to test if it worked. It also requires me to remove those composer lines every time I make a commit to the main app. It is a better approach than what I'm doing, but still sub optimal. – M. Eriksson Mar 01 '18 at 22:07
  • 1
    What I rather would like, even though I realize it's probably not possible, is to have composer check if the `/dev` folder exists and if it does, load any packages inside it. Or something similar. – M. Eriksson Mar 01 '18 at 22:13
  • I get that it's extra steps, but you need to follow a consistent workflow that matches how your code will be deployed, otherwise you're going to have unanticipated problems when you try to deploy the finished product via composer. Eg: missing and/or mismatched dependencies, changes made manually in a dependent library that were never actually pushed, or were tagged as a different version, etc. – Sammitch Mar 01 '18 at 22:31
  • I totally agree, that's the main reason I don't like the way I have it now. I have done it like that previously but working on multiple extensions and, specially in the beginning, making a lot of small changes was a major pain. I also don't like the idea of committing untested code to the repo, since it makes the commit history anything but clean. I could squash the commits after, but that would be yet an extra step. – M. Eriksson Mar 01 '18 at 22:39
  • Maybe there simply isn't any better/more stream-lined approach of doing it. That could explain why I haven't found any better answers while searching :-) – M. Eriksson Mar 01 '18 at 22:42
  • Personally I don't think that "clean" commit history is something that people should worry about so long as the git workflow is solid. That said, if you're concerned about the amount of git metadata that might get bundled along with installing composer packages you can leverage Packagist's automatic dist archive inclusion by: 1. Using a compatible git host [eg: github or gitlab, afaik] 2. Tagging your releases in said host. 3. Using `--prefer-dist`, or rather not using something that disables it. This way packagist provides a link to your git host's pre-generated dist archive. – Sammitch Mar 02 '18 at 01:01
  • What I mean is if you are multiple people working on some package and, for what ever reason, you then need to rebase or cherry pick specific commits, having a commit history containing hundreds of commits for one single, quite simple, feature will definitely make things messier. The conclusion as I see it is: This isn't a particularly smooth way and it does got drawbacks, but regardless, it 's currently the best (and safest) way. – M. Eriksson Mar 02 '18 at 05:46