Skip to content

Commit

Permalink
Fixes a bug where you cannot use a runtime operator in a SQL query
Browse files Browse the repository at this point in the history
  • Loading branch information
iainmckay committed Mar 27, 2018
1 parent 42fc542 commit 9931438
Show file tree
Hide file tree
Showing 8 changed files with 144 additions and 28 deletions.
5 changes: 4 additions & 1 deletion src/Compiler/Compiler.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use RulerZ\Executor\Executor;
use RulerZ\Parser\Parser;
use RulerZ\Target\Operators\CompileTimeOperator;

class Compiler
{
Expand Down Expand Up @@ -63,6 +64,8 @@ protected function compileToSource($rule, CompilationTarget $compilationTarget,
}

$commentedRule = str_replace(PHP_EOL, PHP_EOL.' // ', $rule);
$compiledRule = $executorModel->getCompiledRule();
$escapedRule = is_string($compiledRule) ? $compiledRule : $compiledRule->format(false);

return <<<EXECUTOR
namespace RulerZ\Compiled\Executor;
Expand All @@ -78,7 +81,7 @@ class {$context['executor_classname']} implements Executor
// $commentedRule
protected function execute(\$target, array \$operators, array \$parameters)
{
return {$executorModel->getCompiledRule()};
return {$escapedRule};
}
}
EXECUTOR;
Expand Down
9 changes: 8 additions & 1 deletion src/Target/GenericSqlVisitor.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use RulerZ\Compiler\Context;
use RulerZ\Exception\OperatorNotFoundException;
use RulerZ\Model;
use RulerZ\Target\Operators\CompileTimeOperator;
use RulerZ\Target\Operators\Definitions as OperatorsDefinitions;

/**
Expand Down Expand Up @@ -46,7 +47,13 @@ public function visitModel(AST\Model $element, &$handle = null, $eldnah = null)
{
$sql = parent::visitModel($element, $handle, $eldnah);

return '"'.$sql.'"';
if (is_string($sql)) {
return '"' . $sql . '"';
} elseif ($sql instanceof CompileTimeOperator) {
return '"' . $sql->format(true) . '"';
} else {
return $sql->format(false);
}
}

/**
Expand Down
10 changes: 6 additions & 4 deletions src/Target/GenericVisitor.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
use RulerZ\Compiler\RuleVisitor;
use RulerZ\Exception\OperatorNotFoundException;
use RulerZ\Model;
use RulerZ\Target\Operators\CompileTimeOperator;
use RulerZ\Target\Operators\RuntimeOperator;
use RulerZ\Target\Operators\Definitions as OperatorsDefinitions;

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

return call_user_func_array($callable, $arguments);
return new CompileTimeOperator(
call_user_func_array($callable, $arguments)
);
}

$inlinedArguments = empty($arguments) ? '' : ', '.implode(', ', $arguments);

// or defer it.
return sprintf('call_user_func($operators["%s"]%s)', $operatorName, $inlinedArguments);
return new RuntimeOperator(sprintf('$operators["%s"]', $operatorName), $arguments);
}
}
23 changes: 12 additions & 11 deletions src/Target/Native/NativeOperators.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace RulerZ\Target\Native;

use RulerZ\Target\Operators\Definitions;
use RulerZ\Target\Operators\OperatorTools;

class NativeOperators
{
Expand All @@ -13,37 +14,37 @@ public static function create(Definitions $customOperators)
{
$defaultInlineOperators = [
'and' => function ($a, $b) {
return sprintf('(%s && %s)', $a, $b);
return sprintf('(%s)', OperatorTools::inlineMixedInstructions([$a, $b], '&&'));
},
'or' => function ($a, $b) {
return sprintf('(%s || %s)', $a, $b);
return sprintf('(%s)', OperatorTools::inlineMixedInstructions([$a, $b], '||'));
},
'not' => function ($a) {
return sprintf('!(%s)', $a);
return sprintf('!(%s)', OperatorTools::inlineMixedInstructions([$a]));
},
'=' => function ($a, $b) {
return sprintf('%s == %s', $a, $b);
return OperatorTools::inlineMixedInstructions([$a, $b], '==');
},
'is' => function ($a, $b) {
return sprintf('%s === %s', $a, $b);
return OperatorTools::inlineMixedInstructions([$a, $b], '===');
},
'!=' => function ($a, $b) {
return sprintf('%s != %s', $a, $b);
return OperatorTools::inlineMixedInstructions([$a, $b], '!=');
},
'>' => function ($a, $b) {
return sprintf('%s > %s', $a, $b);
return OperatorTools::inlineMixedInstructions([$a, $b], '>');
},
'>=' => function ($a, $b) {
return sprintf('%s >= %s', $a, $b);
return OperatorTools::inlineMixedInstructions([$a, $b], '>=');
},
'<' => function ($a, $b) {
return sprintf('%s < %s', $a, $b);
return OperatorTools::inlineMixedInstructions([$a, $b], '<');
},
'<=' => function ($a, $b) {
return sprintf('%s <= %s', $a, $b);
return OperatorTools::inlineMixedInstructions([$a, $b], '<=');
},
'in' => function ($a, $b) {
return sprintf('in_array(%s, %s)', $a, $b);
return sprintf('in_array(%s)', OperatorTools::inlineMixedInstructions([$a, $b], ','));
},
];

Expand Down
21 changes: 21 additions & 0 deletions src/Target/Operators/CompileTimeOperator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

namespace RulerZ\Target\Operators;

class CompileTimeOperator
{
/**
* @var string
*/
private $compiledOperator;

public function __construct($compiled)
{
$this->compiledOperator = $compiled;
}

public function format($shouldBreakString)
{
return $this->compiledOperator;
}
}
30 changes: 19 additions & 11 deletions src/Target/Operators/GenericSqlDefinitions.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,37 +11,45 @@ public static function create(Definitions $customOperators)
{
$defaultInlineOperators = [
'and' => function ($a, $b) {
return sprintf('(%s AND %s)', $a, $b);
return sprintf('(%s)', OperatorTools::inlineMixedInstructions([$a, $b], 'AND'));
},
'or' => function ($a, $b) {
return sprintf('(%s OR %s)', $a, $b);
return sprintf('(%s)', OperatorTools::inlineMixedInstructions([$a, $b], 'OR'));
},
'not' => function ($a) {
return sprintf('NOT (%s)', $a);
return sprintf('NOT (%s)', OperatorTools::inlineMixedInstructions([$a]));
},
'=' => function ($a, $b) {
return sprintf('%s = %s', $a, $b);
return OperatorTools::inlineMixedInstructions([$a, $b], '=');
},
'!=' => function ($a, $b) {
return sprintf('%s != %s', $a, $b);
return OperatorTools::inlineMixedInstructions([$a, $b], '!=');
},
'>' => function ($a, $b) {
return sprintf('%s > %s', $a, $b);
return OperatorTools::inlineMixedInstructions([$a, $b], '>');
},
'>=' => function ($a, $b) {
return sprintf('%s >= %s', $a, $b);
return OperatorTools::inlineMixedInstructions([$a, $b], '>=');
},
'<' => function ($a, $b) {
return sprintf('%s < %s', $a, $b);
return OperatorTools::inlineMixedInstructions([$a, $b], '<');
},
'<=' => function ($a, $b) {
return sprintf('%s <= %s', $a, $b);
return OperatorTools::inlineMixedInstructions([$a, $b], '<=');
},
'in' => function ($a, $b) {
return sprintf('%s IN %s', $a, $b[0] === '(' ? $b : '('.$b.')');
if ($b[0] === '(') {
return OperatorTools::inlineMixedInstructions([$a, $b], 'IN');
} else {
return sprintf(
'%s IN (%s)',
OperatorTools::inlineMixedInstructions([$a]),
OperatorTools::inlineMixedInstructions([$b])
);
}
},
'like' => function ($a, $b) {
return sprintf('%s LIKE %s', $a, $b);
return OperatorTools::inlineMixedInstructions([$a, $b], 'LIKE');
},
];

Expand Down
27 changes: 27 additions & 0 deletions src/Target/Operators/OperatorTools.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

namespace RulerZ\Target\Operators;

class OperatorTools
{
public static function inlineMixedInstructions(array $instructions, $operator = null)
{
$elements = [];

foreach ($instructions as $instruction) {
if ($instruction instanceof RuntimeOperator) {
$elements[] = $instruction->format(false);
} else if ($instruction instanceof CompileTimeOperator) {
$elements[] = sprintf('%s', $instruction->format(false));
} else {
$elements[] = sprintf('%s', $instruction);
}
}

if (null === $operator) {
return join('', $elements);
} else {
return join(' '.$operator.' ', $elements);
}
}
}
47 changes: 47 additions & 0 deletions src/Target/Operators/RuntimeOperator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php

namespace RulerZ\Target\Operators;

class RuntimeOperator
{
/**
* @var string
*/
private $callable;

/**
* @var array
*/
private $arguments;

public function __construct($callable, $arguments)
{
$this->callable = $callable;
$this->arguments = $arguments;
}

public function format($shouldBreakString)
{
$formattedArguments = join(',', array_map(function ($argument) {
if ('$' === $argument[0]) {
return $argument;
} else {
return sprintf('"%s"', $argument);
}
}, $this->arguments));

if (true === $shouldBreakString) {
return sprintf(
'".call_user_func_array(%s, [%s])."',
$this->callable,
$formattedArguments
);
} else {
return sprintf(
'call_user_func_array(%s, [%s])',
$this->callable,
$formattedArguments
);
}
}
}

0 comments on commit 9931438

Please sign in to comment.