Completed
Push — master ( 201082...1bff1a )
by Shcherbak
05:24
created

ParametersPattern::withoutArgument()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 2
Bugs 0 Features 2
Metric Value
dl 0
loc 8
c 2
b 0
f 2
ccs 5
cts 5
cp 1
rs 9.4285
cc 1
eloc 5
nc 1
nop 1
crap 1
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
   *
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|null
27
     */
28
    private $outputPreparedArgument = null;
29
30
31
    /**
32
     *
33
     */
34 43
    public function __construct() {
35 34
      $this->outputFull();
36 43
    }
37
38
39
    /**
40
     * @param QuerySequence $querySequence
41
     * @return Collection|null
42
     * @throws \Exception
43
     */
44 42
    public function __invoke(QuerySequence $querySequence) {
45 34
      $section = $querySequence->section('(', ')');
46 34
      if ($section->count() === 0) {
47 30
        return null;
48
      }
49
50 34
      $section->slice(1, -1);
51
52 39
      if (empty($this->argumentCheck) and $this->outputArgument === null) {
53 10
        return $section;
54
      }
55
56
57
      /** @var Collection[] $arguments */
58 32
      $arguments = $this->getArguments($section);
59
60 37
      foreach ($this->argumentCheck as $index => $check) {
61
62 28
        $argumentTokens = isset($arguments[$index]) ? $arguments[$index] : new Collection();
63 24
        $result = $check($argumentTokens);
64
65 24
        if (!is_bool($result)) {
66 2
          throw new \Exception('Argument check function should return boolean');
67
        }
68
69 26
        if ($result === false) {
70 23
          return null;
71
        }
72 16
      }
73
74 35
      if ($this->outputArgument !== null) {
75 20
        $argumentCollection = !empty($arguments[$this->outputArgument]) ? $arguments[$this->outputArgument] : null;
76
77
        # Do not remove T_WHITESPACE tokens from the argument output
78 20
        if ($this->outputPreparedArgument === false) {
79 12
          return $argumentCollection;
80
        }
81
82
        // trim first and last whitespace token
83 16
        $first = $argumentCollection->getFirst();
84 16
        $last = $argumentCollection->getLast();
85
86 24
        $from = 0;
87 24
        $to = null;
88 16
        if ($first !== null and $first->getType() == T_WHITESPACE) {
89 15
          $from = 1;
90 5
        }
91 16
        if ($last and $last->getType() == T_WHITESPACE) {
92 12
          $to = -1;
93 4
        }
94
95 16
        return $argumentCollection->extractItems($from, $to);
96
      }
97
98
      # output full
99 15
      return $section;
100
    }
101
102
103
    /**
104
     * @param int $int
105
     * @param callable $check
106
     * @return $this
107
     */
108 34
    public function withArgument($int, callable $check = null) {
109 34
      if ($check === null) {
110
        $check = function (Collection $argumentTokens) {
111 20
          return $argumentTokens->count() !== 0;
112 20
        };
113 10
      }
114 34
      $this->argumentCheck[$int] = $check;
115 34
      return $this;
116 10
    }
117
118
119
    /**
120
     * @param int $int
121
     * @return $this
122
     */
123
    public function withoutArgument($int) {
124 2
      $check = function (Collection $argumentTokens) {
125 2
        return $argumentTokens->count() === 0;
126 2
      };
127
128 3
      $this->argumentCheck[$int] = $check;
129 3
      return $this;
130
    }
131
132
133
    /**
134
     * @param Collection $section
135
     * @return Collection[]
136
     */
137 42
    protected function getArguments(Collection $section) {
138
      /** @var Token $skipToToken */
139 37
      $skipToToken = null;
140 37
      $argumentIndex = 1;
141 37
      $arguments = [];
142 32
      $tokensNum = ($section->count() - 1);
143 32
      for ($tokenIndex = 0; $tokenIndex <= $tokensNum; $tokenIndex++) {
144
145 32
        $token = $section->offsetGet($tokenIndex);
146
147 32
        if ($token === null) {
148
          return null;
149
        }
150
151 32
        if ($skipToToken === null or $token->getIndex() >= $skipToToken->getIndex()) {
152 32
          if ($token->getValue() === ',') {
153 42
            $argumentIndex++;
154 42
            continue;
155
          }
156 32
          $skipToToken = $this->getEndArray($token, $section, $tokenIndex);
157 16
        }
158
159
160 32
        if (!isset($arguments[$argumentIndex])) {
161 32
          $arguments[$argumentIndex] = new Collection();
162 16
        }
163 32
        $arguments[$argumentIndex][] = $token;
164 16
      }
165
166 37
      return $arguments;
167 5
    }
168
169
170
    /**
171
     * @param Token $token
172
     * @param Collection $section
173
     * @param $index
174
     * @return Token
175
     */
176 32
    private function getEndArray(Token $token, Collection $section, $index) {
177
      # check if we have array start
178
179 32
      if ($token->getValue() === '[') {
180 14
        $result = (new Section())->setDelimiters('[', ']')->process($section, $index);
181 14
        return $result->getToken();
182
      }
183
184 32
      if ($token->getValue() === '(') {
185 4
        $result = (new Section())->setDelimiters('(', ')')->process($section, $index);
186 4
        return $result->getToken();
187
      }
188
189 32
      return null;
190
    }
191
192
193
    /**
194
     * @return $this
195
     */
196 43
    public function outputFull() {
197 43
      $this->outputArgument = null;
198 43
      $this->outputPreparedArgument = null;
199 43
      return $this;
200
    }
201
202
203
    /**
204
     * @param int $int
205
     * @param bool $prepared
206
     * @return $this
207
     */
208 27
    public function outputArgument($int, $prepared = true) {
209 23
      $this->outputArgument = $int;
210 23
      $this->outputPreparedArgument = $prepared;
211 23
      return $this;
212 9
    }
213
214
  }