PHPのパッケージ管理ツールComposeをDockerで使う

# What's？

PHPのパッケージ管理ツールとしてComposerというものがあるようなのですが。

https://getcomposer.org/

Composerの要求事項として、PHP自体があります。

> Composer requires PHP 5.3.2+ to run.

[Getting Started / System Requirements](https://getcomposer.org/doc/00-intro.md#system-requirements)

できればPHPはDockerコンテナで使いたいので、ComposerのためにPHPをインストールするのも…と見ていたらDockerイメージがあったのでこちらを使うことにしました。

[Composer](https://hub.docker.com/_/composer)

# 環境

今回の環境は、こちら。

```shell
$ docker version
Client: Docker Engine - Community
 Version:           20.10.11
 API version:       1.41
 Go version:        go1.16.9
 Git commit:        dea9396
 Built:             Thu Nov 18 00:37:06 2021
 OS/Arch:           linux/amd64
 Context:           default
 Experimental:      true

Server: Docker Engine - Community
 Engine:
  Version:          20.10.11
  API version:      1.41 (minimum version 1.12)
  Go version:       go1.16.9
  Git commit:       847da18
  Built:            Thu Nov 18 00:35:15 2021
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.4.12
  GitCommit:        7b11cfaabd73bb80907dd23182b9347b4245eb5d
 runc:
  Version:          1.0.2
  GitCommit:        v1.0.2-0-g52b36a2
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0
```

Composerは、現時点での最新版である`2.1.12`を使うことにします。

使用するPHPのバージョンは、こちら。8.0ではなく、7.4を使用します。

```shell
$ php --version
PHP 7.4.26 (cli) (built: Nov 18 2021 16:12:41) ( NTS )
Copyright (c) The PHP Group
Zend Engine v3.4.0, Copyright (c) Zend Technologies
```

# ComposerのDockerイメージを使う

まずは、ComposerのDockerイメージを取得。

```shell
$ docker image pull composer:2.1.12
```

基本的な使いかたはComposerのDockerイメージのページに書いてありますが、

[Composer](https://hub.docker.com/_/composer)

カレントディレクトリを`/app`ディレクトリにマウントして使うみたいです。

```shell
$ docker run --rm --interactive --tty \
  --volume $PWD:/app \
  composer <command>
```

Composerで扱うファイルは、ローカルのOSユーザーのパーミッションで扱いところなので、こんな感じしましょうか？

```shell
$ docker container run -it --rm --name composer -v $PWD:/app -u $(id -u):$(id -g) composer:2.1.12 [command]
```

コマンドとして使えた方がよいかなと思うので、こんなスクリプトを作成して

```shell:/usr/local/bin/composer
#!/bin/bash

docker container run -it --rm --name composer -v $PWD:/app -u $(id -u):$(id -g) composer:2.1.12 "$@"
```

実行権限を付与しておきます。

```shell
$ sudo chmod a+x /usr/local/bin/composer
```

OKですね。

```shell
$ composer --version
Composer version 2.1.12 2021-11-09 16:02:04
```

# 使ってみる

では、Composerを使って軽くプログラムを書いてみます。

使用するライブラリは、ロギングライブラリであるmonologにしました。

https://github.com/Seldaek/monolog

と思ったら、Basic usageに例として登場しますね。

https://getcomposer.org/doc/01-basic-usage.md

monologのインストール…なのですが。

```shell
$ composer require monolog/monolog
```

現時点で素直にこれを実行すると、PHP 8.0を要求するように構成されるみたいなので…。

monologを使ったソースコードを書いて実行すると、以下のようにエラーになります。

```shell
Fatal error: Composer detected issues in your platform: Your Composer dependencies require a PHP version ">= 8.0.0". You are running 7.4.26. in /app/vendor/composer/platform_check.php on line 24
```

ちょっとやり直します。

最初に、使用するPHPのバージョンを`composer config platform.php`で指定します。  
`composer config`は`composer.json`がないと失敗するようなので、先に空で作成しておきます。

```shell
$ echo '{}' > composer.json
$ composer config platform.php 7.4.26
```

`composer init`でComposerの初期セットアップをしてもよいと思います。

`platform`についてのドキュメントは、こちら。

[Config / platform](https://getcomposer.org/doc/06-config.md#platform)

気を取り直して、monologをインストール。

```shell
$ composer require monolog/monolog
```

実行後は、このようなディレクトリツリーになりました。

```shell
$ tree -d
.
└── vendor
    ├── composer
    ├── monolog
    │   └── monolog
    │       └── src
    │           └── Monolog
    │               ├── Formatter
    │               ├── Handler
    │               │   ├── Curl
    │               │   ├── FingersCrossed
    │               │   ├── Slack
    │               │   └── SyslogUdp
    │               ├── Processor
    │               └── Test
    └── psr
        └── log
            └── Psr
                └── Log
                    └── Test

19 directories
```

`composer.json`。


```json:composer.json
{
    "config": {
        "platform": {
            "php": "7.4.26"
        }
    },
    "require": {
        "monolog/monolog": "^2.3"
    }
}
```

`composer.lock`。

```json:composer.lock
{
    "_readme": [
        "This file locks the dependencies of your project to a known state",
        "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
        "This file is @generated automatically"
    ],
    "content-hash": "df67ef98eedae482c1b67363054a6904",
    "packages": [
        {
            "name": "monolog/monolog",
            "version": "2.3.5",
            "source": {
                "type": "git",
                "url": "https://github.com/Seldaek/monolog.git",
                "reference": "fd4380d6fc37626e2f799f29d91195040137eba9"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/Seldaek/monolog/zipball/fd4380d6fc37626e2f799f29d91195040137eba9",
                "reference": "fd4380d6fc37626e2f799f29d91195040137eba9",
                "shasum": ""
            },
            "require": {
                "php": ">=7.2",
                "psr/log": "^1.0.1 || ^2.0 || ^3.0"
            },
            "provide": {
                "psr/log-implementation": "1.0.0 || 2.0.0 || 3.0.0"
            },
            "require-dev": {
                "aws/aws-sdk-php": "^2.4.9 || ^3.0",
                "doctrine/couchdb": "~1.0@dev",
                "elasticsearch/elasticsearch": "^7",
                "graylog2/gelf-php": "^1.4.2",
                "mongodb/mongodb": "^1.8",
                "php-amqplib/php-amqplib": "~2.4 || ^3",
                "php-console/php-console": "^3.1.3",
                "phpspec/prophecy": "^1.6.1",
                "phpstan/phpstan": "^0.12.91",
                "phpunit/phpunit": "^8.5",
                "predis/predis": "^1.1",
                "rollbar/rollbar": "^1.3",
                "ruflin/elastica": ">=0.90@dev",
                "swiftmailer/swiftmailer": "^5.3|^6.0"
            },
            "suggest": {
                "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB",
                "doctrine/couchdb": "Allow sending log messages to a CouchDB server",
                "elasticsearch/elasticsearch": "Allow sending log messages to an Elasticsearch server via official client",
                "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)",
                "ext-curl": "Required to send log messages using the IFTTTHandler, the LogglyHandler, the SendGridHandler, the SlackWebhookHandler or the TelegramBotHandler",
                "ext-mbstring": "Allow to work properly with unicode symbols",
                "ext-mongodb": "Allow sending log messages to a MongoDB server (via driver)",
                "ext-openssl": "Required to send log messages using SSL",
                "ext-sockets": "Allow sending log messages to a Syslog server (via UDP driver)",
                "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server",
                "mongodb/mongodb": "Allow sending log messages to a MongoDB server (via library)",
                "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib",
                "php-console/php-console": "Allow sending log messages to Google Chrome",
                "rollbar/rollbar": "Allow sending log messages to Rollbar",
                "ruflin/elastica": "Allow sending log messages to an Elastic Search server"
            },
            "type": "library",
            "extra": {
                "branch-alias": {
                    "dev-main": "2.x-dev"
                }
            },
            "autoload": {
                "psr-4": {
                    "Monolog\\": "src/Monolog"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Jordi Boggiano",
                    "email": "j.boggiano@seld.be",
                    "homepage": "https://seld.be"
                }
            ],
            "description": "Sends your logs to files, sockets, inboxes, databases and various web services",
            "homepage": "https://github.com/Seldaek/monolog",
            "keywords": [
                "log",
                "logging",
                "psr-3"
            ],
            "support": {
                "issues": "https://github.com/Seldaek/monolog/issues",
                "source": "https://github.com/Seldaek/monolog/tree/2.3.5"
            },
            "funding": [
                {
                    "url": "https://github.com/Seldaek",
                    "type": "github"
                },
                {
                    "url": "https://tidelift.com/funding/github/packagist/monolog/monolog",
                    "type": "tidelift"
                }
            ],
            "time": "2021-10-01T21:08:31+00:00"
        },
        {
            "name": "psr/log",
            "version": "1.1.4",
            "source": {
                "type": "git",
                "url": "https://github.com/php-fig/log.git",
                "reference": "d49695b909c3b7628b6289db5479a1c204601f11"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11",
                "reference": "d49695b909c3b7628b6289db5479a1c204601f11",
                "shasum": ""
            },
            "require": {
                "php": ">=5.3.0"
            },
            "type": "library",
            "extra": {
                "branch-alias": {
                    "dev-master": "1.1.x-dev"
                }
            },
            "autoload": {
                "psr-4": {
                    "Psr\\Log\\": "Psr/Log/"
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "PHP-FIG",
                    "homepage": "https://www.php-fig.org/"
                }
            ],
            "description": "Common interface for logging libraries",
            "homepage": "https://github.com/php-fig/log",
            "keywords": [
                "log",
                "psr",
                "psr-3"
            ],
            "support": {
                "source": "https://github.com/php-fig/log/tree/1.1.4"
            },
            "time": "2021-05-03T11:20:27+00:00"
        }
    ],
    "packages-dev": [],
    "aliases": [],
    "minimum-stability": "stable",
    "stability-flags": [],
    "prefer-stable": false,
    "prefer-lowest": false,
    "platform": [],
    "platform-dev": [],
    "platform-overrides": {
        "php": "7.4.26"
    },
    "plugin-api-version": "2.1.0"
}
```

では、monologを使ったプログラムを書いてみます。

```php:app.php
<?php

require_once __DIR__ . '/vendor/autoload.php';

use Monolog\Handler\StreamHandler;
use Monolog\Logger;

$log = new Logger('my logger');
$log->pushHandler(new StreamHandler('php://stdout'));

$log->info('info logging');
$log->warning('warning logging');

?>
```

`autoload.php`はComposerによって生成されるファイルで、これを`require_once`しておくとComposerでインストールしたライブラリが楽に使えます。

確認。

```shell
$ php app.php
[2021-11-22T07:48:03.823898+00:00] my logger.INFO: info logging [] []
[2021-11-22T07:48:03.824837+00:00] my logger.WARNING: warning logging [] []
```

これで、Composerが使えるようになりました。