Passed
Push — master ( 2162ae...e07b4a )
by Julien
30:30
created

TreeGen::insertByes()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 13
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 13
rs 9.4285
cc 3
eloc 8
nc 4
nop 2
1
<?php
2
3
namespace Xoco70\KendoTournaments\TreeGen;
4
5
use Illuminate\Support\Collection;
6
use Illuminate\Support\Facades\App;
7
use Xoco70\KendoTournaments\Contracts\TreeGenerable;
8
use Xoco70\KendoTournaments\Exceptions\TreeGenerationException;
9
use Xoco70\KendoTournaments\Models\Championship;
10
use Xoco70\KendoTournaments\Models\ChampionshipSettings;
11
use Xoco70\KendoTournaments\Models\FightersGroup;
12
13
class TreeGen implements TreeGenerable
14
{
15
    protected $groupBy;
16
    protected $tree;
17
    public $championship;
18
    public $settings;
19
    protected $numFighters;
20
21
    /**
22
     * @param Championship $championship
23
     * @param $groupBy
24
     */
25
    public function __construct(Championship $championship, $groupBy)
26
    {
27
        $this->championship = $championship;
28
        $this->groupBy = $groupBy;
29
        $this->settings = $championship->getSettings();
30
        $this->tree = new Collection();
31
    }
32
33
    /**
34
     * Generate tree groups for a championship.
35
     *
36
     * @throws TreeGenerationException
37
     */
38
    public function run()
39
    {
40
        $usersByArea = $this->getFightersByArea();
41
        $numFighters = sizeof($usersByArea->collapse());
42
43
        $this->generateGroupsForRound($usersByArea, 1, 1);
44
        $this->pushEmptyGroupsToTree($numFighters);
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Xoco70\KendoTournaments\TreeGen\TreeGen as the method pushEmptyGroupsToTree() does only exist in the following sub-classes of Xoco70\KendoTournaments\TreeGen\TreeGen: Xoco70\KendoTournaments\...nationCompetitorTreeGen, Xoco70\KendoTournaments\...irectEliminationTreeGen, Xoco70\KendoTournaments\...layOffCompetitorTreeGen, Xoco70\KendoTournaments\TreeGen\PlayOffTeamTreeGen, Xoco70\KendoTournaments\TreeGen\PlayOffTreeGen. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

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

class MyUser extends 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 sub-classes 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 parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
45
        $this->addParentToChildren($numFighters);
46
        // Now add parents to all
47
    }
48
49
    /**
50
     * Get the biggest entity group
51
     * @param $userGroups
52
     *
53
     * @return int
54
     */
55
    private function getMaxFightersByEntity($userGroups): int
56
    {
57
        return $userGroups
58
            ->sortByDesc(function ($group) {
59
                return $group->count();
60
            })
61
            ->first()
62
            ->count();
63
64
    }
65
66
    /**
67
     * Get Competitor's list ordered by entities
68
     * Countries for Internation Tournament, State for a National Tournament, etc.
69
     *
70
     * @param $fighters
71
     * @return Collection
72
     */
73
    private function getFightersByEntity($fighters): Collection
74
    {
75
        // Right now, we are treating users and teams as equals.
76
        // It doesn't matter right now, because we only need name attribute which is common to both models
77
78
        // $this->groupBy contains federation_id, association_id, club_id, etc.
79
        if (($this->groupBy) != null) {
80
            $fighterGroups = $fighters->groupBy($this->groupBy); // Collection of Collection
81
        } else {
82
            $fighterGroups = $fighters->chunk(1); // Collection of Collection
83
        }
84
        return $fighterGroups;
85
    }
86
87
    /**
88
     * Get the size the first round will have
89
     * @param $fighterCount
90
     * @param $groupSize
91
     * @return int
92
     */
93
    protected function getTreeSize($fighterCount, $groupSize)
94
    {
95
        $square = collect([1, 2, 4, 8, 16, 32, 64]);
96
        $squareMultiplied = $square->map(function ($item) use ($groupSize) {
97
            return $item * $groupSize;
98
        });
99
100
        foreach ($squareMultiplied as $limit) {
101
            if ($fighterCount <= $limit) {
102
                return $limit;
103
            }
104
        }
105
        return 64 * $groupSize;
106
    }
107
108
    /**
109
     * Repart BYE in the tree,
110
     * @param $fighterGroups
111
     * @param int $max
112
     *
113
     * @return Collection
114
     */
115
    private function repart($fighterGroups, $max)
116
    {
117
        $fighters = new Collection();
118
        for ($i = 0; $i < $max; $i++) {
119
            foreach ($fighterGroups as $fighterGroup) {
120
                $fighter = $fighterGroup->values()->get($i);
121
                if ($fighter != null) {
122
                    $fighters->push($fighter);
123
                }
124
            }
125
        }
126
127
        return $fighters;
128
    }
129
130
    /**
131
     * Insert byes in an homogen way.
132
     *
133
     * @param Collection $fighters
134
     * @param Collection $byeGroup
135
     *
136
     * @return Collection
137
     */
138
    private function insertByes(Collection $fighters, Collection $byeGroup)
139
    {
140
        $bye = count($byeGroup) > 0 ? $byeGroup[0] : [];
141
        $sizeFighters = count($fighters);
142
        $sizeGroupBy = count($byeGroup);
143
144
        $frequency = $sizeGroupBy != 0
145
            ? (int)floor($sizeFighters / $sizeGroupBy)
146
            : -1;
147
148
        // Create Copy of $competitors
149
        return $this->getFullFighterList($fighters, $frequency, $sizeGroupBy, $bye);
150
    }
151
152
    /**
153
     * @param Collection $usersByArea
154
     * @param integer $area
155
     * @param integer $round
156
     * @param integer $shuffle
0 ignored issues
show
Bug introduced by
There is no parameter named $shuffle. 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...
157
     *
158
     */
159
    public function generateGroupsForRound($usersByArea, $area, $round)
160
    {
161
        foreach ($usersByArea as $fightersByEntity) {
162
            // Chunking to make small round robin groups
163
            $fightersGroup = $this->chunkAndShuffle($round, $fightersByEntity);
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Xoco70\KendoTournaments\TreeGen\TreeGen as the method chunkAndShuffle() does only exist in the following sub-classes of Xoco70\KendoTournaments\TreeGen\TreeGen: Xoco70\KendoTournaments\...nationCompetitorTreeGen, Xoco70\KendoTournaments\...irectEliminationTreeGen, Xoco70\KendoTournaments\...layOffCompetitorTreeGen, Xoco70\KendoTournaments\TreeGen\PlayOffTeamTreeGen, Xoco70\KendoTournaments\TreeGen\PlayOffTreeGen. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

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

class MyUser extends 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 sub-classes 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 parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
164
            $order = 1;
165
            foreach ($fightersGroup as $fighters) {
166
                $fighters = $fighters->pluck('id');
167
                if (!App::runningUnitTests()){
168
                    $fighters = $fighters->shuffle();
169
                }
170
                $group = $this->saveGroup($area, $order, $round, null);
171
                $this->syncGroup($group, $fighters);
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Xoco70\KendoTournaments\TreeGen\TreeGen as the method syncGroup() does only exist in the following sub-classes of Xoco70\KendoTournaments\TreeGen\TreeGen: Xoco70\KendoTournaments\...nationCompetitorTreeGen, Xoco70\KendoTournaments\...tEliminationTeamTreeGen, Xoco70\KendoTournaments\...layOffCompetitorTreeGen, Xoco70\KendoTournaments\TreeGen\PlayOffTeamTreeGen. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

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

class MyUser extends 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 sub-classes 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 parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
172
                $order++;
173
            }
174
            $area++;
175
        }
176
    }
177
178
    /**
179
     * @param $area
180
     * @param $order
181
     * @param $round
182
     * @param $parent
183
     * @return FightersGroup
184
     */
185
    protected function saveGroup($area, $order, $round, $parent): FightersGroup
186
    {
187
        $group = new FightersGroup();
188
        $group->area = $area;
189
        $group->order = $order;
190
        $group->round = $round;
191
        $group->championship_id = $this->championship->id;
192
        if ($parent != null) {
193
            $group->parent_id = $parent->id;
194
        }
195
        $group->save();
196
        return $group;
197
    }
198
199
200
    /**
201
     * @param integer $groupSize
202
     * @return Collection
203
     */
204 View Code Duplication
    public function createByeGroup($groupSize): Collection
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...
205
    {
206
        $byeFighter = $this->createByeFighter();
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Xoco70\KendoTournaments\TreeGen\TreeGen as the method createByeFighter() does only exist in the following sub-classes of Xoco70\KendoTournaments\TreeGen\TreeGen: Xoco70\KendoTournaments\...nationCompetitorTreeGen, Xoco70\KendoTournaments\...tEliminationTeamTreeGen, Xoco70\KendoTournaments\...layOffCompetitorTreeGen, Xoco70\KendoTournaments\TreeGen\PlayOffTeamTreeGen. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

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

class MyUser extends 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 sub-classes 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 parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
207
        $group = new Collection();
208
        for ($i = 0; $i < $groupSize; $i++) {
209
            $group->push($byeFighter);
210
        }
211
        return $group;
212
    }
213
214
    /**
215
     * @param $fighters
216
     * @param Collection $fighterGroups
217
     * @return Collection
218
     */
219
    public function adjustFightersGroupWithByes($fighters, $fighterGroups): Collection
220
    {
221
        $tmpFighterGroups = clone $fighterGroups;
222
        $byeGroup = $this->getByeGroup($this->championship, $fighters);
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Xoco70\KendoTournaments\TreeGen\TreeGen as the method getByeGroup() does only exist in the following sub-classes of Xoco70\KendoTournaments\TreeGen\TreeGen: Xoco70\KendoTournaments\...nationCompetitorTreeGen, Xoco70\KendoTournaments\...irectEliminationTreeGen, Xoco70\KendoTournaments\...layOffCompetitorTreeGen, Xoco70\KendoTournaments\TreeGen\PlayOffTeamTreeGen, Xoco70\KendoTournaments\TreeGen\PlayOffTreeGen. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

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

class MyUser extends 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 sub-classes 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 parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
223
224
        // Get biggest competitor's group
225
        $max = $this->getMaxFightersByEntity($tmpFighterGroups);
226
227
        // We reacommodate them so that we can mix them up and they don't fight with another competitor of his entity.
228
229
        $fighters = $this->repart($fighterGroups, $max);
230
        $fighters = $this->insertByes($fighters, $byeGroup);
231
232
        return $fighters;
233
    }
234
235
    /**
236
     * Get All Groups on previous round
237
     * @param $currentRound
238
     * @return Collection
239
     */
240
    private function getPreviousRound($currentRound)
241
    {
242
        $previousRound = $this->championship->groupsByRound($currentRound + 1)->get();
243
        return $previousRound;
244
    }
245
246
    /**
247
     * Get the next group on the right ( parent ), final round being the ancestor
248
     * @param $matchNumber
249
     * @param Collection $previousRound
250
     * @return mixed
251
     */
252
    private function getParentGroup($matchNumber, $previousRound)
253
    {
254
        $parentIndex = intval(($matchNumber + 1) / 2);
255
        $parent = $previousRound->get($parentIndex - 1);
256
        return $parent;
257
    }
258
259
    /**
260
     * Save Groups with their parent info
261
     * @param integer $numRounds
262
     * @param $numFightersElim
263
     */
264
    protected function pushGroups($numRounds, $numFightersElim)
265
    {
266
        for ($roundNumber = 2; $roundNumber <= $numRounds; $roundNumber++) {
267
            // From last match to first match
268
            for ($matchNumber = 1; $matchNumber <= ($numFightersElim / pow(2, $roundNumber)); $matchNumber++) {
269
                $fighters = $this->createByeGroup(2);
270
                $group = $this->saveGroup(1, $matchNumber, $roundNumber, null);
271
                $this->syncGroup($group, $fighters);
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Xoco70\KendoTournaments\TreeGen\TreeGen as the method syncGroup() does only exist in the following sub-classes of Xoco70\KendoTournaments\TreeGen\TreeGen: Xoco70\KendoTournaments\...nationCompetitorTreeGen, Xoco70\KendoTournaments\...tEliminationTeamTreeGen, Xoco70\KendoTournaments\...layOffCompetitorTreeGen, Xoco70\KendoTournaments\TreeGen\PlayOffTeamTreeGen. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

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

class MyUser extends 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 sub-classes 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 parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
272
            }
273
        }
274
    }
275
276
    /**
277
     * Group Fighters by area
278
     * @return Collection
279
     * @throws TreeGenerationException
280
     */
281
    private function getFightersByArea()
282
    {
283
        // If previous trees already exists, delete all
284
        $this->championship->fightersGroups()->delete();
285
        $areas = $this->settings->fightingAreas;
286
        $fighters = $this->getFighters();
0 ignored issues
show
Bug introduced by
The method getFighters() does not exist on Xoco70\KendoTournaments\TreeGen\TreeGen. Did you maybe mean getFightersByEntity()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
287
288
289
        if ($fighters->count() / $areas < ChampionshipSettings::MIN_COMPETITORS_BY_AREA) {
290
            throw new TreeGenerationException();
291
        }
292
        // Get Competitor's / Team list ordered by entities ( Federation, Assoc, Club, etc...)
293
        $fighterByEntity = $this->getFightersByEntity($fighters); // Chunk(1)
294
        $fightersWithBye = $this->adjustFightersGroupWithByes($fighters, $fighterByEntity);
295
296
        // Chunk user by areas
297
        return $fightersWithBye->chunk(count($fightersWithBye) / $areas);
298
    }
299
300
    /**
301
     * Attach a parent to every child for nestedSet Navigation
302
     * @param $numFightersElim
303
     */
304
    private function addParentToChildren($numFightersElim)
305
    {
306
        $numRounds = intval(log($numFightersElim, 2));
307
308
        $groupsDesc = $this->championship
309
            ->fightersGroups()
310
            ->where('round', '<', $numRounds)
311
            ->orderByDesc('id')->get();
312
313
        $groupsDescByRound = $groupsDesc->groupBy('round');
314
315
        foreach ($groupsDescByRound as $round => $groups) {
316
            $previousRound = $this->getPreviousRound($round);
317
            foreach ($groups->reverse()->values() as $matchNumber => $group) {
318
                $parent = $this->getParentGroup($matchNumber + 1, $previousRound);
319
                $group->parent_id = $parent->id;
320
                $group->save();
321
            }
322
        }
323
    }
324
325
    /**
326
     * Create Bye Groups to adjust tree
327
     * @param $byeCount
328
     * @return Collection
329
     */
330 View Code Duplication
    protected function createNullsGroup($byeCount): Collection
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...
331
    {
332
        $byeFighter = $this->createByeFighter();
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Xoco70\KendoTournaments\TreeGen\TreeGen as the method createByeFighter() does only exist in the following sub-classes of Xoco70\KendoTournaments\TreeGen\TreeGen: Xoco70\KendoTournaments\...nationCompetitorTreeGen, Xoco70\KendoTournaments\...tEliminationTeamTreeGen, Xoco70\KendoTournaments\...layOffCompetitorTreeGen, Xoco70\KendoTournaments\TreeGen\PlayOffTeamTreeGen. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

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

class MyUser extends 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 sub-classes 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 parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
333
        $byeGroup = new Collection();
334
        for ($i = 0; $i < $byeCount; $i++) {
335
            $byeGroup->push($byeFighter);
336
        }
337
        return $byeGroup;
338
    }
339
340
    /**
341
     * @param Collection $fighters
342
     * @param $frequency
343
     * @param $sizeGroupBy
344
     * @param $bye
345
     * @return Collection
346
     */
347
    private function getFullFighterList(Collection $fighters, $frequency, $sizeGroupBy, $bye): Collection
348
    {
349
        $newFighters = new Collection();
350
        $count = 0;
351
        $byeCount = 0;
352
        foreach ($fighters as $fighter) {
353
            if ($this->shouldInsertBye($frequency, $sizeGroupBy, $count, $byeCount)) {
354
                $newFighters->push($bye);
355
                $byeCount++;
356
            }
357
            $newFighters->push($fighter);
358
            $count++;
359
        }
360
        return $newFighters;
361
    }
362
363
    /**
364
     * @param $frequency
365
     * @param $sizeGroupBy
366
     * @param $count
367
     * @param $byeCount
368
     * @return bool
369
     */
370
    private function shouldInsertBye($frequency, $sizeGroupBy, $count, $byeCount): bool
371
    {
372
        return $frequency != -1 && $count % $frequency == 0 && $byeCount < $sizeGroupBy;
373
    }
374
}
375