Composer: Managing Product Dependencies

Composer: Managing Product Dependencies

There's a lot of awesome libraries out there that you can use with your projects. What's the best way to manage those dependencies and keep everyone using the same version? Commit them to your repository? Use git submodules?

Try Composer. It's an extremely easy way to document dependencies, lock them down to specific versions, and then help to ensure every developer on the project is using the same versions of the included dependencies.

Learn how to use Composer to manage dependencies and then learn how to share your libraries with others by publishing your own composer compatible projects.

68da13f92564f1fef1944f292502b0d3?s=128

Russell Ahlstrom

January 17, 2014
Tweet

Transcript

  1. Introduction Managing Project Dependencies Russell S. Ahlstrom @janiv2

  2. VP of Web Development at Scan, Inc. Scan’s goal is

    to make it easy to connect the digital and physical worlds. Currently, we’re doing that with QR Codes. We have QR codes for business cards, following or liking people on social networks, connecting to wi-fi networks, contacting people, or just showing plain text. We’re currently beta testing two new types of QR Codes. One, which we’re calling Scan to Pay, allows for collecting mobile payments or donating to charities. For example, you see something in the Sky Mall magazine you want to buy. You scan the QR code and buy it immediately. Or maybe someone hands you a flyer from the Red Cross asking for help in the Philippines due to the recent typhoon. You can scan the qr code on the flyer to donate to the Red Cross. The second, called Scan to Market, encourages customers of business to share something on social networks to receive some kind of incentive from the business--such as free chips or a 5% discount, etc. Personally, I’ve been doing PHP for over 13 years and professionally for over 10. I’m a sucker for the Symfony 2 framework. I love writing all my CSS in SASS. And if I need to do something fancy in JavaScript, I love doing it in Ember. I’m a huge board game fan and I spend all my hard earned money on Kickstarter. You can scan my QR code to see my board game collection or view some of the things I’ve pledged for on Kickstarter.
  3. The Problems What Problems Are We Solving? We’re talking about

    Composer today and how we can use it to manage project dependencies. Let’s talk about some of the problems project dependencies can cause.
  4. The Problems Project Setup Let’s say you’re the new guy

    at work. Your first day is spent getting your environment all setup so you can start developing on the company’s website. The senior developer tells you you’ll need to clone the repo down from github and then install some libraries the site depends on. You need to go download Guzzle, Monolog, Doctrine, the Zend Framework, some company specific libraries, PHPUnit, a Selenium WebDrive for PHP, etc. You have to go find where to download all these libraries, figure out where to put them, and then how to hook them all up correctly. It takes you an hour or so and then you try to run the website. Nothing works. You’re getting these errors about method definitions not being defined or wrong arguments being passed, etc. You go and bug the senior developer. He comes and talks a look at your setup and starts debugging. After a while, he figures it out. You downloaded the latest version of the Zend Framework and Doctrine while the company is still a few versions behind. You delete the newer libraries, download the older ones, and things seem to be working. And then you run your unit tests. Looks like the PHPUnit version you installed is wrong too. You have to figure out which version you need to be using and install that one instead.
  5. $ cd ~/projects/companyWebsite $ wget https://github.com/guzzle/guzzle/archive/ master.zip $ unzip master

    -d vendor $ git add ./ $ git commit -m ‘Adds Guzzle Library’ You decide to make this easier for the next new guy. Instead of having to download all the 3rd party libraries, let’s just commit them to our project. That way the next time someone clones down the project, they already have everything. This seems look a good idea, right? You do this with all the libraries and pretty soon have them all committed and push your changes up. The senior developer stops by. Hah. He’s not too happy. You’ve added all this code to the repo that’s not yours. Your repo is huge, if a new version of the library comes out, you have to download it, overwrite all the old files, and commit it. You no longer have the commit history of the libraries. Instead you have the commit history of ‘Updates guzzle”, “Updates guzzle to latest version”, “Updating libraries”. And now that the libraries are committed to the repo, it won’t be long before someone decides they can just modify something in a library to fix an issue they’re having and commit it. Now you’ve forked the library and upgrading will either become a lot harder or someone will overwrite the library and not notice the customizations your company has made to it. It’s a bad idea. Don’t do this.
  6. $ cd ~/projects/companyWebsite $ git submodule add git@github.com:guzzle/guzzle.git $ git

    commit -m ‘Adds Guzzle Library’ Okay, so scratch committing these libraries directly to our repo. Googling around, you find out about git submodules and decide to use those. You add submodules to your repo for each library and commit those up.
  7. $ cd ~/projects/companyWebsite $ git submodule init $ git submodule

    update A new developer comes along and is setting up his project. He clones out the repo and he gets all these blank folders with no code in them. Oh, you explain to him he needs to do a git submodule init, and then a git submodule update and that’ll clone everything down. Perfect.
  8. $ cd ~/projects/companyWebsite $ cd guzzle $ git checkout master

    $ git pull $ cd ../ $ git add ./ $ git commit -m ‘Updates Guzzle Library’ New versions of all the libraries you use come out. Now you need to figure out how to update all your submodules. You google around. Ah, you have to go inside of each folder, and do a git checkout master, git pull, and then go back to the root of the project, and commit the submodule update. Not a huge deal. If you have a ton of libraries you can either do each library by hand one at a time as above, or you can google around until you find out that you can also do, “git submodule foreach git pull origin master”. Problem solved. Things seem to be working well. Why not just use submodules? Sure there’s a little training required and updating submodules is a little weird but no big deal, right.
  9. The Problems What About Non-Git Libraries? What are you going

    to do when you find a non-git library you want to do? You find some cool library using subversion or you need to use something from pear. Now what? Are you going to keep a git mirror of that svn project you want to use? It’s a pain.
  10. A Better Way There Has to be a Better Way!

  11. A Better Way There Has to be a Better Way!

  12. What is Composer? What is Composer?

  13. What is Composer? Tool for Dependency Management Composer is a

    tool for dependency management in PHP. It allows for you to declare the dependent libraries your project needs and it will install them in your project for you. From getcomposer.org
  14. What is Composer? Not a Package Manager Composer is not

    a package manager. Yes, it deals with “packages” or libraries, but it manages them on a per-project basis, install them in a directory (e.g vendor) inside your project. By default it will never install anything globally. Thus, it is a dependency manager. From getcomposer.org This isn’t a new idea. Composer is strongly inspired by Node’s NPM or Ruby’s bundler. There wasn’t a similar tool for PHP until Composer.
  15. What is Composer? What does Composer do? • You have

    a project that depends on a number of libraries • Some of those libraries depend on other libraries. • You declare the things you depend on. • Composer finds out which versions of which packages need to be installed, and installs them (meaning it downloads them into your project). Composer not only downloads the libraries you’ve asked for but also any libraries that those libraries depend on as well. It also figures out what versions of all those libraries to use to get them all working happily together. From getcomposer.org
  16. $ curl -sS https://getcomposer.org/installer | php $ php composer.phar --version

    Composer version b20021cc6aa113069e4223b78d0074a1fc7dd4e8 2014-01-14 16:22:09 Installing locally.
  17. $ curl -Ss https://getcomposer.org/installer | php $ mv composer.phar /usr/local/bin/composer

    $ composer --version Composer version b20021cc6aa113069e4223b78d0074a1fc7dd4e8 2014-01-14 16:22:09 Installing globally. This is what I usually do and it’s worked well for me.
  18. $ brew tap josegonzalez/homebrew-php $ brew install josegonzalez/php/composer $ composer

    --version Composer version b20021cc6aa113069e4223b78d0074a1fc7dd4e8 2014-01-14 16:22:09 I just learned about this and haven’t tried it? Has anyone? How does it handle updating composer? Do you do composer self update still or brew upgrade? If I was installing new, I’d probably try it this way.
  19. $ cd ~/projects/companyWebsite $ vim composer.json { “require”: { “guzzle/guzzle”:

    “~3.8”, “monolog/monlog”: “~1.7” } } You create a composer.json and then minimally list your required dependencies and versions.
  20. $ cd ~/projects/companyWebsite $ composer init Welcome to the Composer

    config generator This command will guide you through creating your composer.json config. Package name (<vendor>/<name>) [scan/company-website]: Description []: The website for our company Author [Russell Ahlstrom <russell@scan.me>]: Minimum Stability []: License []: MIT Define your dependencies. Would you like to define your dependencies (require) interactively [yes]? Search for a package []: guzzle/guzzle Found 15 packages matching guzzle/guzzle [0] guzzle/guzzle Enter package # to add, or the complete package name if it is not listed []: 0 Enter the version constraint to require []: ~3.8 Search for a package []: Would you like to define your dev dependencies (require-dev) interactively [yes]? no { "name": "scan/company-website", "description": "The website for our company", "require": { "guzzle/guzzle": "~3.8", "monolog/monolog": "~1.7" }, "license": "MIT", "authors": [ { "name": "Russell Ahlstrom", "email": "russell@scan.me" } ] } Do you confirm generation [yes]? If you have a hard time remembering the basic format, you can also do composer init and it’ll walk you through creating a composer.json. It’ll ask you a bunch of questions.
  21. { "name": "scan/company-website", "description": "The website for our company", "require":

    { "guzzle/guzzle": "~3.8", "monolog/monolog": "~1.7" }, "license": "MIT", "authors": [ { "name": "Russell Ahlstrom", "email": "russell@scan.me" } ] } Here’s the end result from the script but bigger.
  22. Versioning guzzle/guzzle: “3.4.3” You’re using this version and this version

    only.
  23. Versioning guzzle/guzzle: “3.4.*” 3.4.* means 3.4.1, 3.4.2, 3.4.3, etc.

  24. Versioning guzzle/guzzle: “>=3.4,<4” This means anything 3.4 but less than

    4.
  25. Versioning guzzle/guzzle: “>=3.4,<3.6|>=4” You can also do an “or” with

    a pipe. “And” has higher precedence.
  26. Versioning guzzle/guzzle: “~3.4” Most useful with semantic versioning and this

    is the one I try to use if possible. Semantic versioning works like this. x.y.z Z is changed if it’s just a simple bug fix. Everything is still backwards compatible and no new public API’s are introduced. Y is changed if new public API’s are introduced and everything is still backwards compatible. Z is reset to 0. Y is also changed if some public API’s are deprecated. It can also be changed if there is substantial changes in the private code. X is changed when you break backwards compatibility. Y and Z are reset to 0.
  27. Versioning guzzle/guzzle: “~3.4.2” This is the same as >=3.4.2,<3.5

  28. Versioning guzzle/guzzle: “3.9.*@dev” You can also do @alpha, @beta, @RC

  29. Versioning guzzle/guzzle: “dev-branch” Branch can be any branch name in

    your repo. You see dev-master a lot.
  30. Versioning guzzle/guzzle: “dev-branch#0a66” You really shouldn’t do this. If you

    are doing it, it should be for a very short time only. You want to move off to a version as soon as possible.
  31. $ composer update Loading composer repositories with package information Installing

    dependencies - Installing psr/log (1.0.0) Downloading: 100% - Installing monolog/monolog (1.7.0) Loading from cache ... Writing lock file Generating autoload files Composer update writes a lock file.
  32. Version Control • Commit - composer.json - composer.lock • Add

    to .gitignore - vendor/ Version Control
  33. $ git clone git@github.com:scaninc/website.git $ composer install Loading composer repositories

    with package information Installing dependencies from lock file - Installing psr/log (1.0.0) Downloading: 100% - Installing monolog/monolog (1.7.0) Downloading: 100% - Installing symfony/event-dispatcher (v2.4.1) Downloading: 100% - Installing guzzle/guzzle (v3.8.0) Downloading: 100% Generating autoload files New developer comes on and all they need to do now is clone down the project and run composer install. All the dependencies with all the right versions are automatically downloaded and installed in the correct places. People who aren’t managing dependencies should also use composer install. Do not do composer update. composer install reads the lock file and installs the versions specifically mentioned in that file. It does not use composer.json and find versions meeting the rules specified there.
  34. { “require”: { “guzzle/guzzle”: “~3.8”, “monolog/monolog”: “~1.7” }, “require-dev”: {

    “phpunit/phpunit”: “~3.7” } } $ composer install --dev Loading composer repositories with package information Installing dependencies (including require-dev) - Installing phpunit/phpunit (3.7.28) Downloading: 100% Writing lock file Generating autoload files You can also have dev requirements that are only installed if you pass the dev flag.
  35. { “require”: { “php”: “~5.4”, “ext-intl”: “*”, “lib-curl”: “*”, “guzzle/guzzle”:

    “~3.8”, “monolog/monolog”: “~1.7” }, “require-dev”: { “phpunit/phpunit”: “~3.7” } } You can also require platform packages. They won’t be installed by composer but composer will check to see if they are met and error out with messages about what platform packages are missing.
  36. $ composer update Loading composer repositories with package information Updating

    dependencies (including require-dev) - Removing guzzle/guzzle (v3.8.0) - Installing guzzle/guzzle (v3.8.1) Loading from cache Writing lock file Generating autoload files $ git add composer.json $ git add composer.lock $ git commit -m ‘Updates guzzle to newest version’ When a new version of a library you use comes out, you can update to that new version by running composer update. This updates the composer lock file.
  37. $ composer install Loading composer repositories with package information Updating

    dependencies from lock file - Removing guzzle/guzzle (v3.8.0) - Installing guzzle/guzzle (v3.8.1) Loading from cache Other developers can then just run composer install and get the updates specified in the lock file.
  38. $ composer update Loading composer repositories with package information Updating

    dependencies (including require-dev) Nothing to install or update Generating autoload files Let’s say Guzzle 4.0 comes out and want to upgrade. We run composer update but nothing happens.
  39. $ composer update Loading composer repositories with package information Updating

    dependencies (including require-dev) Nothing to install or update Generating autoload files { “require”: { “guzzle/guzzle”: “~3.8”, “monolog/monolog”: “~1.7” } } What’s wrong? guzzle is ~3.8 which means >=3.8,<4. Guzzle 4.0 doesn’t match.
  40. { “require”: { “guzzle/guzzle”: “~4.0”, “monolog/monolog”: “~1.7” } } $

    composer update Loading composer repositories with package information Updating dependencies (including require-dev) - Removing guzzle/guzzle (v3.8.0) - Installing guzzle/guzzle (v4.0.0) Loading from cache Writing lock file Generating autoload files Update the min version in the composer.json and re-run composer update. Dependencies now update correctly.
  41. require: { “guzzle/guzzle”: “~4.0” “monolog/monolog”: “~1.7” } Checks to make

    sure composer.json is a valid format.
  42. require: { “guzzle/guzzle”: “~4.0” “monolog/monolog”: “~1.7” } $ composer validate

    ./composer.json is invalid, the following errors/ warnings were found: "./composer.json" does not contain valid JSON Parse error on line 3: ...le": "~4.0" "monolog/monolog": " ----------------------^ Expected one of: 'EOF', '}', ':', ',', ']' Checks to make sure composer.json is a valid format.
  43. <?php namespace Scan\BoardGameInfo; use Guzzle\Http\Client; require_once 'vendor/autoload.php'; $client = new

    Client('http://boardgamegeek.com/ xmlapi2/'); $request = $client->get('thing?id=34119'); $response = $request->send(); echo $response->getBody(); The composer autoloader supports psr-0, psr-4, class maps, and file including. Most libraries are going to be using psr-0 or psr-4. All you have to do to use the autoloader is include it in and then you can start using all your dependencies automatically.
  44. $ composer dumpautoload -o Generating autoload files return array( 'Guzzle\\Http\\Client'

    => $vendorDir . '/guzzle/ guzzle/src/Guzzle/Http/Client.php', ); Class maps are the fastest autoloading type. You can user composer to automatically convert PSR-0 or PSR-4 autoloaders into class maps.
  45. Packagist is where you can go to search for packages

    you can install through composer. This also where you’d go to submit your own packages.
  46. { “repositories”: [ { “type”: “vcs”, “url”: “git@github.com:scaninc/topsecret.git” } ],

    “require”: { “guzzle/guzzle”: “~3.8”, “monolog/monolog”: “~1.7”, “scan/topsecret”: “~0.5” } } The url can point to an hg or svn. This will only work though if the repository already has a composer.json file. Good for private repositories that you cannot put up on packagist, etc.
  47. { "repositories": [ { "type": "package", "package": { "name": "chibimagic/webdriver",

    "version": "dev-master", "dist": { "url": "https://github.com/chibimagic/WebDriver-PHP/archive/master.zip", "type": "zip" }, "autoload": { "psr-0": { "WebDriver": "" } } } } ], “require-dev”: { “chibimagic/webdriver”: “dev-master” } } If the project doesn’t have a composer.json file at all and you can’t get one added, you can still use the library by manually specifying all the information for the repository.
  48. { "repositories": [ { "type": "pear", "url": "http://pear2.php.net" } ],

    "require": { "pear-pear2.php.net/PEAR2_Text_Markdown": "*", "pear-pear2/PEAR2_HTTP_Request": "*" } } Composer can also manage pear dependencies. Package name becomes pear-{channelName} or pear-{channelAlias}. Still installed in vendor.
  49. Conclusion Share Your Libraries Upload them to packagist. Control your

    version numbers via tags or branches in the repo. Don’t try specifying your version manually in the composer.json file.
  50. Conclusion • You can suggest, replace, or conflict with other

    packages. • Composer can run scripts before and after the command runs or before/after a package is installed. • There’s a global config file if you need to tweak things. • You can install packages globally if you really want to. • A whole bunch of command line stuff. • You can host your own private packagist-like site. Things I Didn’t Tell You
  51. Conclusion Questions? More info also at http://getcomposer.org