Chrome for Android fix.

Uncommon (Ab)Uses of Composer

Alain Schlesser
Software Engineer & WordPress Consultant

Introduction to Composer

  • The package manager for PHP

  • Manages dependencies of your project

  • Generates optimized autoloaders

  • Can fetch packages from diverse, de-centralized sources

  • Together with PHP-FIG initiative, has changed the PHP landscape

 

https://getcomposer.org/

Composer's Role in a PHP Project

  • Centralized control over project lifecycle

  • Provides separate production and development paths

  • Makes projects self-contained

 


                $ git clone mycompany/project
                $ cd project
                $ composer install
            

Scripts vs Plugins

  • Two mechanisms for enhancing default Composer functionality

  • Use same hooks

    ( i.e. "post-install-cmd" <=> ScriptEvents::POST_INSTALL_CMD )

  • Overlap in multiple areas

Composer Scripts

  • Stored in project's composer.json file

  • Run on root package only

  • Intended for project automation

Composer Plugins

  • Stored in external package of type composer-plugin

  • Run on all included packages

  • Intended for Composer extension

Composer Script


        {
          "name": "mycompany/package",
          "require-dev": {
            "phpunit/phpunit": "~6"
          },
          "scripts": {
            "post-update-cmd": "@clearCache",
            "test": [
              "@clearCache",
              "phpunit"
            ],
            "clearCache": "rm -rf cache/*"
          }
        }
      

Composer Plugin


            {
              "name": "mycompany/composer-plugin",
              "type": "composer-plugin"
              "require": {
                "composer-plugin-api": "~1"
              },
              "autoload": {
                "psr-4": {
                  "MyCompany\\ComposerPlugin\\": "src/"
                }
              },
              "extra": {
                "class": "MyCompany\\ComposerPlugin\\Plugin"
              }
            }
          

            <?php namespace MyCompany\ComposerPlugin;

            use Composer\EventDispatcher\EventSubscriberInterface;
            use Composer\Plugin\PluginInterface;
            use Composer\Script\Event;

            class Plugin implements PluginInterface,
                                    EventSubscriberInterface
            {
              // Hook your code up to Composer events.
              public static function getSubscribedEvents()
              {
                return array(
                  ScriptEvents::POST_INSTALL_CMD => 'doSomething',
                  ScriptEvents::POST_UPDATE_CMD  => 'doSomething',
                );
              }
              // Actually do something when triggered.
              public static function doSomething(Event $event) {}
            }
          

Patching

Making modifications to external dependencies

Patching (1/2)

Forking

  • Needs upstream sync after every commit

  • Branch alias needed, but causes issues

Patching

  • Automatic upstream updates

  • Errors when it needs attention

  • Provides means for documentation

 

https://github.com/cweagans/composer-patches

Tip: Get patches from GitHub by appending .diff to any commit URL

Patching (2/2)

Namespace Prefixing

Avoiding collisions between multiple conflicting versions of a same library

Namespace Prefixing (1/2)

  • Composer can only handle conflicts if it controls the entire project, starting from the root

  • In legacy systems like WordPress, individual plugins might want to use Composer, even though the Core does not use it

  • Prefixing allows multiple, potentially conflicting versions of a same library to co-exist in a project

 

https://github.com/coenjacobs/mozart

Namespace Prefixing (2/2)

Keeping Data Up-to-date

Treating external data as just another dependency in the update flow

Keeping Data Up-to-date (1/2)

  • Treat external data as a managed dependency

  • Changes can be (version-)controlled

  • Makes data part of project's deployment flow

  • No need to remember manual updates

 

Example project: https://github.com/brightnucleus/geolite2-country

Keeping Data Up-to-date (2/2)

Commit Hooks

Treating local commit hooks as development dependencies

Commit Hooks (1/3)

  • Treated as a development dependency

  • Immediately active after a composer install

  • Logic can be coded in PHP

  • Multiple, prioritisable scripts on same hook

 

https://github.com/php-composter/php-composter

Commit Hooks (2/3)


            {
              "name": "mycompany/custom-hook",
              "type": "php-composter-action"
              "require": {
                "php-composter/php-composter": "*"
              },
              "autoload": {
                "psr-4": {
                  "MyCompany\\CustomHook\\": "src/"
                }
              },
              "extra": {
                "php-composter-hooks": {
                  "20.pre-commit":
                    "MyCompany\\CustomHook\\MyAction::preCommit"
                }
              }
            }
          

            <?php namespace MyCompany\CustomHook;

            use PHPComposter\PHPComposter\BaseAction;

            class MyAction implements BaseAction
            {
              /**
               * Example pre-commit action method.
               */
              public function preCommit()
              {
                echo sprintf(
                  'Example Pre-Commit Hook %s @ %s' . PHP_EOL,
                  $this->hook,
                  $this->root,
                );
              }
            }
          

Commit Hooks (3/3)

Automating Boilerplate Code

Using a Composer package as a scaffold

Automating Boilerplate Code (1/2)

  • Use create-project for scaffolding:

    composer create-project <package> <dir>

  • Provide logic in PHP, hooked into Autoloader

  • Provide configuration in "extra" keys

  • Hook into post-create-project-cmd hook to trigger logic and to clean up after generation


          {
            "name": "mycompany/boilerplate",
            "autoload": {
              "psr-4": {
                "Awesome\\Boilerplate\\Scripts\\": "_scripts/"
              }
            },
            "extra": {
              "mycompany-boilerplate": {
                "config-file": "_config/defaults.php",
              }
            },
            "scripts": {
              "post-create-project-cmd": [
                "MyCompany\\Boilerplate\\Scripts\\Setup::run",
                "rm -rf _scripts",
                "composer update --ansi",
                "phpunit --color=always"
              ]
            }
          }
          

Example project: https://github.com/brightnucleus/boilerplate

Automating Boilerplate Code (2/2)

Embedded Composer

Using Composer as a framework

Embedded Composer (1/2)

  • Provides access to all supported sources

  • Built-in event dispatching

  • Flexible scripting/plugin system still usable

 

https://developer.wordpress.org/cli/commands/package/

Embedded Composer (2/2)

How will You abuse Composer?

Questions ?

I'm Alain Schlesser.


Follow me on Twitter:

  @schlessera

Or visit my Personal Blog:

   www.alainschlesser.com