GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

GitActions::fetchGitConfig()   B
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 24
Code Lines 18

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 24
rs 8.9713
cc 3
eloc 18
nc 3
nop 3
1
<?php
2
/**
3
  * Copyright (c) Xerox Corporation, Codendi Team, 2001-2009. All rights reserved
4
  * Copyright (c) Enalean, 2011 - 2014. All Rights Reserved.
5
  *
6
  * This file is a part of Tuleap.
7
  *
8
  * Codendi is free software; you can redistribute it and/or modify
9
  * it under the terms of the GNU General Public License as published by
10
  * the Free Software Foundation; either version 2 of the License, or
11
  * (at your option) any later version.
12
  *
13
  * Codendi is distributed in the hope that it will be useful,
14
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
  * GNU General Public License for more details.
17
  *
18
  * You should have received a copy of the GNU General Public License
19
  * along with Codendi. If not, see <http://www.gnu.org/licenses/
20
  */
21
22
require_once('common/layout/Layout.class.php');
23
24
/**
25
 * GitActions
26
 * @todo call Event class instead of SystemEvent
27
 * @author Guillaume Storchi
28
 */
29
class GitActions extends PluginActions {
30
31
    /**
32
     * @var Git_Backend_Gitolite
33
     */
34
    private $backend_gitolite;
35
36
    /**
37
     * @var Logger
38
     */
39
    private $logger;
40
41
    /**
42
     * @var Git_SystemEventManager
43
     */
44
    protected $git_system_event_manager;
45
46
    /**
47
     * @var GitRepositoryFactory
48
     */
49
    private $factory;
50
51
    /**
52
     * @var GitRepositoryManager
53
     */
54
    private $manager;
55
56
    /**
57
     * @var Git_RemoteServer_GerritServerFactory
58
     */
59
    private $gerrit_server_factory;
60
61
    /** @var Git_Driver_Gerrit_GerritDriverFactory */
62
    private $driver_factory;
63
64
    /** @var Git_Driver_Gerrit_UserAccountManager */
65
    private $gerrit_usermanager;
66
67
    /** @var Git_Driver_Gerrit_ProjectCreator */
68
    private $project_creator;
69
70
    /** @var Git_Driver_Gerrit_Template_TemplateFactory */
71
    private $template_factory;
72
73
    /** @var ProjectManager */
74
    private $project_manager;
75
76
    /** @var GitPermissionsManager */
77
    private $git_permissions_manager;
78
79
    /** @var Git_GitRepositoryUrlManager */
80
    private $url_manager;
81
82
    /** @var Git_Mirror_MirrorDataMapper */
83
    private $mirror_data_mapper;
84
85
    /** @var ProjectHistoryDao*/
86
    private $history_dao;
87
88
    /**
89
     *
90
     * @param Git $controller
91
     * @param Git_SystemEventManager                     $system_event_manager
92
     * @param GitRepositoryFactory                       $factory
93
     * @param GitRepositoryManager                       $manager
94
     * @param Git_RemoteServer_GerritServerFactory       $gerrit_server_factory
95
     * @param Git_Driver_Gerrit_GerritDriverFactory      $driver_factory
96
     * @param Git_Driver_Gerrit_UserAccountManager       $gerrit_usermanager
97
     * @param Git_Driver_Gerrit_ProjectCreator           $project_creator
98
     * @param Git_Driver_Gerrit_Template_TemplateFactory $template_factory
99
     * @param ProjectManager                             $project_manager
100
     * @param GitPermissionsManager                      $git_permissions_manager
101
     * @param ProjectHistoryDao                          $history_dao
102
     */
103
    public function __construct(
104
        Git                $controller,
105
        Git_SystemEventManager $system_event_manager,
106
        GitRepositoryFactory $factory,
107
        GitRepositoryManager $manager,
108
        Git_RemoteServer_GerritServerFactory $gerrit_server_factory,
109
        Git_Driver_Gerrit_GerritDriverFactory $driver_factory,
110
        Git_Driver_Gerrit_UserAccountManager $gerrit_usermanager,
111
        Git_Driver_Gerrit_ProjectCreator $project_creator,
112
        Git_Driver_Gerrit_Template_TemplateFactory $template_factory,
113
        ProjectManager $project_manager,
114
        GitPermissionsManager $git_permissions_manager,
115
        Git_GitRepositoryUrlManager $url_manager,
116
        Logger $logger,
117
        Git_Backend_Gitolite $backend_gitolite,
118
        Git_Mirror_MirrorDataMapper $mirror_data_mapper,
119
        ProjectHistoryDao $history_dao
120
    ) {
121
        parent::__construct($controller);
122
        $this->git_system_event_manager = $system_event_manager;
123
        $this->factory                  = $factory;
124
        $this->manager                  = $manager;
125
        $this->gerrit_server_factory    = $gerrit_server_factory;
126
        $this->driver_factory           = $driver_factory;
127
        $this->gerrit_usermanager       = $gerrit_usermanager;
128
        $this->project_creator          = $project_creator;
129
        $this->template_factory         = $template_factory;
130
        $this->project_manager          = $project_manager;
131
        $this->git_permissions_manager  = $git_permissions_manager;
132
        $this->url_manager              = $url_manager;
133
        $this->logger                   = $logger;
134
        $this->backend_gitolite         = $backend_gitolite;
135
        $this->mirror_data_mapper       = $mirror_data_mapper;
136
        $this->history_dao              = $history_dao;
137
    }
138
139
    protected function getText($key, $params = array()) {
140
        return $GLOBALS['Language']->getText('plugin_git', $key, $params);
141
    }
142
143
    public function process($action, $params) {
144
       return call_user_func_array(array($this,$action), $params);
145
    }
146
147
    public function deleteRepository( $projectId, $repositoryId ) {
148
        $controller   = $this->getController();
149
        $projectId    = intval($projectId);
150
        $repositoryId = intval($repositoryId);
151
        if ( empty($projectId) || empty($repositoryId) ) {
152
            $this->addError('actions_params_error');
153
            return false;
154
        }
155
156
        $repository = $this->factory->getRepositoryById($repositoryId);
157
        if ($repository) {
158
            if ($repository->canBeDeleted()) {
159
                $this->markAsDeleted($repository);
160
                $controller->addInfo($this->getText('actions_delete_process', array($repository->getFullName())));
161
                $controller->addInfo($this->getText('actions_delete_backup', array($repository->getFullName())).' : '.$controller->getPlugin()->getConfigurationParameter('git_backup_dir'));
162
            } else {
163
                $this->addError('backend_delete_haschild_error');
164
                $this->redirectToRepo($repository);
165
                return false;
166
            }
167
        } else {
168
            $this->addError('actions_repo_not_found');
169
        }
170
        $controller->redirect('/plugins/git/?action=index&group_id='.$projectId);
171
    }
172
173
    private function markAsDeleted(GitRepository $repository) {
174
        $repository->markAsDeleted();
175
        $this->git_system_event_manager->queueRepositoryDeletion($repository);
176
177
        $this->history_dao->groupAddHistory(
178
            "git_repo_delete",
179
            $repository->getName(),
180
            $repository->getProjectId()
0 ignored issues
show
Security Bug introduced by
It seems like $repository->getProjectId() targeting GitRepository::getProjectId() can also be of type false; however, ProjectHistoryDao::groupAddHistory() does only seem to accept integer, did you maybe forget to handle an error condition?
Loading history...
181
        );
182
    }
183
184
    public function createReference($projectId, $repositoryName) {
185
        $controller = $this->getController();
186
        $projectId  = intval( $projectId );
187
188
        try {
189
            $creator = UserManager::instance()->getCurrentUser();
190
            $project = ProjectManager::instance()->getProject($projectId);
191
            $repository = $this->factory->buildRepository($project, $repositoryName, $creator, $this->backend_gitolite);
192
193
            $this->manager->create($repository, $this->backend_gitolite);
194
195
            $this->history_dao->groupAddHistory(
196
                "git_repo_create",
197
                $repositoryName,
198
                $projectId
199
            );
200
201
            $this->redirectToRepo($repository);
202
        } catch (Exception $exception) {
203
            $controller->addError($exception->getMessage());
204
        }
205
206
        $controller->redirect('/plugins/git/?action=index&group_id='.$projectId);
207
        return;
208
    }
209
210
    /**
211
     * Action to load the user's repositories of a project. If user is not given, then load the project repositories instead.
212
     *
213
     * @param int $projectId The project id
214
     * @param int $userId    The user id. (== null for project repositories)
215
     *
216
     * @return bool true if success false otherwise
217
     */
218
    public function getProjectRepositoryList($projectId, $userId = null) {
219
        $onlyGitShell = false;
220
        $scope        = true;
221
        $dao          = $this->getDao();
222
        $this->addData(array(
223
            'repository_list'     => $dao->getProjectRepositoryList($projectId, $onlyGitShell, $scope, $userId),
224
            'repositories_owners' => $dao->getProjectRepositoriesOwners($projectId),
225
        ));
226
        return true;
227
    }
228
229
    /**
230
     * Generates a list of GitRepositoryWithPermissions which are migrated to a
231
     * gerrit server and belong to the project or the project's parent.
232
     *
233
     * @param Project $project
234
     * @param PFUser $user
235
     * @param Project[] $parent_projects
0 ignored issues
show
Documentation introduced by
There is no parameter named $parent_projects. Did you maybe mean $project?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
236
     */
237
    public function generateGerritRepositoryAndTemplateList(Project $project, PFUser $user) {
238
        $repos            = $this->factory->getAllGerritRepositoriesFromProject($project, $user);
239
        $templates        = $this->template_factory->getAllTemplatesOfProject($project);
240
        $parent_templates = $this->template_factory->getTemplatesAvailableForParentProjects($project);
241
242
        $this->addData(array(
243
            'repository_list'        => $repos,
244
            'templates_list'         => $templates,
245
            'parent_templates_list'  => $parent_templates,
246
            'has_gerrit_servers_set_up' => $this->gerrit_server_factory->hasRemotesSetUp()
247
        ));
248
    }
249
250
    protected function getDao() {
251
        return new GitDao();
252
    }
253
254
    /**
255
     * Displays the contents of the config file of a repository migrated to gerrit.
256
     * (used in AJAX)
257
     *
258
     * @param int $repo_id
259
     * @param PFUser $user
260
     * @param Project $project
261
     * @return void if error
262
     */
263
    public function fetchGitConfig($repo_id, PFUser $user, Project $project) {
264
        $git_repo = $this->getGitRepository($repo_id);
265
266
        try {
267
            $this->checkRepoValidity($git_repo, $project);
0 ignored issues
show
Bug introduced by
It seems like $git_repo defined by $this->getGitRepository($repo_id) on line 264 can be null; however, GitActions::checkRepoValidity() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
268
            $this->checkUserIsAdmin($project, $user);
269
        } catch (Exception $e) {
270
            $GLOBALS['Response']->addFeedback(Feedback::ERROR, get_class($e).$e->getTraceAsString());
271
            $GLOBALS['Response']->sendStatusCode($e->getCode());
272
            return;
273
        }
274
275
        $gerrit_server           = $this->gerrit_server_factory->getServerById($git_repo->getRemoteServerId());
276
        $git_repo_name_on_gerrit = $this->driver_factory->getDriver($gerrit_server)->getGerritProjectName($git_repo);
0 ignored issues
show
Bug introduced by
It seems like $git_repo defined by $this->getGitRepository($repo_id) on line 264 can be null; however, Git_Driver_GerritREST::getGerritProjectName() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
Bug introduced by
It seems like $git_repo defined by $this->getGitRepository($repo_id) on line 264 can be null; however, Git_Driver_GerritLegacy::getGerritProjectName() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
277
        $url                     = $gerrit_server->getCloneSSHUrl($git_repo_name_on_gerrit);
278
279
        try {
280
            echo $this->project_creator->getGerritConfig($gerrit_server, $url);
281
        } catch (Git_Driver_Gerrit_Exception $e) {
0 ignored issues
show
Unused Code introduced by
catch (\Git_Driver_Gerri...ode(500); return; } does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
282
            $GLOBALS['Response']->addFeedback(Feedback::ERROR, 'Cannot access Gerrit ' . $e->getTraceAsString());
283
            $GLOBALS['Response']->sendStatusCode(500);
284
            return;
285
        }
286
    }
287
288
    /**
289
     * Delete a given template
290
     *
291
     * @param int the $template_id
292
     * @param Project $project
293
     * @param PFUser $user
294
     */
295
    public function deleteGerritTemplate($template_id, Project $project, PFUser $user) {
296
        if (! $this->isUserAdmin($user, $project)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->isUserAdmin($user, $project) of type null|boolean is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
297
            $GLOBALS['Response']->addFeedback(Feedback::ERROR, $GLOBALS['Language']->getText('plugin_git', 'gerrit_template_delete_error'));
298
            return;
299
        }
300
301
        try {
302
            $template = $this->template_factory->getTemplate($template_id);
303
304
            if ($template->belongsToProject($project->getID())) {
305
                $this->template_factory->deleteTemplate($template_id);
306
307
                $this->history_dao->groupAddHistory(
308
                    "git_delete_template",
309
                    $template->getName(),
310
                    $project->getID()
311
                );
312
313
                $GLOBALS['Response']->addFeedback(Feedback::INFO, $GLOBALS['Language']->getText('plugin_git', 'gerrit_template_delete_success'));
314
                return;
315
            }
316
        } catch (Exception $e) {}
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
317
318
        $GLOBALS['Response']->addFeedback(Feedback::ERROR, $GLOBALS['Language']->getText('plugin_git', 'gerrit_template_delete_error'));
319
    }
320
321
    /**
322
     *
323
     * @param GitRepository $git_repo
324
     * @param Project $project
325
     * @throws Git_ProjectNotFoundException
326
     * @throws GitRepoNotFoundException
327
     * @throws GitRepoNotInProjectException
328
     * @throws GitRepoNotOnGerritException
329
     */
330
    private function checkRepoValidity($git_repo, $project) {
331
        if($project->isError()) {
332
            throw new Git_ProjectNotFoundException('unable to get config', 404);
333
        }
334
335
        if(! $git_repo) {
336
            throw new GitRepoNotFoundException('unable to get config', 404);
337
        }
338
339
        if(! $git_repo->belongsToProject($project)) {
340
            throw new GitRepoNotInProjectException('unable to get config', 403);
341
        }
342
343
        if(! $git_repo->isMigratedToGerrit()) {
344
            throw new GitRepoNotOnGerritException('unable to get config', 500);
345
        }
346
    }
347
348
    /**
349
     * Displays the content of a template (used in AJAX)
350
     *
351
     * @param int $template_id
352
     * @param PFUser $user
353
     * @param Project $project
354
     * @return void
355
     */
356
    public function fetchGitTemplate($template_id, PFUser $user, Project $project) {
357
        try {
358
            $template = $this->template_factory->getTemplate($template_id);
359
            $this->checkTemplateIsAccessible($template, $project, $user);
360
        } catch (Exception $e) {
361
            $GLOBALS['Response']->addFeedback(Feedback::ERROR, get_class($e).$e->getTraceAsString());
362
            $GLOBALS['Response']->sendStatusCode($e->getCode());
363
            return;
364
        }
365
366
        echo $template->getContent();
367
    }
368
369
    /**
370
     * @param  Project $project
371
     * @param  PFUser  $user
372
     *
373
     * @throws GitUserNotAdminException
374
     */
375
    private function checkUserIsAdmin(Project $project, PFUser $user) {
376
        if(! $this->git_permissions_manager->userIsGitAdmin($user, $project)) {
377
             throw new GitUserNotAdminException('unable to get template', 401);
378
        }
379
380
        return true;
381
    }
382
383
    /**
384
     * @param Git_Driver_Gerrit_Template_Template $template
385
     * @param Project $project
386
     * @param PFUser $user
387
     * @throws Git_ProjectNotInHierarchyException
388
     */
389
    private function checkTemplateIsAccessible(Git_Driver_Gerrit_Template_Template $template, Project $project, PFUser $user) {
390
        $template_id = $template->getId();
391
392
        foreach ($this->template_factory->getTemplatesAvailableForProject($project) as $available_template) {
393
            if ($available_template->getId() == $template_id) {
394
                $template_project = $this->project_manager->getProject($available_template->getProjectId());
395
                $this->checkUserIsAdmin($template_project, $user);
396
397
                return true;
398
            }
399
        }
400
401
        throw new Git_TemplateNotInProjectHierarchyException('Project not in hierarchy', 404);
402
    }
403
404
    /**
405
     * @param Project $project
406
     * @param PFUser $user
407
     * @param string $template_content
408
     * @param int $template_id
409
     * @return void
410
     */
411
    public function updateTemplate(Project $project, PFUser $user, $template_content, $template_id) {
412
        if ($project->isError() || ! $this->checkUserIsAdmin($project, $user)) {
413
            $GLOBALS['Response']->addFeedback(Feedback::ERROR, $GLOBALS['Language']->getText('plugin_git', 'view_admin_template_invalid_project'));
414
            return;
415
        }
416
417
        if (! $template_id) {
418
            $GLOBALS['Response']->addFeedback(Feedback::ERROR, $GLOBALS['Language']->getText('plugin_git', 'view_admin_template_invalid_template_id'));
419
            return;
420
        }
421
422
        try {
423
            $template = $this->template_factory->getTemplate($template_id);
424
        } catch (Exception $e) {
425
            $GLOBALS['Response']->addFeedback(Feedback::ERROR, $GLOBALS['Language']->getText('plugin_git', 'Unable to update template'));
426
            return;
427
        }
428
429
        if (! $template->belongsToProject($project->getID())) {
430
            $GLOBALS['Response']->addFeedback(Feedback::ERROR, $GLOBALS['Language']->getText('plugin_git', 'view_admin_template_invalid_template_id'));
431
            return;
432
        }
433
434
        $template->setContent($template_content);
435
436
        if ($this->template_factory->updateTemplate($template)) {
437
            $GLOBALS['Response']->addFeedback(Feedback::INFO, $GLOBALS['Language']->getText('plugin_git', 'view_admin_template_updated'));
438
        } else {
439
            $GLOBALS['Response']->addFeedback(Feedback::ERROR, $GLOBALS['Language']->getText('plugin_git', 'Unable to update template'));
440
        }
441
    }
442
443
    /**
444
     *
445
     * @param Project $project
446
     * @param PFUser $user
447
     * @param string $template_content
448
     * @param string $template_name
449
     * @return void
450
     */
451
    public function createTemplate(Project $project, PFUser $user, $template_content, $template_name) {
452
        if (! $this->checkIfProjectIsValid($project) || ! $this->isUserAdmin($user, $project)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->checkIfProjectIsValid($project) of type null|boolean is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
Bug Best Practice introduced by
The expression $this->isUserAdmin($user, $project) of type null|boolean is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
453
            $GLOBALS['Response']->addFeedback(Feedback::ERROR, $GLOBALS['Language']->getText('plugin_git', 'view_admin_template_cannot_create'));
454
            return;
455
        }
456
457
        if ($this->template_factory->createTemplate($project->getID(), $template_content, $template_name)) {
458
            $this->history_dao->groupAddHistory(
459
                "git_create_template",
460
                $template_name,
461
                $project->getID()
462
            );
463
464
            $GLOBALS['Response']->addFeedback(Feedback::INFO, $GLOBALS['Language']->getText('plugin_git', 'view_admin_template_created'));
465
        } else {
466
            $GLOBALS['Response']->addFeedback(Feedback::ERROR, $GLOBALS['Language']->getText('plugin_git', 'view_admin_template_cannot_create'));
467
        }
468
    }
469
470
    public function getRepositoryDetails($projectId, $repositoryId) {
471
        $c = $this->getController();
472
        $projectId    = intval($projectId);
473
        $repositoryId = intval($repositoryId);
474
        if ( empty($repositoryId) ) {
475
            $this->addError('actions_params_error');
476
            return false;
477
        }
478
        $repository = $this->factory->getRepositoryById($repositoryId);
479
        if (!$repository) {
480
            $this->addError('actions_repo_not_found');
481
            $c->redirect('/plugins/git/?action=index&group_id='.$projectId);
482
            return;
483
        }
484
        $this->addData(array(
485
            'repository'         => $repository,
486
            'gerrit_servers'     => $this->gerrit_server_factory->getServers(),
487
            'driver_factory'     => $this->driver_factory,
488
            'gerrit_usermanager' => $this->gerrit_usermanager
489
        ));
490
        return true;
491
    }
492
493
    public function repoManagement(GitRepository $repository) {
494
        $this->addData(array('repository'=>$repository));
495
        $this->displayFeedbacksOnRepoManagement($repository);
496
        $this->addData(array(
497
            'gerrit_servers'   => $this->gerrit_server_factory->getServers(),
498
            'driver_factory'   => $this->driver_factory,
499
            'gerrit_templates' => $this->template_factory->getTemplatesAvailableForRepository($repository)
500
        ));
501
        return true;
502
    }
503
504
    private function displayFeedbacksOnRepoManagement(GitRepository $repository) {
505
        if ($this->git_system_event_manager->isRepositoryMigrationToGerritOnGoing($repository)) {
506
            $GLOBALS['Response']->addFeedback(Feedback::INFO, $this->getText('gerrit_migration_ongoing'));
507
        }
508
509
        if ($this->git_system_event_manager->isProjectDeletionOnGerritOnGoing($repository)) {
510
            $GLOBALS['Response']->addFeedback(Feedback::INFO, $this->getText('gerrit_deletion_ongoing'));
511
        }
512
513
        if ($this->git_system_event_manager->isProjectSetReadOnlyOnGerritOnGoing($repository)) {
514
            $GLOBALS['Response']->addFeedback(Feedback::INFO, $this->getText('gerrit_readonly_ongoing'));
515
        }
516
    }
517
518
    public function notificationUpdatePrefix($projectId, $repositoryId, $mailPrefix, $pane) {
519
        $c = $this->getController();
520
        if (empty($repositoryId)) {
521
            $this->addError('actions_params_error');
522
            return false;
523
        }
524
        $repository = $this->_loadRepository($projectId, $repositoryId);
525
        if ($repository->getMailPrefix() != $mailPrefix) {
526
            $repository->setMailPrefix($mailPrefix);
527
            $repository->save();
528
            $repository->changeMailPrefix();
529
            $c->addInfo($this->getText('mail_prefix_updated'));
530
            $this->addData(array('repository'=>$repository));
531
        }
532
        $this->git_system_event_manager->queueRepositoryUpdate($repository);
0 ignored issues
show
Bug introduced by
It seems like $repository defined by $this->_loadRepository($projectId, $repositoryId) on line 524 can be null; however, Git_SystemEventManager::queueRepositoryUpdate() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
533
534
        $this->history_dao->groupAddHistory(
535
            "git_repo_update",
536
            $repository->getName() . ': update notification prefix',
537
            $repository->getProjectId()
538
        );
539
540
        return true;
541
    }
542
543
    public function notificationAddMail($projectId, $repositoryId, $mails, $pane) {
544
        $controller = $this->getController();
545
        $repository = $this->_loadRepository($projectId, $repositoryId);
546
        if (empty($repositoryId) || empty($mails)) {
547
            $this->addError('actions_params_error');
548
            return false;
549
        }
550
551
        $res = true;
552
        foreach ($mails as $mail) {
553
            if ($repository->isAlreadyNotified($mail)) {
554
                $res = false;
555
                $controller->addInfo($this->getText('mail_existing', array($mail)));
556
            } else {
557
                if (!$repository->notificationAddMail($mail)) {
558
                    $res = false;
559
                    $controller->addError($this->getText('mail_not_added', array($mail)));
560
                }
561
            }
562
        }
563
        $this->git_system_event_manager->queueRepositoryUpdate($repository);
0 ignored issues
show
Bug introduced by
It seems like $repository defined by $this->_loadRepository($projectId, $repositoryId) on line 545 can be null; however, Git_SystemEventManager::queueRepositoryUpdate() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
564
565
        $this->history_dao->groupAddHistory(
566
            "git_repo_update",
567
            $repository->getName() . ': add notification email',
568
            $repository->getProjectId()
569
        );
570
571
        //Display this message, just if all the entred mails have been added
572
        if ($res) {
573
            $controller->addInfo($this->getText('mail_added'));
574
        }
575
        return true;
576
    }
577
578
    public function notificationRemoveMail($projectId, $repositoryId, $mails, $pane) {
579
        $controller = $this->getController();
580
        $repository = $this->_loadRepository($projectId, $repositoryId);
581
        if (empty($repositoryId) || empty($mails)) {
582
            $this->addError('actions_params_error');
583
            return false;
584
        }
585
        $ret = true;
586
        foreach ($mails as $mail) {
587
            if ($repository->notificationRemoveMail($mail)) {
588
                $controller->addInfo($this->getText('mail_removed', array($mail)));
589
            } else {
590
                $controller->addError($this->getText('mail_not_removed', array($mail)));
591
                $ret = false;
592
            }
593
        }
594
        $this->git_system_event_manager->queueRepositoryUpdate($repository);
0 ignored issues
show
Bug introduced by
It seems like $repository defined by $this->_loadRepository($projectId, $repositoryId) on line 580 can be null; however, Git_SystemEventManager::queueRepositoryUpdate() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
595
596
        $this->history_dao->groupAddHistory(
597
            "git_repo_update",
598
            $repository->getName() . ': remove notification email',
599
            $repository->getProjectId()
600
        );
601
602
        return $ret;
603
    }
604
605
    public function redirectToRepoManagement($projectId, $repositoryId, $pane) {
606
        $redirect_url = GIT_BASE_URL .'/?'. http_build_query(
607
            array(
608
                'action'   => 'repo_management',
609
                'group_id' => $projectId,
610
                'repo_id'  => $repositoryId,
611
                'pane'     => $pane,
612
            )
613
        );
614
        $this->getController()->redirect($redirect_url);
615
    }
616
617
    public function redirectToRepoManagementWithMigrationAccessRightInformation($projectId, $repositoryId, $pane) {
618
        $redirect_url = GIT_BASE_URL .'/?'. http_build_query(
619
            array(
620
                'action'               => 'repo_management',
621
                'group_id'             => $projectId,
622
                'repo_id'              => $repositoryId,
623
                'pane'                 => $pane,
624
            )
625
        );
626
        $this->getController()->redirect($redirect_url);
627
    }
628
629
    public function confirmPrivate($projectId, $repoId, $repoAccess, $repoDescription) {
630
        $c = $this->getController();
631
        if (empty($repoId) || empty($repoAccess) || empty($repoDescription)) {
632
            $this->addError('actions_params_error');
633
            return false;
634
        }
635
        $repository = $this->_loadRepository($projectId, $repoId);
636
        if (strcmp($repoAccess, 'private') == 0 && strcmp($repository->getAccess(), $repoAccess) != 0) {
637
            $mailsToDelete = $repository->getNonMemberMails();
638
            if (!empty($mailsToDelete)) {
639
                $repository->setDescription($repoDescription);
640
                $repository->save();
641
                $this->addData(array('repository' => $repository));
642
                $this->addData(array('mails' => $mailsToDelete));
643
                $c->addWarn($this->getText('set_private_warn'));
644
                return true;
645
            }
646
        }
647
        $this->save($projectId, $repoId, $repoAccess, $repoDescription);
0 ignored issues
show
Bug introduced by
The call to save() misses a required argument $pane.

This check looks for function calls that miss required arguments.

Loading history...
648
        return true;
649
    }
650
651
    public function setPrivate($projectId, $repoId) {
652
        $c = $this->getController();
653
        if (empty($repoId)) {
654
            $this->addError('actions_params_error');
655
            return false;
656
        }
657
        $repository = $this->_loadRepository($projectId, $repoId);
658
        $mailsToDelete = $repository->getNonMemberMails();
659
        foreach ($mailsToDelete as $mail) {
660
            $repository->notificationRemoveMail($mail);
661
        }
662
        $this->git_system_event_manager->queueGitShellAccess($repository, 'private');
0 ignored issues
show
Bug introduced by
It seems like $repository defined by $this->_loadRepository($projectId, $repoId) on line 657 can be null; however, Git_SystemEventManager::queueGitShellAccess() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
663
        $c->addInfo($this->getText('actions_repo_access'));
664
    }
665
666
    /**
667
     * This method allows one to save any repository attribues changes from the web interface.
668
     * @param <type> $repoId
0 ignored issues
show
Documentation introduced by
The doc-type <type> could not be parsed: Unknown type name "<" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
669
     * @param <type> $repoAccess
0 ignored issues
show
Documentation introduced by
The doc-type <type> could not be parsed: Unknown type name "<" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
670
     * @param <type> $repoDescription
0 ignored issues
show
Documentation introduced by
The doc-type <type> could not be parsed: Unknown type name "<" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
671
     * @return <type>
0 ignored issues
show
Documentation introduced by
The doc-type <type> could not be parsed: Unknown type name "<" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
672
     */
673
    public function save( $projectId, $repoId, $repoAccess, $repoDescription, $pane) {
674
        $controller = $this->getController();
675
        if ( empty($repoId) ) {
676
            $this->addError('actions_params_error');
677
            $controller->redirect('/plugins/git/?action=index&group_id='.$projectId);
678
            return false;
679
        }
680
        $repository = $this->factory->getRepositoryById($repoId);
681
        if (! $repository) {
682
            $this->addError('actions_repo_not_found');
683
            $controller->redirect('/plugins/git/?group_id='.$projectId);
684
            return false;
685
        }
686
        if (empty($repoAccess) && empty($repoDescription)) {
687
            $this->addError('actions_params_error');
688
            $this->redirectToRepo($repository);
689
            return false;
690
        }
691
692
        if ($repoDescription) {
693
            if (strlen($repoDescription) > 1024) {
694
                $this->addError('actions_long_description');
695
            } else {
696
                $repository->setDescription($repoDescription);
697
698
                $this->history_dao->groupAddHistory(
699
                    "git_repo_update",
700
                    $repository->getName() . ': update description',
701
                    $repository->getProjectId()
0 ignored issues
show
Security Bug introduced by
It seems like $repository->getProjectId() targeting GitRepository::getProjectId() can also be of type false; however, ProjectHistoryDao::groupAddHistory() does only seem to accept integer, did you maybe forget to handle an error condition?
Loading history...
702
                );
703
            }
704
        }
705
706
        try {
707
            $repository->save();
708
            if ( !empty($repoAccess) ) {
709
                //TODO use Polymorphism to handle this
710
                if ($repository->getBackend() instanceof Git_Backend_Gitolite) {
711
                    $repository->getBackend()->savePermissions($repository, $repoAccess);
712
                } else {
713
                    if ($repository->getAccess() != $repoAccess) {
714
                        $this->git_system_event_manager->queueGitShellAccess($repository, $repoAccess);
715
                        $controller->addInfo( $this->getText('actions_repo_access') );
716
                    }
717
                }
718
            }
719
            $this->git_system_event_manager->queueRepositoryUpdate($repository);
720
721
        } catch (GitDaoException $e) {
722
            $controller->addError( $e->getMessage() );
723
            $this->redirectToRepoManagement($projectId, $repoId, $pane);
724
            return false;
725
        }
726
        $controller->addInfo( $this->getText('actions_save_repo_process') );
727
        $this->redirectToRepoManagement($projectId, $repoId, $pane);
728
        return;
729
    }
730
731
    /**
732
     * Internal method called by SystemEvent_PROJECT_IS_PRIVATE
733
     * @param <type> $projectId
0 ignored issues
show
Documentation introduced by
The doc-type <type> could not be parsed: Unknown type name "<" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
734
     * @param <type> $isPublic
0 ignored issues
show
Documentation introduced by
The doc-type <type> could not be parsed: Unknown type name "<" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
Bug introduced by
There is no parameter named $isPublic. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
735
     * @return <type>
0 ignored issues
show
Documentation introduced by
The doc-type <type> could not be parsed: Unknown type name "<" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
736
     */
737
    public static function changeProjectRepositoriesAccess($projectId, $isPrivate, GitDao $dao, GitRepositoryFactory $factory) {
738
        //if the project is private, then no changes may be applied to repositories,
739
        //in other words only if project is set to private, its repositories have to be set to private
740
        if ( empty($isPrivate) ) {
741
            return;
742
        }
743
        $repositories = $dao->getProjectRepositoryList($projectId);
744
        foreach ( $repositories as $repoId=>$repoData ) {
745
            $r = $factory->getRepositoryById($repoId);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $r is correct as $factory->getRepositoryById($repoId) (which targets GitRepositoryFactory::getRepositoryById()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
746
            if ( !$r ) {
747
                continue;
748
            }
749
            if ( $r->getAccess() == GitRepository::PRIVATE_ACCESS) {
750
                continue;
751
            }
752
            $r->setAccess( GitRepository::PRIVATE_ACCESS );
753
            $r->changeAccess();
754
            unset($r);
755
        }
756
757
758
    }
759
760
    /**
761
     * Method called by SystemEvent_PROJECT_RENAME event
762
     *
763
     * @param Project $project Project to modify
764
     * @param String  $newName New unix group name
765
     *
766
     * @return Boolean
767
     */
768
    public static function renameProject(Project $project, $newName) {
769
        $r = new GitRepository();
770
        return $r->renameProject($project, $newName);
771
    }
772
773
    function _loadRepository($projectId, $repositoryId) {
774
        $repository = $this->getGitRepository($repositoryId);
775
        if ($repository) {
776
            $this->addData(array('repository'=>$repository));
777
            return $repository;
778
        } else {
779
            $c = $this->getController();
780
            $this->addError('actions_repo_not_found');
781
            $c->redirect('/plugins/git/?action=index&group_id='.$projectId);
782
        }
783
    }
784
785
    /**
786
     * Wrapper used for tests to get a new GitRepository
787
     */
788
    function getGitRepository($repositoryId) {
789
        return $this->factory->getRepositoryById($repositoryId);
790
    }
791
792
    /**
793
     * Fork a bunch of repositories in a project for a given user
794
     *
795
     * @param int    $groupId         The project id
0 ignored issues
show
Bug introduced by
There is no parameter named $groupId. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
796
     * @param array  $repos_ids       The array of id of repositories to fork
0 ignored issues
show
Bug introduced by
There is no parameter named $repos_ids. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
797
     * @param string $namespace       The namespace where the new repositories will live
798
     * @param PFUser   $user            The owner of those new repositories
799
     * @param Layout $response        The response object
800
     * @param array  $forkPermissions Permissions to be applied for the new repository
801
     */
802
    public function fork(array $repos, Project $to_project, $namespace, $scope, PFUser $user, Layout $response, $redirect_url, array $forkPermissions) {
803
        try {
804
            if ($this->manager->forkRepositories($repos, $to_project, $user, $namespace, $scope, $forkPermissions)) {
805
806
                $this->history_dao->groupAddHistory(
807
                    "git_fork_repositories",
808
                    $to_project->getID(),
809
                    $to_project->getID()
810
                );
811
812
                $GLOBALS['Response']->addFeedback('info', $this->getText('successfully_forked'));
813
                $response->redirect($redirect_url);
814
            }
815
        } catch(Exception $e) {
816
            $GLOBALS['Response']->addFeedback('error', $e->getMessage());
817
        }
818
    }
819
820
    /**
821
     * Prepare data for fork permissions action
822
     *
823
     * @param array  $repos     Repositories Ids we want to fork
824
     * @param array  $project   The project Id where repositories would be forked
825
     * @param string $namespace The namespace where the new repositories will live
826
     * @param string $scope     The scope of the fork: personal or cross project.
827
     *
828
     * @return void
829
     */
830
    public function forkRepositoriesPermissions($repos, $project, $namespace, $scope) {
831
        $this->addData(array('repos'     => join(',', $repos),
832
                             'group_id'  => $project,
833
                             'namespace' => $namespace,
834
                             'scope'     => $scope));
835
    }
836
837
    /**
838
     *
839
     * @param GitRepository $repository
840
     * @param int $remote_server_id the id of the server to which we want to migrate
841
     * @param Boolean $migrate_access_right if the acess right will be migrated or not
0 ignored issues
show
Bug introduced by
There is no parameter named $migrate_access_right. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
842
     * @param int $gerrit_template_id the id of template if any chosen
843
     */
844
    public function migrateToGerrit(GitRepository $repository, $remote_server_id, $gerrit_template_id) {
845
        if ($repository->canMigrateToGerrit()) {
846
847
            try {
848
                $this->gerrit_server_factory->getServerById($remote_server_id);
849
                $this->git_system_event_manager->queueMigrateToGerrit($repository, $remote_server_id, $gerrit_template_id);
850
851
                $this->history_dao->groupAddHistory(
852
                    "git_repo_to_gerrit",
853
                    $repository->getName(),
854
                    $repository->getProjectId()
0 ignored issues
show
Security Bug introduced by
It seems like $repository->getProjectId() targeting GitRepository::getProjectId() can also be of type false; however, ProjectHistoryDao::groupAddHistory() does only seem to accept integer, did you maybe forget to handle an error condition?
Loading history...
855
                );
856
            } catch (Exception $e) {
857
                $this->logger->log($e->getMessage(), Feedback::ERROR);
0 ignored issues
show
Deprecated Code introduced by
The method Logger::log() has been deprecated with message: use explicit methods

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
858
            }
859
        }
860
    }
861
862
    private function redirectToRepo(GitRepository $repository) {
863
        $this->getController()->redirect($this->url_manager->getRepositoryBaseUrl($repository));
864
    }
865
866
    private function addError($error_key) {
867
        $this->getController()->addError($this->getText($error_key));
868
    }
869
870
    public function disconnectFromGerrit(GitRepository $repository) {
871
        $repository->getBackend()->disconnectFromGerrit($repository);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Git_Backend_Interface as the method disconnectFromGerrit() does only exist in the following implementations of said interface: Git_Backend_Gitolite.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
872
        $this->git_system_event_manager->queueRepositoryUpdate($repository);
873
874
        $server = $this->gerrit_server_factory->getServerById($repository->getRemoteServerId());
875
        $driver = $this->driver_factory->getDriver($server);
876
877
        $disconnect_option = $this->request->get(GitViews_RepoManagement_Pane_Gerrit::OPTION_DISCONNECT_GERRIT_PROJECT);
878
879
        if ($disconnect_option == GitViews_RepoManagement_Pane_Gerrit::OPTION_DELETE_GERRIT_PROJECT) {
880
            $this->git_system_event_manager->queueRemoteProjectDeletion($repository, $driver);
881
882
            $this->history_dao->groupAddHistory(
883
                "git_disconnect_gerrit_delete",
884
                $repository->getName(),
885
                $repository->getProjectId()
0 ignored issues
show
Security Bug introduced by
It seems like $repository->getProjectId() targeting GitRepository::getProjectId() can also be of type false; however, ProjectHistoryDao::groupAddHistory() does only seem to accept integer, did you maybe forget to handle an error condition?
Loading history...
886
            );
887
        }
888
889
        if ($disconnect_option == GitViews_RepoManagement_Pane_Gerrit::OPTION_READONLY_GERRIT_PROJECT) {
890
            $this->git_system_event_manager->queueRemoteProjectReadOnly($repository, $driver);
891
892
            $this->history_dao->groupAddHistory(
893
                "git_disconnect_gerrit_read_only",
894
                $repository->getName(),
895
                $repository->getProjectId()
0 ignored issues
show
Security Bug introduced by
It seems like $repository->getProjectId() targeting GitRepository::getProjectId() can also be of type false; however, ProjectHistoryDao::groupAddHistory() does only seem to accept integer, did you maybe forget to handle an error condition?
Loading history...
896
            );
897
        }
898
    }
899
900
    public function updateGitAdminGroups(Project $project, PFUser $user, array $selected_group_ids) {
901
        if (! $this->checkIfProjectIsValid($project) || ! $this->isUserAdmin($user, $project)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->checkIfProjectIsValid($project) of type null|boolean is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
Bug Best Practice introduced by
The expression $this->isUserAdmin($user, $project) of type null|boolean is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
902
            return;
903
        }
904
905
        $selected_group_ids = $this->removeUndesiredUgroupsFromRequest($selected_group_ids);
906
907
        list ($return_code, $feedback) = permission_process_selection_form(
0 ignored issues
show
Deprecated Code introduced by
The function permission_process_selection_form() has been deprecated.

This function has been deprecated.

Loading history...
908
            $project->getId(),
909
            Git::PERM_ADMIN,
910
            $project->getID(),
911
            $selected_group_ids
912
        );
913
914
        if (! $return_code) {
915
            $GLOBALS['Response']->addFeedback(Feedback::ERROR, $GLOBALS['Language']->getText('plugin_git', 'view_admin_git_admins_update_feedback', $feedback));
916
            return;
917
        }
918
919
        $GLOBALS['Response']->addFeedback(Feedback::INFO, $GLOBALS['Language']->getText('plugin_git', 'view_admin_git_admins_update_feedback', $feedback));
920
921
        $this->history_dao->groupAddHistory(
922
            "git_admin_groups",
923
            '',
924
            $project->getID()
925
        );
926
927
        $this->redirectToGitHomePageIfUserIsNoMoreGitAdmin($user, $project);
928
    }
929
930
    private function redirectToGitHomePageIfUserIsNoMoreGitAdmin(PFUser $user, Project $project) {
931
        if (! $this->isUserAdmin($user, $project)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->isUserAdmin($user, $project) of type null|boolean is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
932
            $GLOBALS['Response']->addFeedback(Feedback::INFO, $GLOBALS['Language']->getText('plugin_git','no_longer_access_to_admin'));
933
            $GLOBALS['HTML']->redirect('/plugins/git/?action=index&group_id='. $project->getId());
934
        }
935
    }
936
937
    private function removeUndesiredUgroupsFromRequest(array $selected_group_ids) {
938
        $selected_group_ids = $this->removeNobodyFromSelectGroups($selected_group_ids);
939
        $selected_group_ids = $this->removeAnonymousFromSelectGroups($selected_group_ids);
940
        $selected_group_ids = $this->removeRegisteredUsersFromSelectGroups($selected_group_ids);
941
942
        return $selected_group_ids;
943
    }
944
945
    private function removeNobodyFromSelectGroups(array $select_group_ids) {
946
        return array_diff($select_group_ids, array(ProjectUGroup::NONE));
947
    }
948
949
    private function removeAnonymousFromSelectGroups(array $select_group_ids) {
950
        return array_diff($select_group_ids, array(ProjectUGroup::ANONYMOUS));
951
    }
952
953
    private function removeRegisteredUsersFromSelectGroups(array $select_group_ids) {
954
        return array_diff($select_group_ids, array(ProjectUGroup::REGISTERED));
955
    }
956
957
    private function checkIfProjectIsValid(Project $project) {
958
        if ($project->isError()) {
959
            $GLOBALS['Response']->addFeedback(Feedback::ERROR, $GLOBALS['Language']->getText('plugin_git', 'view_admin_template_invalid_project'));
960
            return;
961
        }
962
963
        return true;
964
    }
965
966
    private function isUserAdmin(PFUser $user, Project $project) {
967
        try {
968
            $this->checkUserIsAdmin($project, $user);
969
        } catch (GitUserNotAdminException $e) {
970
            return;
971
        }
972
973
        return true;
974
    }
975
976
    public function updateMirroring(Project $project, array $repositories, $selected_mirror_ids) {
977
        $current_mirror_ids_per_repository = $this->mirror_data_mapper->getListOfMirrorIdsPerRepositoryForProject($project);
978
        foreach($repositories as $repository) {
979
            if (! isset($selected_mirror_ids[$repository->getId()]) || ! is_array($selected_mirror_ids[$repository->getId()])) {
980
                continue;
981
            }
982
983
            $mirror_ids = $this->getSelectedMirrorIdsFromRequest($selected_mirror_ids, $repository->getId());
984
985
            if (! $this->areThereAnyChanges($repository, $mirror_ids, $current_mirror_ids_per_repository)) {
986
                continue;
987
            }
988
989
            if (! $this->updateRepositoryMirrors($repository, $mirror_ids)) {
990
                $GLOBALS['Response']->addFeedback('error', $GLOBALS['Language']->getText('plugin_git', 'mirroring_mirroring_error'));
991
                return;
992
            }
993
        }
994
995
        $more_than_one_repository = count($repositories) > 1;
996
997
        if ($more_than_one_repository && ! $selected_mirror_ids) {
998
            $GLOBALS['Response']->addFeedback('warning', $GLOBALS['Language']->getText('plugin_git', 'mirroring_unmirroring_successful_plural'));
999
1000
        } elseif ($more_than_one_repository && $selected_mirror_ids) {
1001
            $GLOBALS['Response']->addFeedback('warning', $GLOBALS['Language']->getText('plugin_git', 'mirroring_mirroring_successful_plural'));
1002
1003
        } elseif (! $more_than_one_repository && ! $selected_mirror_ids) {
1004
            $GLOBALS['Response']->addFeedback('warning', $GLOBALS['Language']->getText('plugin_git', 'mirroring_unmirroring_successful'));
1005
1006
        } else {
1007
            $GLOBALS['Response']->addFeedback('warning', $GLOBALS['Language']->getText('plugin_git', 'mirroring_mirroring_successful'));
1008
        }
1009
    }
1010
1011
    /**
1012
     * @param array $selected_mirror_ids
1013
     * @param int   $request_key The request_key can be either project_id (default mirror) or repository_id (repository's mirror)
1014
     *
1015
     * @return array
1016
     */
1017
    private function getSelectedMirrorIdsFromRequest(array $selected_mirror_ids, $request_key) {
1018
        $mirror_ids = array();
1019
        foreach ($selected_mirror_ids[$request_key] as $mirror_id => $should_be_mirrored) {
1020
            if ($should_be_mirrored) {
1021
                $mirror_ids[] = $mirror_id;
1022
            }
1023
        }
1024
1025
        return $mirror_ids;
1026
    }
1027
1028
    private function updateRepositoryMirrors(GitRepository $repository, $mirror_ids) {
1029
        if ($this->mirror_data_mapper->doesAllSelectedMirrorIdsExist($mirror_ids)
1030
            && $this->mirror_data_mapper->unmirrorRepository($repository->getId())
1031
            && $this->mirror_data_mapper->mirrorRepositoryTo($repository->getId(), $mirror_ids)) {
1032
1033
            $this->git_system_event_manager->queueRepositoryUpdate($repository);
1034
            $this->history_dao->groupAddHistory(
1035
                "git_repo_mirroring_update",
1036
                $repository->getName(),
1037
                $repository->getProjectId()
0 ignored issues
show
Security Bug introduced by
It seems like $repository->getProjectId() targeting GitRepository::getProjectId() can also be of type false; however, ProjectHistoryDao::groupAddHistory() does only seem to accept integer, did you maybe forget to handle an error condition?
Loading history...
1038
            );
1039
1040
            return true;
1041
        }
1042
1043
        return false;
1044
    }
1045
1046
    private function areThereAnyChanges(
1047
        GitRepository $repository,
1048
        array $mirror_ids,
1049
        array $current_mirror_ids_per_repository
1050
    ) {
1051
        $current_mirrors = array();
1052
        if (isset($current_mirror_ids_per_repository[$repository->getId()])) {
1053
            $current_mirrors = $current_mirror_ids_per_repository[$repository->getId()];
1054
        }
1055
1056
        return count(array_diff($mirror_ids, $current_mirrors)) > 0
1057
            || count(array_diff($current_mirrors, $mirror_ids)) > 0;
1058
    }
1059
1060
    public function setSelectedRepositories($repositories) {
1061
        $this->addData(array('repositories' => $repositories));
1062
    }
1063
1064
    public function restoreRepository($repo_id, $project_id) {
1065
        $repository = $this->factory->getDeletedRepository($repo_id);
1066
        $url        = '/admin/show_pending_documents.php?group_id='.$project_id.'&focus=git_repository';
1067
1068
        if (! $repository) {
1069
            $GLOBALS['Response']->addFeedback('error', $GLOBALS['Language']->getText('plugin_git', 'restore_invalid_id'));
1070
            $GLOBALS['Response']->redirect($url);
1071
        }
1072
1073
        $active_repository = $this->factory->getRepositoryByPath($project_id, $repository->getPath());
1074
        if ($active_repository instanceof GitRepository) {
1075
            $GLOBALS['Response']->addFeedback('error', $GLOBALS['Language']->getText('plugin_git', 'restore_invalid_name'));
1076
        } else {
1077
            $this->git_system_event_manager->queueRepositoryRestore($repository);
0 ignored issues
show
Bug introduced by
It seems like $repository defined by $this->factory->getDeletedRepository($repo_id) on line 1065 can be null; however, Git_SystemEventManager::queueRepositoryRestore() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
1078
            $GLOBALS['Response']->addFeedback('info', $GLOBALS['Language']->getText('plugin_git', 'restore_event_created').' : '.$repository->getName());
1079
        }
1080
        $GLOBALS['Response']->redirect($url);
1081
    }
1082
1083
    public function updateDefaultMirroring(Project $project, array $selected_mirror_ids) {
1084
        $mirror_ids = $this->getSelectedMirrorIdsFromRequest($selected_mirror_ids, $project->getID());
1085
1086
        if ($this->mirror_data_mapper->doesAllSelectedMirrorIdsExist($mirror_ids)
1087
            && $this->mirror_data_mapper->removeAllDefaultMirrorsToProject($project)
1088
            && $this->mirror_data_mapper->addDefaultMirrorsToProject($project, $mirror_ids)
1089
        ) {
1090
            $GLOBALS['Response']->addFeedback(
1091
                Feedback::INFO,
1092
                $GLOBALS['Language']->getText('plugin_git', 'default_mirros_update_success')
1093
            );
1094
1095
            return true;
1096
        }
1097
1098
        $GLOBALS['Response']->addFeedback(
1099
            Feedback::ERROR,
1100
            $GLOBALS['Language']->getText('plugin_git', 'default_mirros_update_error')
1101
        );
1102
1103
        return false;
1104
    }
1105
}
1106