ChatGPT解决这个技术问题 Extra ChatGPT

Composer require local package

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!

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á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.
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ánGrignoli you betcha

E
Emile Bergeron

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.


Note, that you are not allowed by composer to specify a path that is in the same directory the composer.json is placed.
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.
composer require "vendorname/packagename @dev" translates to "require":{ "vendorname/packagename": "@dev" } in your app's composer.json if you want to run composer install
Please, add this: composer config repositories.local path /full/or/relative/path/to/development/package as correct way of adding repositories
Is it possible to tell composer to install it to the vendors folder for prod instead of creating a symlink?
D
Danny Kopping

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.


getcomposer.org/doc/05-repositories.md#path is also potentially useful and seemed to work better for me.
@JasmineHegman indeed! I've used that too - great for development
To make this a good answer, you should show HOW to do it, and not just name the feature and link the docs (although that is also important). Other answers below have proper examples.
For me, as of 2022, @dev worked rather than * for the package version.
w
whyer

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();

"rm -rf vendor/company/package" is important
@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