●はじめに
以下の記事は当時の自分の検索スキルが足らず、該当する内容を見つけられず仕方なく自分で考察・実装した時のメモです。
コメントで教えて頂いたhttps://github.com/php-pds/skeletonの方がより洗練されて美しいので、こちらを参考にしてもらった方が良いと思います。
以上ご了承の上で敢えて読みたい方は以下から本編です。
●概要
経緯として複数のWebアプリケーションで使用するモデルとビジネスロジックや、コマンドラインで使用するモジュールをコピペで再利用していた。
当然、追加機能の開発やらバグ対応やら運用がツライ → 爆死。
本当にありがとうございました。
これじゃーイカンなと、Composerパッケージとして開発・運用する必要があるなと痛感した次第であります。
「ではどうするか?」となった時に調査して考えた内容で、ある程度土台(テンプレートともいう)は共通化する必要があるのでまとめた。
要するにcomposerでパッケージ作る時の土台部分の話。
●目的
土台を共通化することで
- 開発効率を上げる
- 属人化しない。(未来の自分に優しく)
- 再利用しやすい
- 開発・バグ対応が簡単
●方針
個人・自社のみの使用を想定。 ← クローズドな環境で使用するの意味。
再利用する単位(モジュール)でパッケージにする。
基本的にはWebアプリケーションでは無い。
- 複数のWebアプリケーションで利用するモデルやビジネスロジック
- コマンドラインで使用する
などなど
Webフレームワークを使用する場合はそれに合わせる。ほとんどのWebフレームワークには以下の構成・運用に大体なっていると思うが。
■使用パッケージ
Composerが親の仇っていう人は帰ってどうぞ。
それ以外の人はComposerを使ってパッケージを入れる。最低限これだけは入れとけってヤツ。
- monolog
- .env
- phpunit
- psysh
■基本的な運用
必要なパッケージをcomposerで追加。
→車輪の再発明はイヤだ。
composerの自作パッケージとして再利用出来るようにする。
→自分が作ったからと言ってもコピペはツライ。
当然gitも使用する。
→composerのパッケージとして利用出来るように、gitのtagとcomposer.jsonのバージョンを合わせる
●パッケージ名の命名規則
全て小文字でダッシュを挟む。
my-package
Composer ドキュメント日本語訳 → 全てのプロジェクトはパッケージである → 注意
今後この記事では説明の為にmy-package
をパッケージ名として作る。
●ディレクトリ構成とファイル
いろんなパッケージを参考にしている。
■最低限必要だよねっていうディレクトリ
- logs:ログファイルを置き場
- src/MyPackage/Exception:ソースファル置き場
- tests:テストファイル置き場
「MyPackage」のところは、作るパッケージ名合わせて変える。
■必要に応じて入れればいいんじゃないかっていうディレクトリ
- docs:readme.mdで収まり切らない場合の説明を入れる
- example:readme.mdで収まり切らない場合の使用例
- bin:バイナリファイル
■諸々の設定ファイルとか
- .gitignore:2箇所。git無視ファイル
- composer.json:パッケージの依存性とかのファイル
- phpunit.xml:phpunit設定ファイル
- psysh.php:psyshの実行を簡単にするファイル
- readme.md:目的と使い方ぐらいは書きたい
■composerが作成するファイル
- composer.lock:
composer install
とかを実行すると自動で作られる - vendor/autoload.php:クラスのオートロードファイル
- vendor/bin/phpunit:phpunitバイナリファイル
- vendor/bin/psysh:psyshバイナリファイル
■最終的な構成例
my-package
├── .gitignore
├── bin
├── composer.json
├── composer.lock
├── docs
├── example
├── logs
│ └── .gitignore
├── phpunit.xml
├── psysh.php
├── readme.md
├── src
│ └── MyPackage
│ └── Exception
├── tests
└── vendor
├── autoload.php
├── bin
│ ├── php-parse -> ../nikic/php-parser/bin/php-parse
│ ├── phpunit -> ../phpunit/phpunit/phpunit
│ └── psysh -> ../psy/psysh/bin/psysh
├── composer
...
●作り方
パッケージのvendor(所属)をきめておく。
個人ならgithubのアカウント。会社なら会社名とか。
composerをいれる。ここはググればいくらでも検索が出るので省く
■composer.jsonの作成
$ mkdir my-package
$ cd ./my-package/
$ composer init
Welcome to the Composer config generator
This command will guide you through creating your composer.json config.
Package name (<vendor>/<name>) [busyoumono/my-package]: busyoumono99/my-package
Description []:
Author [busyoumono99 <xxxx@xxxxxx.com>, n to skip]
Minimum Stability []: stable
Package Type (e.g. library, project, metapackage, composer-plugin) []: library
License []: MIT
Define your dependencies.
Would you like to define your dependencies (require) interactively [yes]?
Search for a package:
Would you like to define your dev dependencies (require-dev) interactively [yes]?
Search for a package:
{
"name": "busyoumono99/my-package",
"type": "library",
"license": "MIT",
"minimum-stability": "stable",
"require": {}
}
Do you confirm generation [yes]?
$ composer require vlucas/phpdotenv
$ composer require monolog/monolog
$ composer require phpunit/phpunit --dev
$ composer require psy/psysh:@stable --dev
■各ディレクトリの作成
$ mkdir -p src/MyPackage/Exception
$ mkdir logs
$ mkdir tests
$ mkdir docs
$ mkdir example
$ mkdir bin
■オートロード
composer.jsonにオートロードの設定を追加する。
"autoload": {
"psr-4": {
"busyoumono99\\MyPackage\\": "src/MyPackage/"
}
},
autoloadの設定を反映させる
$ composer dump-autoload
■.envを作成
$ vi .env
TEST_NAME=hoge
■gitignore
vi .gitignore
/vendor
.env
vi logs/.gitignore
*
!.gitignore
■psysh用のシェルスクリプト作成
デバッグ時にphpを対話方式で実行する。autoload等毎回設定するのは面倒なので、決まりきった処理を書いて楽をする。
$ vi psysh.php
#!/usr/bin/env php
<?php
// ↓名前空間を利用するプロジェクトでは記述しておく
namespace busyoumono99\MyPackage;
// Composerのオートローダーを読み込む
require_once __DIR__ . '/vendor/autoload.php';
// ほかに初期化用のPHPファイルがあれば読み込んでおく
// require_once …
// .envの読込。.envが必要ない、使用しない場合はコメントアウトする
$dotenv = new \Dotenv\Dotenv(__DIR__.'/');
$dotenv->load();
echo __NAMESPACE__ . " shell\n";
$sh = new \Psy\Shell();
// シェル起動直後にプロジェクトのnamespaceを設定する
// 名前空間を利用しないプロジェクトでは↓の行は不要
$sh->addCode(sprintf("namespace %s;", __NAMESPACE__));
$sh->run();
// 終了時に表示するメッセージ
echo "Bye.\n";
# 実行権限を追加する
$ chmod +x ./psysh.php
■phpunit.xml
phpunitの設定ファイル。
<?xml version="1.0" encoding="utf-8" ?>
<phpunit colors="true" bootstrap="vendor/autoload.php">
<testsuites>
<testsuite name="All tests">
<directory>tests/</directory>
</testsuite>
</testsuites>
</phpunit>
bootstrapは基本的にautoloadしか設定しないのでこうした。
もしbootstrapに.envの読込やタイムゾーンの設定が必要になったらtests直下にbootstrap.phpを作って、その中でゴニョゴニョする。
それに合わせてbootstrap="tests/bootstrap.php"
に変更する。
●使い方(開発時)
■monolog
ログの簡単な使い方。
<?php
namespace busyoumono99\MyPackage;
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use Monolog\Handler\RotatingFileHandler;
class LogSample
{
public static function write($message){
$log = new Logger('my-package');
// ログのパスを設定する
$log_path = __DIR__. '/../../logs/test.log';
//ログレベルをDEBUG(最も低い)に設定
$handler = new StreamHandler($log_path,Logger::DEBUG);
$log->pushHandler($handler);
// ログローテーションのハンドラーを使う
$rotating_handler = new RotatingFileHandler($log_path, 30, Logger::DEBUG, true);
$log->pushHandler($rotating_handler);
// 書き込む
return $log->addDebug($message);
}
}
解説
-
Logger
クラスと必要なハンドラークラスを読み込む。 - ハンドラーは複数設定出来る。
- ハンドラーを複数設定した場合は同時に書き込む。
- ログレベルの設定で開発・本番等の切り替えが出来る。
他にも色々と機能等があるので詳細は別途ググると良い。以下大きいところ。
- ハンドラーにはファイルの書き込み以外に、DBに書き込み、メール送信、Slack等の外部サービスと連携とかがある。
- フォーマットの機能もある
■.env
利用前の設定。bootstrap.php的なライブラリのセットアップ時に読込処理をしておく。
// Composerのオートローダーを読み込む
require_once __DIR__ . '/vendor/autoload.php';
// .envの読込。
$dotenv = new \Dotenv\Dotenv(__DIR__.'/');
$dotenv->load();
実際に値を取得する。
<?php
namespace busyoumono99\MyPackage;
class EnvSample
{
/**
* .envの使用例
* @return string
*/
public static function getTestName()
{
return getenv('TEST_NAME');
}
}
■phpunit
○クラス
例えば四則演算のクラス
src/MyPackage/Arithmetic.php
<?php
namespace busyoumono99\MyPackage;
class Arithmetic
{
/**
* 足し算
*/
public function add($x, $y) {
return($x + $y);
}
/**
* 引き算
*/
public function subtract($x, $y) {
return($x - $y);
}
/**
* 掛け算
*/
public function multiply($x, $y) {
return($x * $y);
}
/**
* 割り算
*/
public function divide($x, $y) {
return($x / $y);
}
}
○テストケース
tests/ArithmeticTest.php
<?php
use PHPUnit\Framework\TestCase;
use busyoumono99\MyPackage\Arithmetic;
class ArithmeticTest extends TestCase
{
/**
* @var Arithmetic
*/
protected $object;
/**
* setUpは各テストメソッドが実行される前に実行する
*/
protected function setUp() {
// テストするオブジェクトを生成する
$this->object = new Arithmetic();
}
/**
* 足し算関数の検証
* @test
*/
public function 3足す5は8() {
$this->assertEquals(8, $this->object->add(3, 5));
}
/**
* @test
*/
public function 15足す30は45() {
$this->assertEquals(45, $this->object->add(15, 30));
}
/**
* 引き算関数の検証
* @test
*/
public function 10引く3は7() {
$this->assertEquals(7, $this->object->subtract(10, 3));
}
/**
* @test
*/
public function 3引く9はー6() {
$this->assertEquals(-6, $this->object->subtract(3, 9));
}
/**
* 掛け算関数の検証
* @test
*/
public function 4かける6は24() {
$this->assertEquals(24, $this->object->multiply(4, 6));
}
/**
* @test
*/
public function 4かけるー5はー20() {
$this->assertEquals(-20, $this->object->multiply(4, -5));
}
/**
* 割り算関数の検証
*/
public function testDivide() {
// 引数に6,2を渡すと3が返ってくることを確認する
$this->assertEquals(3, $this->object->divide(6, 2));
// 引数に6,6を渡すと1が返ってくることを確認する
$this->assertEquals(1, $this->object->divide(6, 6));
}
}
-
setUp
に各テスト前に実行する共通処理を書く - ドキュメントに
@test
が含まれているとテストする - メソッドのプレフィックスが
test
ならテストする -
assertEquals
は値が等しい事をテストする。 -
assertEquals
で結構いける - 他にもアサートはある。
- 時間がかかる時は
@group
を検討する
○テスト実行
ターミナルで以下実行
$ ./vendor/bin/phpunit
PHPUnit 6.0.8 by Sebastian Bergmann and contributors.
....... 7 / 7 (100%)
Time: 188 ms, Memory: 4.00MB
OK (7 tests, 8 assertions)
■psysh
psyshでデバッグ実行する
○シェルスクリプトを使用する
$ cd /path/to/my-package
$ ./psysh.php
busyoumono99\MyPackage shell
Psy Shell v0.8.2 (PHP 7.0.15 — cli) by Justin Hileman
>>> $ar = new Arithmetic()
=> busyoumono99\MyPackage\Arithmetic {#162}
>>> show $ar
> 6| class Arithmetic
7| {
8| /**
9| * 足し算
10| */
11| public function add($x, $y) {
12| // eval(\Psy\sh());
13| return($x + $y);
14| }
15|
16| /**
17| * 引き算
18| */
19| public function subtract($x, $y) {
20| return($x - $y);
21| }
22|
23| /**
24| * 掛け算
25| */
26| public function multiply($x, $y) {
27| return($x * $y);
28| }
29|
30| /**
31| * 割り算
32| */
33| public function divide($x, $y) {
34| return($x / $y);
35| }
36| }
>>> ls $ar
Class Methods: add, divide, multiply, subtract
>>> $ar->add(5,9)
=> 14
>>> dump $ar
busyoumono99\MyPackage\Arithmetic {#162}
>>> q
Exit: Goodbye.
Bye.
○簡単に試す時とか
$ ./vendor/bin/psysh
Psy Shell v0.8.2 (PHP 7.0.15 — cli) by Justin Hileman
>>> $a = ["hoge"=>"piyo", "fuga"=>"poyo"]
=> [
"hoge" => "piyo",
"fuga" => "poyo",
]
>>> json_encode($a)
=> "{"hoge":"piyo","fuga":"poyo"}"
>>> q
Exit: Goodbye.
○ブレイクポイントとして利用
以下をブレイクしたい箇所に追加。例えば足し算メソッドに仕込む。
eval(\Psy\sh());
...
/**
* 足し算
*/
public function add($x, $y) {
eval(\Psy\sh());
return($x + $y);
}
...
シェルスクリプトで起動してブレイクポイントを発生させる
$ ./psysh.php
busyoumono99\MyPackage shell
Psy Shell v0.8.2 (PHP 7.0.15 — cli) by Justin Hileman
>>> $ar = new Arithmetic()
=> busyoumono99\MyPackage\Arithmetic {#162}
>>> $ar->add(5, 13)
Psy Shell v0.8.2 (PHP 7.0.15 — cli) by Justin Hileman ← ここで2重にpsyshが実行されている。
>>> ls
Variables: $this, $x, $y
>>> dump $x
5
>>> dump $y
13
>>> ls $this
Class Methods: add, divide, multiply, subtract
>>> dump $this
busyoumono99\MyPackage\Arithmetic {#162}
>>> q
Exit: Goodbye.
=> 18
>>> q
Exit: Goodbye.
Bye.
./psysh.php
で無くても$ php -f index.php
やブラウザアクセス等でも良い。eval(\Psy\sh());
の箇所まで処理が進めばターミナルが起動してpsyshが開始される。ただ、面倒なので./psysh.php
がオススメ
○主に使うコマンド
自分が現在の所、主に使うコマンド3つ。
* ls : 現在有効な変数の一覧
* ls $obj : オブジェクト変数のプロパティ、メソッドとか
* dump $var : 変数の中身を表示
* q : psyshの終了
■git
gitは通常にコミット。バージョンで区切るときはタグをv1.0.0
等として追加する。プレフィックスのv
は無くてもOKだが付けておく。
その他一般的なバージョンの規則は以下を参考。
Composer ドキュメント日本語訳 → タグ
●使い方(利用時)
パッケージを利用する側のcomposer.json
"repositories": [
{
"type": "vcs",
"url": "/path/to/my-package",
"//url": "https://hoge.org/busyoumono99/my-package.git"
}
],
"require": {
"busyoumono99/my-package": "1.*"
}
設定後にターミナルでインストール
$ composer install
元パッケージ側をgitでコミットして、新しくタグv1.1.0
を追加すればアップデート出来る
$ composer update
●まとめ
以上で自作パッケージの基本的な部分の作り方、利用の仕方終わり。
何があれば、コメントか編集リクエストお願いします。
●参考
- PackagistとComposerで自作パッケージを使う - きじとら
- PHPのちょっとしたライブラリを作る時の構成やcomposer.jsonなど | kanonjiのブログ
- 素のPHPでも最低限.envとmonologくらいは使う際の手順 - Qiita
- Composer が PSR-4 に対応していた - ngの日記
- Composerに自分の作ったクラスを追加してみる。 - Qiita
- phpdotenvを使う - Qiita
- 規模別PHPUnitでのテストの書き方いろいろ: Architect Note
- PHPUnit入門の入門 – Simple IT Life
- PHPを「シェル化」する [psy/psysh] - 超PHPerになろう
- PsySH公式サイト日本語訳 - Qiita
- 2015 12 16 PHPのREPL、PsySHのメモ - log