174

I've got a couple of libraries [Foo and Bar] that I'm developing in concert, but are still technically separate things. Previously I've just re-defined the autoloader to like "Foo\\": "../Foo/src", but now that I've added a Guzzle dependency to Foo, Bar flips it's lid because it's not one of its dependencies.

Directory structure:

/home/user/src/
    Foo/
        src/
            FooClient.php
        composer.json
    Bar/
        src/
            BarClient.php
        composer.json

Theoretical Autoload Statement: [in Bar/composer.json]

"require": {
    "local": "../Foo/composer.json"
}

Example code:

require('vendor/autoload.php');

$f = new \Bar\BarClient(new \Foo\FooClient());

How can I resolve this without setting up a local Composer repo? I want to maintain these as separate packages, just that one requires the other, and therefor processes the other's dependencies.

post-answer edit:

Thanks to infomaniac I've done the following:

Initialized the git repo:

cd ~/src/Foo && git init && echo -e "vendor\ncomposer.lock" > .gitignore && git add ./ && git commit -m "Initial Commit"

Added the composer config:

"require": {
    "sammitch/foo": "dev-master"
},
"repositories": [{
    "type": "vcs",
    "url": "/home/sammitch/src/Foo"
}],

And then composer update!

Community
  • 1
  • 1
Sammitch
  • 30,782
  • 7
  • 50
  • 77
  • How does this json specify the identity between the reference to "sammitch/foo" and the address of "/home/sammitch/src/Foo" ? Is it following any convention? – Sebastián Grignoli Mar 03 '16 at 22:33
  • @SebastiánGrignoli `sammitch/foo` is the package name and has literally nothing to do with where it is located. Will construct a list of available packages based on its configured repos, in this case fetching the composer.json from the specified local git repo, and then composer handles the rest. The `sammitch/foo` package is copied to the current app's `vendor` folder the same as any other package. – Sammitch Mar 03 '16 at 23:51
  • Oh, I think I get it now. It's just a custom repo, like in APT, that might happen to contain the "sammit/foo" package. Did I get it right? – Sebastián Grignoli Mar 04 '16 at 01:08
  • @SebastiánGrignoli you betcha – Sammitch Mar 04 '16 at 18:22
  • thanks for saying "flips it's lid"...made me laugh :D – Jono Oct 25 '22 at 12:37
  • i guess the term "Theoretical" is not a good choice here, use "Hypothetical" instead :D – Mehdi Feb 15 '23 at 15:13

4 Answers4

284

The way to link to a local, in-development package is to first add in your main project's composer.json a repository, like this:

"repositories": [
    {
        "type": "path",
        "url": "/full/or/relative/path/to/development/package"
    }
]

You also need to either have a version specified in your development package's composer.json or the way I do it is to require the package using @dev, like this:

composer require "vendorname/packagename @dev"

It should output:

- Installing vendor/packagename (dev-develop)
Symlinked from /full/or/relative/path/to/development/package

The @dev in the require command is important, composer uses this to pickup the source code and symlink it to your new package.

It's a stability flag added to the version constraint (see package link).

These allow you to further restrict or expand the stability of a package beyond the scope of the minimum-stability setting.

The minimum-stability flags are:

Available options (in order of stability) are dev, alpha, beta, RC, and stable.

Emile Bergeron
  • 17,074
  • 5
  • 83
  • 129
Dhiraj Gupta
  • 9,704
  • 8
  • 49
  • 54
  • 13
    Note, that you are not allowed by composer to specify a path that is in the same directory the composer.json is placed. – MaPePeR Apr 12 '18 at 06:41
  • Interesting point, MaPePeR I did not know this. However, I guess all the web frameworks already take care of this by putting all the dependencies into a "vendor" folder? Yii2 does this, at least. – Dhiraj Gupta Apr 12 '18 at 10:55
  • I think i confused this with something else. It is not allowed to have a repository in a parent folder. A child folder should be fine, but it caused some weird errors for me. – MaPePeR Apr 12 '18 at 10:59
  • 9
    `composer require "vendorname/packagename @dev"` translates to `"require":{ "vendorname/packagename": "@dev" }` in your app's composer.json if you want to run composer install – Sir_Faenor Jul 05 '18 at 08:48
  • 4
    Please, add this: composer config repositories.local path /full/or/relative/path/to/development/package as correct way of adding repositories – basil Dec 21 '18 at 20:56
  • I followed these instructions but I get an error that `autoload.php` can't be found when deploying to an Elastic Beanstalk instance – Chuck Le Butt Nov 07 '19 at 15:57
  • @chuck-le-butt try and see what the composer version you're getting on your EB instance, might need updating? Maybe this: https://forums.aws.amazon.com/thread.jspa?threadID=149579 – Dhiraj Gupta Nov 07 '19 at 16:43
  • 2
    Is it possible to tell composer to install it to the vendors folder for prod instead of creating a symlink? – BenjaminH Jun 24 '20 at 12:28
  • For anyone that has the package installed, you might need to remove the existing package `composer remove "package-name"` before running these instructions. – Louis Sayers Nov 20 '20 at 00:35
  • Is this okay to be used in production? – Steve Moretz Apr 24 '22 at 08:25
97

You can use Composer's repositories feature

https://getcomposer.org/doc/05-repositories.md#path

{
  "repositories": [
    {
        "type": "path",
        "url": "../../packages/my-package"
    }
  ],
  "require": {
    "my/package": "*"
  }
}

Instead of using the http format, specify a file path on disk.

Danny Kopping
  • 4,862
  • 2
  • 29
  • 38
13

After spending some time, I finally understood the solution. Maybe it'll be useful for someone like me and will save you some time, so I've decided that I have to share it here.

Assuming that you have the following directory structure (relative to your project root directory):

composer.json
config
config/composition-root.php
local
local/bar-project
local/bar-project/composer.json
local/bar-project/src
local/bar-project/src/Bar.php
public
public/index.php
src
src/Foo.php

In this example you may see that the local folder is meant for nested projects of your company, e.g. bar-project. But you could configure any other layout, if you wish.

Each project has to have its own composer.json file, e.g. root composer.json and local/bar-project/composer.json. Then their contents would be as follows:

(root composer.json:)

{
  "name": "your-company/foo-project",
  "require": {
    "php": "^7",
    "your-company/bar-project": "@dev"
  },
  "autoload": {
    "psr-4": {
      "YourCompany\\FooProject\\": "src/"
    }
  },
  "repositories": [
    {
      "type": "path",
      "url": "local/bar-project"
    }
  ]
}

(local/bar-project/composer.json:)

{
  "name": "your-company/bar-project",
  "autoload": {
    "psr-4": {
      "YourCompany\\BarProject\\": "src/"
    }
  }
}

If, for example, you wish to locate each project in a separate sibling directory, as follows:

your-company
your-company/foo-project
your-company/foo-project/composer.json
your-company/foo-project/config
your-company/foo-project/config/composition-root.php
your-company/foo-project/public
your-company/foo-project/public/index.php
your-company/foo-project/src
your-company/foo-project/src/Foo.php
your-company/bar-project
your-company/bar-project/composer.json
your-company/bar-project/src
your-company/bar-project/src/Bar.php

- then you need to link to respective directory in repositories section:

  "repositories": [
    {
      "type": "path",
      "url": "../bar-project"
    }
  ]

After that don't forget to composer update (or even rm -rf vendor && composer update -v as the docs suggest)! Under the hood, composer will create a vendor/your-company/bar-project symlink that targets to local/bar-project (or ../bar-project respectively).

Assuming that your public/index.php is just a front controller, e.g.:

<?php
require_once __DIR__ . '/../config/composition-root.php';

Then your config/composition-root.php would be:

<?php

declare(strict_types=1);

use YourCompany\BarProject\Bar;
use YourCompany\FooProject\Foo;

require_once __DIR__ . '/../vendor/autoload.php';

$bar = new Bar();
$foo = new Foo($bar);
$foo->greet();
whyer
  • 783
  • 5
  • 16
  • 1
    "rm -rf vendor/company/package" is important – Alex83690 Oct 14 '19 at 08:08
  • @Alex83690 only if you have already run `composer update` with similar `composer.json` and therefore you need to remove the previous symlink created by composer – whyer Oct 14 '19 at 09:48
2

The command line way to do this is

composer config repositories.package_name local path/to/package
composer require group/package_name

If the group/package_name is available both from repository and local, the local version is used.

The local command can be replaced with vcs to reference a direct repository, or composer for default.

stacey.mosier
  • 393
  • 2
  • 8
  • So you can not have project that use repository for a package while other are using local, nope ? – Loenix May 04 '23 at 12:47