Test Failed
Push — master ( 176748...45bb0c )
by Jean
02:34
created

InlineSqlMinimalConverter::convert()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 7
nc 2
nop 1
dl 0
loc 9
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * InlineSqlMinimalConverter
4
 *
5
 * @package php-logical-filter
6
 * @author  Jean Claveau
7
 */
8
namespace JClaveau\LogicalFilter\Converter;
9
use       JClaveau\LogicalFilter\LogicalFilter;
10
use       JClaveau\LogicalFilter\Rule\EqualRule;
11
use       JClaveau\LogicalFilter\Rule\NotEqualRule;
12
use       JClaveau\LogicalFilter\Rule\AboveRule;
13
use       JClaveau\LogicalFilter\Rule\BelowRule;
14
use       JClaveau\LogicalFilter\Rule\RegexpRule;
15
use       JClaveau\LogicalFilter\Rule\InRule;
16
use       JClaveau\LogicalFilter\Rule\NotInRule;
17
use       JClaveau\LogicalFilter\Rule\AboveOrEqualRule;
18
use       JClaveau\LogicalFilter\Rule\BelowOrEqualRule;
19
20
/**
21
 * This class implements a converter for MySQL.
22
 */
23
class InlineSqlMinimalConverter extends MinimalConverter
24
{
25
    /** @var array $output */
26
    protected $output = [];
27
28
    /** @var array $parameters */
29
    protected $parameters = [];
30
31
    /**
32
     * @return string parameter id
33
     */
34
    public function addParameter($value)
35
    {
36
        if (is_numeric($value))
37
            return $value;
38
39
        $uid = 'param_'.hash('crc32b', serialize($value));
40
41
        if (isset($this->parameters[$uid]))
42
            return ':'.$uid;
43
44
        $this->parameters[$uid] = $value;
45
46
        return ':'.$uid;
47
    }
48
49
    /**
50
     * @param LogicalFilter $filter
51
     */
52
    public function convert( LogicalFilter $filter )
53
    {
54
        $this->output = [];
55
        parent::convert($filter);
56
        return [
57
            'sql' => ! $this->output
58
                   ? '1' // True
59
                   : '('.implode(') OR (', $this->output).')',
60
            'parameters' => $this->parameters,
61
        ];
62
    }
63
64
    /**
65
     */
66
    public function onOpenOr()
67
    {
68
        $this->output[] = [];
69
    }
70
71
    /**
72
     */
73
    public function onCloseOr()
74
    {
75
        $last_key = $this->getLastOrOperandKey();
76
        $this->output[ $last_key ] = implode(' AND ', $this->output[ $last_key ]);
77
    }
78
79
    /**
80
     * Pseudo-event called while for each And operand of the root Or.
81
     * These operands must be only atomic Rules.
82
     */
83
    public function onAndPossibility($field, $operator, $rule, array $allOperandsByField)
84
    {
85
        if ($rule instanceof EqualRule) {
86
            $value = $rule->getValue();
87
        }
88
        elseif ($rule instanceof InRule) {
89
            $value = $rule->getPossibilities();
90
        }
91
        elseif ($rule instanceof NotInRule) {
92
            $operator = 'NOT IN';
93
            $value = $rule->getPossibilities();
94
        }
95
        elseif ($rule instanceof AboveRule) {
96
            $value = $rule->getLowerLimit();
97
        }
98
        elseif ($rule instanceof BelowRule) {
99
            $value = $rule->getUpperLimit();
100
        }
101
        elseif ($rule instanceof AboveOrEqualRule) {
102
            $value = $rule->getMinimum();
103
        }
104
        elseif ($rule instanceof BelowOrEqualRule) {
105
            $value = $rule->getMaximum();
106
        }
107
        elseif ($rule instanceof NotEqualRule) {
108
            $value = $rule->getValue();
109
        }
110
        elseif ($rule instanceof RegexpRule) {
111
            $value = RegexpRule::php2mariadbPCRE( $rule->getPattern() );
112
        }
113
        else {
114
            throw new \InvalidArgumentException(
115
                "Unhandled operator '$operator' during SQL query generation"
116
            );
117
        }
118
119
        if (gettype($value) == 'integer') {
120
        }
121
        elseif (gettype($value) == 'double') {
122
            // TODO disable locale to handle separators
123
        }
124
        elseif ($value instanceof \DateTime) {
125
            $value = "'" . $value->format('Y-m-d H:i:s') . "'";
126
        }
127
        elseif (gettype($value) == 'string') {
128
            $value = $this->addParameter($value);
129
        }
130
        elseif (gettype($value) == 'array') {
131
            $sql_part = [];
132
            foreach ($value as $possibility) {
133
                $sql_part[] = $this->addParameter($possibility);
134
            }
135
            $value = '(' . implode(', ', $sql_part) . ')';
136
        }
137
        elseif ($value === null) {
138
            $value = "NULL";
139
            if ($rule instanceof EqualRule) {
140
                $operator = 'IS';
141
            }
142
            elseif ($rule instanceof NotEqualRule) {
143
                $operator = 'IS NOT';
144
            }
145
            else {
146
                throw new \InvalidArgumentException(
147
                    "NULL is only handled for equality / difference"
148
                );
149
            }
150
        }
151
        else {
152
            throw new \InvalidArgumentException(
153
                "Unhandled type of value: ".gettype($value). ' | ' .var_export($value, true)
154
            );
155
        }
156
157
        $operator = strtoupper($operator);
158
159
        $new_rule = "$field $operator $value";
160
161
        $this->appendToLastOrOperandKey($new_rule);
162
    }
163
164
    /**
165
     */
166
    protected function getLastOrOperandKey()
167
    {
168
        end($this->output);
169
        return key($this->output);
170
    }
171
172
    /**
173
     * @param string $rule
174
     */
175
    protected function appendToLastOrOperandKey($rule)
176
    {
177
        $last_key = $this->getLastOrOperandKey();
178
        $this->output[ $last_key ][] = $rule;
179
    }
180
181
    /**/
182
}
183