Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes a bug where you cannot use a runtime operator in a SQL query #82

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
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], '&&', false));
},
'or' => function ($a, $b) {
return sprintf('(%s || %s)', $a, $b);
return sprintf('(%s)', OperatorTools::inlineMixedInstructions([$a, $b], '||', false));
},
'not' => function ($a) {
return sprintf('!(%s)', $a);
return sprintf('!(%s)', OperatorTools::inlineMixedInstructions([$a], null, false));
},
'=' => function ($a, $b) {
return sprintf('%s == %s', $a, $b);
return OperatorTools::inlineMixedInstructions([$a, $b], '==', false);
},
'is' => function ($a, $b) {
return sprintf('%s === %s', $a, $b);
return OperatorTools::inlineMixedInstructions([$a, $b], '===', false);
},
'!=' => function ($a, $b) {
return sprintf('%s != %s', $a, $b);
return OperatorTools::inlineMixedInstructions([$a, $b], '!=', false);
},
'>' => function ($a, $b) {
return sprintf('%s > %s', $a, $b);
return OperatorTools::inlineMixedInstructions([$a, $b], '>', false);
},
'>=' => function ($a, $b) {
return sprintf('%s >= %s', $a, $b);
return OperatorTools::inlineMixedInstructions([$a, $b], '>=', false);
},
'<' => function ($a, $b) {
return sprintf('%s < %s', $a, $b);
return OperatorTools::inlineMixedInstructions([$a, $b], '<', false);
},
'<=' => function ($a, $b) {
return sprintf('%s <= %s', $a, $b);
return OperatorTools::inlineMixedInstructions([$a, $b], '<=', false);
},
'in' => function ($a, $b) {
return sprintf('in_array(%s, %s)', $a, $b);
return sprintf('in_array(%s)', OperatorTools::inlineMixedInstructions([$a, $b], ',', false));
},
];

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, $useStringBreakingLogic = true)
{
$elements = [];

foreach ($instructions as $instruction) {
if ($instruction instanceof RuntimeOperator) {
$elements[] = $instruction->format($useStringBreakingLogic);
} 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
);
}
}
}