Completed
Push — master ( a14e1b...dc020a )
by Shcherbak
02:20
created

ParametersPattern::__invoke()   C

Complexity

Conditions 11
Paths 30

Size

Total Lines 46
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 26
CRAP Score 11.0061

Importance

Changes 2
Bugs 0 Features 1
Metric Value
c 2
b 0
f 1
dl 0
loc 46
ccs 26
cts 27
cp 0.963
rs 5.2653
cc 11
eloc 24
nc 30
nop 1
crap 11.0061

How to fix   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 Funivan\PhpTokenizer\Pattern\Patterns;
4
5
  use Funivan\PhpTokenizer\Collection;
6
  use Funivan\PhpTokenizer\QuerySequence\QuerySequence;
7
  use Funivan\PhpTokenizer\Strategy\Section;
8
  use Funivan\PhpTokenizer\Token;
9
10
  /**
11
   * Find method in code
12
   */
13
  class ParametersPattern implements PatternInterface {
14
15
    /**
16
     * @var array
17
     */
18
    private $argumentCheck = [];
19
20
    /**
21
     * @var int|null
22
     */
23
    private $outputArgument = null;
24
25
    /**
26
     * @var bool
27
     */
28
    private $fullSearch = true;
29
30
31
    /**
32
     *
33
     */
34 20
    public function __construct() {
35 16
      $this->outputFull();
36 16
      $this->startFromFunction();
37 20
    }
38
39
40
    /**
41
     * @return $this
42
     */
43 2
    public function startFromParenthesis() {
44 2
      $this->fullSearch = false;
45 2
      return $this;
46
    }
47
48
49
    /**
50
     * @return $this
51
     */
52 20
    public function startFromFunction() {
53 20
      $this->fullSearch = true;
54 20
      return $this;
55
    }
56
57
58
    /**
59
     * @param QuerySequence $querySequence
60
     * @return Collection|null
61
     * @throws \Exception
62
     */
63 20
    public function __invoke(QuerySequence $querySequence) {
64 16
      if ($this->fullSearch) {
65 14
        $querySequence->strict(T_FUNCTION);
66 14
        $querySequence->possible(T_WHITESPACE);
67 14
        $querySequence->possible(T_STRING);
68 14
        $querySequence->possible(T_WHITESPACE);
69 7
      }
70
71 16
      $section = $querySequence->section('(', ')');
72 16
      if ($section->count() === 0) {
73 14
        return null;
74
      }
75
76 16
      $section->slice(1, -1);
77
78 19
      if (empty($this->argumentCheck) and $this->outputArgument === null) {
79 10
        return $section;
80
      }
81
82
83
      /** @var Collection[] $arguments */
84 14
      $arguments = $this->getArguments($section);
85
86 17
      foreach ($this->argumentCheck as $index => $check) {
87
88 11
        $argumentTokens = isset($arguments[$index]) ? $arguments[$index] : new Collection();;
89 10
        $result = $check($argumentTokens);
90
91 10
        if (!is_bool($result)) {
92 2
          throw new \Exception('Argument check function should return boolean');
93
        }
94
95 8
        if ($result === false) {
96 10
          return null;
97
        }
98 9
      }
99
100 16
      if ($this->outputArgument !== null) {
101 6
        $argumentCollection = !empty($arguments[$this->outputArgument]) ? $arguments[$this->outputArgument] : null;
102
103 6
        return $argumentCollection;
104
      }
105
106
      # output full
107 10
      return $section;
108
    }
109
110
111
    /**
112
     * @param int $int
113
     * @param callable $check
114
     * @return $this
115
     */
116 13
    public function withArgument($int, callable $check = null) {
117 13
      if ($check === null) {
118
        $check = function (Collection $argumentTokens) {
119 6
          return $argumentTokens->count() !== 0;
120 6
        };
121 3
      }
122 13
      $this->argumentCheck[$int] = $check;
123 13
      return $this;
124 3
    }
125
126
127
    /**
128
     * @param int $int
129
     * @return $this
130
     */
131
    public function withoutArgument($int) {
132 2
      $check = function (Collection $argumentTokens) {
133 2
        return $argumentTokens->count() === 0;
134 2
      };
135
136 3
      $this->argumentCheck[$int] = $check;
137 3
      return $this;
138
    }
139
140
141
    /**
142
     * @param $section
143
     * @return array
144
     */
145 19
    protected function getArguments(Collection $section) {
146
      /** @var Token $skipToToken */
147 17
      $skipToToken = null;
148 17
      $argumentIndex = 1;
149 17
      $arguments = [];
150 14
      $tokensNum = ($section->count() - 1);
151 14
      for ($tokenIndex = 0; $tokenIndex <= $tokensNum; $tokenIndex++) {
152
153 14
        $token = $section->offsetGet($tokenIndex);
154
155 14
        if ($token === null) {
156
          return null;
157
        }
158
159 14
        if ($skipToToken === null or $token->getIndex() >= $skipToToken->getIndex()) {
160 14
          if ($token->getValue() === ',') {
161 19
            $argumentIndex++;
162 19
            continue;
163
          }
164 14
          $skipToToken = $this->getEndArray($token, $section, $tokenIndex);
165 7
        }
166
167
168 14
        if (!isset($arguments[$argumentIndex])) {
169 14
          $arguments[$argumentIndex] = new Collection();
170 7
        }
171 14
        $arguments[$argumentIndex][] = $token;
172 7
      }
173
174 17
      return $arguments;
175 3
    }
176
177
178
    /**
179
     * @param Token $token
180
     * @param Collection $section
181
     * @param $index
182
     * @return Token
183
     */
184 14
    private function getEndArray(Token $token, Collection $section, $index) {
185
      // # check if we have array start
186
187 14
      if ($token->getValue() === '[') {
188 10
        $result = (new Section())->setDelimiters('[', ']')->process($section, $index);
189 10
        return $result->getToken();
190
      }
191
192 14
      if ($token->getValue() === 'array') {
193
194
        # skip whitespace
195 2
        $nextIndex = ($index + 1);
196 2
        $nextToken = $section->offsetGet($nextIndex);
197 2
        if ($nextToken->getType() === T_WHITESPACE) {
198 2
          $nextIndex = ($index + 2);
199 1
        }
200
201 2
        $result = (new Section())->setDelimiters('(', ')')->process($section, $nextIndex);
202 2
        return $result->getToken();
203
      }
204
205 14
      return null;
206
    }
207
208
209
    /**
210
     * @return $this
211
     */
212 20
    public function outputFull() {
213 20
      $this->outputArgument = null;
214 20
      return $this;
215
    }
216
217
218
    /**
219
     * @param $int
220
     * @return $this
221
     */
222 6
    public function outputArgument($int) {
223 6
      $this->outputArgument = $int;
224 6
      return $this;
225
    }
226
227
  }