Composer

composer.json and composer.lock

Dependencies are specified in the composer.json in the root of your package

These dependencies can demand specific versions, or allow for some flexibility

When running the upgrade command, composer will traverse all the dependencies in all packages' composer.json files. Once it's calculated the result of this, it'll write specific versions to composer.lock

Here's a starting point for a composer.json:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
{
  "name": "edmondscommerce/module",
  "description": "Project description goes here.",
  "license": "MIT",
  "autoload": {
    "psr-4": {
      "EdmondsCommerce\\Module\\": [
        "src/"
      ]
    }
  },
  "autoload-dev": {
    "psr-4": {
      "EdmondsCommerce\\Module\\": [
        "tests/"
      ]
    }
  },
  "require": {
  },
  "require-dev": {
  },
  "config": {
    "bin-dir": "bin",
    "preferred-install": {
      "vendor/*": "source",
      "*": "dist"
    },
    "optimize-autoloader": true
  }
}

require and require-dev

require is for dependencies when your package is itself is a dependency of another

require-dev is for dependencies when your package is the root of the project

Version matching

  • "1.0.0" requires exactly version 1.0.0
  • ">=1.0"" requires at least version 1.0
  • ">=1.0 <=2.0"" requires anything between 1.0 and 2.0
  • "1.0.*"" requires version 1-point-0-point-anything
  • "^1.0.0"" allows for anything matching the major version. Equivalent to ">=1.0.0 <2.0.0"
  • "~1.0.0"" allows for anything matching the minor version. Equivalent to ">=1.0.0 <1.0.0"

Commands

Install

Installs all of the required dependcies based on the composer.lock file

Update

Ignores the composer.lock file and pulls in the latest versions that satisfy the requirements in composer.json, then writes the result to composer.lock

Require

Adds a new require node to the composer.json and runs an upgrade

Verbose mode

Add -v, -vv, -vvv to any command to increase the verbosity

Fixing a third party composer lib

Track the master branch of the lib. This is done by adding an inline alias.

Figure out which version is currently required. This can be done by looking at either the composer lock file of your project, or the composer.json file of the project that pulls in the module

Update your composer.json file to include the following in the require section

1
2
3
"require": {
    "example/example": "dev-master as 2.0"
}

The run composer update example/example and the master branch will be pulled in. You can confirm this if the following is output

1
2
  - Removing example/example (2.0.9)
  - Installing example/example (dev-master a96add4) Cloning a96add4526

At this point you should check if the bug you are wanting to fix is still in the master branch. IF it is you will need to fork the repo and pull in that version to your project

Package Versioning

When working on a package, whether it is hosted open source on Github or hosted privately you will often find that you need to mark certain features as stable. Stability means that it is easier to pull your package as a dependency in a library in the future.

To give your package a version, you need to tag your current commit and push your tags to the repository. If you are using Packagist and it is hooked up to your repository correctly then the version will auto update.

When versioning, be sure to maintain a consistent versioning scheme, most developers follow some form of Semantic Versioning. 1.5.8 - Major.Minor.Incremental A major version change usually means backwards compatibility breakages and other major changes, a minor version would be smaller changes or added features. The incremental version will often be a fix or path for bugs.

To set your version, tag your repository with your desired version, Composer will try to fetch the package that resembles the requirements set as close as possible.

1
2
git tag 0.1.0
git push --tags

This will ensure that your tags are pushed up to your remote repository, you do not need a new commit to be awaiting push to be able to tag and push the new tag reference.

Forking

Find the project on github and press the fork button.

Update your composer.json file to use your version as a repository and pull in the source version so you can work on it, i.e.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
"repositories": {
     "my-example-repo": {
          "type": "vcs",
          "url": "git@github.com:edmondscommerce/example.git"
     }
},
"config": {
    "preferred-install": {
         "example/example": "source"
    }
}

Run composer update again to bring in the remote version. Note that if the lib has already been installed you may need to delete the current version, i.e. rm -rf vendor/example/example

composer update example/example

Confirm that this has worked by cd in to the directory and running git remote. This should now be pointing at your version of the repo

1
2
3
4
5
6
cd vendor/example/example
git remote -v
composer    https://github.com/edmondscommerce/example.git (fetch)
composer    https://github.com/edmondscommerce/example.git (push)
origin  https://github.com/edmondscommerce/example.git (fetch)
origin  git@github.com:edmondscommerce/example.git (push)

Create a new branch, update the composer alias to point at that branch e.g. dev-myfix, and apply your fix

Push the changes up to your repo, and then make a pull request to the main repo.

If this is accepted then you can remove all of the above and go back to using the main repo.

Working on a Local Repo

If you are working on a library that is already on Packagist, but you want to bring in bleeding edge local updates, you have 2 basic ways of working:

1. Work inside the vendor folder directly

You can bring in your dependency and as long as it was brought in as source rather than dist then it is a checked out git repo and you can work on it.

Simply

1
2
3
4
5
cd vendor/edmondscommerce/packagename
git checkout master
#do stuff
git commit -m 'commit message'
git push

2. Work in a separate project

Perhaps a cleaner way to work is to do this:

1. Bring in the dependency to your main project as normal using

composer require edmondscommerce/packagename

2. Also clone the repo as a standalone project in a separate location
1
2
cd /var/www/vhosts/;
git clone git@github.com:edmondscommerce/packagename.git
3. Open this as a separate PHPStorm project and work on it as normal

Simply as normal, open the folder using PHPStorm as a new project

4. Commit changes locally
1
2
3
cd /var/www/vhosts/packagename
git add -A;
git commit -m 'commit message'
5. On your main project, add a local remote
1
2
3
cd /var/www/vhosts/main-project
cd vendor/edmondscommerce/packagename
git remote add local ../../path/to/packagename.git
6. As required, pull in local changes
1
2
3
cd /var/www/vhosts/main-project
cd vendor/edmondscommerce/packagename
git pull local master
7. When you are happy with your changes, push them
1
2
cd /var/www/vhosts/packagename
git push

Warning

Don't forget to push your work to Github!!!

Composer Packages Best Practices

It is a very good idea to develop small defined packages that can easily be reused in other projects.

Here are some tips on the best way to do this:

Bin Directory

The bin directory is where you should put any command line tools that you want your package to expose.

The file can be a BASH, PHP or any other language you want although these two are most common and advisable.

Bash Command Files

Best suited for commands that will actually call other command line tools

Should adhere to standard EC Bash coding practices and implemement the standard Bash script header

Again should make proper use of exit codes.

PHP Command Files

Using PHP is the most obvious and is recommended for single use commands.

For complex commands with options, arguments etc then it is highly advised to use the Symfony Console Component.

For commands with no arguments then this is not required.

Finding and Including Composer Autoloader

To resolve the path to vendor/autoload.php and then require it, the following code snippet is very useful:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
#!/usr/bin/env php
<?php declare(strict_types=1);
$files = [
    __DIR__.'/../../../autoload.php',
    __DIR__.'/../vendor/autoload.php',
];

$autoloadFileFound = false;
foreach ($files as $file) {
    if (file_exists($file)) {
        require $file;
        $autoloadFileFound = true;
        break;
    }
}

if (!$autoloadFileFound) {
    echo 'You need to set up the project dependencies using the following commands:'.PHP_EOL.
         'curl -s http://getcomposer.org/installer | php'.PHP_EOL.
         'php composer.phar install'.PHP_EOL;
    die(1);
}
This allows the bin command to work if the library is checked out as the root project or as a dependency of another project.

Basic Rules

The file name should not have .php on the end

This is just for aesthetics but is recommended

The file should have a shebang at the top

The first line should be a shebang that uses env to associate the file with PHP #!/usr/bin/env php

The exit code should be 0 on sucesss

To be explicit, your script should exit(0); when it has run successfully

The exit code should be a positive integer on failure

To be explicity, your script should exit(1); on failure. (Or another number if you prefer, though 1 is usual for "general error"

Adding Bin Commands to composer.json

To actually have you bin command available to projects that install your library as a dependency, you need to add something like the following to your composer.json

1
2
3
4
 "bin": [
    "bin/my-bin-cmd-script",
    "bin/another-bin-cmd-script"
  ],

Packagist

Importing a GitHub project into Packagist

After importing and hooking up GitHub project to update Packagist, developers will be able to download your project as a dependency using composer require edmondscommerce/somemodule --dev

Note

If you want to get the the most up to date dev version of dependency enter this instead composer require edmondscommerce/somemodule:dev-master@dev --dev. You can read more about branch aliases in here

  • Go to Packagist repository submit page which is here
  • Click button "Check" and if your Github repository will be found, click "Submit". After that, your packagist repository will get created.
  • To sync any changes that are pushed to your Github repository with Packagist, you need to setup a "GitHub Service Hook"
  • Got your GitHub repository
  • Click "Settings"
  • Then "Integrations & services"
  • Then search for "Packagist" service
  • Enter your Packagist username and token (press "Show API Token" button), which you can find in this page.
  • And click "Add". This will create a Packagist service for your repository.
  • To test if service is working, select the service and click "Test service button" in up right corner.
  • Go back to your Packagist repository and check if you no longer see a message that your repository is not autoupdated. Or go to your packages page and check if it doesn't have words "Not Auto-Updated" nearby your repository. If it doesn't exists, you have successfully hooked Github repository with Packagist auto update.