TreeQueryParser::parse()   F
last analyzed

Complexity

Conditions 32
Paths 91

Size

Total Lines 122
Code Lines 110

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
eloc 110
dl 0
loc 122
rs 3.3333
c 1
b 0
f 1
cc 32
nc 91
nop 1

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace arc\store;
4
5
final class TreeQueryParser {
6
	
7
	private $tokenizer;
8
9
	public function __construct($tokenizer) {
10
		$this->tokenizer = $tokenizer;
11
	}
12
13
    /**
14
     * @param string $query
15
     * @return string postgresql 'where' part of the sql query
16
     * @throws \LogicException when a parse error occurs
17
     */
18
    public function parse($query)
19
    {
20
        $indent   = 0;
21
        $part     = '';
22
        $fn       = [];
23
        $currentCheck = [];
0 ignored issues
show
Unused Code introduced by
The assignment to $currentCheck is dead and can be removed.
Loading history...
24
        $position = 0;
25
        $expect   = 'name|parenthesis_open|not';
26
27
        foreach( call_user_func($this->tokenizer, $query) as $token ) {
28
            $type = key($token);
29
            list($token, $offset)=$token[$type];
30
            if ( !preg_match("/^$expect$/",$type) ) {
31
                throw new \LogicException('Parse error at '.$position.': expected '.$expect.', got '.$type.': '
32
                    .(substr($query,0, $position)." --> ".substr($query,$position)) );
33
            }
34
            switch($type) {
35
                case 'number':
36
                case 'string':
37
                    if (strpos($part, '{placeholder}')!==false) {
38
                        $fn[] = str_replace('{placeholder}', $token, $part);
39
                    } else {
40
                        $fn[] = $part.$token;
41
                    }
42
                    $part   = '';
43
                    $expect = 'operator|parenthesis_close';
44
                break;
45
                case 'name':
46
                    switch ($token) {
47
                        case 'nodes.path':
48
                            $part = '$node->getPath()';
49
                        break;
50
                        case 'nodes.parent':
51
                            $part = '$node->parentNode->getPath()';
52
                        break;
53
                        case 'nodes.name':
54
                            $part = '$node->nodeName';
55
                        break;
56
                        case 'nodes.mtime':
57
                            $part = '$node->nodeValue->mtime';
58
                        break;
59
                        case 'nodes.ctime':
60
                            $part = '$node->nodeValue->ctime';
61
                        break;
62
                        default:
63
                            $part = '$node->nodeValue->'.str_replace('.','->',$token);
64
                        break;
65
                    }
66
                    $expect = 'compare';
67
                break;
68
                case 'compare':
69
                    switch( $token ) {
70
                        case '>':
71
                        case '>=':
72
                        case '<':
73
                        case '<=':
74
                        case '!=':
75
                            $part = '( '.$part. ' ?? null ) '.$token;
76
                        break;
77
                        case '<>':
78
                            $part = '( '.$part. ' ?? null ) !=';
79
                        break;
80
                        case '=':
81
                            $part = '( '.$part. ' ?? null ) ==';
82
                        break;
83
                        case '?':
84
                            $part ='property_exists('.$part.' ?? null,{placeholder})';
85
                        break;
86
                        case '~=':
87
                            $part = '$like('.$part.' ?? null,{placeholder})';
88
                        break;
89
                        case '!~':
90
                            $part = '!$like('.$part.' ?? null,{placeholder})';
91
                        break;
92
                    }
93
                    $expect = 'number|string';
94
                break;
95
                case 'not':
96
                    $fn[]   = '!';
97
                    $expect = 'name|parenthesis_open';
98
                break;
99
                case 'operator':
100
                    switch($token) {
101
                        case 'and':
102
                            $fn[] = '&&';
103
                        break;
104
                        case 'or':
105
                            $fn[] = '||';
106
                        break;
107
                    }
108
                    $expect = 'name|parenthesis_open|not';
109
                break;
110
                case 'parenthesis_open':
111
                    $fn[]   = $token;
112
                    $indent++;
113
                    $expect = 'name|parenthesis_open|not';
114
                break;
115
                case 'parenthesis_close':
116
                    $fn[]   = $token;
117
                    $indent--;
118
                    if ( $indent>0 ) {
119
                        $expect = 'operator|parenthesis_close';
120
                    } else {
121
                        $expect = 'operator';
122
                    }
123
                break;
124
            }
125
            $position += $offset + strlen($token);
126
        }
127
        if ( $indent!=0 ) {
0 ignored issues
show
introduced by
The condition $indent != 0 is always false.
Loading history...
128
            throw new \LogicException('unbalanced parenthesis');
129
        } else if ( trim($part) ) {
130
            $position -= strlen($token);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $token seems to be defined by a foreach iteration on line 27. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
131
            throw new \LogicException('parse error at '.$position.': '.(substr($query,0, $position)." --> ".substr($query,$position)));
132
        } else {
133
            $fn = implode(' ',$fn);
134
                $like = function($haystack, $needle) {
0 ignored issues
show
Unused Code introduced by
The assignment to $like is dead and can be removed.
Loading history...
135
                $re = str_replace('%', '.*', $needle);
136
                return preg_match('|'.$re.'|i', $haystack);
137
            };
138
            $script = 'return function($node) use ($like) { return '.$fn.'; };';
139
            return eval($script);
0 ignored issues
show
introduced by
The use of eval() is discouraged.
Loading history...
140
        }
141
    }
142
143
}
144