Completed
Push — master ( 9b1a28...1180e8 )
by Greg
01:29
created

HubphCommands::switchDefaultBranch()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 35

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 35
rs 9.36
c 0
b 0
f 0
cc 3
nc 3
nop 2
1
<?php
2
3
namespace Hubph\Cli;
4
5
use Consolidation\AnnotatedCommand\CommandData;
6
use Consolidation\Filter\FilterOutputData;
7
use Consolidation\Filter\LogicalOpFactory;
8
use Consolidation\OutputFormatters\Options\FormatterOptions;
9
use Consolidation\OutputFormatters\StructuredData\RowsOfFields;
10
use Consolidation\OutputFormatters\StructuredData\PropertyList;
11
use Psr\Log\LoggerAwareInterface;
12
use Psr\Log\LoggerAwareTrait;
13
use Robo\Common\ConfigAwareTrait;
14
use Robo\Contract\ConfigAwareInterface;
15
use Consolidation\AnnotatedCommand\CommandError;
16
use Hubph\HubphAPI;
17
use Hubph\VersionIdentifiers;
18
use Hubph\PullRequests;
19
20
class HubphCommands extends \Robo\Tasks implements ConfigAwareInterface, LoggerAwareInterface
21
{
22
    use ConfigAwareTrait;
23
    use LoggerAwareTrait;
24
25
    /**
26
     * Report who we have authenticated as
27
     *
28
     * @command whoami
29
     */
30
    public function whoami($options = ['as' => 'default'])
31
    {
32
        $api = $this->api($options['as']);
33
        $authenticated = $api->whoami();
34
        $authenticatedUser = $authenticated['login'];
35
36
        $this->say("Authenticated as $authenticatedUser.");
37
    }
38
39
    /**
40
     * @command pr:close
41
     */
42 View Code Duplication
    public function prClose($projectWithOrg = '', $number = '', $options = ['as' => 'default'])
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...
43
    {
44
        if (empty($number) && preg_match('#^[0-9]*$#', $projectWithOrg)) {
45
            $number = $projectWithOrg;
46
            $projectWithOrg = '';
47
        }
48
        $projectWithOrg = $this->projectWithOrg($projectWithOrg);
49
        list($org, $project) = explode('/', $projectWithOrg, 2);
50
51
        $api = $this->api($options['as']);
52
        $api->prClose($org, $project, $number);
0 ignored issues
show
Documentation introduced by
$number is of type string, but the function expects a object<Hubph\PullRequests>.

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...
53
    }
54
55
    /*
56
     * hubph pr:check --vid=php-7.0./31 --vid=php-7.1./20
57
     *
58
     * status 0 and csv with PR numbers to close
59
     *
60
     *  - or -
61
     *
62
     * status 1 if all vid/vvals exist and nothing more needs to be done
63
     */
64
65
    /**
66
     * @command pr:check
67
     */
68
    public function prCheck(
69
        $options = [
70
            'message|m' => '',
71
            'file|F' => '',
72
            'base' => '',
73
            'head' => '',
74
            'as' => 'default',
75
            'format' => 'yaml',
76
            'idempotent' => false
77
        ]
78
    ) {
79
        $projectWithOrg = $this->projectWithOrg();
80
81
        // Get the commit message from --message or --file
82
        $message = $this->getMessage($options);
83
84
        // Determine all of the vid/vval pairs if idempotent
85
        $vids = $this->getVids($options, $message);
86
87
        $api = $this->api($options['as']);
88
        list($status, $result) = $api->prCheck($projectWithOrg, $vids);
89
90
        if ($status) {
91
            return new CommandError($result, $status);
0 ignored issues
show
Documentation introduced by
$status is of type boolean, but the function expects a integer.

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...
92
        }
93
        if (is_string($result)) {
94
            $this->logger->notice("No open pull requests that need to be closed.");
95
            return;
96
        }
97
        return implode(',', (array)$result);
98
    }
99
100
    /**
101
     * @command pr:create
102
     * @aliases pull-request
103
     */
104
    public function prCreate(
105
        $options = [
106
            'message|m' => '',
107
            'body' => '',
108
            'file|F' => '',
109
            'base' => 'master',
110
            'head' => '',
111
            'as' => 'default',
112
            'format' => 'yaml',
113
            'idempotent' => false
114
        ]
115
    ) {
116
        $projectWithOrg = $this->projectWithOrg();
117
118
        // Get the commit message from --message or --file
119
        $message = $this->getMessage($options);
120
        list($org, $project) = explode('/', $projectWithOrg, 2);
121
122
        // Determine all of the vid/vval pairs if idempotent
123
        $vids = $this->getVids($options, $message);
124
125
        $api = $this->api($options['as']);
126
        list($status, $result) = $api->prCheck($projectWithOrg, $vids);
127
128
        if ($status) {
129
            return new CommandError($result, $status);
0 ignored issues
show
Documentation introduced by
$status is of type boolean, but the function expects a integer.

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...
130
        }
131
132
        // TODO: We could look up 'head' if it is not provided.
133
        if (empty($options['head'])) {
134
            throw new \Exceptions('Must provide --head');
135
        }
136
137
        // Create the requested PR
138
        $api->prCreate($org, $project, $message, $options['body'], $options['base'], $options['head']);
139
140
        // If $result is an array, it will contain
141
        // all of the pull request numbers to close.
142
        // TODO: We should make a wrapper object for $result
143
        if (is_array($result)) {
144
            list($org, $project) = explode('/', $projectWithOrg, 2);
145
            $api->prClose($org, $project, $result);
0 ignored issues
show
Documentation introduced by
$result is of type array, but the function expects a object<Hubph\PullRequests>.

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...
146
        }
147
    }
148
149
    protected function getMessage($options)
150
    {
151
        if (!empty($options['message'])) {
152
            return $options['message'];
153
        }
154
        if (!empty($options['file'])) {
155
            return file_get_contents($options['file']);
156
        }
157
        return '';
158
    }
159
160
    protected function getVids($options, $message)
161
    {
162
        $vids = new VersionIdentifiers();
163
164
        //if (empty($options['idempotent'])) {
165
        //    return $vids;
166
        //}
167
168
        // Allow the caller to define more specific vid / vval patterns
169
        if (!empty($options['vid'])) {
170
            $vids->setVidPattern($options['vid']);
171
        }
172
        if (!empty($options['vval'])) {
173
            $vids->setVvalPattern($options['vval']);
174
        }
175
176
        $vids->addVidsFromMessage($message);
177
        return $vids;
178
    }
179
180
    protected function projectWithOrg($projectWithOrg = '')
181
    {
182
        if (!empty($projectWithOrg)) {
183
            return $projectWithOrg;
184
        }
185
186
        return $this->getProjectWithOrgFromRemote();
187
    }
188
189
    protected function getProjectWithOrgFromRemote($remote = 'origin', $cwd = '')
190
    {
191
        $remote = $this->getRemote($remote, $cwd);
192
193
        return $this->getProjectWithOrfFromUrl($remote);
194
    }
195
196 View Code Duplication
    protected function getProjectWithOrfFromUrl($remote)
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...
197
    {
198
        $remote = preg_replace('#^git@[^:]*:#', '', $remote);
199
        $remote = preg_replace('#^[^:]*://[^/]/#', '', $remote);
200
        $remote = preg_replace('#\.git$#', '', $remote);
201
202
        return $remote;
203
    }
204
205
    protected function getRemote($remote = 'origin', $cwd = '')
206
    {
207
        if (!empty($cwd)) {
208
            $cwd = "-C $cwd";
209
        }
210
        return exec("git {$cwd} config --get remote.{$remote}.url");
211
    }
212
213
    /**
214
     * @command pr:find
215
     * @param $projectWithOrg The project to work on, e.g. org/project
216
     * @option $q Query term
217
     * @filter-output
218
     * @field-labels
219
     *   url: Url
220
     *   id: ID
221
     *   node_id: Node ID
222
     *   html_url: HTML Url
223
     *   diff_url: Diff Url
224
     *   patch_url: Patch Url
225
     *   issue_url: Issue Url
226
     *   number: Number
227
     *   state: State
228
     *   locked: Locked
229
     *   title: Title
230
     *   user: User
231
     *   body: Boday
232
     *   created_at: Created
233
     *   updated_at: Updated
234
     *   closed_at: Closed
235
     *   merged_at: Merged
236
     *   merge_commit_sha: Merge Commit
237
     *   assignee: Assignee
238
     *   assignees: Assignees
239
     *   requested_reviewers: Requested Reviewers
240
     *   requested_teams: Requested Teams
241
     *   labels: Labels
242
     *   milestone: Milestone
243
     *   commits_url: Commit Url
244
     *   review_comments_url: Review Comments Url
245
     *   review_comment_url: Review Comment Url
246
     *   comments_url: Comments Url
247
     *   statuses_url: Statuses Url
248
     *   head: Head
249
     *   base: Base
250
     *   _links: Links
251
     * @default-fields number,user,title
252
     * @default-string-field number
253
     * @return Consolidation\OutputFormatters\StructuredData\RowsOfFields
254
    */
255
    public function prFind($projectWithOrg = '', $options = ['as' => 'default', 'format' => 'yaml', 'q' => ''])
256
    {
257
        $api = $this->api($options['as']);
258
        $projectWithOrg = $this->projectWithOrg($projectWithOrg);
259
        $q = $options['q'];
260
261
        if (!empty($q)) {
262
            $q = $q . ' ';
263
        }
264
        $q = $q . 'repo:' . $projectWithOrg;
265
        $searchResults = $api->gitHubAPI()->api('search')->issues($q);
266
        $pullRequests = $searchResults['items'];
267
268
        $pullRequests = $this->keyById($pullRequests, 'number');
269
        $result = new RowsOfFields($pullRequests);
270
        $this->addTableRenderFunction($result);
271
272
        return $result;
273
    }
274
275
    /**
276
     * @command org:repos
277
     * @param $org The org to list
278
     * @filter-output
279
     * @field-labels
280
     *   url: Url
281
     *   id: ID
282
     *   owner: Owner
283
     *   name: Shortname
284
     *   full_name: Name
285
     *   private: Private
286
     *   fork: Fork
287
     *   created_at: Created
288
     *   updated_at: Updated
289
     *   pushed_at: Pushed
290
     *   git_url: Git URL
291
     *   ssh_url: SSH URL
292
     *   svn_url: SVN URL
293
     *   homepage: Homepage
294
     *   size: Size
295
     *   stargazers_count: Stargazers
296
     *   watchers_count: Watchers
297
     *   language: Language
298
     *   has_issues: Has Issues
299
     *   has_projects: Has Projects
300
     *   has_downloads: Has Downloads
301
     *   has_wiki: Has Wiki
302
     *   has_pages: Has Pages
303
     *   forks_count: Forks
304
     *   archived: Archived
305
     *   disabled: Disabled
306
     *   open_issues_count: Open Issues
307
     *   default_branch: Default Branch
308
     *   license: License
309
     *   permissions: Permissions
310
     * @default-fields full_name,language,default_branch
311
     * @default-string-field full_name
312
     *
313
     * @return Consolidation\OutputFormatters\StructuredData\RowsOfFields
314
     */
315
    public function orgRepos($org, $options = ['as' => 'default', 'format' => 'table'])
316
    {
317
        $api = $this->api($options['as']);
318
        $pager = $api->resultPager();
319
320
        $repoApi = $api->gitHubAPI()->api('organization');
321
        $repos = $pager->fetchAll($repoApi, 'repositories', [$org]);
322
323
        $data = new \Consolidation\OutputFormatters\StructuredData\RowsOfFields($repos);
324
        $this->addTableRenderFunction($data);
325
326
        return $data;
327
    }
328
329
    /**
330
     * @command repo:info
331
     * @param $projectWithOrg The project to work on, e.g. org/project
332
     * @field-labels
333
     *   url: Url
334
     *   id: ID
335
     *   owner: Owner
336
     *   name: Shortname
337
     *   full_name: Name
338
     *   private: Private
339
     *   fork: Fork
340
     *   created_at: Created
341
     *   updated_at: Updated
342
     *   pushed_at: Pushed
343
     *   git_url: Git URL
344
     *   ssh_url: SSH URL
345
     *   svn_url: SVN URL
346
     *   homepage: Homepage
347
     *   size: Size
348
     *   stargazers_count: Stargazers
349
     *   watchers_count: Watchers
350
     *   language: Language
351
     *   has_issues: Has Issues
352
     *   has_projects: Has Projects
353
     *   has_downloads: Has Downloads
354
     *   has_wiki: Has Wiki
355
     *   has_pages: Has Pages
356
     *   forks_count: Forks
357
     *   archived: Archived
358
     *   disabled: Disabled
359
     *   open_issues_count: Open Issues
360
     *   default_branch: Default Branch
361
     *   license: License
362
     *   permissions: Permissions
363
     *
364
     * @return Consolidation\OutputFormatters\StructuredData\PropertyList
365
     */
366
    public function repoInfo($projectWithOrg = '', $options = ['as' => 'default', 'format' => 'table'])
367
    {
368
        $api = $this->api($options['as']);
369
370
        $projectWithOrg = $this->projectWithOrg($projectWithOrg);
371
        list($org, $project) = explode('/', $projectWithOrg, 2);
372
373
        $info = $api->gitHubAPI()->api('repo')->show($org, $project);
374
375
        $data = new \Consolidation\OutputFormatters\StructuredData\PropertyList($info);
376
        $this->addTableRenderFunction($data);
377
378
        return $data;
379
    }
380
381
    /**
382
     * @command repo:default-branch:switch
383
     * @aliases switch-default
384
     */
385
    public function switchDefaultBranch($projectWithOrg = '', $options = ['as' => 'default', 'branch' => 'main'])
386
    {
387
        $api = $this->api($options['as']);
388
389
        $projectWithOrg = $this->projectWithOrg($projectWithOrg);
390
        list($org, $project) = explode('/', $projectWithOrg, 2);
391
392
        $repoApi = $api->gitHubAPI()->api('repo');
393
        $info = $repoApi->show($org, $project);
394
        $currentDefault = $info['default_branch'];
395
        $newDefault = $options['branch'];
396
397
        if ($currentDefault == $newDefault) {
398
            $this->logger->notice("Default branch is already {default}.", ['default' => $currentDefault]);
399
            return;
400
        }
401
402
        $referencesApi = $api->gitHubAPI()->api('gitData')->references();
403
404
        // Get the sha of the current HEAD of the current default
405
        $currentDefaultInfo = $referencesApi->show($org, $project, "heads/$currentDefault");
406
        $currentHeadSha = $currentDefaultInfo['object']['sha'];
407
408
        // Create a new branch for the new default. If it's already there,
409
        // then we'll assume it's at the desired SHA.
410
        try {
411
            $referencesApi->create($org, $project, ['ref' => "refs/heads/$newDefault", 'sha' => $currentHeadSha]);
412
        }
413
        catch (\Exception $e) {
414
            $this->logger->notice("Branch {new} already exists; using it as-is.", ['new' => $newDefault]);
415
        }
416
417
        $result = $repoApi->update($org, $project, ['default_branch' => $newDefault]);
0 ignored issues
show
Unused Code introduced by
$result 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...
418
        $this->logger->notice("Set default branch to {new}.", ['new' => $newDefault]);
419
    }
420
421
    /**
422
     * @command pr:show
423
     * @field-labels
424
     *   url: Url
425
     *   id: ID
426
     *   node_id: Node ID
427
     *   html_url: HTML Url
428
     *   diff_url: Diff Url
429
     *   patch_url: Patch Url
430
     *   issue_url: Issue Url
431
     *   number: Number
432
     *   state: State
433
     *   locked: Locked
434
     *   title: Title
435
     *   user: User
436
     *   body: Boday
437
     *   created_at: Created
438
     *   updated_at: Updated
439
     *   closed_at: Closed
440
     *   mergeable: Mergeable
441
     *   mergeable_state: Mergable State
442
     *   merged_at: Merged
443
     *   merge_commit_sha: Merge Commit
444
     *   assignee: Assignee
445
     *   assignees: Assignees
446
     *   requested_reviewers: Requested Reviewers
447
     *   requested_teams: Requested Teams
448
     *   labels: Labels
449
     *   milestone: Milestone
450
     *   commits_url: Commit Url
451
     *   review_comments_url: Review Comments Url
452
     *   review_comment_url: Review Comment Url
453
     *   comments_url: Comments Url
454
     *   statuses_url: Statuses Url
455
     *   head: Head
456
     *   base: Base
457
     *   _links: Links
458
     * @return Consolidation\OutputFormatters\StructuredData\PropertyList
459
     */
460
    public function prShow($projectWithOrg = '', $number = '', $options = ['as' => 'default', 'format' => 'table'])
461
    {
462
        if (empty($number) && preg_match('#^[0-9]*$#', $projectWithOrg)) {
463
            $number = $projectWithOrg;
464
            $projectWithOrg = '';
465
        }
466
        $api = $this->api($options['as']);
467
        $projectWithOrg = $this->projectWithOrg($projectWithOrg);
468
469
        list($org, $project) = explode('/', $projectWithOrg, 2);
470
471
        $pullRequest = $api->gitHubAPI()->api('pull_request')->show($org, $project, $number);
472
473
        $result = new PropertyList($pullRequest);
474
        $this->addTableRenderFunction($result);
475
476
        return $result;
477
    }
478
479
    /**
480
     * @command pr:statuses
481
     * @field-labels
482
     *   url: Url
483
     *   id: ID
484
     *   state: State
485
     *   description: Description
486
     *   node_id: Node ID
487
     *   context: Context
488
     *   avatar_url: Avatar URL
489
     *   target_url: Target URL
490
     *   creator: Creator
491
     *   created_at: Created
492
     *   updated_at: Updated
493
     * @default-fields id,creator,state,description
494
     * @default-string-field description
495
     * @return Consolidation\OutputFormatters\StructuredData\RowsOfFields
496
     */
497 View Code Duplication
    public function prStatuses($projectWithOrg = '', $number = '', $options = ['as' => 'default', 'format' => 'yaml'])
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...
498
    {
499
        if (empty($number) && preg_match('#^[0-9]*$#', $projectWithOrg)) {
500
            $number = $projectWithOrg;
501
            $projectWithOrg = '';
502
        }
503
        $api = $this->api($options['as']);
504
        $projectWithOrg = $this->projectWithOrg($projectWithOrg);
505
506
        $pullRequestStatus = $api->prStatuses($projectWithOrg, $number);
507
508
        $result = new RowsOfFields($pullRequestStatus);
509
        $this->addTableRenderFunction($result);
510
511
        return $result;
512
    }
513
514
    /**
515
     * @command pr:list
516
     * @param $projectWithOrg The project to work on, e.g. org/project
517
     * @filter-output
518
     * @field-labels
519
     *   url: Url
520
     *   id: ID
521
     *   node_id: Node ID
522
     *   html_url: HTML Url
523
     *   diff_url: Diff Url
524
     *   patch_url: Patch Url
525
     *   issue_url: Issue Url
526
     *   number: Number
527
     *   state: State
528
     *   locked: Locked
529
     *   title: Title
530
     *   user: User
531
     *   body: Boday
532
     *   created_at: Created
533
     *   updated_at: Updated
534
     *   closed_at: Closed
535
     *   merged_at: Merged
536
     *   merge_commit_sha: Merge Commit
537
     *   assignee: Assignee
538
     *   assignees: Assignees
539
     *   requested_reviewers: Requested Reviewers
540
     *   requested_teams: Requested Teams
541
     *   labels: Labels
542
     *   milestone: Milestone
543
     *   commits_url: Commit Url
544
     *   review_comments_url: Review Comments Url
545
     *   review_comment_url: Review Comment Url
546
     *   comments_url: Comments Url
547
     *   statuses_url: Statuses Url
548
     *   head: Head
549
     *   base: Base
550
     *   _links: Links
551
     * @default-fields number,user,title
552
     * @default-string-field number
553
     * @return Consolidation\OutputFormatters\StructuredData\RowsOfFields
554
     */
555
    public function prList($projectWithOrg = '', $options = ['state' => 'open', 'as' => 'default', 'format' => 'table'])
556
    {
557
        $api = $this->api($options['as']);
558
        $projectWithOrg = $this->projectWithOrg($projectWithOrg);
559
560
        list($org, $project) = explode('/', $projectWithOrg, 2);
561
562
        $pullRequests = $api->gitHubAPI()->api('pull_request')->all($org, $project, ['state' => $options['state']]);
563
564
        $pullRequests = $this->keyById($pullRequests, 'number');
565
566
        $result = new RowsOfFields($pullRequests);
567
        $this->addTableRenderFunction($result);
568
569
        return $result;
570
    }
571
572
    protected function addTableRenderFunction($data)
573
    {
574
        $data->addRendererFunction(
575
            function ($key, $cellData, FormatterOptions $options, $rowData) {
0 ignored issues
show
Unused Code introduced by
The parameter $options is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $rowData is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
576
                if (empty($cellData)) {
577
                    return '';
578
                }
579
                if (is_array($cellData)) {
580
                    if ($key == 'permissions') {
581
                        return implode(',', array_filter(array_keys($cellData)));
582
                    }
583
                    foreach (['login', 'label', 'name'] as $k) {
584
                        if (isset($cellData[$k])) {
585
                            return $cellData[$k];
586
                        }
587
                    }
588
                    // TODO: simplify
589
                    //   assignees
590
                    //   requested_reviewers
591
                    //   requested_teams
592
                    //   labels
593
                    //   _links
594
                    return json_encode($cellData, true);
595
                }
596
                if (!is_string($cellData)) {
597
                    return var_export($cellData, true);
598
                }
599
                return $cellData;
600
            }
601
        );
602
    }
603
604
    protected function keyById($data, $field)
605
    {
606
        return
607
            array_column(
608
                array_map(
609
                    function ($k) use ($data, $field) {
610
                        return [$data[$k][$field], $data[$k]];
611
                    },
612
                    array_keys($data)
613
                ),
614
                1,
615
                0
616
            );
617
    }
618
619
    protected function api($as = 'default')
620
    {
621
        $api = new HubphAPI($this->getConfig());
622
        $api->setAs($as);
623
624
        return $api;
625
    }
626
}
627