VGTech is a blog where the developers and devops of Norways most visited website share code and tricks of the trade… Read more



Are you brilliant? We're hiring. Read more

Using local packages as composer dependencies

PHP

Composer changed pretty much everything when it comes to including dependencies in PHP projects. No more SVN externals or copying large library folders into your project. This is really great, but there’s one thing I’ve been struggling to find a smooth process for; developing dependencies for your project.

When implementing your project, the need for some module, library, service provider or something else will arise, and sometimes you’ll have to implement it yourself. So, how to do that?

Possible solutions

  1. Implementing it inside your project, maybe in the library or source folder. Would work, but if you want to publish it [on packagist.org or somewhere else] as a separate package later on, you will make extra work for yourself.
  2. Publish the non-working package on packagist and push changes to you VCS. This is a really slow work process, as it requires you to push and do a composer update to see the changes.
  3. The other way™

The other way

I’ve found that setting up a repository with a relative file system reference is the easiest way. This way, you can have your dependency separate from the application and getting changes in the dependency is as simple as running composer update.

Example

Given the following folder structure;

/
    project/
        composer.json
    package/
        composer.json
        src/Foobar.php

package/composer.json

Show code
{
    "name": "kbrabrand/package",
    "description": "My test package",
    "license": "MIT",
    "authors": [
        {
            "name": "Kristoffer Brabrand",
            "email": "kristoffer@brabrand.no"
        }
    ],
    "autoload": {
        "psr-0": {
            "KBrabrand": "src" 
        }
    }
}

This simply defines a composer compatible package and is pretty straight forward. Now, let’s take a look at the composer.json file for the project, where the “magic” happens.

project/composer.json

Show code
{
    "name": "kbrabrand/project",
    "description": "Test project",
    "license": "MIT",
    "authors": [
        {
            "name": "Kristoffer Brabrand",
            "email": "kristoffer@brabrand.no"
        }
    ],
    "minimum-stability": "dev",
    "repositories": [
        {
            "type": "vcs",
            "url": "../package"
        }
    ],
    "require": {
        "kbrabrand/package": "*"
    }
}

The repositories part of the composer file is the only thing special. There are several repository types, but the VCS type refers to some sort of version control resource (locally or externally). The repository portion of the composer schema documentation notes that this could be svn, git or mercurial (hg).

For this example I’ve used a relative local path and git. As shown in the composer doumentation, using absolute paths or external URLs is also possible.

Running composer

Show code
$ cd package
$ git init
Initialized empty Git repository in /development/package/.git/

$ git add *
$ git commit -m "Initial commit"

At this point, a local git repo exists in the package folder and is now ready for inclusion in the project with composer.

Show code
$ cd ../project
$ composer install
Loading composer repositories with package information
Installing dependencies (including require-dev) 
 - Installing kbrabrand/package (dev-master 4a111b4)
 Cloning 4a111b483843e75a3cb0ee794c0b9eaf0b75b395

Writing lock file
Generating autoload files

That’s it! After this point, whenever a change is made to the package, commit your change and do a composer update. Like this;

Show code
$ cd ../project
$ git commit -am "Changes"
$ cd ../project
$ composer update

Pro tip from the comment field

Gauthier Delamarre pointed out that it’s possible to specify the repository definition from composer.json into the users composer config. This is done by adding repository block to the file ~/.composer/config.json. As Gauthier points out, the benefit from doing it this way is that you won’t need to keep you environment specific paths for the repo in the composer.json, which allows you to commit and push it without thinking about messing everything up for other devs.

More documentation on the “home config”: https://getcomposer.org/doc/03-cli.md#composer-home-config-json

PHP. Javascript. Squash. @kbrabrand


16 comments

  • Tomas Votruba

    Hey, I just used this tut and it's really simple and easy to setup and use. Thanks for sharing!


  • Tomas Votruba

    For inspiration: I've also found this tl;dr; version: http://marekkalnik.tumblr.com/post/22929686367/composer-installing-package-from-local-git


  • Darius

    Getting error:

    [InvalidArgumentException]
    No driver found to handle VCS repository /cygdrive/c/duomenys/projektai/no_
    git/others/sandbox/HelloWorld

    how can I fix it? Or do I have to install some repository? Usually I use bitbucket repositories


  • Shambhu Kumar

    No driver found to handle VCS repository ../package

    error is coming


    • Kristoffer Brabrand

      I think you've forgot to run git init inside ../package.

      In order for composer to load the dependency, you have to initialize the git repo and make at least one commit on the the branch you've required in your projects composer.json file (master in the examples in this blog post).

  • Gauthier Delamarre

    This tip could a be time saver for anyone working on some early stage project split in multiple packages. Referring to a local clone allows to make a separate library evolve together with the main project without publishing unstable versions to a public repository (shared with other contributors for instance).

    To make it even comfortable, may I suggest to move the "repository" section from project/composer.json to HOME/.composer/config.json? :)

    Doing this allows to commit and push the composer.json without conflicts with other contributors!

    Learn more this: https://getcomposer.org/doc/03-cli.md#composer-home-config-json

    Gauthier

    PS: by the way, in your last terminal sample, there is a copy/paste issue on line 3 :) Should be "cd ../project", not "cd ../package"


    • Kristoffer Brabrand

      Thanks for the input. I've added the suggestion to the blog post and fixed the typo.

  • Sam

    Could save someone some time with the latest composer (because I assume what you have here worked well).

    In PROJECT/COMPOSER.JSON, your require should look like

    "require": {
    "kbrabrand/package": "dev-master"
    }

    Otherwise composer will scream at you about not finding a version.


  • forever

    Thanks, this helps !

    If anyone is interested, here is my alias for Cmder to do this :
    syncfw=D: $T cd D:\path\to\package $T git add --all . $T git commit -m "dev going on" $T cd ../project $T composer update

    A git init must have been done in the package directory and a composer install in the project directory.


  • Barry

    Since a few days it's possible to just use the path, without creating a new repo :)
    https://medium.com/@barryvdh/using-local-repositories-to-easily-install-private-magento-extensions-with-composer-7eb966dec23e


  • Ozan Müyesseroğlu

    Thanks Sam, before "dev-master" I was getting "Your requirements could not be resolved to an installable set of packages." error.


  • Gauthier Delamarre

    @Kristoffer thanks for quoting me in your blog update ;) I'm sorry I didn't came earlier, but I didn't get any notification about new comments since my last visit ;)


  • Alasdair

    You should update this to use 'path' rather than 'vcs' in the project's composer.json, that way a symlink is used and no commiting / updating is required.


  • rouflak

    If you want to have it working without a git repository, provide the `path` type instead of `vcs`, and keep the `url` key in which you provide the path (full or not) to the folder containing the composer.json


  • TechNikh

    But what about other libraries that need not be in local?
    Like in https://github.com/opdavies/oliverdavies.uk/blob/master/composer.json I want to work & test locally on just one plugin called sculpin-gist-embed-bundle. All other plugins like behat, mink.. I want to be loaded normally.


  • Stephan

    This still forces you to run composer update every single time which is a big hassle. Is there really no way to just keep them in sync with eachother? I know some nasty workarounds but common, there must be a proper way for this right?


Leave your comment