Completed
Push — master ( c34bb5...a29fc9 )
by Andrii
04:22
created

FormulaEngine::getDiscount()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 2
nop 0
dl 0
loc 7
ccs 4
cts 4
cp 1
crap 2
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/**
3
 * PHP Billing Library
4
 *
5
 * @link      https://github.com/hiqdev/php-billing
6
 * @package   php-billing
7
 * @license   BSD-3-Clause
8
 * @copyright Copyright (c) 2017-2018, HiQDev (http://hiqdev.com/)
9
 */
10
11
namespace hiqdev\php\billing\formula;
12
13
use hiqdev\php\billing\charge\ChargeModifier;
14
use hiqdev\php\billing\charge\modifiers\Discount;
15
use hiqdev\php\billing\charge\modifiers\Leasing;
16
use Hoa\Ruler\Context;
17
use Hoa\Ruler\Model;
18
use Hoa\Ruler\Ruler;
19
use Hoa\Visitor\Visit;
20
21
/**
22
 * @author Andrii Vasyliev <[email protected]>
23
 */
24
class FormulaEngine implements FormulaEngineInterface
25
{
26
    /**
27
     * @var Ruler
28
     */
29
    protected $ruler;
30
31
    /**
32
     * @var Visit|Asserter
33
     */
34
    protected $asserter;
35
36
    /**
37
     * @var Context
38
     */
39
    protected $context;
40
41
    /**
42
     * @var ChargeModifier
43
     */
44
    protected $discount;
45
46
    /**
47
     * @var ChargeModifier
48
     */
49
    protected $leasing;
50
51 14
    public function __construct()
52
    {
53 14
        if (!class_exists(Context::class)) {
54
            throw new \Exception('to use formula engine install `hoa/ruler`');
55
        }
56 14
    }
57
58
    /**
59
     * @param string $formula
60
     * @return ChargeModifier
61
     */
62 6
    public function build(string $formula): ChargeModifier
63
    {
64
        try {
65 6
            $model = $this->interpret($formula);
66 5
            $result = $this->getRuler()->assert($model, $this->getContext());
67 1
        } catch (FormulaEngineException $e) {
68 1
            throw $e;
69
        } catch (\Hoa\Ruler\Exception\Asserter $e) {
70
            throw FormulaRuntimeError::fromException($e, $formula);
71
        } catch (\Exception $exception) {
72
            throw FormulaRuntimeError::create($formula, 'Formula run failed');
73
        }
74
75 5
        if (!$result instanceof ChargeModifier) {
0 ignored issues
show
introduced by
$result is never a sub-type of hiqdev\php\billing\charge\ChargeModifier.
Loading history...
76 1
            throw FormulaRuntimeError::create($formula, 'Formula run returned unexpected result');
77
        }
78
79 4
        return $result;
80
    }
81
82
    /**
83
     * @param string $formula
84
     * @throws
85
     * @return Model\Model
86
     */
87 6
    public function interpret(string $formula): Model\Model
88
    {
89
        try {
90 6
            $rule = str_replace("\n", ' AND ', $this->normalize($formula));
91
92 6
            return $this->getRuler()->interpret($rule);
93 1
        } catch (\Hoa\Compiler\Exception\Exception $exception) {
94 1
            throw FormulaSyntaxError::fromException($exception, $formula);
95
        } catch (\Hoa\Ruler\Exception\Interpreter $exception) {
96
            throw FormulaSyntaxError::fromException($exception, $formula);
97
        } catch (\Throwable $exception) {
98
            throw FormulaSyntaxError::create($formula, 'Failed to interpret formula');
99
        }
100
    }
101
102 14
    public function normalize(string $formula): ?string
103
    {
104 14
        $lines = explode("\n", $formula);
105
        $normalized = array_map(function ($value) {
106 14
            $value = trim($value);
107 14
            if (strlen($value) === 0) {
108 5
                return null;
109
            }
110
111 10
            return $value;
112 14
        }, $lines);
113 14
        $cleared = array_filter($normalized);
114
115 14
        return empty($cleared) ? null : implode("\n", $cleared);
116
    }
117
118
    /**
119
     * Validates $formula.
120
     *
121
     * @param string $formula
122
     * @return null|string `null` when formula has no errors or string error message
123
     */
124 4
    public function validate(string $formula): ?string
125
    {
126
        try {
127 4
            $this->build($formula);
128
129 2
            return null;
130 2
        } catch (FormulaEngineException $e) {
131 2
            return $e->getMessage();
132
        }
133
    }
134
135 6
    public function getRuler(): Ruler
136
    {
137 6
        if ($this->ruler === null) {
138 6
            $this->ruler = new Ruler();
139 6
            $this->ruler->setAsserter($this->getAsserter());
140
        }
141
142 6
        return $this->ruler;
143
    }
144
145 6
    public function getAsserter(): Visit
146
    {
147 6
        if ($this->asserter === null) {
148 6
            $this->asserter = new Asserter();
149
        }
150
151 6
        return $this->asserter;
152
    }
153
154 5
    public function getContext(): Context
155
    {
156 5
        if ($this->context === null) {
157 5
            $this->context = $this->buildContext();
158
        }
159
160 5
        return $this->context;
161
    }
162
163 5
    protected function buildContext(): Context
164
    {
165 5
        $context = new Context();
166 5
        $context['discount'] = $this->getDiscount();
167 5
        $context['leasing'] = $this->getLeasing();
168
169 5
        return $context;
170
    }
171
172 5
    public function getDiscount(): ChargeModifier
173
    {
174 5
        if ($this->discount === null) {
175 5
            $this->discount = new Discount();
176
        }
177
178 5
        return $this->discount;
179
    }
180
181 5
    public function getLeasing(): ChargeModifier
182
    {
183 5
        if ($this->leasing === null) {
184 5
            $this->leasing = new Leasing();
185
        }
186
187 5
        return $this->leasing;
188
    }
189
}
190