Completed
Push — master ( b27cfe...644e47 )
by recca
02:37
created

Rule::normalizeAddress()   A

Complexity

Conditions 3
Paths 1

Size

Total Lines 14
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 3

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 14
ccs 8
cts 8
cp 1
rs 9.4285
cc 3
eloc 9
nc 1
nop 2
crap 3
1
<?php
2
3
namespace Recca0120\Twzipcode;
4
5
use Closure;
6
use Recca0120\LoDash\JArray;
7
8
class Rule
9
{
10
    /**
11
     * $zip3.
12
     *
13
     * @var string
14
     */
15
    public $zip3;
16
17
    /**
18
     * $zip5.
19
     *
20
     * @var string
21
     */
22
    public $zip5;
23
24
    /**
25
     * $tokens.
26
     *
27
     * @var \Recca0120\Twzipcode\Address
28
     */
29
    public $address;
30
31
    /**
32
     * $tokens.
33
     *
34
     * @var \Recca0120\LoDash\JArray
35
     */
36
    public $tokens;
37
38
    /**
39
     * __construct.
40
     *
41
     * @param string $rule
42
     */
43 62
    public function __construct($rule)
44
    {
45 62
        if (preg_match('/^(\d+),?(.*)/', $rule, $m)) {
46 51
            $this->zip5 = $m[1];
47 51
            $this->zip3 = substr($this->zip5, 0, 3);
48 51
            $rule = $m[2];
49 51
        }
50
51 62
        $this->tokens = $this->tokenize(
52 62
            $rule,
53
            function ($address) {
54 62
                $this->address = new Address($address);
55 62
            }
56 62
        );
57 62
    }
58
59
    /**
60
     * zip3.
61
     *
62
     * @return string
63
     */
64 1
    public function zip3()
65
    {
66 1
        return $this->zip3;
67
    }
68
69
    /**
70
     * zip5.
71
     *
72
     * @return string
73
     */
74 23
    public function zip5()
75
    {
76 23
        return $this->zip5;
77
    }
78
79
    /**
80
     * zip.
81
     *
82
     * @return string
83
     */
84
    public function zip()
85
    {
86
        return $this->zip3();
87
    }
88
89
    /**
90
     * tokens.
91
     *
92
     * @return \Recca0120\LoDash\JArray
93
     */
94 18
    public function tokens()
95
    {
96 18
        return $this->tokens;
97
    }
98
99
    /**
100
     * match.
101
     *
102
     * @param string $address
103
     * @return bool
104
     */
105 38
    public function match($address)
106
    {
107 38
        $ruleAddressTokens = $this->address->tokens();
108 38
        $address = $this->normalizeAddress(
109 38
            is_a($address, Address::class) === true ? $address : new Address($address),
0 ignored issues
show
Bug introduced by
It seems like is_a($address, \Recca012...pcode\Address($address) can also be of type string; however, Recca0120\Twzipcode\Rule::normalizeAddress() does only seem to accept object<Recca0120\Twzipcode\Address>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
110
            $ruleAddressTokens
0 ignored issues
show
Documentation introduced by
$ruleAddressTokens is of type array, but the function expects a object<Recca0120\LoDash\JArray>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
111 38
        );
112 38
        $addressTokens = $address->tokens();
113
114 38
        $cur = $ruleAddressTokens->length() - 1;
0 ignored issues
show
Bug introduced by
The method length cannot be called on $ruleAddressTokens (of type array).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
115 38
        $cur -= $this->tokens->length() > 0 && $this->tokens->includes('全') === false;
0 ignored issues
show
Documentation introduced by
'全' is of type string, but the function expects a object<Recca0120\LoDash\JArray\mix>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
116 38
        $cur -= $this->tokens->includes('至');
0 ignored issues
show
Documentation introduced by
'至' is of type string, but the function expects a object<Recca0120\LoDash\JArray\mix>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
117
118 38
        if ($this->equalsToken($ruleAddressTokens, $addressTokens, $cur) === false) {
0 ignored issues
show
Documentation introduced by
$ruleAddressTokens is of type array, but the function expects a object<Recca0120\LoDash\JArray>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Documentation introduced by
$addressTokens is of type array, but the function expects a object<Recca0120\LoDash\JArray>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
119 12
            return false;
120
        }
121
122 37
        $addressPoint = $address->getPoint($cur + 1);
123
124 37
        if ($this->tokens->length() > 0 && $addressPoint->isEmpty() === true) {
125 3
            return false;
126
        }
127
128 37
        $left = $this->address->getPoint($ruleAddressTokens->length() - 1);
0 ignored issues
show
Bug introduced by
The method length cannot be called on $ruleAddressTokens (of type array).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
129 37
        $right = $this->address->getPoint($ruleAddressTokens->length() - 2);
0 ignored issues
show
Bug introduced by
The method length cannot be called on $ruleAddressTokens (of type array).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
130
131 37
        foreach ($this->tokens as $token) {
132
            if (
133 33
                ($token === '單' && (bool) (($addressPoint->x & 1) === 1) === false) ||
134 33
                ($token === '雙' && (bool) (($addressPoint->x & 1) === 0) === false) ||
135 33
                ($token === '以上' && $addressPoint->compare($left, '>=') === false) ||
136 33
                ($token === '以下' && $addressPoint->compare($left, '<=') === false) ||
137 33
                ($token === '至' && (
138 9
                    $right->compare($addressPoint, '<=') && $addressPoint->compare($left, '<=') ||
139 8
                    $this->tokens->includes('含附號全') === true && ($addressPoint->x == $left->x)
0 ignored issues
show
Documentation introduced by
'含附號全' is of type string, but the function expects a object<Recca0120\LoDash\JArray\mix>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
140 33
                ) === false) ||
141 33
                ($token == '含附號' && ($addressPoint->x === $left->x) === false) ||
142 33
                ($token == '附號全' && ($addressPoint->x === $left->x && $addressPoint->y > 0) === false) ||
143 33
                ($token == '及以上附號' && $addressPoint->compare($left, '>=') === false) ||
144 33
                ($token == '含附號以下' && (
145 4
                    $addressPoint->compare($left, '<=') ||
146 4
                    $addressPoint->x === $left->x
147 4
                ) === false)
148 33
            ) {
149 25
                return false;
150
            }
151 37
        }
152
153 37
        return true;
154
    }
155
156
    /**
157
     * removeUnits.
158
     *
159
     * @param \Recca0120\LoDash\JArray $ruleAddressTokens
160
     * @return array
161
     */
162 38
    protected function normalizeAddress(Address $address, JArray $ruleAddressTokens)
163
    {
164
        $removeUnits = array_diff(['里', '鄰', '巷', '弄'], (array) $ruleAddressTokens->map(function($token) {
165 38
            return isset($token[Address::UNIT]) === true ? $token[Address::UNIT] : '';
166 38
        })->values());
167
168 38
        return new Address(
169
            new JArray($address->tokens()->filter(function ($token) use ($removeUnits) {
0 ignored issues
show
Bug introduced by
The method filter cannot be called on $address->tokens() (of type array).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
170 38
                return isset($token[Address::UNIT]) === true && in_array($token[Address::UNIT], $removeUnits, true) === false;
171
            })->map(function($token) {
172 38
                return implode('', $token);
173 38
            }))
174 38
        );
175
    }
176
177
    /**
178
     * equalsToken.
179
     *
180
     * @param \Recca0120\LoDash\JArray $ruleAddressTokens [description]
181
     * @param \Recca0120\LoDash\JArray $addressTokens     [description]
182
     * @param int                      $cur
183
     * @return bool
184
     */
185 38
    protected function equalsToken($ruleAddressTokens, $addressTokens, $cur)
186
    {
187 38
        if ($cur >= $addressTokens->length()) {
188 4
            return false;
189
        }
190
191 38
        $i = $cur;
192 38
        while ($i >= 0) {
193 38
            if ($ruleAddressTokens[$i] !== $addressTokens[$i]) {
194 11
                return false;
195
            }
196 37
            $i -= 1;
197 37
        }
198
199 37
        return true;
200
    }
201
202
    /**
203
     * normalize.
204
     *
205
     * @param string $rule
206
     * @return \Recca0120\Twzipcode\Normalizer
207
     */
208 62
    protected function normalize($rule)
209
    {
210 62
        $pattern = '((?P<no>\d+)之)?\s*(?P<left>\d+)至之?\s*(?P<right>\d+)(?P<unit>\w)';
211
212
        return (new Normalizer($rule))->normalize()->replace('/'.$pattern.'/u', function ($m) {
0 ignored issues
show
Documentation introduced by
function ($m) { $pre...nit' => $m['unit'])); } is of type object<Closure>, but the function expects a object<Recca0120\LoDash\JString\mix>|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
213 5
            $prefix = ':left:unit至:right:unit';
214 5
            if (empty($m['no']) === false) {
215 3
                $prefix = ':no之:left:unit至:no之:right:unit';
216 3
            }
217
218 5
            return strtr($prefix, [
219 5
                ':no' => $m['no'],
220 5
                ':left' => $m['left'],
221 5
                ':right' => $m['right'],
222 5
                ':unit' => $m['unit'],
223 5
            ]);
224 62
        });
225
    }
226
227
    /**
228
     * tokenize.
229
     *
230
     * @param string   $rule
231
     * @param \Closure $addressResolver
232
     * @return \Recca0120\LoDash\JArray
233
     */
234 62
    protected function tokenize($rule, Closure $addressResolver)
235
    {
236 62
        $tokens = new JArray();
237
238
        $pattern = [
239 62
            '及以上附號|含附號以下|含附號全|含附號',
240 62
            '以下|以上',
241 62
            '附號全',
242 62
            '[連至單雙全](?=[\d全]|$)',
243 62
        ];
244
245 62
        $addressResolver($this->normalize($rule)->replace('/'.implode('|', $pattern).'/u', function ($m) use ($tokens) {
0 ignored issues
show
Documentation introduced by
function ($m) use($token...號全' ? '號' : ''; } is of type object<Closure>, but the function expects a object<Recca0120\LoDash\JString\mix>|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
246 60
            $token = &$m[0];
247 60
            if ($token === '連') {
248 18
                return;
249
            }
250
251 60
            $tokens->append($token);
252
253 60
            return $token === '附號全' ? '號' : '';
254 62
        }));
255
256 62
        return $tokens;
257
    }
258
}
259