Passed
Push — master ( 59b1f2...d98836 )
by Julien
33:15
created

TreeGen::pushEmptyGroupsToTree()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 11
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 6
nc 2
nop 1
1
<?php
2
3
namespace Xoco70\KendoTournaments\TreeGen;
4
5
use Illuminate\Support\Collection;
6
use Xoco70\KendoTournaments\Contracts\TreeGenerable;
7
use Xoco70\KendoTournaments\Exceptions\TreeGenerationException;
8
use Xoco70\KendoTournaments\Models\Championship;
9
use Xoco70\KendoTournaments\Models\ChampionshipSettings;
10
use Xoco70\KendoTournaments\Models\FightersGroup;
11
12
class TreeGen implements TreeGenerable
13
{
14
    protected $groupBy;
15
    protected $tree;
16
    public $championship;
17
    public $settings;
18
    protected $numFighters;
19
20
    /**
21
     * @param Championship $championship
22
     * @param $groupBy
23
     */
24
    public function __construct(Championship $championship, $groupBy)
25
    {
26
        $this->championship = $championship;
27
        $this->groupBy = $groupBy;
28
        $this->settings = $championship->getSettings();
29
        $this->tree = new Collection();
30
    }
31
32
    /**
33
     * Generate tree groups for a championship.
34
     *
35
     * @throws TreeGenerationException
36
     */
37
    public function run()
38
    {
39
        $usersByArea = $this->getFightersByArea();
40
        $numFighters = sizeof($usersByArea->collapse());
41
42
        $this->generateGroupsForRound($usersByArea, $area = 1, $round = 1, $shuffle = 1);
43
        $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...
44
        $this->addParentToChildren($numFighters);
45
        // Now add parents to all
46
    }
47
48
    /**
49
     * Get the biggest entity group
50
     * @param $userGroups
51
     *
52
     * @return int
53
     */
54
    private function getMaxFightersByEntity($userGroups): int
55
    {
56
        return $userGroups
57
            ->sortByDesc(function ($group) {
58
                return $group->count();
59
            })
60
            ->first()
61
            ->count();
62
63
    }
64
65
    /**
66
     * Get Competitor's list ordered by entities
67
     * Countries for Internation Tournament, State for a National Tournament, etc.
68
     *
69
     * @return Collection
70
     */
71
    private function getFightersByEntity($fighters): Collection
72
    {
73
        // Right now, we are treating users and teams as equals.
74
        // It doesn't matter right now, because we only need name attribute which is common to both models
75
76
        // $this->groupBy contains federation_id, association_id, club_id, etc.
77
        if (($this->groupBy) != null) {
78
            $fighterGroups = $fighters->groupBy($this->groupBy); // Collection of Collection
79
        } else {
80
            $fighterGroups = $fighters->chunk(1); // Collection of Collection
81
        }
82
        return $fighterGroups;
83
    }
84
85
    /**
86
     * Get the size the first round will have
87
     * @param $fighterCount
88
     * @return int
89
     */
90
    protected function getTreeSize($fighterCount, $groupSize)
91
    {
92
        $square = collect([1, 2, 4, 8, 16, 32, 64]);
93
        $squareMultiplied = $square->map(function ($item, $key) use ($groupSize) {
0 ignored issues
show
Unused Code introduced by
The parameter $key is not used and could be removed.

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

Loading history...
94
            return $item * $groupSize;
95
        });
96
97
        foreach ($squareMultiplied as $limit) {
98
            if ($fighterCount <= $limit) {
99
                return $limit;
100
            }
101
        }
102
        return 64 * $groupSize;
103
    }
104
105
    /**
106
     * Repart BYE in the tree,
107
     * @param $fighterGroups
108
     * @param int $max
109
     *
110
     * @return Collection
111
     */
112
    private function repart($fighterGroups, $max)
113
    {
114
        $fighters = new Collection();
115
        for ($i = 0; $i < $max; $i++) {
116
            foreach ($fighterGroups as $fighterGroup) {
117
                $fighter = $fighterGroup->values()->get($i);
118
                if ($fighter != null) {
119
                    $fighters->push($fighter);
120
                }
121
            }
122
        }
123
124
        return $fighters;
125
    }
126
127
    /**
128
     * Insert byes in an homogen way.
129
     *
130
     * @param Collection $fighters
131
     * @param Collection $byeGroup
132
     *
133
     * @return Collection
134
     */
135
    private function insertByes(Collection $fighters, Collection $byeGroup)
136
    {
137
        $bye = count($byeGroup) > 0 ? $byeGroup[0] : [];
138
        $sizeFighters = count($fighters);
139
        $sizeGroupBy = count($byeGroup);
140
141
        $frequency = $sizeGroupBy != 0
142
            ? (int)floor($sizeFighters / $sizeGroupBy)
143
            : -1;
144
145
        // Create Copy of $competitors
146
        $newFighters = new Collection();
147
        $i = 0;
148
        $byeCount = 0;
149
        foreach ($fighters as $fighter) {
150
            if ($frequency != -1 && $i % $frequency == 0 && $byeCount < $sizeGroupBy) {
151
                $newFighters->push($bye);
152
                $byeCount++;
153
            }
154
            $newFighters->push($fighter);
155
            $i++;
156
        }
157
158
        return $newFighters;
159
    }
160
161
    /**
162
     * @param Collection $usersByArea
163
     * @param integer $area
164
     * @param integer $round
165
     * @param integer $shuffle
166
     *
167
     */
168
    public function generateGroupsForRound($usersByArea, $area, $round, $shuffle)
169
    {
170
        foreach ($usersByArea as $fightersByEntity) {
171
            // Chunking to make small round robin groups
172
            $fightersGroup = $this->chunkAndShuffle($round, $shuffle, $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...
173
            $order = 1;
174
            foreach ($fightersGroup as $value => $fighters) {
175
                $fighters = $fighters->pluck('id');
176
                if ($shuffle) {
177
                    $fighters = $fighters->shuffle();
178
                }
179
                $group = $this->saveGroup($area, $order, $round, $parent = null);
180
                $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...
181
                $order++;
182
            }
183
            $area++;
184
        }
185
    }
186
187
    /**
188
     * @param $area
189
     * @param $order
190
     * @param $round
191
     * @param $parent
192
     * @return FightersGroup
193
     */
194
    protected function saveGroup($area, $order, $round, $parent): FightersGroup
195
    {
196
        $group = new FightersGroup();
197
        $group->area = $area;
198
        $group->order = $order;
199
        $group->round = $round;
200
        $group->championship_id = $this->championship->id;
201
        if ($parent != null) {
202
            $group->parent_id = $parent->id;
203
        }
204
        $group->save();
205
        return $group;
206
    }
207
208
209
    /**
210
     * @param integer $groupSize
211
     * @return Collection
212
     */
213 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...
214
    {
215
        $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...
216
        $group = new Collection();
217
        for ($i = 0; $i < $groupSize; $i++) {
218
            $group->push($byeFighter);
219
        }
220
        return $group;
221
    }
222
223
    /**
224
     * @param $fighters
225
     * @param Collection $fighterGroups
226
     * @return Collection
227
     */
228
    public function adjustFightersGroupWithByes($fighters, $fighterGroups): Collection
229
    {
230
        $tmpFighterGroups = clone $fighterGroups;
231
        $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...
232
233
        // Get biggest competitor's group
234
        $max = $this->getMaxFightersByEntity($tmpFighterGroups);
235
236
        // We reacommodate them so that we can mix them up and they don't fight with another competitor of his entity.
237
238
        $fighters = $this->repart($fighterGroups, $max);
239
        $fighters = $this->insertByes($fighters, $byeGroup);
240
241
        return $fighters;
242
    }
243
244
    /**
245
     * Get All Groups on previous round
246
     * @param $currentRound
247
     * @return Collection
248
     */
249
    private function getPreviousRound($currentRound)
250
    {
251
        $previousRound = $this->championship->groupsByRound($currentRound + 1)->get();
252
        return $previousRound;
253
    }
254
255
    /**
256
     * Get the next group on the right ( parent ), final round being the ancestor
257
     * @param $matchNumber
258
     * @param Collection $previousRound
259
     * @return mixed
260
     */
261
    private function getParentGroup($matchNumber, $previousRound)
262
    {
263
        $parentIndex = intval(($matchNumber + 1) / 2);
264
        $parent = $previousRound->get($parentIndex - 1);
265
        return $parent;
266
    }
267
268
    /**
269
     * Save Groups with their parent info
270
     * @param integer $numRounds
271
     * @param $numFightersEliminatory
272
     */
273
    protected function pushGroups($numRounds, $numFightersEliminatory)
274
    {
275
        for ($roundNumber = 2; $roundNumber <= $numRounds; $roundNumber++) {
276
            // From last match to first match
277
            for ($matchNumber = 1; $matchNumber <= ($numFightersEliminatory / pow(2, $roundNumber)); $matchNumber++) {
278
                $fighters = $this->createByeGroup(2);
279
                $group = $this->saveGroup($area = 1, $order = $matchNumber, $roundNumber, $parent = null);
280
                $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...
281
            }
282
        }
283
    }
284
285
    /**
286
     * Group Fighters by area
287
     * @return Collection
288
     * @throws TreeGenerationException
289
     */
290
    private function getFightersByArea()
291
    {
292
        // If previous trees already exists, delete all
293
        $this->championship->fightersGroups()->delete();
294
        $areas = $this->settings->fightingAreas;
295
        $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...
296
297
298
        if ($fighters->count() / $areas < ChampionshipSettings::MIN_COMPETITORS_BY_AREA) {
299
            throw new TreeGenerationException();
300
        }
301
        // Get Competitor's / Team list ordered by entities ( Federation, Assoc, Club, etc...)
302
        $fighterByEntity = $this->getFightersByEntity($fighters); // Chunk(1)
303
        $fightersWithBye = $this->adjustFightersGroupWithByes($fighters, $fighterByEntity);
304
305
        // Chunk user by areas
306
        return $fightersWithBye->chunk(count($fightersWithBye) / $areas);
307
    }
308
309
    /**
310
     * Attach a parent to every child for nestedSet Navigation
311
     */
312
    private function addParentToChildren($numFightersEliminatory)
313
    {
314
        $numRounds = intval(log($numFightersEliminatory, 2));
315
316
        $groupsDesc = $this->championship
317
            ->fightersGroups()
318
            ->where('round', '<', $numRounds)
319
            ->orderByDesc('id')->get();
320
321
        $groupsDescByRound = $groupsDesc->groupBy('round');
322
323
        foreach ($groupsDescByRound as $round => $groups) {
324
            $previousRound = $this->getPreviousRound($round, $numRounds);
0 ignored issues
show
Unused Code introduced by
The call to TreeGen::getPreviousRound() has too many arguments starting with $numRounds.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
325
            foreach ($groups->reverse()->values() as $matchNumber => $group) {
326
                $parent = $this->getParentGroup($matchNumber + 1, $previousRound);
327
                $group->parent_id = $parent->id;
328
                $group->save();
329
            }
330
        }
331
    }
332
333
    /**
334
     * Create Bye Groups to adjust tree
335
     * @param $byeCount
336
     * @return Collection
337
     */
338 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...
339
    {
340
        $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...
341
        $byeGroup = new Collection();
342
        for ($i = 0; $i < $byeCount; $i++) {
343
            $byeGroup->push($byeFighter);
344
        }
345
        return $byeGroup;
346
    }
347
}
348