Jordi Boggiano
@seldaek


Composer Best Practices

Composer

Managing
Packages vs Dependencies

Semantic Versioning

semver.org

MAJOR . MINOR . PATCH

1 . 2 . 3

MAJOR . MINOR . PATCH

Breaks           Features           Fixes  

Releasing Packages

README.md

Short story about what problems the code solves

How to use and contribute

License


Read "On Open Sourcing Libraries" goo.gl/uNTt7K

Check phppackagechecklist.com

Consider a Code of Conduct contributor-covenant.org

CHANGELOG.md

List relevant additions and fixes

List BC breaks prominently

Write an UPGRADE.md if needed

Check keepachangelog.com

Version Constraints

Exact Match

1.0.0   1.2.3-beta2   dev-master

Wildcard Range

1.0.*   2.*

Hyphen Range

1.0 - 2.0   =   >=1.0.0 <2.1

Hyphen Range

1.0.0 - 2.1.0   =   >=1.0.0 <=2.1.0

Unbounded Range (BAD)

>=1.0

Operators

= AND    || = OR

Next Significant Release

~1.2   =   >=1.2.0 <2.0.0

Next Significant Release

~1.2.3   =   >=1.2.3 <1.3.0

Caret / Semver Operator

^1.2.3   =   >=1.2.3 <2.0.0

Caret / Semver Operator

^0.3   =   >=0.3.0 <0.4.0

Libraries should use ^

.. and you should too!

Installing new packages

Require uses ^

composer require monolog/monolog

Require uses ^

"monolog/monolog": "^1.15"

Composer Stabilities

Stabilities

dev -> alpha -> beta -> RC -> stable

Tags

2.0.2 -> stable

2.0.0-beta2 -> beta

Branches

2.0 -> 2.0.x-dev (dev)

master -> dev-master (dev)

lala-feature -> dev-lala-feature (dev)

Allowing various stabilities

{
    "require": {
        "foo/bar": "^1.0@dev",
        "foo/baz": "^1.0@alpha",
        "foo/qux": "1.0.x-dev"
    },
    "minimum-stability": "beta"
}
                

Allowing various stabilities

composer create-project unstable/project --stability dev
composer create-project unstable/project path/ ^1.0@dev
composer create-project unstable/project -s dev
                

Resolution Conflicts

Overly Strict Requirements

// composer.json
{
    "require": {
        "cool/alice": "~1.3",
        "lazy/bob": "~1.2"
    }
}

// dependencies
{
    "name": "cool/alice",
    "require": {
        "monolog/monolog": "~1.6"
    }
}
{
    "name": "lazy/bob",
    "require": {
        "monolog/monolog": "1.3.*"
    }
}
            

Overly Strict Requirements

Your requirements could not be resolved to an installable set of packages.

  Problem 1
    - Installation request for lazy/bob ~1.2 -> satisfiable by lazy/bob[1.4.0].
    - Installation request for cool/alice ~1.3 -> satisfiable by cool/alice[1.3.0].
    - lazy/bob 1.4.0 requires monolog/monolog 1.3.* -> satisfiable by monolog/monolog[1.3.0, 1.3.1].
    - cool/alice 1.3.0 requires monolog/monolog ~1.6 -> satisfiable by monolog/monolog[1.6.0, 1.7.0].
    - Can only install one of: monolog/monolog[1.6.0, 1.3.0].
    - Can only install one of: monolog/monolog[1.6.0, 1.3.1].
    - Conclusion: don't install monolog/monolog 1.3.1
    - Conclusion: don't install monolog/monolog 1.7.0
    - Conclusion: don't install monolog/monolog 1.3.0
    - Conclusion: don't install monolog/monolog 1.6.0
            

Stability Resolution

// composer.json
{
    "minimum-stability": "beta",
    "require": {
        "monolog/monolog": "1.*",
        "symfony/symfony": "~2.4",
        "bad/package": "dev-master"
    }
}

// dependencies
{
    "name": "bad/package",
    "require": {
        "monolog/monolog": "dev-master",
    }
}
            

Stability Resolution

Your requirements could not be resolved to an installable set of packages.

  Problem 1
    - Installation request for bad/package dev-master -> satisfiable by bad/package[dev-master].
    - bad/package dev-master requires monolog/monolog dev-master -> no matching package found.
            

Forced to require monolog in dev version

Stability Resolution

// composer.json
{
    "minimum-stability": "beta",
    "require": {
        "monolog/monolog": "1.*@dev",
        "symfony/symfony": "~2.4",
        "bad/package": "dev-master"
    }
}

// dependencies
{
    "name": "bad/package",
    "require": {
        "monolog/monolog": "dev-master",
    }
}
            

Stability Resolution

// monolog
{
    "name": "monolog/monolog",
    "extra": {
        "branch-alias": {
            "dev-master": "1.12.x-dev"
        }
    }
}
            

Stability Resolution

- Installing monolog/monolog (dev-master 5ad421d)
  Cloning 5ad421d6a1d5d7066a45b617e5164d309c4e2852
            

Stability Resolution

Six months later... monolog 2.0!

Stability Resolution

// monolog
{
    "name": "monolog/monolog",
    "extra": {
        "branch-alias": {
            "dev-master": "2.0.x-dev"
        }
    }
}
            

Stability Resolution

Your requirements could not be resolved to an installable set of packages.

  Problem 1
    - Installation request for monolog/monolog 1.*@dev -> satisfiable by monolog/monolog[1.12.0].
    - Installation request for bad/package dev-master -> satisfiable by bad/package[dev-master].
    - bad/package dev-master requires monolog/monolog dev-master -> satisfiable by monolog/monolog[dev-master].
    - Can only install one of: monolog/monolog[1.12.0, dev-master].
            

Forced to require monolog 2.*@dev or dev-master.

master might have broken bad/package and our project.

The Lock File

composer.lock

Who adds it to .gitignore?

Don't ignore it!

Reproducible installs

Don't ignore it!

Fast installs with low memory usage

composer.lock

Commit it

Use it with composer install

But maybe don't deploy it..

Custom composer commands

Add commands to scripts

{
    "require-dev": {
        "phpunit/phpunit": "~4.0"
    },
    "config": {
        "bin-dir": "vendor/bin"
    },
    "scripts": {
        "test": "phpunit"
    }
}
            

Run commands

composer test
composer test -- --filter Foo
            

Checking your minimum dependencies

Checking your minimum dependencies

composer update --prefer-source --prefer-lowest --prefer-stable
            
language: php
sudo: false
matrix:
    include:
        - php: 5.6
        - php: 5.6
          env: deps=low
    fast_finish: true

before_script:
  - if [ "$deps" == "low" ]; then composer update --prefer-source --prefer-lowest --prefer-stable; fi
  - if [ "$deps" != "low" ]; then composer update --prefer-source; fi

script: ./vendor/bin/phpunit
            

Using a forked project

Using a forked project

{
    "repositories": [
        {
            "type": "vcs",
            "url": "https://github.com/Seldaek/symfony"
        }
    ],
    "require": {
        "symfony/symfony": "dev-master"
    }
}
            

Additional repositories take priority over the default ones

Using a forked project

{
    "repositories": [
        {
            "type": "vcs",
            "url": "https://github.com/Seldaek/symfony"
        }
    ],
    "require": {
        "symfony/symfony": "dev-my-patch"
    }
}
            

Your branches are available as well

Use composer show -v symfony/symfony to see available versions

Using a forked project

{
    "repositories": [
        {
            "type": "vcs",
            "url": "https://github.com/Seldaek/symfony"
        }
    ],
    "require": {
        "symfony/symfony": "dev-my-patch as 2.5.0"
    }
}
            

Community

packanalyst.com

semver.mwl.be

melody.sensiolabs.org

packagist.graphstory.com

github.com/ziadoz/awesome-php

Toran Proxy

Fast and reliable installs

Mirrors git repos and zip files

Private package hosting

Easy web interface configuration

Share the love

Profits not only fund Toran but also Composer and Packagist maintenance

Check it out

toranproxy.com

Contributing

There are no small contributions

Do not be afraid

It is not all about code

Help triaging issues, etc

Don't know what to work on?

Find something that annoys you

Thank you.

Questions?

@seldaek

slides.seld.be