store   A
last analyzed

Complexity

Total Complexity 18

Size/Duplication

Total Lines 185
Duplicated Lines 0 %

Importance

Changes 4
Bugs 0 Features 2
Metric Value
wmc 18
eloc 80
dl 0
loc 185
rs 10
c 4
b 0
f 2

10 Methods

Rating   Name   Duplication   Size   Complexity  
A getStore() 0 4 1
A get() 0 3 1
A exists() 0 3 1
A connect() 0 29 5
A parents() 0 3 1
A find() 0 3 1
A ls() 0 3 1
A cd() 0 3 1
A disconnect() 0 3 1
B tokenizer() 0 68 5
1
<?php
2
3
namespace arc;
4
5
final class store {
6
7
    /**
8
     * @return mixed current store
9
     */
10
    public static function getStore()
11
    {
12
        $context = \arc\context::$context;
13
        return $context->arcStore;
14
    }
15
16
    /**
17
     * Connects to an ARC object store
18
     * @param string $dsn postgresql connection string
19
     * @param string $resultHandler handler that executes the sql query
20
     * @return store\Store the store
21
     */
22
    public static function connect($dsn, $resultHandler=null)
23
    {
24
        $db = new \PDO($dsn);
25
        $db->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
26
        if (strpos($dsn, 'mysql:')===0) {
27
            $storeType = '\arc\store\MySQL';
28
        } else if (strpos($dsn, 'pgsql:')===0) {
29
            $storeType = '\arc\store\PSQL';
30
        }
31
        if (!$storeType) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $storeType does not seem to be defined for all execution paths leading up to this point.
Loading history...
32
            throw new \arc\ConfigError('Unknown database type');
33
        }
34
35
        if (!$resultHandler) {
36
            $resultHandler = array('\arc\store\ResultHandlers', 'getDBHandler');
37
        }
38
39
        $queryParserClassName = $storeType.'QueryParser';
40
        $className = $storeType.'Store';
41
        $store = new $className(
42
            $db, 
43
            new $queryParserClassName(array('\arc\store','tokenizer')), 
44
            $resultHandler($db)
45
        );
46
47
        \arc\context::push([
48
            'arcStore' => $store
49
        ]);
50
        return $store;
51
    }
52
53
    /**
54
     * disconnects the store
55
     */
56
    public static function disconnect()
57
    {
58
        \arc\context::pop();
59
    }
60
61
    /**
62
     *
63
     * @param $path
64
     * @return mixed
65
     */
66
    public static function cd($path)
67
    {
68
        return self::getStore()->cd($path);
69
    }
70
71
    /**
72
     * @param $query
73
     * @return mixed
74
     */
75
    public static function find($query)
76
    {
77
        return self::getStore()->find($query);
78
    }
79
80
    /**
81
     * @param $path
82
     * @return mixed
83
     */
84
    public static function parents($path)
85
    {
86
        return self::getStore()->parents($path);
87
    }
88
89
    /**
90
     * @param $path
91
     * @return mixed
92
     */
93
    public static function ls($path)
94
    {
95
        return self::getStore()->ls($path);
96
    }
97
98
    /**
99
     * @param $path
100
     * @return mixed
101
     */
102
    public static function get($path)
103
    {
104
        return self::getStore()->get($path);
105
    }
106
107
    /**
108
     * @param $path
109
     * @return mixed
110
     */
111
    public static function exists($path)
112
    {
113
        return self::getStore()->exists($path);
114
    }
115
116
    /**
117
     * yields the tokens in the search query expression
118
     * @param string $query
119
     * @return \Generator
120
     * @throws \LogicException
121
     */        
122
    public static function tokenizer($query) {
123
    /*
124
        query syntax:
125
        part     = ( name '.' )* name compare value
126
        query    = part | part operator (not) part | (not) '(' query ')'  
127
        operator = 'and' | 'or'
128
        not      = 'not'
129
        compare  = '<' | '>' | '=' | '>=' | '<=' | 'like' | 'not like' | '?'
130
        value    = number | string
131
        number   = [0-9]* ('.' [0-9]+)?
132
        string   = \' [^\\1]* \'
133
134
        e.g: "contact.address.street like '%Crescent%' and ( name.firstname = 'Foo' or name.lastname = 'Bar')"
135
    */
136
        $token = <<<'REGEX'
137
/^\s*
138
(
139
            (?<compare>
140
                <= | >= | <> | < | > | = | != | ~= | !~ | \?
141
            )
142
            |
143
            (?<operator>
144
                and | or
145
            )
146
            |
147
            (?<not>
148
                not
149
            )
150
            |
151
            (?<name>
152
                [a-z]+[a-z0-9_-]*
153
                (?: \. [a-z]+[a-z0-9_-]* )*
154
            )
155
            |
156
            (?<number>
157
                [+-]?[0-9](\.[0-9]+)?
158
            )
159
            |
160
            (?<string>
161
                 '(?:\\.|[^\\'])*' 
162
            )
163
            |
164
            (?<parenthesis_open>
165
                \(
166
            )
167
            |
168
            (?<parenthesis_close>
169
                \)
170
            )
171
)/xi
172
REGEX;
173
        do {
174
            $result = preg_match($token, $query, $matches, PREG_OFFSET_CAPTURE);
175
            if ($result) {
176
                $value  = $matches[0][0];
177
                $offset = $matches[0][1];
178
                $query = substr($query, strlen($value) + $offset);
179
                yield array_filter(
180
                    $matches,
181
                    function($val, $key) {
182
                        return !is_int($key) && $val[0];
183
                    },
184
                    ARRAY_FILTER_USE_BOTH
185
                );
186
            }
187
        } while($result);
188
        if ( trim($query) ) {
189
            throw new \LogicException('Could not parse '.$query);
190
        }
191
    }
192
193
}
194