Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/phpqa.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ jobs:
steps:
- uses: actions/checkout@master
- name: PHPStan
uses: docker://jakzal/phpqa:php8.0-alpine
uses: docker://jakzal/phpqa:php8.4
with:
args: phpstan analyze src/ -l 1
- name: PHP-CS-Fixer
uses: docker://jakzal/phpqa:php8.0-alpine
uses: docker://jakzal/phpqa:php8.4
with:
args: php-cs-fixer --dry-run --allow-risky=yes --no-interaction --ansi fix
- name: Deptrac
uses: docker://jakzal/phpqa:php8.0-alpine
uses: docker://jakzal/phpqa:php8.4
with:
args: deptrac --no-interaction --ansi --formatter-graphviz-display=0
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@
/coverage.clover
/.phpunit.result.cache
/vendor/
/.idea
/.composer
3 changes: 2 additions & 1 deletion .scrutinizer.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,9 @@ tools:
enabled: true
excluded_dirs: [tests]
build:
image: default-bionic
environment:
php: 8.0.1
php: 8.4.0
nodes:
analysis:
tests:
Expand Down
4 changes: 3 additions & 1 deletion .styleci.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
---
preset: psr2
risky: true
version: 8
version: 8.4
finder:
name:
- "*.php"
enabled:
- short_array_syntax
- cast_spaces
disabled:
- lowercase_constants
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
language: php

php:
- 8.0
- 8.4

script:
- composer install --dev
Expand Down
26 changes: 13 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
## PHP Rule Engine


[![Latest Stable Version](https://travis-ci.org/nicoSWD/php-rule-parser.svg?branch=master)](https://travis-ci.org/nicoSWD/php-rule-parser)
[![Build status][Master coverage image]][Master coverage]
[![Code Quality][Master quality image]][Master quality]
Expand All @@ -13,13 +12,10 @@ You're looking at a standalone PHP library to parse and evaluate text based rule

This library has initially been used to change and configure the behavior of certain "Workflows" (without changing actual code) in an intranet application, but it may serve a purpose elsewhere.


Find me on Twitter: @[nicoSWD](https://twitter.com/nicoSWD)

(If you're using PHP 5, you might want to take a look at [version 0.4.0](https://github.com/nicoSWD/php-rule-parser/tree/0.4.0))

[![SensioLabsInsight](https://insight.sensiolabs.com/projects/67203389-970c-419c-9430-a7f9a005bd94/big.png)](https://insight.sensiolabs.com/projects/67203389-970c-419c-9430-a7f9a005bd94)

## Install

Via Composer
Expand All @@ -40,32 +36,36 @@ This library works best with one of these bundles below, but they're not require

Test if a value is in a given array
```php
$variables = ['foo' => 6];
$variables = [
'coupon_code' => (string) $_POST['coupon_code'],
];

$rule = new Rule('foo in [4, 6, 7]', $variables);
$rule = new Rule('coupon_code in ["summer_discount", "summer21"]', $variables);
var_dump($rule->isTrue()); // bool(true)
```

Simple array manipulation
Performing a regular expression
```php
$rule = new Rule('[1, 4, 3].join(".") === "1.4.3"');
$variables = [
'coupon_code' => (string) $_POST['coupon_code'],
];

$rule = new Rule('coupon_code.test(/^summer20[0-9]{2}$/) == true', $variables);
var_dump($rule->isTrue()); // bool(true)
```

Test if a value is between a given range
```php
$variables = ['threshold' => 80];
$variables = ['points' => 80];

$rule = new Rule('threshold >= 50 && threshold <= 100', $variables);
$rule = new Rule('points >= 50 && points <= 100', $variables);
var_dump($rule->isTrue()); // bool(true)
```

Call methods on objects from within rules
```php
class User
{
// ...

public function points(): int
{
return 1337;
Expand Down Expand Up @@ -180,7 +180,7 @@ $highlighter = new Rule\Highlighter\Highlighter(new Rule\Tokenizer());

// Optional custom styles
$highlighter->setStyle(
Rule\Constants::GROUP_VARIABLE,
TokenType::VARIABLE,
'color: #007694; font-weight: 900;'
);

Expand Down
8 changes: 4 additions & 4 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
{
"name": "Nicolas Oelgart",
"role": "Developer",
"homepage": "https://nicoswd.com"
"homepage": "https://nico.es"
}
],
"autoload": {
Expand All @@ -33,11 +33,11 @@
}
},
"require": {
"php": ">=8.0"
"php": ">=8.4"
},
"require-dev": {
"phpunit/phpunit": "^7.0|^9.0",
"mockery/mockery": "^1.0|^1.4"
"phpunit/phpunit": "^12.3",
"mockery/mockery": "^1.6"
},
"scripts": {
"test": "vendor/bin/phpunit"
Expand Down
15 changes: 8 additions & 7 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd"
bootstrap="src/autoload.php">
<coverage processUncoveredFiles="true">
<include>
<directory suffix=".php">src</directory>
</include>
</coverage>
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/12.3/phpunit.xsd"
bootstrap="src/autoload.php"
cacheDirectory=".phpunit.cache">
<testsuites>
<testsuite name="Unit tests">
<directory>tests/</directory>
</testsuite>
</testsuites>
<source>
<include>
<directory suffix=".php">src</directory>
</include>
</source>
</phpunit>
2 changes: 1 addition & 1 deletion src/Compiler/CompilerFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
/**
* @license http://opensource.org/licenses/mit-license.php MIT
* @link https://github.com/nicoSWD
* @author Nicolas Oelgart <nico@oelgart.com>
* @author Nicolas Oelgart <hello@nico.es>
*/
namespace nicoSWD\Rule\Compiler;

Expand Down
2 changes: 1 addition & 1 deletion src/Compiler/CompilerFactoryInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
/**
* @license http://opensource.org/licenses/mit-license.php MIT
* @link https://github.com/nicoSWD
* @author Nicolas Oelgart <nico@oelgart.com>
* @author Nicolas Oelgart <hello@nico.es>
*/
namespace nicoSWD\Rule\Compiler;

Expand Down
8 changes: 5 additions & 3 deletions src/Compiler/CompilerInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,21 @@
/**
* @license http://opensource.org/licenses/mit-license.php MIT
* @link https://github.com/nicoSWD
* @author Nicolas Oelgart <nico@oelgart.com>
* @author Nicolas Oelgart <hello@nico.es>
*/
namespace nicoSWD\Rule\Compiler;

use nicoSWD\Rule\TokenStream\Token\BaseToken;
use nicoSWD\Rule\TokenStream\Token\Type\Logical;
use nicoSWD\Rule\TokenStream\Token\Type\Parenthesis;

interface CompilerInterface
{
public function getCompiledRule(): string;

public function addParentheses(BaseToken $token): void;
public function addParentheses(BaseToken & Parenthesis $token): void;

public function addLogical(BaseToken $token): void;
public function addLogical(BaseToken & Logical $token): void;

public function addBoolean(bool $bool): void;
}
2 changes: 1 addition & 1 deletion src/Compiler/Exception/MissingOperatorException.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
/**
* @license http://opensource.org/licenses/mit-license.php MIT
* @link https://github.com/nicoSWD
* @author Nicolas Oelgart <nico@oelgart.com>
* @author Nicolas Oelgart <hello@nico.es>
*/
namespace nicoSWD\Rule\Compiler\Exception;

Expand Down
47 changes: 18 additions & 29 deletions src/Compiler/StandardCompiler.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,24 @@
/**
* @license http://opensource.org/licenses/mit-license.php MIT
* @link https://github.com/nicoSWD
* @author Nicolas Oelgart <nico@oelgart.com>
* @author Nicolas Oelgart <hello@nico.es>
*/
namespace nicoSWD\Rule\Compiler;

use nicoSWD\Rule\Compiler\Exception\MissingOperatorException;
use nicoSWD\Rule\Evaluator\Boolean;
use nicoSWD\Rule\Evaluator\Operator;
use nicoSWD\Rule\Parser\Exception\ParserException;
use nicoSWD\Rule\TokenStream\Token\BaseToken;
use nicoSWD\Rule\TokenStream\Token\TokenAnd;
use nicoSWD\Rule\TokenStream\Token\TokenOpeningParenthesis;
use nicoSWD\Rule\TokenStream\Token\Type\Logical;
use nicoSWD\Rule\TokenStream\Token\Type\Parenthesis;

class StandardCompiler implements CompilerInterface
final class StandardCompiler implements CompilerInterface
{
private const BOOL_TRUE = '1';
private const BOOL_FALSE = '0';

private const LOGICAL_AND = '&';
private const LOGICAL_OR = '|';

private const OPENING_PARENTHESIS = '(';
private const CLOSING_PARENTHESIS = ')';
private const string OPENING_PARENTHESIS = '(';
private const string CLOSING_PARENTHESIS = ')';

private string $output = '';
private int $openParenthesis = 0;
Expand Down Expand Up @@ -58,7 +56,7 @@ private function closeParenthesis(): void
}

/** @throws ParserException */
public function addParentheses(BaseToken $token): void
public function addParentheses(BaseToken & Parenthesis $token): void
{
if ($token instanceof TokenOpeningParenthesis) {
if (!$this->expectOpeningParenthesis()) {
Expand All @@ -71,31 +69,27 @@ public function addParentheses(BaseToken $token): void
}

/** @throws ParserException */
public function addLogical(BaseToken $token): void
public function addLogical(BaseToken & Logical $token): void
{
$lastChar = $this->getLastChar();

if ($lastChar === self::LOGICAL_AND || $lastChar === self::LOGICAL_OR) {
if (Operator::tryFrom($this->getLastChar()) !== null) {
throw ParserException::unexpectedToken($token);
}

if ($token instanceof TokenAnd) {
$this->output .= self::LOGICAL_AND;
$this->output .= Operator::LOGICAL_AND->value;
} else {
$this->output .= self::LOGICAL_OR;
$this->output .= Operator::LOGICAL_OR->value;
}
}

/** @throws MissingOperatorException */
public function addBoolean(bool $bool): void
{
$lastChar = $this->getLastChar();

if ($lastChar === self::BOOL_TRUE || $lastChar === self::BOOL_FALSE) {
if (Boolean::tryFrom($this->getLastChar()) !== null) {
throw new MissingOperatorException();
}

$this->output .= $bool ? self::BOOL_TRUE : self::BOOL_FALSE;
$this->output .= Boolean::fromBool($bool)->value;
}

private function numParenthesesMatch(): bool
Expand All @@ -105,11 +99,7 @@ private function numParenthesesMatch(): bool

private function isIncompleteCondition(): bool
{
$lastChar = $this->getLastChar();

return
$lastChar === self::LOGICAL_AND ||
$lastChar === self::LOGICAL_OR;
return Operator::tryFrom($this->getLastChar()) !== null;
}

private function expectOpeningParenthesis(): bool
Expand All @@ -118,9 +108,8 @@ private function expectOpeningParenthesis(): bool

return
$lastChar === '' ||
$lastChar === self::LOGICAL_AND ||
$lastChar === self::LOGICAL_OR ||
$lastChar === self::OPENING_PARENTHESIS;
$lastChar === self::OPENING_PARENTHESIS ||
Operator::tryFrom($lastChar) !== null;
}

private function getLastChar(): string
Expand Down
19 changes: 19 additions & 0 deletions src/Evaluator/Boolean.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php declare(strict_types=1);

/**
* @license http://opensource.org/licenses/mit-license.php MIT
* @link https://github.com/nicoSWD
* @author Nicolas Oelgart <hello@nico.es>
*/
namespace nicoSWD\Rule\Evaluator;

enum Boolean: string
{
case TRUE = '1';
case FALSE = '0';

final public static function fromBool(bool $bool): self
{
return $bool ? self::TRUE : self::FALSE;
}
}
Loading