Completed
Push — master ( 0a3a64...75d382 )
by Julien
02:47
created

TreeGen::adjustFightersGroupWithByes()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 15
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 15
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 7
nc 1
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\DirectEliminationFight;
12
use Xoco70\KendoTournaments\Models\Fight;
13
use Xoco70\KendoTournaments\Models\FightersGroup;
14
use Xoco70\KendoTournaments\Models\PreliminaryFight;
15
16
class TreeGen implements TreeGenerable
17
{
18
    protected $groupBy;
19
    protected $tree;
20
    public $championship;
21
    public $settings;
22
    protected $numFighters;
23
24
    /**
25
     * @param Championship $championship
26
     * @param $groupBy
27
     */
28
    public function __construct(Championship $championship, $groupBy)
29
    {
30
        $this->championship = $championship;
31
        $this->groupBy = $groupBy;
32
        $this->settings = $championship->getSettings();
33
        $this->tree = new Collection();
34
    }
35
36
    /**
37
     * Generate tree groups for a championship.
38
     *
39
     * @throws TreeGenerationException
40
     */
41
    public function run()
42
    {
43
        $usersByArea = $this->getFightersByArea();
44
        $numFighters = sizeof($usersByArea->collapse());
45
46
        $this->generateGroupsForRound($usersByArea, 1, 1);
47
        $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\...tEliminationTeamTreeGen, 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...
48
        $this->addParentToChildren($numFighters);
49
        $this->generateFights();
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 generateFights() does only exist in the following sub-classes of Xoco70\KendoTournaments\TreeGen\TreeGen: Xoco70\KendoTournaments\...nationCompetitorTreeGen, Xoco70\KendoTournaments\...tEliminationTeamTreeGen, 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...
50
        $this->generateNextRoundsFights();
51
//        Fight::generateFightsId($this->championship);
0 ignored issues
show
Unused Code Comprehensibility introduced by
63% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
52
//dd("ok");
53
    }
54
55
    /**
56
     * Get the biggest entity group
57
     * @param $userGroups
58
     *
59
     * @return int
60
     */
61
    private function getMaxFightersByEntity($userGroups): int
62
    {
63
        return $userGroups
64
            ->sortByDesc(function ($group) {
65
                return $group->count();
66
            })
67
            ->first()
68
            ->count();
69
70
    }
71
72
    /**
73
     * Get Competitor's list ordered by entities
74
     * Countries for Internation Tournament, State for a National Tournament, etc.
75
     *
76
     * @param $fighters
77
     * @return Collection
78
     */
79
    private function getFightersByEntity($fighters): Collection
80
    {
81
        // Right now, we are treating users and teams as equals.
82
        // It doesn't matter right now, because we only need name attribute which is common to both models
83
84
        // $this->groupBy contains federation_id, association_id, club_id, etc.
85
        if (($this->groupBy) != null) {
86
            $fighterGroups = $fighters->groupBy($this->groupBy); // Collection of Collection
87
        } else {
88
            $fighterGroups = $fighters->chunk(1); // Collection of Collection
89
        }
90
        return $fighterGroups;
91
    }
92
93
    /**
94
     * Get the size the first round will have
95
     * @param $fighterCount
96
     * @param $groupSize
97
     * @return int
98
     */
99
    protected function getTreeSize($fighterCount, $groupSize)
100
    {
101
        $square = collect([1, 2, 4, 8, 16, 32, 64]);
102
        $squareMultiplied = $square->map(function ($item) use ($groupSize) {
103
            return $item * $groupSize;
104
        });
105
106
        foreach ($squareMultiplied as $limit) {
107
            if ($fighterCount <= $limit) {
108
                return $limit;
109
            }
110
        }
111
        return 64 * $groupSize;
112
    }
113
114
    /**
115
     * Repart BYE in the tree,
116
     * @param $fighterGroups
117
     * @param int $max
118
     *
119
     * @return Collection
120
     */
121
    private function repart($fighterGroups, $max)
122
    {
123
        $fighters = new Collection();
124
        for ($i = 0; $i < $max; $i++) {
125
            foreach ($fighterGroups as $fighterGroup) {
126
                $fighter = $fighterGroup->values()->get($i);
127
                if ($fighter != null) {
128
                    $fighters->push($fighter);
129
                }
130
            }
131
        }
132
133
        return $fighters;
134
    }
135
136
    /**
137
     * Insert byes in an homogen way.
138
     *
139
     * @param Collection $fighters
140
     * @param Collection $byeGroup
141
     *
142
     * @return Collection
143
     */
144
    private function insertByes(Collection $fighters, Collection $byeGroup)
145
    {
146
        $bye = count($byeGroup) > 0 ? $byeGroup[0] : [];
147
        $sizeFighters = count($fighters);
148
        $sizeGroupBy = count($byeGroup);
149
150
        $frequency = $sizeGroupBy != 0
151
            ? (int)floor($sizeFighters / $sizeGroupBy)
152
            : -1;
153
154
        // Create Copy of $competitors
155
        return $this->getFullFighterList($fighters, $frequency, $sizeGroupBy, $bye);
156
    }
157
158
    /**
159
     * @param Collection $usersByArea
160
     * @param integer $area
161
     * @param integer $round
162
     *
163
     */
164
    public function generateGroupsForRound($usersByArea, $area, $round)
165
    {
166
        foreach ($usersByArea as $fightersByEntity) {
167
            // Chunking to make small round robin groups
168
            $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\...tEliminationTeamTreeGen, 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...
169
            $order = 1;
170
            foreach ($fightersGroup as $fighters) {
171
                $fighters = $fighters->pluck('id');
172
                if (!App::runningUnitTests()) {
173
                    $fighters = $fighters->shuffle();
174
                }
175
                $group = $this->saveGroup($area, $order, $round, null);
176
                $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...
177
                $order++;
178
            }
179
            $area++;
180
        }
181
    }
182
183
    /**
184
     * @param $area
185
     * @param $order
186
     * @param $round
187
     * @param $parent
188
     * @return FightersGroup
189
     */
190
    protected function saveGroup($area, $order, $round, $parent): FightersGroup
191
    {
192
        $group = new FightersGroup();
193
        $group->area = $area;
194
        $group->order = $order;
195
        $group->round = $round;
196
        $group->championship_id = $this->championship->id;
197
        if ($parent != null) {
198
            $group->parent_id = $parent->id;
199
        }
200
        $group->save();
201
        return $group;
202
    }
203
204
205
    /**
206
     * @param integer $groupSize
207
     * @return Collection
208
     */
209 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...
210
    {
211
        $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...
212
        $group = new Collection();
213
        for ($i = 0; $i < $groupSize; $i++) {
214
            $group->push($byeFighter);
215
        }
216
        return $group;
217
    }
218
219
    /**
220
     * @param $fighters
221
     * @param Collection $fighterGroups
222
     * @return Collection
223
     */
224
    public function adjustFightersGroupWithByes($fighters, $fighterGroups): Collection
225
    {
226
        $tmpFighterGroups = clone $fighterGroups;
227
        $byeGroup = $this->getByeGroup($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\...tEliminationTeamTreeGen, 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...
228
229
        // Get biggest competitor's group
230
        $max = $this->getMaxFightersByEntity($tmpFighterGroups);
231
232
        // We reacommodate them so that we can mix them up and they don't fight with another competitor of his entity.
233
234
        $fighters = $this->repart($fighterGroups, $max);
235
        $fighters = $this->insertByes($fighters, $byeGroup);
236
237
        return $fighters;
238
    }
239
240
    /**
241
     * Get All Groups on previous round
242
     * @param $currentRound
243
     * @return Collection
244
     */
245
    private function getPreviousRound($currentRound)
246
    {
247
        $previousRound = $this->championship->groupsByRound($currentRound + 1)->get();
248
        return $previousRound;
249
    }
250
251
    /**
252
     * Get the next group on the right ( parent ), final round being the ancestor
253
     * @param $matchNumber
254
     * @param Collection $previousRound
255
     * @return mixed
256
     */
257
    private function getParentGroup($matchNumber, $previousRound)
258
    {
259
        $parentIndex = intval(($matchNumber + 1) / 2);
260
        $parent = $previousRound->get($parentIndex - 1);
261
        return $parent;
262
    }
263
264
265
    /**
266
     * Group Fighters by area
267
     * @return Collection
268
     * @throws TreeGenerationException
269
     */
270
    private function getFightersByArea()
271
    {
272
        // If previous trees already exists, delete all
273
        $this->championship->fightersGroups()->delete();
274
        $areas = $this->settings->fightingAreas;
275
        $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...
276
277
278
        if ($fighters->count() / $areas < ChampionshipSettings::MIN_COMPETITORS_BY_AREA) {
279
            throw new TreeGenerationException();
280
        }
281
        // Get Competitor's / Team list ordered by entities ( Federation, Assoc, Club, etc...)
282
        $fighterByEntity = $this->getFightersByEntity($fighters); // Chunk(1)
283
        $fightersWithBye = $this->adjustFightersGroupWithByes($fighters, $fighterByEntity);
284
285
        // Chunk user by areas
286
        return $fightersWithBye->chunk(count($fightersWithBye) / $areas);
287
    }
288
289
    /**
290
     * Attach a parent to every child for nestedSet Navigation
291
     * @param $numFightersElim
292
     */
293
    private function addParentToChildren($numFightersElim)
294
    {
295
        $numRounds = $this->getNumRounds($numFightersElim);
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 getNumRounds() does only exist in the following sub-classes of Xoco70\KendoTournaments\TreeGen\TreeGen: Xoco70\KendoTournaments\...nationCompetitorTreeGen, Xoco70\KendoTournaments\...tEliminationTeamTreeGen, 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...
296
        $groupsDesc = $this->championship
297
            ->fightersGroups()
298
            ->where('round', '<', $numRounds)
299
            ->orderByDesc('id')->get();
300
301
        $groupsDescByRound = $groupsDesc->groupBy('round');
302
303
        foreach ($groupsDescByRound as $round => $groups) {
304
            $previousRound = $this->getPreviousRound($round);
305
            foreach ($groups->reverse()->values() as $matchNumber => $group) {
306
                $parent = $this->getParentGroup($matchNumber + 1, $previousRound);
307
                $group->parent_id = $parent->id;
308
                $group->save();
309
            }
310
        }
311
    }
312
313
    /**
314
     * Create Bye Groups to adjust tree
315
     * @param $byeCount
316
     * @return Collection
317
     */
318 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...
319
    {
320
        $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...
321
        $byeGroup = new Collection();
322
        for ($i = 0; $i < $byeCount; $i++) {
323
            $byeGroup->push($byeFighter);
324
        }
325
        return $byeGroup;
326
    }
327
328
    /**
329
     * @param Collection $fighters
330
     * @param $frequency
331
     * @param $sizeGroupBy
332
     * @param $bye
333
     * @return Collection
334
     */
335
    private function getFullFighterList(Collection $fighters, $frequency, $sizeGroupBy, $bye): Collection
336
    {
337
        $newFighters = new Collection();
338
        $count = 0;
339
        $byeCount = 0;
340
        foreach ($fighters as $fighter) {
341
            if ($this->shouldInsertBye($frequency, $sizeGroupBy, $count, $byeCount)) {
342
                $newFighters->push($bye);
343
                $byeCount++;
344
            }
345
            $newFighters->push($fighter);
346
            $count++;
347
        }
348
        return $newFighters;
349
    }
350
351
    /**
352
     * @param $frequency
353
     * @param $sizeGroupBy
354
     * @param $count
355
     * @param $byeCount
356
     * @return bool
357
     */
358
    private function shouldInsertBye($frequency, $sizeGroupBy, $count, $byeCount): bool
359
    {
360
        return $frequency != -1 && $count % $frequency == 0 && $byeCount < $sizeGroupBy;
361
    }
362
363
364
    /**
365
     * @param Championship $championship
0 ignored issues
show
Bug introduced by
There is no parameter named $championship. 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...
366
     */
367
    protected function destroyPreviousFights()
368
    {
369
        // Delete previous fight for this championship
370
        $arrGroupsId = $this->championship->fightersGroups()->get()->pluck('id');
371
        Fight::destroy($arrGroupsId);
372
    }
373
374
375
    /**
376
     * Generate Fights for next rounds
377
     */
378
    public function generateNextRoundsFights()
379
    {
380
        $fightersCount = $this->championship->competitors->count() + $this->championship->teams->count();
381
        $maxRounds = $this->getNumRounds($fightersCount);
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 getNumRounds() does only exist in the following sub-classes of Xoco70\KendoTournaments\TreeGen\TreeGen: Xoco70\KendoTournaments\...nationCompetitorTreeGen, Xoco70\KendoTournaments\...tEliminationTeamTreeGen, 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...
382
        for ($numRound = 1; $numRound < $maxRounds; $numRound++) {
383
            $groupsByRound = $this->championship->fightersGroups()->where('round', $numRound)->with('parent', 'children')->get();
384
            $this->updateParentFight($groupsByRound); // should be groupsByRound
385
        }
386
    }
387
388
    /**
389
     * @param $groupsByRound
390
     */
391
    protected function updateParentFight($groupsByRound)
392
    {
393
        foreach ($groupsByRound as $keyGroup => $group) {
394
            $parentGroup = $group->parent;
395
            if ($parentGroup == null) break;
396
            $parentFight = $parentGroup->fights->get(0);
397
398
            // determine whether c1 or c2 must be updated
399
            $this->chooseAndUpdateParentFight($keyGroup, $group, $parentFight);
400
        }
401
    }
402
403
    /**
404
     * @param $group
405
     * @param $parentFight
406
     */
407
    protected function chooseAndUpdateParentFight($keyGroup, FightersGroup $group, Fight $parentFight)
408
    {
409
        // we need to know if the child has empty fighters, is this BYE or undetermined
410
        if ($group->hasDeterminedParent()) {
411
            $valueToUpdate = $group->getValueToUpdate(); // This should be OK
412
            if ($valueToUpdate != null) {
413
                $fighterToUpdate = $group->getParentFighterToUpdate($keyGroup);
414
                dump("key:".$keyGroup."-".$fighterToUpdate."=>".$valueToUpdate);
415
                $parentFight->$fighterToUpdate = $valueToUpdate;
416
                $parentFight->save();
417
            }
418
        }
419
    }
420
}
421