Completed
Pull Request — master (#151)
by Alexander
04:33
created

SafeBrowsingClient   D

Complexity

Total Complexity 82

Size/Duplication

Total Lines 601
Duplicated Lines 18.47 %

Coupling/Cohesion

Components 1
Dependencies 9

Test Coverage

Coverage 100%

Importance

Changes 11
Bugs 5 Features 3
Metric Value
wmc 82
c 11
b 5
f 3
lcom 1
cbo 9
dl 111
loc 601
ccs 302
cts 302
cp 1
rs 4.8678

23 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A setApiKey() 0 4 1
A getApiKey() 0 4 1
A setMalwareShavars() 0 4 1
A getMalwareShavars() 0 4 1
A getServiceUrl() 0 5 1
A getLookupUrl() 0 6 1
A getCheckAdultUrl() 0 6 1
B sendRequest() 29 29 5
A checkHash() 17 17 1
A getChunks() 17 17 1
A getShavarsList() 0 6 1
A lookup() 0 8 2
A checkAdult() 0 8 2
A getChunkByUrl() 0 19 2
D searchUrl() 0 30 9
A getFullHashes() 0 16 3
B getHashesByUrl() 0 35 5
A getHashesByHosts() 0 8 2
A getHashByHost() 0 6 1
C prepareDownloadsRequest() 0 44 16
D getMalwaresData() 42 89 14
C parseChunk() 6 64 10

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like SafeBrowsingClient often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use SafeBrowsingClient, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * Yandex PHP Library
4
 *
5
 * @copyright NIX Solutions Ltd.
6
 * @link https://github.com/nixsolutions/yandex-php-library
7
 */
8
9
/**
10
 * @namespace
11
 */
12
namespace Yandex\SafeBrowsing;
13
14
use Psr\Http\Message\UriInterface;
15
use Yandex\Common\AbstractServiceClient;
16
use GuzzleHttp\Psr7\Response;
17
use GuzzleHttp\Exception\ClientException;
18
use Yandex\Common\Exception\ForbiddenException;
19
use Yandex\Common\Exception\NotFoundException;
20
use Yandex\Common\Exception\UnauthorizedException;
21
22
/**
23
 * Class SafeBrowsingClient
24
 *
25
 * @category Yandex
26
 * @package SafeBrowsing
27
 *
28
 * @author   Alexander Khaylo <[email protected]>
29
 * @created  31.01.14 17:32
30
 */
31
class SafeBrowsingClient extends AbstractServiceClient
32
{
33
    /**
34
     * @var string
35
     */
36
    protected $serviceDomain = 'sba.yandex.net';
37
38
    /**
39
     * @var
40
     */
41
    protected $apiKey;
42
43
    /**
44
     * @var string
45
     */
46
    protected $appVer = '2.3';
47
48
    /**
49
     * @var string
50
     */
51
    protected $pVer = '2.3';
52
53
    /**
54
     * @var array
55
     */
56
    protected $malwareShavars = [
57
        'ydx-malware-shavar',
58
        'ydx-phish-shavar',
59
        'goog-malware-shavar',
60
        'goog-phish-shavar'
61
    ];
62
63
    /**
64
     * @param string $apiKey
65
     */
66 30
    public function __construct($apiKey = '')
67
    {
68 30
        $this->setApiKey($apiKey);
69 30
    }
70
71
    /**
72
     * @param string $apiKey
73
     */
74 30
    public function setApiKey($apiKey)
75
    {
76 30
        $this->apiKey = $apiKey;
77 30
    }
78
79
    /**
80
     * @return string
81
     */
82 3
    public function getApiKey()
83
    {
84 3
        return $this->apiKey;
85
    }
86
87
    /**
88
     * @param array $malwareShavars
89
     */
90 2
    public function setMalwareShavars($malwareShavars)
91
    {
92 2
        $this->malwareShavars = $malwareShavars;
93 2
    }
94
95
    /**
96
     * @return array
97
     */
98 1
    public function getMalwareShavars()
99
    {
100 1
        return $this->malwareShavars;
101
    }
102
103
    /**
104
     * Get url to service resource with parameters
105
     *
106
     * @param string $resource
107
     * @return string
108
     */
109 14
    public function getServiceUrl($resource = '')
110
    {
111 14
        return $this->serviceScheme . '://' . $this->serviceDomain . '/'
112 14
        . $resource . '?client=api&apikey=' . $this->apiKey . '&appver=' . $this->appVer . '&pver=' . $this->pVer;
113
    }
114
115
    /**
116
     * Get url to service Lookup resource with parameters
117
     *
118
     * @param string $url
119
     * @return string
120
     */
121 2
    public function getLookupUrl($url = '')
122
    {
123 2
        $pVer = '3.5'; //Specific version
124 2
        return $this->serviceScheme . '://' . $this->serviceDomain . '/'
125 2
        . 'lookup?client=api&apikey=' . $this->apiKey . '&pver=' . $pVer . '&url=' . $url;
126
    }
127
128
    /**
129
     * Get url to service Check Adult  resource with parameters
130
     *
131
     * @param string $url
132
     * @return string
133
     */
134 6
    public function getCheckAdultUrl($url = '')
135
    {
136 6
        $pVer = '4.0'; //Specific version
137 6
        return $this->serviceScheme . '://' . $this->serviceDomain . '/'
138 6
        . 'cp?client=api&pver=' . $pVer . '&url=' . $url;
139
    }
140
141
    /**
142
     * Sends a request
143
     *
144
     * @param string              $method  HTTP method
145
     * @param string|UriInterface $uri     URI object or string.
146
     * @param array               $options Request options to apply.
147
     *
148
     * @return Response
149
     *
150
     * @throws ForbiddenException
151
     * @throws UnauthorizedException
152
     * @throws SafeBrowsingException
153
     * @throws NotFoundException
154
     */
155 23 View Code Duplication
    protected function sendRequest($method, $uri, array $options = [])
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
156
    {
157
        try {
158 23
            $response = $this->getClient()->request($method, $uri, $options);
159 23
        } catch (ClientException $ex) {
160 4
            $result = $ex->getResponse();
161 4
            $code = $result->getStatusCode();
162 4
            $message = $result->getReasonPhrase();
163
164 4
            if ($code === 403) {
165 1
                throw new ForbiddenException($message);
166
            }
167
168 3
            if ($code === 401) {
169 1
                throw new UnauthorizedException($message);
170
            }
171
172 2
            if ($code === 404) {
173 1
                throw new NotFoundException($message);
174
            }
175
176 1
            throw new SafeBrowsingException(
177 1
                'Service responded with error code: "' . $code . '" and message: "' . $message . '"',
178
                $code
179 1
            );
180
        }
181
182 19
        return $response;
183
    }
184
185
    /**
186
     * @param string $bodyString
187
     * @see https://developers.google.com/safe-browsing/developers_guide_v2#HTTPRequestForHashes
188
     * @return array
189
     */
190 3 View Code Duplication
    public function checkHash($bodyString = '')
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
191
    {
192 3
        $resource = 'gethash';
193
194 3
        $response = $this->sendRequest(
195 3
            'POST',
196 3
            $this->getServiceUrl($resource),
197
            [
198
                'body' => $bodyString
199 3
            ]
200 3
        );
201
202
        return [
203 3
            'code' => $response->getStatusCode(),
204 3
            'data' => $response->getBody()
205 3
        ];
206
    }
207
208
    /**
209
     * @param string $bodyString
210
     * @see https://developers.google.com/safe-browsing/developers_guide_v2#HTTPRequestForData
211
     * @return array
212
     */
213 10 View Code Duplication
    public function getChunks($bodyString = '')
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
214
    {
215 10
        $resource = 'downloads';
216
217 10
        $response = $this->sendRequest(
218 10
            'POST',
219 10
            $this->getServiceUrl($resource),
220
            [
221
                'body' => $bodyString
222 10
            ]
223 10
        );
224
225
        return [
226 10
            'code' => $response->getStatusCode(),
227 10
            'data' => $response->getBody()
228 10
        ];
229
    }
230
231
    /**
232
     * @see https://developers.google.com/safe-browsing/developers_guide_v2#HTTPRequestForList
233
     * @return array
234
     */
235 1
    public function getShavarsList()
236
    {
237 1
        $resource = 'list';
238 1
        $response = $this->sendRequest('GET', $this->getServiceUrl($resource));
239 1
        return explode("\n", trim($response->getBody()));
240
    }
241
242
    /**
243
     * @param string $url
244
     * @return string|false
245
     */
246 2
    public function lookup($url)
247
    {
248 2
        $response = $this->sendRequest('GET', $this->getLookupUrl($url));
249 2
        if ($response->getStatusCode() === 200) {
250 1
            return $response->getBody()->getContents();
251
        }
252 1
        return false;
253
    }
254
255
    /**
256
     * @param string $url
257
     * @return bool
258
     */
259 6
    public function checkAdult($url)
260
    {
261 6
        $response = $this->sendRequest('GET', $this->getCheckAdultUrl($url));
262 2
        if ($response->getBody()->getContents() === 'adult') {
263 1
            return true;
264
        }
265 1
        return false;
266
    }
267
268
    /**
269
     * @param string $url
270
     * @return string
271
     */
272 1
    public function getChunkByUrl($url)
273
    {
274 1
        $client = $this->getClient();
275
276 1
        $host = parse_url($url, PHP_URL_HOST);
277 1
        $headers = $client->getConfig('headers');
278 1
        if ($host) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $host of type string|false is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
279 1
            $headers['Host'] = $host;
280 1
        }
281
282 1
        $response = $this->sendRequest(
283 1
            'GET',
284 1
            $url,
285
            [
286
                'headers' => $headers
287 1
            ]
288 1
        );
289 1
        return $response->getBody()->getContents();
290
    }
291
292
    /**
293
     * @param string $url
294
     * @return bool|array
295
     * @throws \Exception
296
     */
297 3
    public function searchUrl($url)
298
    {
299 3
        $hashes = $this->getHashesByUrl($url);
300
301 3
        foreach ($hashes as $hash) {
302 3
            $prefixPack = pack("H*", $hash['prefix']);
303 3
            $prefixSize = strlen($hash['prefix']) / 2;
304 3
            $length = count($prefixPack) * $prefixSize;
305 3
            $bodyString = "$prefixSize:$length\n" . $prefixPack;
306 3
            $result = $this->checkHash($bodyString);
307
308 3
            if ($result['code'] == 200 && !empty($result['data'])) {
309 1
                $malwareShavars = $this->getFullHashes($result['data']);
310 1
                foreach ($malwareShavars as $fullHashes) {
311 1
                    foreach ($fullHashes as $fullHash) {
312 1
                        if ($fullHash === $hash['full']) {
313 1
                            return $hash;
314
                        }
315 1
                    }
316 1
                }
317 3
            } elseif ($result['code'] == 204 && strlen($result['data']) == 0) {
318
                //204 Means no match
319 1
            } else {
320 1
                throw new SafeBrowsingException(
321 1
                    "ERROR: Invalid response returned from Safe Browsing ({$result['code']})"
322 1
                );
323
            }
324 2
        }
325 1
        return false;
326
    }
327
328
    /**
329
     * @param string $responseData
330
     * @return array
331
     */
332 1
    public function getFullHashes($responseData)
333
    {
334 1
        $hashesData = [];
335 1
        while (strlen($responseData) > 0) {
336 1
            $splithead = explode("\n", $responseData, 2);
337
338 1
            list($listname, $malwareId, $length) = explode(':', $splithead[0]);
339 1
            $data = bin2hex(substr($splithead[1], 0, $length));
340 1
            while (strlen($data) > 0) {
341 1
                $hashesData[$listname][$malwareId] = substr($data, 0, 64);
342 1
                $data = substr($data, 64);
343 1
            }
344 1
            $responseData = substr($splithead[1], $length);
345 1
        }
346 1
        return $hashesData;
347
    }
348
349
    /**
350
     * @param string $url
351
     * @return array
352
     */
353 5
    public function getHashesByUrl($url)
354
    {
355
        //Remove line feeds, return carriages, tabs, vertical tabs
356 5
        $url = trim(str_replace(["\x09", "\x0A", "\x0D", "\x0B"], '', $url));
357
        //extract hostname
358 5
        $parts = parse_url(strtolower($url));
359 5
        if (!isset($parts['scheme'])) {
360
            //Add default scheme
361 3
            $parts = parse_url('http://' . $url);
362 3
        }
363 5
        $host = $parts['host'];
364
365
        //const
366 5
        $maxCountDomains = 5;
367
368
        //Exact hostname in the url
369 5
        $hosts = [];
370 5
        if (filter_var($host, FILTER_VALIDATE_IP)) {
371 1
            $hosts[] = $host . '/';
372 1
        } else {
373 4
            $domains = explode('.', $host);
374 4
            $countDomains = count($domains);
375 4
            if ($countDomains > $maxCountDomains) {
376 1
                $domains = array_slice($domains, $countDomains - $maxCountDomains, $maxCountDomains);
377 1
            }
378
379 4
            while (count($domains) > 1) {
380 4
                $hosts[] = implode('.', $domains) . '/';
381 4
                array_shift($domains);
382 4
            }
383
        }
384
385 5
        $hosts = array_unique($hosts);
386 5
        return $this->getHashesByHosts($hosts);
387
    }
388
389
    /**
390
     * @param array $hosts
391
     * @return array
392
     */
393 5
    private function getHashesByHosts($hosts)
394
    {
395 5
        $hashes = [];
396 5
        foreach ($hosts as $host) {
397 5
            $hashes[] = $this->getHashByHost($host);
398 5
        }
399 5
        return $hashes;
400
    }
401
402
    /**
403
     * @param string $host
404
     * @return array
405
     */
406 5
    private function getHashByHost($host)
407
    {
408
        //SHA-256
409 5
        $hash = hash('sha256', $host);
410 5
        return ["host" => $host, "prefix" => substr($hash, 0, 8), "full" => $hash];
411
    }
412
413
    /**
414
     * @param array $savedChunks
415
     * @return string
416
     * @throws SafeBrowsingException
417
     */
418 11
    private function prepareDownloadsRequest($savedChunks = [])
419
    {
420 11
        $body = '';
421 11
        if (count($this->malwareShavars) < 1) {
422 1
            throw new SafeBrowsingException(
423
                'ERROR: Empty malware shavars'
424 1
            );
425
        }
426
427 10
        foreach ($this->malwareShavars as $malwareShavar) {
428 10
            if ($savedChunks && isset($savedChunks[$malwareShavar])) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $savedChunks of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
429
                //ydx-malware-shavar;s:18888-19061:a:21355-21687
430
431 1
                $range = '';
432 1
                if (isset($savedChunks[$malwareShavar]['removed'])
433 1
                    && isset($savedChunks[$malwareShavar]['removed']['min'])
434 1
                    && isset($savedChunks[$malwareShavar]['removed']['max'])
435 1
                    && $savedChunks[$malwareShavar]['removed']['min'] > 0
436 1
                    && $savedChunks[$malwareShavar]['removed']['max'] > 0
437 1
                ) {
438 1
                    $range .= 's:' . $savedChunks[$malwareShavar]['removed']['min']
439 1
                        . '-' . $savedChunks[$malwareShavar]['removed']['max'];
440 1
                }
441
442 1
                if (isset($savedChunks[$malwareShavar]['added'])
443 1
                    && isset($savedChunks[$malwareShavar]['added']['min'])
444 1
                    && isset($savedChunks[$malwareShavar]['added']['max'])
445 1
                    && $savedChunks[$malwareShavar]['added']['min'] > 0
446 1
                    && $savedChunks[$malwareShavar]['added']['max'] > 0
447 1
                ) {
448 1
                    if ($range) {
449 1
                        $range .= ':';
450 1
                    }
451 1
                    $range .= 'a:' . $savedChunks[$malwareShavar]['added']['min']
452 1
                        . '-' . $savedChunks[$malwareShavar]['added']['max'];
453
454 1
                    $body .= $malwareShavar . ';' . $range . "\n";
455 1
                }
456 1
            } else {
457 10
                $body .= $malwareShavar . ";\n";
458
            }
459 10
        }
460 10
        return $body;
461
    }
462
463
    /**
464
     * Get malwares prefixes data
465
     *
466
     * @param array $savedChunks
467
     * @return array
468
     * @throws SafeBrowsingException
469
     */
470 11
    public function getMalwaresData($savedChunks = [])
471
    {
472 11
        $body = $this->prepareDownloadsRequest($savedChunks);
473
474 10
        $response = $this->getChunks($body);
475 10
        $result = [];
476
477 10
        $response['data'] = (string) $response['data'];
478
479 10
        if (substr_count($response['data'], 'r:pleasereset') > 0) {
480 1
            return 'pleasereset';
481
        }
482
483 9
        $chunksList = [];
484 9
        if (substr_count($response['data'], 'i:') < 1) {
485 1
            throw new SafeBrowsingException(
486
                'ERROR: Incorrect data in list'
487 1
            );
488
        }
489
490 8
        $shavarsData = explode('i:', $response['data']);
491 8
        unset($shavarsData[0]);
492 8
        foreach ($shavarsData as $shavar) {
493 8
            $listData = explode("\n", trim($shavar));
494 8
            $chunksList[array_shift($listData)] = $listData;
495 8
        }
496 8
        foreach ($chunksList as $listName => $list) {
497 8
            $chunksByList = [];
498 8
            foreach ($list as $value) {
499 8
                if (substr_count($value, "u:") > 0) {
500
                    try {
501 7
                        $chunkData = $this->getChunkByUrl('http://' . trim(str_replace('u:', '', $value)));
502 6
                        $processed = $this->parseChunk($chunkData);
503 3
                        $chunksByList[$processed['type']][$processed['chunk_num']] = $processed['prefixes'];
504 7
                    } catch (NotFoundException $e) {
505 1
                        continue;
506
                    }
507 4
                } elseif (substr_count($value, "ad:") > 0) {
508 1 View Code Duplication
                    if (substr_count($value, ',') > 0) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
509 1
                        $ranges = explode(',', trim(str_replace("ad:", "", $value)));
510 1
                        $rangesData = [];
511 1
                        foreach ($ranges as $range) {
512 1
                            list($min, $max) = explode('-', $range);
513 1
                            $rangesData[] = [
514 1
                                'min' => $min,
515
                                'max' => $max
516 1
                            ];
517 1
                        }
518 1
                        $chunksByList['delete_added_ranges'] = $rangesData;
519 1
                    } else {
520 1
                        $range = trim(str_replace("sd:", "", $value));
521 1
                        list($min, $max) = explode('-', $range);
522 1
                        $chunksByList['delete_added_ranges'] = [
523
                            [
524 1
                                'min' => $min,
525
                                'max' => $max
526 1
                            ]
527 1
                        ];
528
                    }
529 1
                } elseif (substr_count($value, "sd:") > 0) {
530 1 View Code Duplication
                    if (substr_count($value, ',') > 0) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
531 1
                        $ranges = explode(',', trim(str_replace("sd:", "", $value)));
532 1
                        $rangesData = [];
533 1
                        foreach ($ranges as $range) {
534 1
                            list($min, $max) = explode('-', $range);
535 1
                            $rangesData[] = [
536 1
                                'min' => $min,
537
                                'max' => $max
538 1
                            ];
539 1
                        }
540 1
                        $chunksByList['delete_removed_ranges'] = $rangesData;
541 1
                    } else {
542 1
                        $range = trim(str_replace("sd:", "", $value));
543 1
                        list($min, $max) = explode('-', $range);
544 1
                        $chunksByList['delete_removed_ranges'] = [
545
                            [
546 1
                                'min' => $min,
547
                                'max' => $max
548 1
                            ]
549 1
                        ];
550
                    }
551 1
                }
552 5
            }
553
554 5
            $result[$listName] = $chunksByList;
555 5
        }
556
557 5
        return $result;
558
    }
559
560
    /**
561
     * Parsing chunk
562
     *
563
     * @param string $data
564
     * @return array
565
     * @throws SafeBrowsingException
566
     */
567 6
    private function parseChunk($data)
568
    {
569 6
        $data = trim($data);
570 6
        if (strlen($data) === 0) {
571 1
            throw new SafeBrowsingException(
572 1
                'ERROR: Incorrect chunk data "' . $data . '"'
573 1
            );
574
        }
575
576 5
        $splitHead = explode("\n", $data, 2);
577 5
        $chunkInfo = explode(':', $splitHead[0]);
578 5
        list($type, $chunkNum, $hashLen, $chunkLen) = $chunkInfo;
579
580 5
        if ($chunkLen < 1) {
581 1
            throw new SafeBrowsingException(
582 1
                'ERROR: In chunkNum "' . $chunkNum . '" incorrect chunk length "' . $chunkLen . '"'
583 1
            );
584
        }
585
586
        //Convert to hex for easy processing
587
        //First get chunkData according to length
588 4
        $chunkData = bin2hex(substr($splitHead[1], 0, $chunkLen));
589 4
        if ($type == 'a') {
590 1
            $prefixes = [];
591 1
            while (strlen($chunkData) > 0) {
592 1
                $prefixes[] = substr($chunkData, 0, 8);
593 1
                $count = hexdec(substr($chunkData, 8, 2));
594 1
                $chunkData = substr($chunkData, 10);
595 1 View Code Duplication
                for ($i = 0; $i < $count; $i++) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
596 1
                    $chunkData = substr($chunkData, (($hashLen * 2)));
597 1
                }
598 1
            }
599
600
            return [
601 1
                'type' => 'added',
602 1
                'chunk_num' => $chunkNum,
603
                'prefixes' => $prefixes
604 1
            ];
605 3
        } elseif ($type == 's') {
606 2
            $prefixes = [];
607 2
            while (strlen($chunkData) > 0) {
608 2
                $prefixes[] = substr($chunkData, 0, 8);
609 2
                $count = hexdec(substr($chunkData, 8, 2));
610 2
                $chunkData = substr($chunkData, 10);
611 2
                if ($count > 0) {
612 1 View Code Duplication
                    for ($i = 0; $i < $count; $i++) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
613 1
                        $chunkData = substr($chunkData, (($hashLen * 2) + 8));
614 1
                    }
615 1
                } else {
616 1
                    $chunkData = substr($chunkData, 8);
617
                }
618 2
            }
619
620
            return [
621 2
                'type' => 'removed',
622 2
                'chunk_num' => $chunkNum,
623
                'prefixes' => $prefixes
624 2
            ];
625
        } else {
626 1
            throw new SafeBrowsingException(
627 1
                'ERROR: In chunkNum "' . $chunkNum . '" incorrect type "' . $type . '"'
628 1
            );
629
        }
630
    }
631
}
632