Completed
Push — master ( 5844c1...ab7dfd )
by Shcherbak
02:28
created

QuerySequence   B

Complexity

Total Complexity 39

Size/Duplication

Total Lines 307
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 11

Test Coverage

Coverage 93.52%

Importance

Changes 8
Bugs 4 Features 2
Metric Value
wmc 39
c 8
b 4
f 2
lcom 1
cbo 11
dl 0
loc 307
ccs 101
cts 108
cp 0.9352
rs 8.2857

18 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A getCollection() 0 3 1
A setPosition() 0 4 1
A getPosition() 0 3 1
A strict() 0 4 1
A possible() 0 4 1
A move() 0 3 1
A moveToToken() 0 20 4
A section() 0 19 3
A search() 0 8 2
A sequence() 0 8 2
A setValid() 0 7 2
A getToken() 0 10 2
A isValid() 0 3 1
A setSkipWhitespaces() 0 4 1
A checkFromSequence() 0 9 2
C process() 0 28 7
B buildStrategyCondition() 0 21 6
1
<?php
2
3
  namespace Funivan\PhpTokenizer\QuerySequence;
4
5
  use Funivan\PhpTokenizer\Collection;
6
  use Funivan\PhpTokenizer\Exception\InvalidArgumentException;
7
  use Funivan\PhpTokenizer\Strategy\Move;
8
  use Funivan\PhpTokenizer\Strategy\Possible;
9
  use Funivan\PhpTokenizer\Strategy\QueryStrategy;
10
  use Funivan\PhpTokenizer\Strategy\Search;
11
  use Funivan\PhpTokenizer\Strategy\StrategyInterface;
12
  use Funivan\PhpTokenizer\Strategy\Strict;
13
  use Funivan\PhpTokenizer\Token;
14
15
  /**
16
   * Start from specific position and check token from this position according to strategies
17
   */
18
  class QuerySequence implements QuerySequenceInterface {
19
20
    /**
21
     * @var bool
22
     */
23
    private $valid = true;
24
25
    /**
26
     * @var int
27
     */
28
    private $position = 0;
29
30
    /**
31
     * @var Collection
32
     */
33
    private $collection;
34
35
    /**
36
     * @var
37
     */
38
    private $skipWhitespaces = false;
39
40
41
    /**
42
     * @inheritdoc
43
     */
44 258
    public function __construct(Collection $collection, $initialPosition = 0) {
45 258
      $this->collection = $collection;
46 258
      $this->position = $initialPosition;
47 228
    }
48
49
50
    /**
51
     * @return Collection
52
     */
53 47
    public function getCollection() {
54 47
      return $this->collection;
55
    }
56
57
58
    /**
59
     * @param int $position
60
     * @return QuerySequence
61
     */
62 207
    public function setPosition($position) {
63 207
      $this->position = $position;
64 207
      return $this;
65
    }
66
67
68
    /**
69
     * @return int
70
     */
71 215
    public function getPosition() {
72 215
      return $this->position;
73
    }
74
75
76
    /**
77
     * Strict validation of condition
78
     *
79
     * @param int|string|Strict $condition
80
     * @return Token
81
     */
82 158
    public function strict($condition) {
83 158
      $query = $this->buildStrategyCondition($condition, Strict::create());
84 152
      return $this->process($query);
85
    }
86
87
88
    /**
89
     * Check if token possible valid for our condition
90
     *
91
     * @param int|string|Possible $condition
92
     * @return Token
93
     */
94 102
    public function possible($condition) {
95 102
      $query = $this->buildStrategyCondition($condition, Possible::create());
96 94
      return $this->process($query);
97
    }
98
99
100
    /**
101
     * @param string $start
102
     * @param string $end
103
     * @return Collection
104
     */
105 106
    public function section($start, $end) {
106
107 106
      $token = $this->strict($start);
108 106
      if (!$token->isValid()) {
109
        # cant find start position
110 106
        return new Collection();
111
      }
112
113 100
      $this->moveToToken($token);
114
115 100
      $section = new \Funivan\PhpTokenizer\Strategy\Section();
116 100
      $section->setDelimiters($start, $end);
117 100
      $lastToken = $this->process($section);
118 100
      if (!$lastToken->isValid()) {
119
        return new Collection();
120
      }
121
122 100
      return $this->collection->extractByTokens($token, $lastToken);
123
    }
124
125
126
    /**
127
     * By default we search forward
128
     *
129
     * @param int|string|Search $condition
130
     * @param null $direction
131
     * @return Token
132
     */
133 48
    public function search($condition, $direction = null) {
134 48
      $strategy = Search::create();
135 48
      if ($direction !== null) {
136 2
        $strategy->setDirection($direction);
137 2
      }
138 48
      $query = $this->buildStrategyCondition($condition, $strategy);
139 48
      return $this->process($query);
140
    }
141
142
143
    /**
144
     * Relative move
145
     * +10 move forward 10 tokens
146
     * -5 move backward 5 tokens
147
     *
148
     * @param int $steps
149
     * @return Token
150
     */
151 10
    public function move($steps) {
152 10
      return $this->process(Move::create($steps));
153
    }
154
155
156
    /**
157
     * Move to specific position
158
     *
159
     * @param Token $token
160
     * @return Token|null
161
     */
162 121
    public function moveToToken(Token $token) {
163
164 106
      if (!$token->isValid()) {
165 38
        $this->setValid(false);
166 38
        return new Token();
167
      }
168
169 104
      $tokenIndex = $token->getIndex();
170
171
172 119
      foreach ($this->collection as $index => $collectionToken) {
173 104
        if ($collectionToken->getIndex() === $tokenIndex) {
174 104
          $this->setPosition($index);
175 119
          return $collectionToken;
176
        }
177 104
      }
178
179 2
      $this->setValid(false);
180 2
      return new Token();
181 14
    }
182
183
184
    /**
185
     * Array may contain Int, String or any StrategyInterface object
186
     *
187
     * @param array $conditions
188
     * @return Collection
189
     */
190 50
    public function sequence(array $conditions) {
191 50
      $range = new Collection();
192 50
      foreach ($conditions as $value) {
193 50
        $range[] = $this->checkFromSequence($value);
194 50
      }
195
196 50
      return $range;
197
    }
198
199
200
    /**
201
     * @param string|int|StrategyInterface $value
202
     * @return Token
203
     */
204 50
    private function checkFromSequence($value) {
205 50
      if ($value instanceof StrategyInterface) {
206 34
        $query = $value;
207 34
      } else {
208 30
        $query = $this->buildStrategyCondition($value, Strict::create());
209
      }
210
211 50
      return $this->process($query);
212
    }
213
214
215
    /**
216
     * @inheritdoc
217
     */
218 215
    public function process(StrategyInterface $strategy) {
219
220 200
      if ($this->isValid() === false) {
221 148
        return new Token();
222
      }
223
224 200
      $result = $strategy->process($this->collection, $this->getPosition());
225
226 200
      if ($result->isValid() === false) {
227 180
        $this->setValid(false);
228 180
        return new Token();
229
      }
230
231 190
      $position = $result->getNexTokenIndex();
232 190
      $this->setPosition($position);
233
234 190
      $token = $result->getToken();
235 202
      if ($token === null) {
236 112
        $token = new Token();
237 112
      }
238
239 202
      if ($this->skipWhitespaces and isset($this->collection[$position]) and $this->collection[$position]->getType() === T_WHITESPACE) {
240
        # skip whitespaces in next check
241 26
        $this->setPosition($position + 1);
242 26
      }
243
244 202
      return $token;
245 15
    }
246
247
248
    /**
249
     *
250
     * @param StrategyInterface|string|int $value
251
     * @param QueryStrategy $defaultStrategy
252
     * @return QueryStrategy
253
     */
254 212
    private function buildStrategyCondition($value, QueryStrategy $defaultStrategy) {
255
256 190
      if (is_object($value) and get_class($value) === get_class($defaultStrategy)) {
257 18
        return $value;
258
      }
259
260 200
      $query = $defaultStrategy;
261
262 182
      if (is_string($value) or $value === null) {
263 160
        $query->valueIs($value);
264 167
        return $query;
265
      }
266
267 128
      if (is_int($value)) {
268 114
        $query->typeIs($value);
269 118
        return $query;
270
      }
271
272
273 14
      throw new InvalidArgumentException('Invalid token condition. Expect string or int or StrategyInterface');
274 22
    }
275
276
277
    /**
278
     * @param boolean $valid
279
     * @return $this
280
     */
281 199
    public function setValid($valid) {
282 186
      if (!is_bool($valid)) {
283 2
        throw new InvalidArgumentException('Invalid flag. Expect boolean. Given:' . gettype($valid));
284
      }
285 197
      $this->valid = $valid;
286 197
      return $this;
287
    }
288
289
290
    /**
291
     * @return Token
292
     */
293
    public function getToken() {
294
      $position = $this->getPosition();
295
      $token = $this->getCollection()->offsetGet($position);
296
297
      if ($token !== null) {
298
        return $token;
299
      }
300
301
      return new Token();
302
    }
303
304
305
    /**
306
     * Indicate state of all conditions
307
     *
308
     * @return bool
309
     */
310 206
    public function isValid() {
311 206
      return ($this->valid === true);
312
    }
313
314
315
    /**
316
     * @param boolean $skipWhitespaces
317
     * @return $this
318
     */
319 29
    public function setSkipWhitespaces($skipWhitespaces) {
320 29
      $this->skipWhitespaces = $skipWhitespaces;
321 29
      return $this;
322
    }
323
324
  }