Skip to content

Commit a345f6f

Browse files
committed
Fixes a bug where you cannot use a runtime operator in a SQL query
1 parent 42fc542 commit a345f6f

File tree

7 files changed

+129
-26
lines changed

7 files changed

+129
-26
lines changed

src/Compiler/Compiler.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use RulerZ\Executor\Executor;
66
use RulerZ\Parser\Parser;
7+
use RulerZ\Target\Operators\CompileTimeOperator;
78

89
class Compiler
910
{
@@ -63,6 +64,13 @@ protected function compileToSource($rule, CompilationTarget $compilationTarget,
6364
}
6465

6566
$commentedRule = str_replace(PHP_EOL, PHP_EOL.' // ', $rule);
67+
$compiledRule = $executorModel->getCompiledRule();
68+
69+
if ($compiledRule instanceof CompileTimeOperator) {
70+
$escapedRule = sprintf('"%s"', $compiledRule->format(false));
71+
} else {
72+
$escapedRule = $compiledRule->format(false);
73+
}
6674

6775
return <<<EXECUTOR
6876
namespace RulerZ\Compiled\Executor;
@@ -78,7 +86,7 @@ class {$context['executor_classname']} implements Executor
7886
// $commentedRule
7987
protected function execute(\$target, array \$operators, array \$parameters)
8088
{
81-
return {$executorModel->getCompiledRule()};
89+
return {$escapedRule};
8290
}
8391
}
8492
EXECUTOR;

src/Target/GenericSqlVisitor.php

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -39,16 +39,6 @@ public function __construct(Context $context, OperatorsDefinitions $operators, $
3939
$this->allowStarOperator = (bool) $allowStarOperator;
4040
}
4141

42-
/**
43-
* {@inheritdoc}
44-
*/
45-
public function visitModel(AST\Model $element, &$handle = null, $eldnah = null)
46-
{
47-
$sql = parent::visitModel($element, $handle, $eldnah);
48-
49-
return '"'.$sql.'"';
50-
}
51-
5242
/**
5343
* {@inheritdoc}
5444
*/

src/Target/GenericVisitor.php

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
use RulerZ\Compiler\RuleVisitor;
99
use RulerZ\Exception\OperatorNotFoundException;
1010
use RulerZ\Model;
11+
use RulerZ\Target\Operators\CompileTimeOperator;
12+
use RulerZ\Target\Operators\RuntimeOperator;
1113
use RulerZ\Target\Operators\Definitions as OperatorsDefinitions;
1214

1315
/**
@@ -114,12 +116,12 @@ public function visitOperator(AST\Operator $element, &$handle = null, $eldnah =
114116
if ($this->operators->hasInlineOperator($operatorName)) {
115117
$callable = $this->operators->getInlineOperator($operatorName);
116118

117-
return call_user_func_array($callable, $arguments);
119+
return new CompileTimeOperator(
120+
call_user_func_array($callable, $arguments)
121+
);
118122
}
119123

120-
$inlinedArguments = empty($arguments) ? '' : ', '.implode(', ', $arguments);
121-
122124
// or defer it.
123-
return sprintf('call_user_func($operators["%s"]%s)', $operatorName, $inlinedArguments);
125+
return new RuntimeOperator(sprintf('$operators["%s"]', $operatorName), $arguments);
124126
}
125127
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
namespace RulerZ\Target\Operators;
4+
5+
class CompileTimeOperator
6+
{
7+
/**
8+
* @var string
9+
*/
10+
private $compiledOperator;
11+
12+
public function __construct($compiled)
13+
{
14+
$this->compiledOperator = $compiled;
15+
}
16+
17+
public function format($shouldBreakString)
18+
{
19+
return $this->compiledOperator;
20+
}
21+
}

src/Target/Operators/GenericSqlDefinitions.php

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,37 +11,45 @@ public static function create(Definitions $customOperators)
1111
{
1212
$defaultInlineOperators = [
1313
'and' => function ($a, $b) {
14-
return sprintf('(%s AND %s)', $a, $b);
14+
return sprintf('(%s)', OperatorTools::inlineMixedInstructions([$a, $b], 'AND'));
1515
},
1616
'or' => function ($a, $b) {
17-
return sprintf('(%s OR %s)', $a, $b);
17+
return sprintf('(%s)', OperatorTools::inlineMixedInstructions([$a, $b], 'OR'));
1818
},
1919
'not' => function ($a) {
20-
return sprintf('NOT (%s)', $a);
20+
return sprintf('NOT (%s)', OperatorTools::inlineMixedInstructions([$a]));
2121
},
2222
'=' => function ($a, $b) {
23-
return sprintf('%s = %s', $a, $b);
23+
return OperatorTools::inlineMixedInstructions([$a, $b], '=');
2424
},
2525
'!=' => function ($a, $b) {
26-
return sprintf('%s != %s', $a, $b);
26+
return OperatorTools::inlineMixedInstructions([$a, $b], '!=');
2727
},
2828
'>' => function ($a, $b) {
29-
return sprintf('%s > %s', $a, $b);
29+
return OperatorTools::inlineMixedInstructions([$a, $b], '>');
3030
},
3131
'>=' => function ($a, $b) {
32-
return sprintf('%s >= %s', $a, $b);
32+
return OperatorTools::inlineMixedInstructions([$a, $b], '>=');
3333
},
3434
'<' => function ($a, $b) {
35-
return sprintf('%s < %s', $a, $b);
35+
return OperatorTools::inlineMixedInstructions([$a, $b], '<');
3636
},
3737
'<=' => function ($a, $b) {
38-
return sprintf('%s <= %s', $a, $b);
38+
return OperatorTools::inlineMixedInstructions([$a, $b], '<=');
3939
},
4040
'in' => function ($a, $b) {
41-
return sprintf('%s IN %s', $a, $b[0] === '(' ? $b : '('.$b.')');
41+
if ($b[0] === '(') {
42+
return OperatorTools::inlineMixedInstructions([$a, $b], 'IN');
43+
} else {
44+
return sprintf(
45+
'%s IN (%s)',
46+
OperatorTools::inlineMixedInstructions([$a]),
47+
OperatorTools::inlineMixedInstructions([$b])
48+
);
49+
}
4250
},
4351
'like' => function ($a, $b) {
44-
return sprintf('%s LIKE %s', $a, $b);
52+
return OperatorTools::inlineMixedInstructions([$a, $b], 'LIKE');
4553
},
4654
];
4755

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
namespace RulerZ\Target\Operators;
4+
5+
class OperatorTools
6+
{
7+
public static function inlineMixedInstructions(array $instructions, $operator = null)
8+
{
9+
$elements = [];
10+
11+
foreach ($instructions as $instruction) {
12+
if ($instruction instanceof RuntimeOperator) {
13+
$elements[] = $instruction->format(true);
14+
} else if ($instruction instanceof CompileTimeOperator) {
15+
$elements[] = sprintf('%s', $instruction->format(false));
16+
} else {
17+
$elements[] = sprintf('%s', $instruction);
18+
}
19+
}
20+
21+
if (null === $operator) {
22+
return join('', $elements);
23+
} else {
24+
return join(' '.$operator.' ', $elements);
25+
}
26+
}
27+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?php
2+
3+
namespace RulerZ\Target\Operators;
4+
5+
class RuntimeOperator
6+
{
7+
/**
8+
* @var string
9+
*/
10+
private $callable;
11+
12+
/**
13+
* @var array
14+
*/
15+
private $arguments;
16+
17+
public function __construct($callable, $arguments)
18+
{
19+
$this->callable = $callable;
20+
$this->arguments = $arguments;
21+
}
22+
23+
public function format($shouldBreakString)
24+
{
25+
$formattedArguments = join(',', array_map(function ($argument) {
26+
if ('$' === $argument[0]) {
27+
return $argument;
28+
} else {
29+
return sprintf('"%s"', $argument);
30+
}
31+
}, $this->arguments));
32+
33+
if (true === $shouldBreakString) {
34+
return sprintf(
35+
'".call_user_func_array(%s, [%s])."',
36+
$this->callable,
37+
$formattedArguments
38+
);
39+
} else {
40+
return sprintf(
41+
'call_user_func_array(%s, [%s])',
42+
$this->callable,
43+
$formattedArguments
44+
);
45+
}
46+
}
47+
}

0 commit comments

Comments
 (0)