プログラミングしていてこんな経験ありませんか?
機能自体は単純なのに、判定条件だけがどんどん増えていく。
ユーザーの状態、設定項目、商品属性、会員レベル...
無数の if/else が絡み合い、コードが混沌としていく。
一箇所修正するだけで、他の部分への影響が心配になる。
私も同じ状況に直面しました。最初は我慢していましたが、ついに決断しました:シンプルなルールエンジンを自作して、これらの複合判定を専門に処理しようと。
こうして生まれたのが:hejunjie/simple-rule-engine
このルールエンジンで何ができる?
要約すると: 軽量で使いやすい PHP ルールエンジン。複数条件の組み合わせ、動的ルール実行をサポートし、ビジネスルール判定やデータ検証などのシーンに適しています。
プロジェクトのこうした場面で活躍します:
- 複雑なビジネスロジックの多条件判定(例:ユーザーがキャンペーン条件を満たしているか)
- データ保存前のルール検証
- カスタムロジックの設定化・構造化
- if 地獄から抜け出して整理整頓したい場合 😅
なぜ作ったのか?
実際のビジネスでは、多くの判定ロジックを「複数のフィールド + いくつかのルール + 条件の組み合わせ」として表現できます。
従来はこのように書いていました:
if (
$profile['status'] === 'active' &&
$profile['age'] >= 18 &&
in_array($profile['permission'], ['admin', 'editor'])
) {
// 処理...
}
今ではこう書けます:
// ルール定義
$criteria = [
new Criterion('age', '>=', 18, '年齢は18歳以上必要'),
new Criterion('status', '==', 'active', 'ステータスはactive必須'),
new Criterion('permission', 'in', ['admin', 'editor'], '権限が必要'),
];
// 評価実行
$result = RuleEngine::assess($criteria, $profile, 'AND'); // true/falseを返す
// 詳細評価情報(各ルールの実行状況取得)
$report = RuleEngine::assessWithDetails($criteria, $profile);
/*
返却例:
[
['description' => '年齢は18歳以上必要', 'passed' => true],
['description' => 'ステータスはactive必須', 'passed' => true],
['description' => '権限が必要', 'passed' => true]
]
*/
すっきりしませんか?さらに、ルールをデータベースに保存すれば「ビジネス判定の設定化」も実現できます。
プロジェクトの特徴
- 軽量でシンプル:依存関係なし、フレームワーク非依存、数行で使える
- ファクトリ登録機能:独自のオペレーターを登録して拡張可能
- 標準オペレーター搭載:一般的なオペレーターを多数サポート(記事末尾のリスト参照)
- 組み合わせ可能:AND/OR関係の組み合わせをサポート、複数ルールセットの拡張が容易
導入方法
composer require hejunjie/simple-rule-engine
️ サンプルコード
use Hejunjie\SimpleRuleEngine\Criterion;
use Hejunjie\SimpleRuleEngine\RuleEngine;
// ルールセット定義
$criteria = [
new Criterion('age', '>=', 18, '年齢は18歳以上必要'),
new Criterion('status', '==', 'active', 'ステータスはactive必須'),
new Criterion('permission', 'in', ['admin', 'editor'], '権限が必要'),
];
$target = ['age' => 20, 'nation' => 'Japan'];
// 全ルールのパス判定
if (RuleEngine::assess($criteria, $target, 'AND')) {
echo 'ルールを満たしています';
}
// 各ルールの詳細結果
foreach (RuleEngine::assessWithDetails($criteria, $target) as $item) {
echo $item['description'] . ':' . ($item['passed'] ? '✅ 合格' : '❌ 不合格') . PHP_EOL;
}
カスタムオペレーター
独自の判定ロジックを実装し、任意のオペレーターを定義してルールに組み込めます。
OperatorInterface を実装し、OperatorFactory 経由で登録するだけ:
use Hejunjie\SimpleRuleEngine\Interface\OperatorInterface;
use Hejunjie\SimpleRuleEngine\OperatorFactory;
class CustomOperator implements OperatorInterface
{
/**
* 評価メソッド
*
* @param mixed $inputValue 入力データ
* @param mixed $compareValue 比較データ
*
* @return bool
*/
public function evaluate(mixed $inputValue, mixed $compareValue): bool
{
// TODO: 判定ロジックを実装
}
/**
* オペレーター名
*
* @return string
*/
public function name(): string
{
return 'custom';
}
}
// カスタムオペレーター custom を登録
$factory = OperatorFactory::getInstance();
$factory->register(new CustomOperator());
// ルール定義で custom を使用可能
$criteria = [
new Criterion('field', 'custom', 'value', 'カスタムルール説明'),
// ...
// ...
];
// RuleEngine::assess($criteria, $target, 'AND')
// RuleEngine::assessWithDetails($criteria, $target)
標準オペレーター一覧
| オペレーター | 説明 | 補足 |
|---|---|---|
== |
等しい | なし |
!= |
等しくない | なし |
> |
より大きい | なし |
>= |
以上 | なし |
< |
より小さい | なし |
<= |
以下 | なし |
in |
配列に含まれる | 配列:\[値1,値2,...\] |
not\_in |
配列に含まれない | 配列:\[値1,値2,...\] |
contains |
文字列を含む | なし |
not\_contains |
文字列を含まない | なし |
start\_swith |
指定文字列で始まる | なし |
end\_swith |
指定文字列で終わる | なし |
between |
範囲内 | 配列:\[最小値,最大値\] |
not\_between |
範囲外 | 配列:\[最小値,最大値\] |
before\_date |
指定日付より前 | 通常の日付形式、タイムスタンプも可 |
after\_date |
指定日付より後 | 通常の日付形式、タイムスタンプも可 |
date\_equal |
日付が等しい | 通常の日付形式、タイムスタンプも可 |
まとめ
このルールエンジンは、高度な技術課題を解決するものではありません。ただよりエレガントな解決策を提供するだけです。 もし同様の if/else の悩みに直面しているなら、この小さなツールが少しでも役立つことを願っています。
Star、Issue、PR をお待ちしています。一緒に改善していきましょう 🙌 役立つと思っていただけたら、いいねいただけると更新の励みになります~