Completed
Push — master ( 298c5c...85ea16 )
by Sam
10s
created

ApiHelper::displayTitles()   B

Complexity

Conditions 6
Paths 11

Size

Total Lines 38
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 38
c 0
b 0
f 0
rs 8.439
cc 6
eloc 25
nc 11
nop 2
1
<?php
2
3
namespace AppBundle\Helper;
4
5
use DateInterval;
6
use Mediawiki\Api\MediawikiApi;
7
use Mediawiki\Api\SimpleRequest;
8
use Mediawiki\Api\FluentRequest;
9
use Psr\Cache\CacheItemPoolInterface;
10
use Symfony\Component\Config\Definition\Exception\Exception;
11
use Symfony\Component\Debug\Exception\FatalErrorException;
12
use Symfony\Component\DependencyInjection\ContainerInterface;
13
14
class ApiHelper extends HelperBase
15
{
16
    /** @var MediawikiApi */
17
    private $api;
18
19
    /** @var LabsHelper */
20
    private $labsHelper;
21
22
    /** @var CacheItemPoolInterface */
23
    protected $cache;
24
25
    /** @var ContainerInterface */
26
    protected $container;
27
28
    public function __construct(ContainerInterface $container, LabsHelper $labsHelper)
29
    {
30
        $this->container = $container;
31
        $this->labsHelper = $labsHelper;
32
        $this->cache = $container->get('cache.app');
33
    }
34
35
    private function setUp($project)
36
    {
37
        if (!isset($this->api)) {
38
            $normalizedProject = $this->labsHelper->normalizeProject($project);
39
            $apiPath = $this->container->getParameter('api_path');
40
41
            try {
42
                $this->api = MediawikiApi::newFromApiEndpoint($normalizedProject . $apiPath);
43
            } catch (Exception $e) {
44
                // Do nothing...
45
            } catch (FatalErrorException $e) {
46
                // Do nothing...
47
            }
48
        }
49
    }
50
51
    /**
52
     * Get general siteinfo and namespaces for a project and cache it.
53
     * @param  string [$project] Base project domain with or without protocal, or database name
54
     *                           such as 'en.wikipedia.org', 'https://en.wikipedia.org' or 'enwiki'
55
     *                           Can be left blank for single wikis.
56
     * @return string[] with keys 'general' and 'namespaces'. General info will include 'dbName',
57
     *                           'wikiName', 'url', 'lang', 'articlePath', 'scriptPath',
58
     *                           'script', 'timezone', and 'timeOffset'
59
     */
60
    public function getSiteInfo($project = '')
61
    {
62
        if ($this->container->getParameter('app.single_wiki')) {
63
            $project = $this->container->getParameter('wiki_url');
64
        }
65
        $normalizedProject = $this->labsHelper->normalizeProject($project);
66
67
        if (!$normalizedProject) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $normalizedProject of type string|false is loosely compared to false; 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...
68
            throw new Exception("Unable to find project '$project'");
69
        }
70
71
        $cacheKey = "siteinfo." . str_replace('/', '_', $normalizedProject);
72
        if ($this->cacheHas($cacheKey)) {
73
            return $this->cacheGet($cacheKey);
74
        }
75
76
        $this->setUp($normalizedProject);
77
        $params = [ 'meta'=>'siteinfo', 'siprop'=>'general|namespaces' ];
78
        $query = new SimpleRequest('query', $params);
79
80
        $result = [
81
            'general' => [],
82
            'namespaces' => []
83
        ];
84
85
        try {
86
            $res = $this->api->getRequest($query);
87
88
            if (isset($res['query']['general'])) {
89
                $info = $res['query']['general'];
90
                $result['general'] = [
91
                    'wikiName' => $info['sitename'],
92
                    'dbName' => $info['wikiid'],
93
                    'url' => $info['server'],
94
                    'lang' => $info['lang'],
95
                    'articlePath' => $info['articlepath'],
96
                    'scriptPath' => $info['scriptpath'],
97
                    'script' => $info['script'],
98
                    'timezone' => $info['timezone'],
99
                    'timeOffset' => $info['timeoffset'],
100
                ];
101
102
                if ($this->container->getParameter('app.is_labs') && substr($result['general']['dbName'], -2) != '_p') {
103
                    $result['general']['dbName'] .= '_p';
104
                }
105
            }
106
107
            if (isset($res['query']['namespaces'])) {
108
                foreach ($res['query']['namespaces'] as $namespace) {
109
                    if ($namespace['id'] < 0) {
110
                        continue;
111
                    }
112
113
                    if (isset($namespace['name'])) {
114
                        $name = $namespace['name'];
115
                    } elseif (isset($namespace['*'])) {
116
                        $name = $namespace['*'];
117
                    } else {
118
                        continue;
119
                    }
120
121
                    // FIXME: Figure out a way to i18n-ize this
122
                    if ($name === '') {
123
                        $name = 'Article';
124
                    }
125
126
                    $result['namespaces'][$namespace['id']] = $name;
127
                }
128
            }
129
130
            $this->cacheSave($cacheKey, $result, 'P7D');
0 ignored issues
show
Documentation introduced by
$result is of type array<string,array>, but the function expects a string.

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...
131
        } catch (Exception $e) {
132
            // The api returned an error!  Ignore
133
        }
134
135
        return $result;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $result; (array<string,array>) is incompatible with the return type documented by AppBundle\Helper\ApiHelper::getSiteInfo of type string[].

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
136
    }
137
138 View Code Duplication
    public function groups($project, $username)
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...
139
    {
140
        $this->setUp($project);
141
        $params = [ "list"=>"users", "ususers"=>$username, "usprop"=>"groups" ];
142
        $query = new SimpleRequest('query', $params);
143
        $result = [];
144
145
        try {
146
            $res = $this->api->getRequest($query);
147
            if (isset($res["batchcomplete"]) && isset($res["query"]["users"][0]["groups"])) {
148
                $result = $res["query"]["users"][0]["groups"];
149
            }
150
        } catch (Exception $e) {
151
            // The api returned an error!  Ignore
152
        }
153
154
        return $result;
155
    }
156
157 View Code Duplication
    public function globalGroups($project, $username)
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...
158
    {
159
        $this->setUp($project);
160
        $params = [ "meta"=>"globaluserinfo", "guiuser"=>$username, "guiprop"=>"groups" ];
161
        $query = new SimpleRequest('query', $params);
162
        $result = [];
163
164
        try {
165
            $res = $this->api->getRequest($query);
166
            if (isset($res["batchcomplete"]) && isset($res["query"]["globaluserinfo"]["groups"])) {
167
                $result = $res["query"]["globaluserinfo"]["groups"];
168
            }
169
        } catch (Exception $e) {
170
            // The api returned an error!  Ignore
171
        }
172
173
        return $result;
174
    }
175
176
    /**
177
     * Get a list of namespaces on the given project.
178
     *
179
     * @param string    $project such as en.wikipedia.org
180
     * @return string[] Array of namespace IDs (keys) to names (values).
181
     */
182
    public function namespaces($project)
183
    {
184
        return $this->getSiteInfo($project)['namespaces'];
185
    }
186
187
    public function getAdmins($project)
188
    {
189
        $params = [
190
            'list' => 'allusers',
191
            'augroup' => 'sysop|bureaucrat|steward|oversight|checkuser',
192
            'auprop' => 'groups',
193
            'aulimit' => '500',
194
        ];
195
196
        $result = [];
197
        $admins = $this->massApi($params, $project, 'allusers', 'aufrom')['allusers'];
198
199
        foreach ($admins as $admin) {
200
            $groups = [];
201
            if (in_array("sysop", $admin["groups"])) {
202
                $groups[] = "A";
203
            }
204
            if (in_array("bureaucrat", $admin["groups"])) {
205
                $groups[] = "B";
206
            }
207
            if (in_array("steward", $admin["groups"])) {
208
                $groups[] = "S" ;
209
            }
210
            if (in_array("checkuser", $admin["groups"])) {
211
                $groups[] = "CU";
212
            }
213
            if (in_array("oversight", $admin["groups"])) {
214
                $groups[] = "OS";
215
            }
216
            if (in_array("bot", $admin["groups"])) {
217
                $groups[] = "Bot";
218
            }
219
            $result[ $admin["name"] ] = [
220
                "groups" => implode('/', $groups)
221
            ];
222
        }
223
224
        return $result;
225
    }
226
227
    /**
228
     * Get basic info about a page via the API
229
     * @param  string  $project      Full domain of project (en.wikipedia.org)
230
     * @param  string  $page         Page title
231
     * @param  boolean $followRedir  Whether or not to resolve redirects
232
     * @return array   Associative array of data
233
     */
234
    public function getBasicPageInfo($project, $page, $followRedir)
235
    {
236
        $this->setUp($project);
237
238
        // @TODO: Also include 'extlinks' prop when we start checking for dead external links.
239
        $params = [
240
            'prop' => 'info|pageprops',
241
            'inprop' => 'protection|talkid|watched|watchers|notificationtimestamp|subjectid|url|readable',
242
            'converttitles' => '',
243
            // 'ellimit' => 20,
1 ignored issue
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
244
            // 'elexpandurl' => '',
1 ignored issue
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
245
            'titles' => $page,
246
            'formatversion' => 2
247
            // 'pageids' => $pageIds // FIXME: allow page IDs
1 ignored issue
show
Unused Code Comprehensibility introduced by
43% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
248
        ];
249
250
        if ($followRedir) {
251
            $params['redirects'] = '';
252
        }
253
254
        $query = new SimpleRequest('query', $params);
255
        $result = [];
256
257
        try {
258
            $res = $this->api->getRequest($query);
259
            if (isset($res['query']['pages'])) {
260
                $result = $res['query']['pages'][0];
261
            }
262
        } catch (Exception $e) {
263
            // The api returned an error!  Ignore
264
        }
265
266
        return $result;
267
    }
268
269
    /**
270
     * Get HTML display titles of a set of pages (or the normal title if there's no display title).
271
     * This will send t/50 API requests where t is the number of titles supplied.
272
     * @param string $project The project.
273
     * @param string[] $pageTitles The titles to fetch.
274
     * @return string[] Keys are the original supplied title, and values are the display titles.
275
     */
276
    public function displayTitles($project, $pageTitles)
277
    {
278
        $this->setUp($project);
279
        $displayTitles = [];
280
        for ($n = 0; $n < count($pageTitles); $n += 50) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
281
            $titleSlice = array_slice($pageTitles, $n, 50);
282
            $params = [
283
                'prop' => 'info|pageprops',
284
                'inprop' => 'displaytitle',
285
                'titles' => join('|', $titleSlice),
286
            ];
287
            $query = new SimpleRequest('query', $params);
288
            $result = $this->api->getRequest($query);
289
290
            // Extract normalization info.
291
            $normalized = [];
292
            if (isset($result['query']['normalized'])) {
293
                array_map(
294
                    function ($e) use (&$normalized) {
295
                        $normalized[$e['to']] = $e['from'];
296
                    },
297
                    $result['query']['normalized']
298
                );
299
            }
300
301
            // Match up the normalized titles with the display titles and the original titles.
302
            foreach ($result['query']['pages'] as $pageInfo) {
303
                $displayTitle = isset($pageInfo['pageprops']['displaytitle'])
304
                    ? $pageInfo['pageprops']['displaytitle']
305
                    : $pageInfo['title'];
306
                $origTitle = isset($normalized[$pageInfo['title']])
307
                    ? $normalized[$pageInfo['title']] : $pageInfo['title'];
308
                $displayTitles[$origTitle] = $displayTitle;
309
            }
310
        }
311
312
        return $displayTitles;
313
    }
314
315
    /**
316
     * Get assessments of the given pages, if a supported project
317
     * @param  string       $project    Project such as en.wikipedia.org
318
     * @param  string|array $pageTitles Single page title or array of titles
319
     * @return array|null               Page assessments info or null if none found
320
     */
321
    public function getPageAssessments($project, $pageTitles)
322
    {
323
        // From config/assessments.yml
324
        $config = $this->getAssessmentsConfig();
325
326
        // return null if unsupported project
327
        if (!in_array($project, array_keys($config))) {
328
            return null;
329
        }
330
331
        $config = $config[$project];
332
333
        $params = [
334
            'prop' => 'pageassessments',
335
            'titles' => is_string($pageTitles) ? $pageTitles : implode('|', $pageTitles),
336
            'palimit' => 500,
337
        ];
338
339
        // get assessments for this page from the API
340
        $assessments = $this->massApi($params, $project, function ($data) {
0 ignored issues
show
Documentation introduced by
function ($data) { r...essments'] : array(); } is of type object<Closure>, but the function expects a string|object<AppBundle\Helper\func>.

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...
341
            return isset($data['pages'][0]['pageassessments']) ? $data['pages'][0]['pageassessments'] : [];
342
        }, 'pacontinue')['pages'];
343
344
        $decoratedAssessments = [];
345
346
        // Set the default decorations for the overall quality assessment
347
        // This will be replaced with the first valid class defined for any WikiProject
348
        $overallQuality = $config['class']['Unknown'];
349
        $overallQuality['value'] = '???';
350
351
        if (empty($assessments)) {
352
            return null;
353
        }
354
355
        // loop through each assessment and decorate with colors, category URLs and images, if applicable
356
        foreach ($assessments as $wikiproject => $assessment) {
357
            $classValue = $assessment['class'];
358
359
            // Use ??? as the presented value when the class is unknown or is not defined in the config
360
            if ($classValue === 'Unknown' || $classValue === '' || !isset($config['class'][$classValue])) {
361
                $classAttrs = $config['class']['Unknown'];
362
                $assessment['class']['value'] = '???';
363
                $assessment['class']['category'] = $classAttrs['category'];
364
                $assessment['class']['badge'] = "https://upload.wikimedia.org/wikipedia/commons/". $classAttrs['badge'];
365
            } else {
366
                $classAttrs = $config['class'][$classValue];
367
                $assessment['class'] = [
368
                    'value' => $classValue,
369
                    'color' => $classAttrs['color'],
370
                    'category' => $classAttrs['category'],
371
                ];
372
373
                // add full URL to badge icon
374
                if ($classAttrs['badge'] !== '') {
375
                    $assessment['class']['badge'] = "https://upload.wikimedia.org/wikipedia/commons/" .
376
                        $classAttrs['badge'];
377
                }
378
379
                if ($overallQuality['value'] === '???') {
380
                    $overallQuality = $assessment['class'];
381
                    $overallQuality['category'] = $classAttrs['category'];
382
                }
383
            }
384
385
            $importanceValue = $assessment['importance'];
386
            $importanceUnknown = $importanceValue === 'Unknown' || $importanceValue === '';
387
388
            if ($importanceUnknown || !isset($config['importance'][$importanceValue])) {
389
                $importanceAttrs = $config['importance']['Unknown'];
390
                $assessment['importance'] = $importanceAttrs;
391
                $assessment['importance']['value'] = '???';
392
                $assessment['importance']['category'] = $importanceAttrs['category'];
393
            } else {
394
                $importanceAttrs = $config['importance'][$importanceValue];
395
                $assessment['importance'] = [
396
                    'value' => $importanceValue,
397
                    'color' => $importanceAttrs['color'],
398
                    'weight' => $importanceAttrs['weight'], // numerical weight for sorting purposes
399
                    'category' => $importanceAttrs['category'],
400
                ];
401
            }
402
403
            $decoratedAssessments[$wikiproject] = $assessment;
404
        }
405
406
        return [
407
            'assessment' => $overallQuality,
408
            'wikiprojects' => $decoratedAssessments,
409
            'wikiproject_prefix' => $config['wikiproject_prefix']
410
        ];
411
    }
412
413
    /**
414
     * Get the image URL of the badge for the given page assessment
415
     * @param  string $project Project such as en.wikipedia.org
416
     * @param  string $class   Valid classification for project, such as 'Start', 'GA', etc.
417
     * @return string          URL to image
418
     */
419
    public function getAssessmentBadgeURL($project, $class)
420
    {
421
        $config = $this->getAssessmentsConfig();
422
423
        if (isset($config[$project]['class'][$class])) {
424
            return "https://upload.wikimedia.org/wikipedia/commons/" . $config[$project]['class'][$class]['badge'];
425
        } elseif (isset($config[$project]['class']['Unknown'])) {
426
            return "https://upload.wikimedia.org/wikipedia/commons/" . $config[$project]['class']['Unknown']['badge'];
427
        } else {
428
            return "";
429
        }
430
    }
431
432
    /**
433
     * Fetch assessments data from config/assessments.yml and cache in static variable
434
     * @return array Mappings of project/quality/class with badges, colors and category links
435
     */
436
    private function getAssessmentsConfig()
437
    {
438
        static $assessmentsConfig = null;
439
        if ($assessmentsConfig === null) {
440
            $assessmentsConfig = $this->container->getParameter('assessments');
441
        }
442
        return $assessmentsConfig;
443
    }
444
445
    /**
446
     * Does the given project support page assessments?
447
     * @param  string  $project Project to query, e.g. en.wikipedia.org
448
     * @return boolean True or false
449
     */
450
    public function projectHasPageAssessments($project)
451
    {
452
        return in_array($project, array_keys($this->getAssessmentsConfig()));
453
    }
454
455
    /**
456
     * Make mass API requests to MediaWiki API
457
     * The API normally limits to 500 pages, but gives you a 'continue' value
458
     *   to finish iterating through the resource.
459
     * Adapted from https://github.com/MusikAnimal/pageviews
460
     * @param  array       $params        Associative array of params to pass to API
461
     * @param  string      $project       Project to query, e.g. en.wikipedia.org
462
     * @param  string|func $dataKey       The key for the main chunk of data, in the query hash
463
     *                                    (e.g. 'categorymembers' for API:Categorymembers).
464
     *                                    If this is a function it is given the response data,
465
     *                                    and expected to return the data we want to concatentate.
466
     * @param  string      [$continueKey] the key to look in the continue hash, if present
467
     *                                    (e.g. 'cmcontinue' for API:Categorymembers)
468
     * @param  integer     [$limit]       Max number of pages to fetch
469
     * @return array                      Associative array with data
470
     */
471
    public function massApi($params, $project, $dataKey, $continueKey = 'continue', $limit = 5000)
472
    {
473
        $this->setUp($project);
474
475
        // Passed by reference to massApiInternal so we can keep track of
476
        //   everything we need during the recursive calls
477
        // The magically essential part here is $data['promise'] which we'll
478
        //   wait to be resolved
479
        $data = [
480
            'params' => $params,
481
            'project' => $project,
482
            'continueKey' => $continueKey,
483
            'dataKey' => $dataKey,
484
            'limit' => $limit,
485
            'resolveData' => [
486
                'pages' => []
487
            ],
488
            'continueValue' => null,
489
            'promise' => new \GuzzleHttp\Promise\Promise(),
490
        ];
491
492
        // wait for all promises to complete, even if some of them fail
493
        \GuzzleHttp\Promise\settle($this->massApiInternal($data))->wait();
494
495
        return $data['resolveData'];
496
    }
497
498
    /**
499
     * Internal function used by massApi() to make recursive calls
500
     * @param  array &$data Everything we need to keep track of, as defined in massApi()
501
     * @return null         Nothing. $data['promise']->then is used to continue flow of
502
     *                      execution after all recursive calls are complete
503
     */
504
    private function massApiInternal(&$data)
505
    {
506
        $requestData = array_merge([
507
            'action' => 'query',
508
            'format' => 'json',
509
            'formatversion' => '2',
510
        ], $data['params']);
511
512
        if ($data['continueValue']) {
513
            $requestData[$data['continueKey']] = $data['continueValue'];
514
        }
515
516
        $query = FluentRequest::factory()->setAction('query')->setParams($requestData);
517
        $innerPromise = $this->api->getRequestAsync($query);
518
519
        $innerPromise->then(function ($result) use (&$data) {
520
            // some failures come back as 200s, so we still resolve and let the outer function handle it
521
            if (isset($result['error']) || !isset($result['query'])) {
522
                return $data['promise']->resolve($data);
523
            }
524
525
            $dataKey = $data['dataKey'];
526
            $isFinished = false;
0 ignored issues
show
Unused Code introduced by
$isFinished is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
527
528
            // allow custom function to parse the data we want, if provided
529
            if (is_callable($dataKey)) {
530
                $data['resolveData']['pages'] = array_merge(
531
                    $data['resolveData']['pages'],
532
                    $data['dataKey']($result['query'])
533
                );
534
                $isFinished = count($data['resolveData']['pages']) >= $data['limit'];
535
            } else {
536
                // append new data to data from last request. We might want both 'pages' and dataKey
537
                if (isset($result['query']['pages'])) {
538
                    $data['resolveData']['pages'] = array_merge(
539
                        $data['resolveData']['pages'],
540
                        $result['query']['pages']
541
                    );
542
                }
543
                if ($result['query'][$dataKey]) {
544
                    $newValues = isset($data['resolveData'][$dataKey]) ? $data['resolveData'][$dataKey] : [];
545
                    $data['resolveData'][$dataKey] = array_merge($newValues, $result['query'][$dataKey]);
546
                }
547
548
                // If pages is not the collection we want, it will be either an empty array or one entry with
549
                //   basic page info depending on what API we're hitting. So resolveData[dataKey] will hit the limit
550
                $isFinished = count($data['resolveData']['pages']) >= $data['limit'] ||
551
                    count($data['resolveData'][$dataKey]) >= $data['limit'];
552
            }
553
554
            // make recursive call if needed, waiting 100ms
555
            if (!$isFinished && isset($result['continue']) && isset($result['continue'][$data['continueKey']])) {
556
                usleep(100000);
557
                $data['continueValue'] = $result['continue'][$data['continueKey']];
558
                return $this->massApiInternal($data);
559
            } else {
560
                // indicate there were more entries than the limit
561
                if ($result['continue']) {
562
                    $data['resolveData']['continue'] = true;
563
                }
564
                $data['promise']->resolve($data);
565
            }
566
        });
567
    }
568
}
569