composer requireがメモリ不足で失敗しないために

## この記事の目的

パッケージ間の依存関係の解消に
メモリを多く消費するパッケージ ( 筆者が遭遇するケース：[Larastan](https://github.com/nunomaduro/larastan) ) を、

デフォルトの設定 ( 通常使用時には上限128MB[php.ini], 
Composer使用時には上限1.5GBまで拡張される ) のまま、

インストール(composer require)を試みると、
次のように標準エラー出力されます。

```sh
Fatal error: Allowed memory size of 1610612736 bytes exhausted
 (tried to allocate 4096 bytes)

Check https://getcomposer.org/doc/articles/
troubleshooting.md#memory-limit-errors 
for more info on how to handle out of memory errors
```

エラー文に書いてある[Composer公式](https://getcomposer.org/doc/articles/troubleshooting.md#memory-limit-errors)URLにアクセスすると、
２種類の解決策が提示されていて、

１番目の解決策の方は、既に多くの先人達が
Qiitaにたくさん記事を残してくださっていました。

ただ、[バージニア州に拠点を置くagileana社のブログ記事](https://www.agileana.com/blog/composer-memory-limit-troubleshooting/)を読んで以来、
私は２番目の解決策の方が好ましく感じているので
そちらも推したいと思い執筆に至りました。

表現の誤りや「もっとこうしたら便利だよ」といったアドバイスなどありましたら、
教えて頂けると勉強になります。

## 筆者の実行環境

|software|version|
|:--:|:--:|
|Docker|19.03.13|
|Docker Compose|1.27.4|
|PHP|7.4|

コンテナのベースイメージ ( FROM命令で指定したイメージ ) は、
php:7.4-fpm を使用してします。

## なぜこの方法が好ましいと思ったか？

agileana社のブログ記事の中で
Symfonyを採用したプロジェクトに携わっていたFlynn氏が
記しているこの部分です。



<blockquote>
My initial thought to fix this issue was to modify the memory_limit setting found in my PHP main configuration file,  <br>
but it will not only apply for the PHP CLI but to all the PHP process that I’m running (i.e. Local web server and Symfony commands.) <br>
 <br>
This is a side effect I was not willing to accept.<br>
 <br>
I started to research about this case and found a very straightforward solution to it and it will only apply to the Composer command you apply it to.  <br>
 <br>
The solution is to use the COMPOSER_MEMORY_LIMIT environment variable, setting its value to -1. It can be added to the current Terminal session with
</blockquote>

「PHPのメイン設定ファイル(php.ini)の
　memory_limitディレクティブの値を
　直接、無制限にしてしまうと、
　PHP CLIに適用されるだけでなく、
　**ローカルWebサーバやSymfonyフレームワークのコマンドからの
　メモリ使用制限までも、無制限になってしまうため、
　そういった副作用を受けたくありません**。」

「<font color="Magenta">**composerコマンド実行時のみ、メモリ上限を引き上げる方法を見つけました。**</font>」

と書いてありますね。

ちなみに、私がメインで触っているLaravelも、
vendorディレクトリ下で動くたくさんのSymfonyで書かれたプログラムに
お世話になっていますから親近感がありますね。

## 変更方法

### ディレクトリ構成はこうなっています。

```
$ tree -L 3

.
├── docker
│   ├── db
│   │   └── data
│   ├── nginx
│   │   └── default.conf
│   └── php
│       ├── Dockerfile
│       └── php.ini
└─── docker-compose.yml
```

コンテナは３つ

- appコンテナ (PHP環境)
- nginxコンテナ
- dbコンテナ

#### [手順1] コンテナ内で使用しているcomposerのPATHを確認

```sh
$ which composer

/usr/bin/composer
```

#### [手順２] environmentキーに環境変数COMPOSER_MEMORY_LIMITを追加。

`appコンテナ (PHP環境)`の部分に追記。

```docker-compose.yml
services:
  app:
    environment:
      - COMPOSER_MEMORY_LIMIT= -1 /usr/bin/composer
```

- 値を`-1`にすると、無制限。
  - [OK] 略称表記 (例： 2G )
  - [OK] 数値表記 (例：2147483648 )

#### [手順3] docker-compose.ymlの変更を反映させるために再ビルドを実行。

```sh
docker-compose up -d --build app
```



## そもそも何にそんなにメモリを使用しているのか知りたい

```
composer require --dev nunomaduro/larastan --profile -vvv
```


###### --profileオプション

タイムとメモリ使用量を表示する。

###### -vvvオプション

インストール時の挙動を詳細に表示する
(`-v`よりも、`-vv`、そして、`-vvv`と、`v`の数が増えると、
より詳しく表示してくれます。)

実行すると、このようになります。

```sh
root@79255afbc2bf:/var/www/html# composer require --dev nunomaduro/larastan --profile -vvv

Reading ./composer.json
Loading config file ./composer.json
Checked CA file /etc/ssl/certs/ca-certificates.crt: valid
Executing command (/var/www/html): git branch --no-color --no-abbrev -v
Reading /root/.composer/composer.json
Loading config file /root/.composer/composer.json
Reading /var/www/html/vendor/composer/installed.json
Reading /root/.composer/vendor/composer/installed.json
Running 1.10.13 (2020-09-09 11:46:34) with PHP 7.4.11 on Linux / 4.19.76-linuxkit

[9.2MiB/0.01s] Downloading https://repo.packagist.org/packages.json
[9.3MiB/0.87s] Writing /root/.composer/cache/repo/https---repo.packagist.org/packages.json into cache
[9.3MiB/0.87s] Reading /root/.composer/cache/repo/https---repo.packagist.org/p-provider-2013.json from cache
[12.6MiB/0.89s] Reading /root/.composer/cache/repo/https---repo.packagist.org/p-provider-2014.json from cache
[19.8MiB/0.93s] Reading /root/.composer/cache/repo/https---repo.packagist.org/p-provider-2015.json from cache
[31.7MiB/0.98s] Reading /root/.composer/cache/repo/https---repo.packagist.org/p-provider-2016.json from cache
[46.6MiB/1.05s] Reading /root/.composer/cache/repo/https---repo.packagist.org/p-provider-2017.json from cache
[68.9MiB/1.15s] Downloading http://repo.packagist.org/p/provider-2018%2481dd16ef8b70152a886f398fcf7d35c20870bf64605fbcf78e7fb9baac1f5ac2.json
.
.
.
(中略)
.
.
.
[860.0MiB/174.09s] Generating optimized autoload files
[866.5MiB/209.97s] > post-autoload-dump: Illuminate\Foundation\ComposerScripts::postAutoloadDump
[870.4MiB/210.14s] > post-autoload-dump: @php artisan package:discover --ansi
[870.4MiB/210.14s] Executing command (CWD): '/usr/local/bin/php' -d allow_url_fopen='1' -d disable_functions='' -d memory_limit=' -1 /usr/bin/composer' artisan package:discover --ansi

[870.4MiB/211.65s] Discovered Package: facade/ignition
[870.4MiB/211.65s] Discovered Package: fideloper/proxy
[870.4MiB/211.65s] Discovered Package: laravel/tinker
[870.4MiB/211.65s] Discovered Package: nesbot/carbon
[870.4MiB/211.65s] Discovered Package: nunomaduro/collision

[870.4MiB/211.65s] Package manifest generated successfully.
[867.2MiB/211.79s] 56 packages you are using are looking for funding.
[867.2MiB/211.79s] Use the `composer fund` command to find out more!
[867.2MiB/211.79s] Memory usage: 867.22MiB (peak: 1783.7MiB), time: 211.79s

root@79255afbc2bf:/var/www/html# 
```

<font color="red">**無事に、インストールできました!!**</font> :tada:

### (余談) ちなみに、Allowed memory size of 1610612736 bytes exhaustedって数字は何？

```sh
1,610,612,736 bytes 
= 1.5 * (2^30)
= 1.5 * (2^10) * (2^10) * (2^10)
= 1.5 * 1,024 * 1,024 * 1,024
= 1.5 GB
```

この記事の冒頭で触れた、
`Composer使用時のメモリ上限の1.5GB`を使い果たしてしまいました。。。
ということですね！

[Composer使用時にメモリ上限が1.5GBまで引き上げられる資料の参照](https://getcomposer.org/doc/articles/troubleshooting.md#memory-limit-errors)
`Note: Composer internally increases the memory_limit to 1.5G.`


## まとめ

メモリリークやcomposerについて調べるとても良い機会になりました。

思いの外、長くなってしまいましたが
ご覧頂きありがとうございます。