{"id":22452373,"url":"https://github.com/revotale/php-shopping-cart","last_synced_at":"2026-02-15T13:37:11.189Z","repository":{"id":265198856,"uuid":"894509368","full_name":"RevoTale/php-shopping-cart","owner":"RevoTale","description":"PHP library providing basic shopping cart object and interfaces to implement any type of cart items + promotions combination.","archived":false,"fork":false,"pushed_at":"2025-11-24T09:35:42.000Z","size":181,"stargazers_count":2,"open_issues_count":3,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-11-27T09:28:43.009Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/RevoTale.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2024-11-26T13:34:24.000Z","updated_at":"2025-11-19T21:57:30.000Z","dependencies_parsed_at":"2025-10-21T23:21:16.216Z","dependency_job_id":"6bfee4c8-5fe6-41d7-86f5-9c2ad3fc38a6","html_url":"https://github.com/RevoTale/php-shopping-cart","commit_stats":null,"previous_names":["revotale/php-shopping-cart"],"tags_count":11,"template":false,"template_full_name":null,"purl":"pkg:github/RevoTale/php-shopping-cart","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RevoTale%2Fphp-shopping-cart","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RevoTale%2Fphp-shopping-cart/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RevoTale%2Fphp-shopping-cart/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RevoTale%2Fphp-shopping-cart/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/RevoTale","download_url":"https://codeload.github.com/RevoTale/php-shopping-cart/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RevoTale%2Fphp-shopping-cart/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29480252,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-15T11:35:25.641Z","status":"ssl_error","status_checked_at":"2026-02-15T11:34:57.128Z","response_time":118,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-12-06T06:10:49.920Z","updated_at":"2026-02-15T13:37:11.183Z","avatar_url":"https://github.com/RevoTale.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# RevoTale Shopping Cart\n\nA powerful and flexible PHP library for implementing shopping cart functionality with advanced promotion system and precise decimal calculations.\n\n**Notice!** This README was \"vibe coded\". If you encouter any errors please feel free to open the issue.\n\n[![PHP Version](https://img.shields.io/badge/php-%5E8.2-blue)](https://php.net)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\n## Features\n\n- 🛒 **Flexible Cart Management**: Add, remove, and manage cart items with quantity control\n- 🏷️ **Advanced Promotion System**: Support for multiple promotion types (percentage discounts, fixed amount discounts, free products, etc.)\n- 💰 **Precise Decimal Calculations**: Built-in arbitrary precision decimal arithmetic using BCMath\n- 🔧 **Extensible Architecture**: Easy to extend with custom items and promotions\n- 📊 **Comprehensive Totals**: Detailed cart totals with promotion impacts and item subtotals\n- 🎯 **Promotion Stacking**: Support for multiple promotions that can interact with each other\n- 🧮 **Mathematical Functions**: Full-featured decimal class with trigonometric, logarithmic, and power functions\n\n## Installation\n\n```bash\ncomposer require revotale/shopping-cart\n```\n\n### Requirements\n\n- PHP ^8.2\n- BCMath extension\n\n## Quick Start\n\n### Basic Cart Operations\n\n```php\n\u003c?php\n\nuse RevoTale\\ShoppingCart\\Cart;\nuse RevoTale\\ShoppingCart\\CartItemInterface;\n\n// Create a cart item (implement CartItemInterface)\nclass Product implements CartItemInterface\n{\n    public function __construct(\n        private string $id,\n        private string $name,\n        private int $priceInCents\n    ) {}\n\n    public function getCartId(): string\n    {\n        return $this-\u003eid;\n    }\n\n    public function getCartType(): string\n    {\n        return 'product';\n    }\n\n    public function getUnitPrice(): int\n    {\n        return $this-\u003epriceInCents; // Price in smallest currency unit (cents)\n    }\n}\n\n// Create cart and add items\n$cart = new Cart();\n$product = new Product('SKU123', 'T-Shirt', 2999); // $29.99\n\n$cart-\u003eaddItem($product, 2); // Add 2 t-shirts\n\n// Get cart totals\n$totals = $cart-\u003eperformTotals();\necho $totals-\u003egetTotal()-\u003easFloat(); // 59.98\n```\n\n### Adding Promotions\n\n```php\nuse RevoTale\\ShoppingCart\\PromotionTemplates\\CartPercentageDiscount;\nuse RevoTale\\ShoppingCart\\CartInterface;\n\n// Create a 10% discount promotion\nclass TenPercentDiscount extends CartPercentageDiscount\n{\n    public function getCartId(): string\n    {\n        return 'ten_percent_off';\n    }\n\n    public function getCartType(): string\n    {\n        return 'discount';\n    }\n\n    public function isEligible(CartInterface $cart): bool\n    {\n        // Apply if cart total is over $50\n        return $cart-\u003egetItems() !== [] \u0026\u0026 \n               $cart-\u003eperformTotals()-\u003egetTotal()-\u003eisGreaterThan(\n                   \\RevoTale\\ShoppingCart\\Decimal::fromInteger(5000)\n               );\n    }\n\n    public function getDiscountMultiplier(): float\n    {\n        return 0.9; // 90% of original price = 10% discount\n    }\n}\n\n// Add promotion to cart\n$cart-\u003eaddPromotion(new TenPercentDiscount());\n\n$totals = $cart-\u003eperformTotals();\necho $totals-\u003egetTotal()-\u003easFloat(); // 53.98 (10% off $59.98)\n```\n\n## Core Components\n\n### Cart\n\nThe main cart class that manages items and promotions.\n\n```php\n$cart = new Cart();\n\n// Item management\n$cart-\u003eaddItem($item, $quantity);\n$cart-\u003eremoveItem($item, $quantity);\n$cart-\u003ehasItem($item);\n$cart-\u003egetItemQuantity($item);\n\n// Promotion management\n$cart-\u003eaddPromotion($promotion);\n$cart-\u003eremovePromotion($promotion);\n$cart-\u003ehasPromo($promotion);\n\n// Clear operations\n$cart-\u003eclearItems();\n$cart-\u003eclearPromotions();\n$cart-\u003eclear(); // Clear both items and promotions\n\n// Calculate totals\n$totals = $cart-\u003eperformTotals();\n```\n\n### Cart Items\n\nItems must implement `CartItemInterface`:\n\n```php\ninterface CartItemInterface\n{\n    public function getCartId(): string;      // Unique identifier\n    public function getCartType(): string;    // Type category\n    public function getUnitPrice(): int;      // Price in smallest currency unit\n}\n```\n\n### Promotions\n\nPromotions implement `PromotionInterface` and can:\n\n- **Reduce item prices**: Modify individual item subtotals\n- **Add/remove items**: Add free products or remove items\n- **Control other promotions**: Enable/disable other promotions\n- **Apply cart-wide effects**: Fixed amount discounts, shipping rules\n\n#### Built-in Promotion Templates\n\n1. **CartPercentageDiscount**: Apply percentage discounts\n\n```php\nclass MyPercentageDiscount extends CartPercentageDiscount\n{\n    public function getDiscountMultiplier(): float\n    {\n        return 0.85; // 15% discount\n    }\n    \n    public function isEligible(CartInterface $cart): bool\n    {\n        return true; // Always eligible\n    }\n    \n    // ... implement required methods\n}\n```\n\n2. **CartFixedSumDiscount**: Apply fixed amount discounts\n\n```php\nclass MyFixedDiscount extends CartFixedSumDiscount\n{\n    public function getDiscountAmount(): float\n    {\n        return 10.00; // $10 off\n    }\n    \n    public function isEligible(CartInterface $cart): bool\n    {\n        return true;\n    }\n    \n    // ... implement required methods\n}\n```\n\n#### Custom Promotions\n\nFor complex promotion logic, implement `PromotionInterface` directly:\n\n```php\nclass BuyOneGetOneFree implements PromotionInterface\n{\n    public function isEligible(CartInterface $cart): bool\n    {\n        return $cart-\u003egetItemQuantity($this-\u003etargetItem) \u003e= 2;\n    }\n\n    public function reduceItems(ModifiedCartData $cart, array $itemCounters): array\n    {\n        // Add free items based on cart contents\n        foreach ($itemCounters as $counter) {\n            if ($counter-\u003eitem === $this-\u003etargetItem) {\n                $freeQuantity = intval($counter-\u003equantity / 2);\n                if ($freeQuantity \u003e 0) {\n                    $itemCounters[] = new CartItemCounter($this-\u003efreeItem, $freeQuantity);\n                }\n            }\n        }\n        return $itemCounters;\n    }\n    \n    // ... implement other required methods\n}\n```\n\n### Decimal Arithmetic\n\nThe library includes a comprehensive `Decimal` class for precise calculations:\n\n```php\nuse RevoTale\\ShoppingCart\\Decimal;\n\n// Create decimals\n$price = Decimal::fromFloat(29.99);\n$quantity = Decimal::fromInteger(3);\n$discount = Decimal::fromString(\"0.1\");\n\n// Arithmetic operations\n$subtotal = $price-\u003emul($quantity);           // 89.97\n$discountAmount = $subtotal-\u003emul($discount);  // 8.997\n$total = $subtotal-\u003esub($discountAmount);     // 80.973\n\n// Rounding and formatting\n$finalTotal = $total-\u003eround(2);               // 80.97\necho $finalTotal-\u003easFloat();                  // 80.97\n\n// Comparisons\nif ($total-\u003eisGreaterThan(Decimal::fromInteger(80))) {\n    echo \"Total exceeds $80\";\n}\n\n// Mathematical functions\n$sqrt = Decimal::fromInteger(16)-\u003esqrt();     // 4.0\n$log = Decimal::fromInteger(100)-\u003elog10();    // 2.0\n$power = Decimal::fromInteger(2)-\u003epow(Decimal::fromInteger(8)); // 256.0\n```\n\n## Advanced Usage\n\n### Promotion Context\n\nUse `PromoCalculationsContext` to share data between promotions during calculation:\n\n```php\npublic function reduceItemSubtotal(\n    ModifiedCartData $cart, \n    CartItemInterface $item, \n    Decimal $subTotal, \n    PromoCalculationsContext $context\n): Decimal {\n    // Store data for later use\n    $context-\u003esetValue($this, 'discount_applied', true);\n    \n    // Retrieve data from other promotions\n    $previousDiscount = $context-\u003egetValue($otherPromotion, 'discount_amount');\n    \n    return $subTotal-\u003emul(Decimal::fromFloat(0.9));\n}\n```\n\n### Cart Totals Analysis\n\nThe `CartTotals` object provides detailed information:\n\n```php\n$totals = $cart-\u003eperformTotals();\n\n// Basic totals\n$grandTotal = $totals-\u003egetTotal();\n$items = $totals-\u003egetItems();\n$promotions = $totals-\u003egetPromotions();\n\n// Detailed item information\nforeach ($totals-\u003egetItemSubTotals() as $itemSubTotal) {\n    echo sprintf(\n        \"Item: %s, Qty: %d, Before: %s, After: %s\\n\",\n        $itemSubTotal-\u003eitem-\u003egetCartId(),\n        $itemSubTotal-\u003equantity,\n        $itemSubTotal-\u003esubTotalBeforePromo-\u003easFloat(),\n        $itemSubTotal-\u003esubTotalAfterPromo-\u003easFloat()\n    );\n}\n\n// Promotion impacts\nforeach ($totals-\u003egetPromotionItemsImpact() as $impact) {\n    echo sprintf(\n        \"Promotion %s affected %s by %s\\n\",\n        $impact-\u003epromotion-\u003egetCartId(),\n        $impact-\u003eitem-\u003egetCartId(),\n        $impact-\u003epriceImpact-\u003easFloat()\n    );\n}\n\n// Check for changes\nif ($totals-\u003eisPromotionDiff()) {\n    echo \"Promotions were added or removed during calculation\\n\";\n}\n\nif ($totals-\u003eisItemsDiff()) {\n    echo \"Items were added or removed during calculation\\n\";\n}\n```\n\n### Promotion Execution Order\n\nPromotions are executed in the order they were added to the cart. Later promotions can modify the effects of earlier ones. Use `reducePromotions()` to control which other promotions are active:\n\n```php\npublic function reducePromotions(ModifiedCartData $cart, array $promotions): array\n{\n    // Remove conflicting promotions\n    return array_filter($promotions, function($promo) {\n        return !($promo instanceof ConflictingPromotionType);\n    });\n}\n```\n\n## Error Handling\n\nThe library throws specific exceptions for various error conditions:\n\n```php\ntry {\n    // Division by zero\n    $result = $decimal-\u003ediv(Decimal::fromInteger(0));\n} catch (DomainException $e) {\n    echo \"Mathematical error: \" . $e-\u003egetMessage();\n}\n\ntry {\n    // Invalid number format\n    $decimal = Decimal::fromString(\"not-a-number\");\n} catch (UnexpectedValueException $e) {\n    echo \"Invalid input: \" . $e-\u003egetMessage();\n}\n```\n\n## Performance Considerations\n\n- The library uses BCMath for precise decimal calculations, which is slower than float arithmetic but provides exact results\n- Promotion calculations are performed each time `performTotals()` is called\n- For high-performance scenarios, consider caching totals when cart contents haven't changed\n- The `Decimal` class is immutable, so operations create new instances\n\n## Testing\n\n```bash\n# Run tests\ncomposer run phpunit\n\n# Run static analysis\ncomposer run phpstan\n\n# Run code style fixes\ncomposer run rector:fix\n```\n\n## Contributing\n\n1. Fork the repository\n2. Create a feature branch\n3. Add tests for new functionality\n4. Ensure all tests pass\n5. Submit a pull request\n\n## License\n\nThis library is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.\n\n## Support\n\nFor questions, issues, or feature requests, please use the GitHub issue tracker.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frevotale%2Fphp-shopping-cart","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frevotale%2Fphp-shopping-cart","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frevotale%2Fphp-shopping-cart/lists"}