Composerやクラスのオートローディングの概念がなかった時代に作られたPHPライブラリを、いい感じに管理を試みます。
この記事の前提として、PHPにおける「SDK」とは、概ね外部Webサービスをプログラミング言語から透過的に連携するためのHTTPリクエスト処理と、そのレスポンスをマッピングしたクラスから構成されるものを指すことが多い気がします。
あとこの記事は個人の見解であり、この内容は架空のものです。いいですね?
背景1: PHP
現代的なPHPライブラリはComposerを前提に設計され、ひとつひとつのファイルで明示的にinclude
やrequire
を書いて読み込む必要も、読み込まれる必要もありません。
そのあたりの話は先月北海道で、Composerの説明と絡めて話してきました。
今回の記事に関連のある点を要約すると、こんな感じです。
- Composerやオートロード以前の時代は、ライブラリが提供するPHPファイルは
include_path
に依存したファイル読み込みが主流だった - Composerのオートロード機能を利用すれば基本的には明示的にクラスを
require
する必要も、include_path
を設定する必要もない
背景2: SDK
とある事情により、ある組織の提供するSDKをプライベートなプロダクトに取り込むことになりました。
そのSDKは以下のような特徴を持ちます。
- ファイル一式は基本的にzipで提供される
- PHP 5.x(謎)と7系で互換性があり、エラーが発生しないように記述されてる
- SDKの機能を呼び出す前に、インストールディレクトリを
include_path
に追加する必要がある - このSDKを構成する各クラスは、基本的に全ファイルの冒頭に
include_path
に依存したinclude
文がある - このSDKの設定ファイルは
include_path
の決められた位置に配置する必要がある
各ファイル冒頭の文は以下のような感じです。
require_once ('com/example/sdk/output/Foo.php');
この書きかたをされてしまっては、下手な手を打つとめんどくさいことになります。
対応策の検討
-
require_once
行をスクリプトで除去してクラスマップを作成する -
include_path
を設定して、本来の設定意図通りに動作するようにする
今回は元の素材を活かす形で2の方を採用するようにしました。まあコードに手を入れてしまったらアップデートの追従がめんどくさくなりますしね。
ちなみに、こちらのソースコードは既に何年も前にinclude_path
に依存した記述は一切削除済みですので、include_path
がひとつふたつ増えようが、パフォーマンスへの影響は全然ありません。
方針は決まりました。これだけならComposerの基本機能だけで実現できます。つまりComposerでインストール可能なパッケージにしてやれば良いことです。
なんとなく厄介なのが設定ファイルです。これを配置するスクリプトも書きましょう。
Gitリポジトリを用意しよう
Composerパッケージとは何でしょうか? その解は簡単で、composer.json
が含まれるディレクトリやアーカイブファイル、またはVCSのリポジトリです。zipやGitのみならず、多様な形式から取得することができます。
開発の上ではGitやMercurialなどのVSCのリポジトリにするのがお手軽です。
ライセンス上再配布できないものなので、プライベートなリポジトリで管理します。treeでディレクトリ構成を表すと、こんな感じになります。
├── README.md
├── bin
│ └── example-sdk-setup (← これは自前で書く)
├── composer.json
├── composer.lock
├── example-sdk
│ ├── (ここにzipの内容をぶちまける)
ソースコードは加工せずディレクトリに展開するだけなので、アップデートもらくちんですね。
Composerを設定しよう
こうします。
{
"name": "zonuexe/example-sdk",
"license": "proprietary",
"description": "Example Service PHP SDK",
"include-path": ["example-sdk/src/"],
"bin": ["bin/example-sdk-setup"],
"require-dev": {
"sstalle/php7cc": "^1.2"
}
}
この設定ファイルを置いた状態でcomposer install
とかcomposer dump-autoload
とか実行すると、vendor/autoload.php
などが生成されるはずです。
bin/example-sdk-setup
は後述する設定例ファイル生成スクリプトです。実行ファイルにしておくと、Composerをインストールした側から利用しやすくなります。"bin"
を設定しないとvendor/bin/
ディレクトリにスクリプトが展開されないので、実行ファイルは登録しておいた方がいいです。
Composerの機能で大事なのはinclude-path
です。この設定項目については、公式ドキュメントcomposer.json Schema #include-pathにこんなことが書いてあります。
DEPRECATED: This is only present to support legacy projects, and all new code should preferably use autoloading. As such it is a deprecated practice, but the feature itself will not likely disappear from Composer.
要は「レガシープロジェクト向けの機能だからおすすめはしないけど、機能はたぶんずっと残すよ」ってことですね。煮えきらないですが、ここは安心して使っておきます。
これほんとにPHP7と互換性があるのかな、と石橋を叩いて渡りたかったのでrequire-dev
にsstalle/php7cc: PHP 7 Compatibility Checkerを持ってきました。./vendor/bin/php7cc example-sdk
とかやるとチェックできます。
設定ファイル
設定ファイルはini
形式です。ファイルは二種類あり、「APIの接続先定義(本番環境と開発用環境)」と、「ログの出力先のファイル設定」です。
こちらでは、以下のような感じでセットアップ用スクリプトを起動すればパスの通った場所に設定ファイルを自動で配置するようにします。
# 開発時
./vendor/bin/example-sdk-setup development /path/to/log/example-sdk/error.log
# 本番時
./vendor/bin/example-sdk-setup production /path/to/log/example-sdk/error.log
固定ファイルに書き出されるのは苦しいので、せめてPSR-3形式のロガーを登録するとか、エラーハンドリング用の関数をコールバックで設定させてくれるとか、PHPの関数からファイルリソースを直接登録させてくれるとかだと嬉しかったです。
読者のみなさまもSDKを提供なさるときは、情報の伝達方法には心を砕いていただきたいところです。
……話がそれましたね。
スクリプトを実装しよう
長くなったのでGistに置きました https://gist.github.com/zonuexe/9abc2ac3a1425637edb8d9bc9a69ee09
石橋を叩いて渡りたかったので、冗長なチェックが入ってます。みなさまは、file_get_contents()
の第2引数を指定したことはありますか? 私は生まれて初めて指定しました。その意味は「ファイルをinclude_path
から検索する」です。
.gitattributes
を設定しよう
実はSDKのリポジトリにはサンプルコードとかドキュメントも入ってたのですが、これは開発時に役に立つことはあっても本番運用時には無用の長物です。こういったものは.gitattributes
でexport-ignore
に登録することで、composer install --prefer-dist
したときにはファイルが展開されなくなります。
example_sdk/doc/ export-ignore
sample/ export-ignore
これは今回のケースに限らず知っておくとお得な知識ですが、うっかり必要なファイルが除外されないように、テストはきちんとしてくださいね。
SDKをインストールしよう
プライベートなSatisとか運用してる場合は単に登録すればいけますね。
そんなことをしてない場合はどうすればいいか。前述の通り、ComposerはGitやMercurialなどのVCSをサポートするので、リポジトリをそのままインストールできます。
公式ドキュメントのこのあたりを見て、いい感じに操作するとインストールできるかと。
セットアップスクリプトはいつ実行するか
- 開発者が開発環境でセットアップ時
- CI環境でのテスト実行前のセットアップ時
- 本番環境にデプロイ前のセットアップ時
それぞれ全体のセットアップスクリプト内で実行されます。
SDKが更新されたらどうする
(大規模に構成が変更されない限りは)zipファイルをもらって、展開して、git push
するだけです。それほどの手間ではない。
まとめ
Composerが想定されてないSDKでも勝手にパッケージングしてやれば、なんかいい感じになることがわかりましたね。