PHPackages                             hejunjie/promotion-engine - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. hejunjie/promotion-engine

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

hejunjie/promotion-engine
=========================

一个 灵活可扩展 的 PHP 促销策略引擎，让购物车的各种复杂促销逻辑（满减、打折、阶梯优惠、会员折扣…）实现更优雅、可维护 ｜ A flexible and scalable PHP promotional strategy engine that enables various complex promotional logics (money off, discounts, tiered offers, member discounts, etc.) in shopping carts to be implemented more elegantly and maintainably.

v2.2.1(1mo ago)1963MITPHPPHP ^7.4 || ^8.0

Since Jul 29Pushed 1mo agoCompare

[ Source](https://github.com/zxc7563598/php-promotion-engine)[ Packagist](https://packagist.org/packages/hejunjie/promotion-engine)[ RSS](/packages/hejunjie-promotion-engine/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (6)DependenciesVersions (7)Used By (0)

hejunjie/promotion-engine
=========================

[](#hejunjiepromotion-engine)

English ｜ [简体中文](./README.zh-CN.md)

A flexible and extensible PHP promotion strategy engine that abstracts complex shopping cart promotion logic — money-off, discounts, tiered offers, member discounts — out of if-else spaghetti and into composable rule classes.

**This project has been parsed by Zread. If you need a quick overview of the project, you can click here to view it：[Understand this project](https://zread.ai/zxc7563598/php-promotion-engine)**

---

Features
--------

[](#features)

- 🧩 **Rules as classes**：Each promotion strategy is an independent rule class — clear, testable, and reusable
- 🔀 **Three calculation modes**：Independent, sequential (stacked), and lock modes for different business scenarios
- 🏷️ **Tag filtering**：Rules match items by product tags, making category-specific promotions effortless
- 📊 **Priority control**：Rules execute in priority order, giving you full control over discount stacking
- 🔌 **Easy to extend**：Add custom rules by extending `AbstractPromotionRule` — no changes to the engine core
- 🪶 **Zero dependencies**：Requires only PHP &gt;= 7.4, no third-party libraries

Requirements
------------

[](#requirements)

- PHP &gt;= 7.4 (also supports PHP 8.x)
- Composer

Installation
------------

[](#installation)

```
composer require hejunjie/promotion-engine
```

Quick Start
-----------

[](#quick-start)

```
use Hejunjie\PromotionEngine\Models\Cart;
use Hejunjie\PromotionEngine\Models\User;
use Hejunjie\PromotionEngine\PromotionEngine;
use Hejunjie\PromotionEngine\Rules;

// Create a VIP user
$user = new User(true);

// Create a shopping cart
$cart = new Cart();
$cart->addItem('Chips', 30, 2, ['snacks']);
$cart->addItem('T-shirt', 100, 1, ['clothes']);
$cart->addItem('Mouse', 200, 1, ['electronics']);

// Initialize the engine and set calculation mode
$engine = new PromotionEngine();
$engine->setMode('independent');

// Register rules
$engine->addRule(new Rules\FullReductionRule(300, 30));   // ¥30 off when total ≥ ¥300
$engine->addRule(new Rules\VipDiscountRule(0.95));         // VIP 5% off

// Calculate
$result = $engine->calculate($cart, $user);

echo "Original: ¥{$result['original']}\n";   // Original: ¥360
echo "Discount: -¥{$result['discount']}\n";  // Discount: -¥48
echo "Final: ¥{$result['final']}\n";         // Final: ¥312
print_r($result['details']);                 // Discount detail array
```

Calculation Modes
-----------------

[](#calculation-modes)

The engine supports three calculation modes, switchable via `setMode()`：

ModeIdentifierBehaviorIndependent`independent`Each rule calculates its discount against the **original price** independently, then all discounts are summedSequential`sequential`Rules execute in order. Each rule's discount is distributed back into item prices, so subsequent rules calculate against **already-discounted prices** (stacked discounts)Lock`lock`Each item can be discounted by at most one rule. Discounted items are locked and skipped by subsequent rules```
// Independent: each rule works against original prices
$engine->setMode('independent');

// Sequential: discount-on-discount
$engine->setMode('sequential');

// Lock: each item receives only one discount
$engine->setMode('lock');
```

Note

In independent mode, rule order does not affect the final discount. In sequential and lock modes, rules execute in priority order.

Built-in Rules
--------------

[](#built-in-rules)

All rules accept two optional trailing parameters for **tag filtering** and **priority**：

```
// Only applies to items tagged 'snacks', priority 10 (lower = higher priority)
new Rules\FullReductionRule(100, 20, ['snacks'], 10);
```

RuleDescriptionExample`FullReductionRule`Spend ¥X, get ¥Y off`new FullReductionRule(100, 20)``FullDiscountRule`Spend ¥X, get Z% off`new FullDiscountRule(200, 0.9)``FullQuantityReductionRule`Buy X items, get ¥Y off`new FullQuantityReductionRule(3, 20)``FullQuantityDiscountRule`Buy X items, get Z% off`new FullQuantityDiscountRule(5, 0.9)``TieredDiscountRule`Tiered discount (highest eligible tier)`new TieredDiscountRule([100 => 0.9, 200 => 0.8, 500 => 0.7])``TieredReductionRule`Tiered money-off (highest eligible tier)`new TieredReductionRule([100 => 10, 200 => 30, 500 => 80])``NthItemDiscountRule`Nth item at Z% off`new NthItemDiscountRule(3, 0.5)``NthItemReductionRule`Nth item at special price ¥X`new NthItemReductionRule(3, 9.9)``VipDiscountRule`VIP members get Z% off`new VipDiscountRule(0.9)``VipReductionRule`VIP members get ¥X off`new VipReductionRule(5)`Advanced Usage
--------------

[](#advanced-usage)

### Tag Filtering

[](#tag-filtering)

Use product tags to restrict rules to specific categories：

```
$cart = new Cart();
$cart->addItem('Cola', 5, 10, ['drinks']);
$cart->addItem('Keyboard', 300, 1, ['electronics']);

// Only drinks: 20% off when buying 3 or more
$engine->addRule(new Rules\FullQuantityDiscountRule(3, 0.8, ['drinks']));

// Only electronics: ¥50 off when spending ¥200
$engine->addRule(new Rules\FullReductionRule(200, 50, ['electronics']));
```

### Custom Rules

[](#custom-rules)

Extend `AbstractPromotionRule` and implement the `apply()` method：

```
use Hejunjie\PromotionEngine\Models\Cart;
use Hejunjie\PromotionEngine\Models\User;
use Hejunjie\PromotionEngine\PromotionResult;
use Hejunjie\PromotionEngine\Rules\AbstractPromotionRule;

class BirthdayDiscountRule extends AbstractPromotionRule
{
    public function apply(Cart $cart, User $user, array $eligibleIndexes = []): PromotionResult
    {
        $items = $this->filterEligibleItems($cart, $eligibleIndexes);
        $total = $cart->calculateItemsTotal($items);

        if ($total > 0) {
            $discount = round($total * 0.1, 2);
            return new PromotionResult($discount, 'Birthday 10% off');
        }

        return new PromotionResult(0, 'Birthday discount not triggered');
    }
}

// Use the custom rule
$engine->addRule(new BirthdayDiscountRule(['all'], 1));
```

### Priority Control

[](#priority-control)

Lower numbers have higher priority (default is 1). Priority determines execution order in multi-rule scenarios：

```
// Money-off runs first (priority 5), then VIP discount (priority 10)
$engine->addRule(new Rules\FullReductionRule(300, 30, [], 5));
$engine->addRule(new Rules\VipDiscountRule(0.9, [], 10));
```

Directory Structure
-------------------

[](#directory-structure)

```
src/
├── PromotionEngine.php             # Engine entry point, rule management and calculation dispatch
├── PromotionResult.php             # Rule result value object (discount amount + description)
├── Calculators/
│   ├── IndependentCalculator.php   # Independent mode
│   ├── SequentialCalculator.php    # Sequential (stacked) mode
│   └── LockCalculator.php          # Lock mode
├── Contracts/
│   ├── PromotionCalculatorInterface.php  # Calculator interface
│   └── PromotionRuleInterface.php        # Rule interface
├── Models/
│   ├── Cart.php                    # Shopping cart model
│   └── User.php                    # User model
└── Rules/
    ├── AbstractPromotionRule.php         # Abstract rule base class
    ├── FullReductionRule.php             # Spend X, get Y off
    ├── FullDiscountRule.php              # Spend X, get Z% off
    ├── FullQuantityReductionRule.php     # Buy X items, get Y off
    ├── FullQuantityDiscountRule.php      # Buy X items, get Z% off
    ├── TieredDiscountRule.php            # Tiered discount
    ├── TieredReductionRule.php           # Tiered money-off
    ├── NthItemDiscountRule.php           # Nth item discount
    ├── NthItemReductionRule.php          # Nth item special price
    ├── VipDiscountRule.php               # VIP discount
    └── VipReductionRule.php              # VIP money-off

```

Contributing
------------

[](#contributing)

PRs are welcome! Areas that especially need help：

- New promotion rules (coupons, buy-one-get-one, flash sales, etc.)
- Unit tests
- Performance improvements

Fork the repository, submit a Pull Request, or open an Issue to discuss ideas.

###  Health Score

43

—

FairBetter than 89% of packages

Maintenance93

Actively maintained with recent releases

Popularity15

Limited adoption so far

Community10

Small or concentrated contributor base

Maturity46

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 83.3% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~70 days

Recently: every ~87 days

Total

6

Last Release

35d ago

Major Versions

v1.0.0 → v2.0.02025-08-01

PHP version history (3 changes)v1.0.0PHP &gt;=8.1

v2.2.0PHP &gt;=7.4

v2.2.1PHP ^7.4 || ^8.0

### Community

Maintainers

![](https://www.gravatar.com/avatar/5b65d4b40ae456172fb38f63f84bf737ac88031484b1f228b1cc8d71baa80adf?d=identicon)[苏青安](/maintainers/%E8%8B%8F%E9%9D%92%E5%AE%89)

---

Top Contributors

[![zxc7563598](https://avatars.githubusercontent.com/u/46590942?v=4)](https://github.com/zxc7563598 "zxc7563598 (5 commits)")[![mingzhanliao](https://avatars.githubusercontent.com/u/63911596?v=4)](https://github.com/mingzhanliao "mingzhanliao (1 commits)")

---

Tags

cart-discountcomposerecommercephp8promotion-enginestrategy-pattern

### Embed Badge

![Health badge](/badges/hejunjie-promotion-engine/health.svg)

```
[![Health](https://phpackages.com/badges/hejunjie-promotion-engine/health.svg)](https://phpackages.com/packages/hejunjie-promotion-engine)
```

###  Alternatives

[guiliredu/laravel-seed-migration-estados-cidades-brasil

Pacote com informações de todas cidades e estados brasileiros.

7718.5k](/packages/guiliredu-laravel-seed-migration-estados-cidades-brasil)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
