tsyama記

プログラミングとそのほか

CakePHPのアレはGitにコミットすべきか問題

さて、CakePHPのアレやコレやはGitにコミットすべきかについて考えましょう。
なおCakePHP3です。

CakePHPのアレ①:tmpディレクト

初級編です。当然Git管理しません。してはいけません。

CakePHPのデフォルトの.gitignore/tmp/*の記載があるので悩むこともないでしょう。

一応説明すると、tmpディレクトリ内にはスクリプト実行時に適宜ファイルキャッシュが作られるので、tmpディレクトリをignoreしておかないとキャッシュファイルが差分になりまくります。

なお、余談ですがtmpディレクトリはサーバ設置時にパーミッションを777にしてやる必要があります。composer経由でcakephp/appプロジェクトを作成したりcomposer installしたりするとtmpディレクトリは自動的に777パーミッションを適用する処理が走るので意識することはあまりありません。

この処理はcomposer.jsonにある下記の設定によるものです。

    "scripts": {
        "post-install-cmd": "App\\Console\\Installer::postInstall",
        "post-create-project-cmd": "App\\Console\\Installer::postInstall",

ここで呼び出しているpostInstallメソッドはsrc/Console/Installer.phpにあります。あまり機会はないかもしれませんが、ここに処理を足してやるとプロジェクトに対する何かしらのプロビジョニング処理を挟めそうです。

CakePHPのアレ②:composer.lock

まず先に結論を書くと、composer.lockはGit管理するべきです。

composerの処理の流れとして、まずcomposer.jsonの設定をもとに依存性を解決しながらインストールすべきパッケージとそのバージョンを確定しcomposer.lockに記録、そしてcomposer.lockに記録されたバージョン情報にしたがってインストール処理が行われます。

また、composer updateを行う、もしくは同ファイルが存在しない状態でcomposer installを行うと各パッケージのバージョンの設定をやり直し、composer.lockの作成および上書きが行われます。そのため、composerで明示的に環境を統一させるためにはcomposer.lockをGit管理した上で、開発時を除いてcomposer updateは行わず、常にGit管理されたcomposer.lockをもとにcomposer installしてやる必要があります。

composer.lockをGit管理しなかった場合、例えばexample/exampleというパッケージを使用するためにcomposer.json

   "require" : {
      "example/example" : "5.0.*"
   },

と記載していたとき、開発環境でのcomposer install後にパッケージのパッチバージョンアップが行われ、気づかないうちに開発環境と本番環境の間に差分が生まれてしまう、という事態が起こりえるのです。

CakePHPのアレ③:schema-dump-default.lock

CakePHPでmigrationをしているうち、知らない間に作成されているファイルです。 厳密にはbin/cake migrations migrateもしくはbin/cake migrations rollbackが呼ばれた後に生成されます。bin/cake migrations dumpで直接ファイルの生成のみを行うこともできます。

これは生成された時点でのDBの全スキーマの状態をシリアライズ化された状態で保持しています。これを何に使うのかというと、migration_diff機能のために必要になるデータです。 bin/cake bake migration_diffは現在生成されている最新のmigrationの状態と現在のDBの状態を比較し、差分をまとめたmigrationを生成するコマンドなのですが、このときに参照する"現在生成されている最新のmigrationの状態"というのがschema-dump-default.lockに保持されているデータというわけです。

上記を踏まえたうえでschema-dump-default.lockをGit管理すべきかというのは運用によりけりだとは思うのですが、個人的には"すべきではない"のではないでしょうか。 これはあくまでdumpデータであり、複数人での開発の際に個々人のDBのdumpデータがGit管理されてしまう、というのはちょっと違和感があります。migration_diff自体もそう頻繁に行うものではないでしょうし、migration_diffで生成されたmigrationファイルさえ管理していれば問題ないかと思います。

CakePHPのアレ④:config/app.php

CakePHPの大元となる設定ファイルです。

管理を行うGitリポジトリがプライベートかパブリックかにより扱いは変わってくるでしょう。DBの接続情報や何かしらのシークレットキーなどを設定することもあるかと思うので、パブリックリポジトリでそんなファイルをコミットするわけにはいきません。これを前提としたうえで、設定ファイルのうまいGit管理を考えてみましょう。

実はCakePHPはv3.5以降から地味にDotEnvに対応しています。プロジェクト作成時点でconfig/.env.defaultファイルがあることを確認してください。

DotEnvで設定を行うためにはconfig/.env.defaultをコピーしてconfig/.envを作成します。ただ、このままでは設定が有効化されないため、config/bootstrap.phpにある下記のコメントアウトを外しておく必要があります。

// if (!env('APP_NAME') && file_exists(CONFIG . '.env')) {
//     $dotenv = new \josegonzalez\Dotenv\Loader([CONFIG . '.env']);
//     $dotenv->parse()
//         ->putenv()
//         ->toEnv()
//         ->toServer();
// }

config/.envで設定した値はenv()メソッドで呼び出すことができます。これを踏まえたうえでconfig/app.phpを見てみましょう。例えばdebug設定などは

    'debug' => filter_var(env('DEBUG', true), FILTER_VALIDATE_BOOLEAN),

となっており、config/.envから設定値取得を試みたうえで設定が行われていなければenv()の第二引数に設定しているデフォルト値が使用される、という処理になっていることがわかります。つまり、今までconfig/app.phpに直接記述していた設定は単にデフォルト値を書き換えていただけ、ということになりますね。

これをパブリックリポジトリを使用していたとしても、公開したくない接続情報などをすべてconfig/.envに逃がしておくことができればconfig/app.php自体はGit管理に含めてしまってもいいかもしれません。いかんせんCakePHP自体のconfigファイルは一覧性があまりよくないので、DotEnvの形式にできるのはそれなりのメリットといえるでしょうか。

さらに、プライベートリポジトリであれば環境ごとの設定自動切り替えもスマートめに実装することができます。先ほどコメントアウトをはずしたbootstrap.phpの処理をこのように置き換えます。

if (env("CAKE_ENV") === 'production') {
    $dotEnvFilePath = CONFIG . '.env.production';
} elseif (env("CAKE_ENV") === 'staging') {
    $dotEnvFilePath = CONFIG . '.env.staging';
} elseif (env("CAKE_ENV") === 'development') {
    $dotEnvFilePath = CONFIG . '.env.development';
} else {
    $dotEnvFilePath = CONFIG . '.env.local';
}
$dotenv = new \josegonzalez\Dotenv\Loader([$dotEnvFilePath]);
$dotenv->parse()
    ->putenv()
    ->toEnv()
    ->toServer();

これでapacheの設定ファイルなどでCAKE_ENV環境変数を定義してやると、Git管理から外れることなく設定の切り替えを行うことができます。これはDotEnvを使わなくても実現できますが…。

なお、当然っちゃ当然なのですが、このやり方だとCakeのShellなどではapacheを通らずに処理が行われるためこのままでは環境変数が定義されず、適切な設定ファイルが適用されません。こういった場合は適宜環境変数を入れてあげるようにしましょう。