Skip to content

Commit 2d806cc

Browse files
committed
added inner the function "if" working as the tenar operator
solved issue: #3
1 parent 9abeac7 commit 2d806cc

File tree

3 files changed

+92
-29
lines changed

3 files changed

+92
-29
lines changed

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "optimistex/math-expression",
3-
"version": "2.1.2",
3+
"version": "2.1.3",
44
"type": "library",
55
"description": "Taken from http://www.phpclasses.org/browse/file/11680.html, cred to Miles Kaufmann",
66
"keywords": [

expression.php

Lines changed: 74 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -105,35 +105,73 @@ functions, which are stored in the object. Try it, it's fun!
105105

106106
class Expression
107107
{
108-
109108
public $suppress_errors = false;
110109
public $last_error;
111110

112-
public $v = array('e' => 2.71, 'pi' => 3.14); // variables (and constants)
113-
public $f = array(); // user-defined functions
114-
public $vb = array('e', 'pi'); // constants
115-
public $fb = array( // built-in functions
111+
/**
112+
* Function defined outside of Expression as closures
113+
*
114+
* Example:
115+
* ```php
116+
* $expr = new Expression();
117+
* $expr->functions = [
118+
* 'foo' => function ($a, $b) {
119+
* return $a + $b;
120+
* }
121+
* ];
122+
* $expr->e('2 * foo(3, 4)'); //return: 14
123+
* ```
124+
*
125+
* @var array
126+
*/
127+
public $functions = [];
128+
129+
/**
130+
* Variables (and constants)
131+
* @var array
132+
*/
133+
public $v = ['e' => 2.71, 'pi' => 3.14];
134+
135+
private $f = []; // user-defined functions
136+
private $vb = ['e', 'pi']; // constants
137+
private $fb = [ // built-in functions
116138
'sin', 'sinh', 'arcsin', 'asin', 'arcsinh', 'asinh',
117139
'cos', 'cosh', 'arccos', 'acos', 'arccosh', 'acosh',
118140
'tan', 'tanh', 'arctan', 'atan', 'arctanh', 'atanh',
119-
'sqrt', 'abs', 'ln', 'log');
141+
'sqrt', 'abs', 'ln', 'log'];
120142

121-
public $functions = array(); // function defined outside of Expression as closures
143+
private $_functions = [];
122144

123145
public function __construct()
124146
{
125147
// make the variables a little more accurate
126148
$this->v['pi'] = M_PI;
127149
$this->v['e'] = exp(1);
150+
$this->_functions = [
151+
'if' => function ($condition, $valueIfTrue, $valueIfFalse) {
152+
return $condition ? $valueIfTrue : $valueIfFalse;
153+
}
154+
];
128155
}
129156

157+
/**
158+
* @param string $expr
159+
* @return bool|mixed|null
160+
* @throws ReflectionException
161+
*/
130162
public function e($expr)
131163
{
132164
return $this->evaluate($expr);
133165
}
134166

167+
/**
168+
* @param string $expr
169+
* @return bool|mixed|null
170+
* @throws ReflectionException
171+
*/
135172
public function evaluate($expr)
136173
{
174+
$this->_functions = array_merge($this->_functions, $this->functions);
137175
$this->last_error = null;
138176
$expr = preg_replace("/\r|\n/", '', trim($expr));
139177
if ($expr && $expr[strlen($expr) - 1] === ';') {
@@ -157,7 +195,7 @@ public function evaluate($expr)
157195
return $this->trigger("cannot redefine built-in function '$matches[1]()'");
158196
}
159197

160-
$args = array();
198+
$args = [];
161199
if ($matches[2] !== '') {
162200
$args = explode(',', preg_replace("/\s+/", '', $matches[2])); // get the arguments
163201
}
@@ -174,23 +212,29 @@ public function evaluate($expr)
174212
}
175213
}
176214
}
177-
$this->f[$fnn] = array('args' => $args, 'func' => $stack);
215+
$this->f[$fnn] = ['args' => $args, 'func' => $stack];
178216
return true;
179217
//===============
180218
}
181219
return $this->pfx($this->nfx($expr)); // straight up evaluation, woo
182220
}
183221

222+
/**
223+
* @return array
224+
*/
184225
public function vars()
185226
{
186227
$output = $this->v;
187228
unset($output['pi'], $output['e']);
188229
return $output;
189230
}
190231

232+
/**
233+
* @return array
234+
*/
191235
public function funcs()
192236
{
193-
$output = array();
237+
$output = [];
194238
foreach ($this->f as $fnn => $dat) {
195239
$output[] = $fnn . '(' . implode(',', $dat['args']) . ')';
196240
}
@@ -203,20 +247,21 @@ public function funcs()
203247
* Convert infix to postfix notation
204248
* @param string $expr
205249
* @return array|bool
250+
* @throws ReflectionException
206251
*/
207-
public function nfx($expr)
252+
private function nfx($expr)
208253
{
209254
$index = 0;
210255
$stack = new ExpressionStack;
211-
$output = array(); // postfix form of expression, to be passed to pfx()
256+
$output = []; // postfix form of expression, to be passed to pfx()
212257
$expr = trim($expr);
213258

214-
$ops = array('+', '-', '*', '/', '^', '_', '%', '>', '<', '>=', '<=', '==', '!=', '=~', '&&', '||', '!');
215-
$ops_r = array('+' => 0, '-' => 0, '*' => 0, '/' => 0, '%' => 0, '^' => 1, '>' => 0,
259+
$ops = ['+', '-', '*', '/', '^', '_', '%', '>', '<', '>=', '<=', '==', '!=', '=~', '&&', '||', '!'];
260+
$ops_r = ['+' => 0, '-' => 0, '*' => 0, '/' => 0, '%' => 0, '^' => 1, '>' => 0,
216261
'<' => 0, '>=' => 0, '<=' => 0, '==' => 0, '!=' => 0, '=~' => 0,
217-
'&&' => 0, '||' => 0, '!' => 0); // right-associative operator?
218-
$ops_p = array('+' => 3, '-' => 3, '*' => 4, '/' => 4, '_' => 4, '%' => 4, '^' => 5, '>' => 2, '<' => 2,
219-
'>=' => 2, '<=' => 2, '==' => 2, '!=' => 2, '=~' => 2, '&&' => 1, '||' => 1, '!' => 5); // operator precedence
262+
'&&' => 0, '||' => 0, '!' => 0]; // right-associative operator?
263+
$ops_p = ['+' => 3, '-' => 3, '*' => 4, '/' => 4, '_' => 4, '%' => 4, '^' => 5, '>' => 2, '<' => 2,
264+
'>=' => 2, '<=' => 2, '==' => 2, '!=' => 2, '=~' => 2, '&&' => 1, '||' => 1, '!' => 5]; // operator precedence
220265

221266
$expecting_op = false; // we use this in syntax-checking the expression
222267
// and determining when a - is a negation
@@ -308,8 +353,8 @@ public function nfx($expr)
308353
if ($arg_count !== count($this->f[$fnn]['args'])) {
309354
return $this->trigger("wrong number of arguments ($arg_count given, " . count($this->f[$fnn]['args']) . ' expected) ' . json_encode($this->f[$fnn]['args']));
310355
}
311-
} elseif (array_key_exists($fnn, $this->functions)) {
312-
$func_reflection = new ReflectionFunction($this->functions[$fnn]);
356+
} elseif (array_key_exists($fnn, $this->_functions)) {
357+
$func_reflection = new ReflectionFunction($this->_functions[$fnn]);
313358
$count = $func_reflection->getNumberOfParameters();
314359
if ($arg_count !== $count) {
315360
return $this->trigger("wrong number of arguments ($arg_count given, " . $count . ' expected)');
@@ -355,7 +400,7 @@ public function nfx($expr)
355400
} elseif (preg_match("/^([a-z]\w*)\($/", $val, $matches)) { // may be func, or variable w/ implicit multiplication against parentheses...
356401
if (in_array($matches[1], $this->fb, true) ||
357402
array_key_exists($matches[1], $this->f) ||
358-
array_key_exists($matches[1], $this->functions)
403+
array_key_exists($matches[1], $this->_functions)
359404
) { // it's a func
360405
if ($begin_argument && !$stack->incrementArgument()) {
361406
$this->trigger("unexpected '('");
@@ -411,16 +456,17 @@ public function nfx($expr)
411456
* @param array|bool $tokens
412457
* @param array $vars
413458
* @return bool|mixed|null
459+
* @throws ReflectionException
414460
*/
415-
public function pfx($tokens, array $vars = array())
461+
private function pfx($tokens, array $vars = [])
416462
{
417463
if ($tokens === false) {
418464
return false;
419465
}
420466
$stack = new ExpressionStack();
421467
foreach ((array)$tokens as $token) { // nice and easy
422468
// if the token is a binary operator, pop two values off the stack, do the operation, and push the result back on
423-
if (in_array($token, array('+', '-', '*', '/', '^', '<', '>', '<=', '>=', '==', '&&', '||', '!=', '=~', '%'), true)) {
469+
if (in_array($token, ['+', '-', '*', '/', '^', '<', '>', '<=', '>=', '==', '&&', '||', '!=', '=~', '%'], true)) {
424470
$op2 = $stack->pop();
425471
$op1 = $stack->pop();
426472
switch ($token) {
@@ -530,8 +576,8 @@ public function pfx($tokens, array $vars = array())
530576
$args[$this->f[$fnn]['args'][$i]] = $stack->pop();
531577
}
532578
$stack->push($this->pfx($this->f[$fnn]['func'], $args)); // yay... recursion!!!!
533-
} else if (array_key_exists($fnn, $this->functions)) {
534-
$reflection = new ReflectionFunction($this->functions[$fnn]);
579+
} else if (array_key_exists($fnn, $this->_functions)) {
580+
$reflection = new ReflectionFunction($this->_functions[$fnn]);
535581
$count = $reflection->getNumberOfParameters();
536582
$args = [];
537583
for ($i = $count - 1; $i >= 0; $i--) {
@@ -565,8 +611,8 @@ public function pfx($tokens, array $vars = array())
565611
$stack->push(0 + $token);
566612
} else if (preg_match("/^['\\\"](.*)['\\\"]$/", $token)) {
567613
$stack->push(json_decode(preg_replace_callback("/^['\\\"](.*)['\\\"]$/", function ($matches) {
568-
$m = array("/\\\\'/", '/(?<!\\\\)"/');
569-
$r = array("'", '\\"');
614+
$m = ["/\\\\'/", '/(?<!\\\\)"/'];
615+
$r = ["'", '\\"'];
570616
return '"' . preg_replace($m, $r, $matches[1]) . '"';
571617
}, $token)));
572618
} elseif (array_key_exists($token, $this->v)) {
@@ -590,7 +636,7 @@ public function pfx($tokens, array $vars = array())
590636
* @param string $msg
591637
* @return bool
592638
*/
593-
public function trigger($msg)
639+
private function trigger($msg)
594640
{
595641
$this->last_error = $msg;
596642
if (!$this->suppress_errors) {
@@ -605,7 +651,7 @@ public function trigger($msg)
605651
*/
606652
class ExpressionStack
607653
{
608-
public $stack = array();
654+
public $stack = [];
609655
public $count = 0;
610656

611657
public function push($val)

tests/Expression.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,9 +371,13 @@ public function testFunctionOrderParameters()
371371
'max' => function ($v1, $v2) {
372372
return max($v1, $v2);
373373
},
374+
'foo' => function ($a, $b) {
375+
return $a + $b;
376+
}
374377
];
375378

376379
$data = [
380+
'2*foo(3,4)' => 14,
377381
'max(2,(2+2)*2)' => 8,
378382
'max((2),(0))' => 2,
379383
'max((2+2)*2,(3+2)*2)' => 10,
@@ -406,4 +410,17 @@ public function testRowBreaking()
406410
2'
407411
));
408412
}
413+
414+
public function testFunctionIf()
415+
{
416+
$expr = new Expression();
417+
$expr->functions = [
418+
'fake' => function () {
419+
return 'fake';
420+
}
421+
];
422+
$this->assertEquals('fake', $expr->evaluate('fake()'));
423+
$this->assertEquals(10, $expr->evaluate('if(2 > 3, 5, 10)'));
424+
$this->assertEquals(5, $expr->evaluate('if(2 < 3, 5, 10)'));
425+
}
409426
}

0 commit comments

Comments
 (0)