Completed
Push — master ( f22a46...046041 )
by Patrick
02:27 queued 12s
created

FilterClause::getMongoClauseArray()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 2
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
namespace Flipside\Data;
3
4
class FilterClause
5
{
6
    public $var1;
7
    public $var2;
8
    public $op;
9
10
    /**
11
     * Create a filter clause from the string
12
     *
13
     * @param string $string The filter clause string
14
     */
15
    public function __construct($string = false)
16
    {
17
        if(is_string($string))
18
        {
19
            $this->process_filter_string($string);
20
        }
21
    }
22
23
    /**
24
     * Find the string inside the other string
25
     *
26
     * @param string $haystack The string to search inside
27
     * @param string $needle The string to search for
28
     *
29
     * @return boolean True if the needle exists in the haystack, false otherwise
30
     */
31
    protected static function str_startswith($haystack, $needle)
32
    {
33
        return substr($haystack, 0, strlen($needle)) === $needle;
34
    }
35
36
    /**
37
     * Is the specified filter a function?
38
     *
39
     * @param string $string The filter clause
40
     *
41
     * @return boolean True if the filter is an operation, false otherwise
42
     */
43
    protected function filterIsFunction($string)
44
    {
45
        return (self::str_startswith($string, 'substringof') || self::str_startswith($string, 'contains') ||
46
                self::str_startswith($string, 'indexof'));
47
    }
48
49
    /**
50
     * Convert the OData simple op to standard operations
51
     *
52
     * @param string $op The OData op
53
     *
54
     * @return string The standard programatic notation
55
     */
56
    protected function odataOpToStd($op)
57
    {
58
        switch($op)
59
        {
60
            case 'ne':
61
                return '!=';
62
            case 'eq':
63
                return '=';
64
            case 'lt':
65
                return '<';
66
            case 'le':
67
                return '<=';
68
            case 'gt':
69
                return '>';
70
            case 'ge':
71
                return '>=';
72
            default:
73
                return $op;
74
        }
75
    }
76
77
    /**
78
     * Convert the string into an OData Filter
79
     *
80
     * @param string $string The string to turn into a filter
81
     */
82
    protected function process_filter_string($string)
83
    {
84
        if($this->filterIsFunction($string))
85
        {
86
            $this->op   = strtok($string, '(');
87
            $this->var1 = strtok(',');
88
            $this->var2 = trim(strtok(')'));
89
            return;
90
        }
91
        $field = strtok($string, ' ');
92
        $op = strtok(' ');
93
        $rest = strtok("\0");
94
        $this->var1  = $field;
95
        $this->op    = $this->odataOpToStd($op);
96
        $this->var2  = $rest;
97
    }
98
99
    public function to_sql_string()
100
    {
101
        switch($this->op)
102
        {
103
            case 'substringof':
104 View Code Duplication
            case 'contains':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
105
                return $this->var1.' LIKE \'%'.trim($this->var2, "'").'%\'';
106
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
107
            default:
108
                return $this->var1.$this->op.$this->var2;
109
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
110
        }
111
    }
112
113
    public function to_ldap_string()
114
    {
115
        $str = '(';
116
        switch($this->op)
117
        {
118
            case 'substringof':
119 View Code Duplication
            case 'contains':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
120
                $str .= $this->var1.'=*'.trim($this->var2, "'").'*';
121
                break;
122
            case '!=':
123
                $str .= '!('.$this->var1.'='.$this->var2.')';
124
                break;
125
            default:
126
                $str .= $this->var1.$this->op.$this->var2;
127
                break;
128
        }
129
        return $str.')';
130
    }
131
132
    private function getMongoIndexOfOperator()
133
    {
134
        $field = $this->var1;
135
        $case  = false;
136
        if(self::str_startswith($this->var1, 'tolower'))
137
        {
138
            $field = substr($this->var1, strpos($this->var1, '(') + 1);
139
            $field = substr($field, 0, strpos($field, ')'));
140
            $case = true;
141
        }
142
        if($case)
143
	{
144
            if(class_exists('MongoRegex'))
145
	    {
146
                return array($field=>array('$regex'=>new \MongoRegex('/'.$this->var2.'/i')));
147
            }
148
            else
149
            {
150
                return array($field=>array('$regex'=>new \MongoDB\BSON\Regex($this->var2, 'i')));
151
            }
152
        }
153
        return array($field=>$this->var2);       
154
    }
155
156
    /**
157
     * Convert the standard operations to Mongo operations
158
     *
159
     * @param string $op The standard op
160
     *
161
     * @return string The mongo operator
162
     */
163
    private function opToMongo($op)
164
    {
165
        switch($op)
166
        {
167
            case '!=':
168
                return '$ne';
169
            case '<';
170
                return '$lt';
171
            case '<=':
172
                return '$lte';
173
            case '>':
174
                return '$gt';
175
            case '>=':
176
                return '$gte';
177
            default:
178
                return $op;
179
        }
180
    }
181
182
    /**
183
     * Convert the right hand side of the filter clause into an Mongo array
184
     *
185
     * @param string $op The standard operator
186
     * @param string $var The second variable in the operator
0 ignored issues
show
Bug introduced by
There is no parameter named $var. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
187
     *
188
     * @return array An array of mongo operations
189
     */
190
    private function getMongoClauseArray($op, $var2)
191
    {
192
        return array($this->opToMongo($op)=>$var2);
193
    }
194
195
    public function toMongoFilter()
196
    {
197
        if($this->var2 === 'true')
198
        {
199
            $this->var2 = true;
200
        }
201
        else if($this->var2 === 'false')
202
        {
203
            $this->var2 = false;
204
        }
205
        else if(is_numeric($this->var2))
206
        {
207
            $this->var2 = intval($this->var2);
208
        }
209
        else
210
        {
211
            $this->var2 = trim($this->var2, "'");
212
        }
213
        if($this->var1 === '_id')
214
        {
215
            try
216
            {
217
                if(class_exists('MongoId'))
218
                {
219
                    $this->var2 = new \MongoId($this->var2);
220
                }
221
                else
222
                {
223
                    $this->var2 = new \MongoDB\BSON\ObjectId($this->var2);
224
                }
225
            }
226
            catch(\MongoException $e)
227
            {
228
                //Not a valid mongo ID. Just leave the variable alone and try the query...
229
            }
230
            catch(\MongoDB\Driver\Exception\InvalidArgumentException $e)
0 ignored issues
show
Bug introduced by
The class MongoDB\Driver\Exception\InvalidArgumentException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
231
            {
232
                //Not a valid mongo ID. Just leave the variable alone and try the query...
233
            }
234
        }
235
        switch($this->op)
236
        {
237
            case '=':
238
                return array($this->var1=>$this->var2);
239
	    case 'substringof':
240
		if(class_exists('MongoRegex'))
241
		{
242
		    return array($this->var1=>array('$regex'=>new \MongoRegex('/'.$this->var2.'/i')));
243
		}
244
		else
245
		{
246
                    return array($this->var1=>array('$regex'=>new \MongoDB\BSON\Regex($this->var2, 'i')));
247
		}
248
            case 'indexof':
249
                return $this->getMongoIndexOfOperator();
250
            default:
251
                return array($this->var1=>$this->getMongoClauseArray($this->op, $this->var2));
252
        }
253
    }
254
255
    public function php_compare($value)
256
    {
257
	if(is_array($value))
258
	{
259
            return $this->php_compare($value[$this->var1]);
260
	}
261
        switch($this->op)
262
        {
263
            case '!=':
264
                return $value != $this->var2;
265
	    case '=':
266
                return $value == $this->var2;
267
            case '<':
268
                return $value < $this->var2;
269
            case '<=':
270
                return $value <= $this->var2;
271
            case '>':
272
                return $value > $this->var2;
273
            case '>=':
274
                return $value >= $this->var2;
275
        }
276
    }
277
}
278