Completed
Push — master ( f65102...332511 )
by Julien
02:46
created

TreeGen::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 7
rs 9.4285
cc 1
eloc 5
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\Fight;
12
use Xoco70\KendoTournaments\Models\FightersGroup;
13
14
class TreeGen implements TreeGenerable
15
{
16
    protected $groupBy;
17
    protected $tree;
18
    public $championship;
19
    public $settings;
20
    protected $numFighters;
21
22
    /**
23
     * @param Championship $championship
24
     * @param $groupBy
25
     */
26
    public function __construct(Championship $championship, $groupBy)
27
    {
28
        $this->championship = $championship;
29
        $this->groupBy = $groupBy;
30
        $this->settings = $championship->getSettings();
31
        $this->tree = new Collection();
32
    }
33
34
    /**
35
     * Generate tree groups for a championship.
36
     *
37
     * @throws TreeGenerationException
38
     */
39
    public function run()
40
    {
41
        $usersByArea = $this->getFightersByArea();
42
        $numFighters = sizeof($usersByArea->collapse());
43
44
        $this->generateGroupsForRound($usersByArea, 1, 1);
45
        $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...
46
        $this->addParentToChildren($numFighters);
47
        $this->generateFights($this->championship);
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\...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->generateNextRoundsFights();
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 generateNextRoundsFights() 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...
49
        Fight::generateFightsId($this->championship);
50
    }
51
52
    /**
53
     * Get the biggest entity group
54
     * @param $userGroups
55
     *
56
     * @return int
57
     */
58
    private function getMaxFightersByEntity($userGroups): int
59
    {
60
        return $userGroups
61
            ->sortByDesc(function ($group) {
62
                return $group->count();
63
            })
64
            ->first()
65
            ->count();
66
67
    }
68
69
    /**
70
     * Get Competitor's list ordered by entities
71
     * Countries for Internation Tournament, State for a National Tournament, etc.
72
     *
73
     * @param $fighters
74
     * @return Collection
75
     */
76
    private function getFightersByEntity($fighters): Collection
77
    {
78
        // Right now, we are treating users and teams as equals.
79
        // It doesn't matter right now, because we only need name attribute which is common to both models
80
81
        // $this->groupBy contains federation_id, association_id, club_id, etc.
82
        if (($this->groupBy) != null) {
83
            $fighterGroups = $fighters->groupBy($this->groupBy); // Collection of Collection
84
        } else {
85
            $fighterGroups = $fighters->chunk(1); // Collection of Collection
86
        }
87
        return $fighterGroups;
88
    }
89
90
    /**
91
     * Get the size the first round will have
92
     * @param $fighterCount
93
     * @param $groupSize
94
     * @return int
95
     */
96
    protected function getTreeSize($fighterCount, $groupSize)
97
    {
98
        $square = collect([1, 2, 4, 8, 16, 32, 64]);
99
        $squareMultiplied = $square->map(function ($item) use ($groupSize) {
100
            return $item * $groupSize;
101
        });
102
103
        foreach ($squareMultiplied as $limit) {
104
            if ($fighterCount <= $limit) {
105
                return $limit;
106
            }
107
        }
108
        return 64 * $groupSize;
109
    }
110
111
    /**
112
     * Repart BYE in the tree,
113
     * @param $fighterGroups
114
     * @param int $max
115
     *
116
     * @return Collection
117
     */
118
    private function repart($fighterGroups, $max)
119
    {
120
        $fighters = new Collection();
121
        for ($i = 0; $i < $max; $i++) {
122
            foreach ($fighterGroups as $fighterGroup) {
123
                $fighter = $fighterGroup->values()->get($i);
124
                if ($fighter != null) {
125
                    $fighters->push($fighter);
126
                }
127
            }
128
        }
129
130
        return $fighters;
131
    }
132
133
    /**
134
     * Insert byes in an homogen way.
135
     *
136
     * @param Collection $fighters
137
     * @param Collection $byeGroup
138
     *
139
     * @return Collection
140
     */
141
    private function insertByes(Collection $fighters, Collection $byeGroup)
142
    {
143
        $bye = count($byeGroup) > 0 ? $byeGroup[0] : [];
144
        $sizeFighters = count($fighters);
145
        $sizeGroupBy = count($byeGroup);
146
147
        $frequency = $sizeGroupBy != 0
148
            ? (int)floor($sizeFighters / $sizeGroupBy)
149
            : -1;
150
151
        // Create Copy of $competitors
152
        return $this->getFullFighterList($fighters, $frequency, $sizeGroupBy, $bye);
153
    }
154
155
    /**
156
     * @param Collection $usersByArea
157
     * @param integer $area
158
     * @param integer $round
159
     *
160
     */
161
    public function generateGroupsForRound($usersByArea, $area, $round)
162
    {
163
        foreach ($usersByArea as $fightersByEntity) {
164
            // Chunking to make small round robin groups
165
            $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...
166
            $order = 1;
167
            foreach ($fightersGroup as $fighters) {
168
                $fighters = $fighters->pluck('id');
169
                if (!App::runningUnitTests()) {
170
                    $fighters = $fighters->shuffle();
171
                }
172
                $group = $this->saveGroup($area, $order, $round, null);
173
                $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...
174
                $order++;
175
            }
176
            $area++;
177
        }
178
    }
179
180
    /**
181
     * @param $area
182
     * @param $order
183
     * @param $round
184
     * @param $parent
185
     * @return FightersGroup
186
     */
187
    protected function saveGroup($area, $order, $round, $parent): FightersGroup
188
    {
189
        $group = new FightersGroup();
190
        $group->area = $area;
191
        $group->order = $order;
192
        $group->round = $round;
193
        $group->championship_id = $this->championship->id;
194
        if ($parent != null) {
195
            $group->parent_id = $parent->id;
196
        }
197
        $group->save();
198
        return $group;
199
    }
200
201
202
    /**
203
     * @param integer $groupSize
204
     * @return Collection
205
     */
206 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...
207
    {
208
        $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...
209
        $group = new Collection();
210
        for ($i = 0; $i < $groupSize; $i++) {
211
            $group->push($byeFighter);
212
        }
213
        return $group;
214
    }
215
216
    /**
217
     * @param $fighters
218
     * @param Collection $fighterGroups
219
     * @return Collection
220
     */
221
    public function adjustFightersGroupWithByes($fighters, $fighterGroups): Collection
222
    {
223
        $tmpFighterGroups = clone $fighterGroups;
224
        $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...
225
226
        // Get biggest competitor's group
227
        $max = $this->getMaxFightersByEntity($tmpFighterGroups);
228
229
        // We reacommodate them so that we can mix them up and they don't fight with another competitor of his entity.
230
231
        $fighters = $this->repart($fighterGroups, $max);
232
        $fighters = $this->insertByes($fighters, $byeGroup);
233
234
        return $fighters;
235
    }
236
237
    /**
238
     * Get All Groups on previous round
239
     * @param $currentRound
240
     * @return Collection
241
     */
242
    private function getPreviousRound($currentRound)
243
    {
244
        $previousRound = $this->championship->groupsByRound($currentRound + 1)->get();
245
        return $previousRound;
246
    }
247
248
    /**
249
     * Get the next group on the right ( parent ), final round being the ancestor
250
     * @param $matchNumber
251
     * @param Collection $previousRound
252
     * @return mixed
253
     */
254
    private function getParentGroup($matchNumber, $previousRound)
255
    {
256
        $parentIndex = intval(($matchNumber + 1) / 2);
257
        $parent = $previousRound->get($parentIndex - 1);
258
        return $parent;
259
    }
260
261
    /**
262
     * Save Groups with their parent info
263
     * @param integer $numRounds
264
     * @param $numFightersElim
265
     */
266
    protected function pushGroups($numRounds, $numFightersElim)
267
    {
268
        for ($roundNumber = 2; $roundNumber <= $numRounds; $roundNumber++) {
269
            // From last match to first match
270
            for ($matchNumber = 1; $matchNumber <= ($numFightersElim / pow(2, $roundNumber)); $matchNumber++) {
271
                $fighters = $this->createByeGroup(2);
272
                $group = $this->saveGroup(1, $matchNumber, $roundNumber, null);
273
                $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...
274
            }
275
        }
276
    }
277
278
    /**
279
     * Group Fighters by area
280
     * @return Collection
281
     * @throws TreeGenerationException
282
     */
283
    private function getFightersByArea()
284
    {
285
        // If previous trees already exists, delete all
286
        $this->championship->fightersGroups()->delete();
287
        $areas = $this->settings->fightingAreas;
288
        $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...
289
290
291
        if ($fighters->count() / $areas < ChampionshipSettings::MIN_COMPETITORS_BY_AREA) {
292
            throw new TreeGenerationException();
293
        }
294
        // Get Competitor's / Team list ordered by entities ( Federation, Assoc, Club, etc...)
295
        $fighterByEntity = $this->getFightersByEntity($fighters); // Chunk(1)
296
        $fightersWithBye = $this->adjustFightersGroupWithByes($fighters, $fighterByEntity);
297
298
        // Chunk user by areas
299
        return $fightersWithBye->chunk(count($fightersWithBye) / $areas);
300
    }
301
302
    /**
303
     * Attach a parent to every child for nestedSet Navigation
304
     * @param $numFightersElim
305
     */
306
    private function addParentToChildren($numFightersElim)
307
    {
308
        $numRounds = intval(log($numFightersElim, 2));
309
310
        $groupsDesc = $this->championship
311
            ->fightersGroups()
312
            ->where('round', '<', $numRounds)
313
            ->orderByDesc('id')->get();
314
315
        $groupsDescByRound = $groupsDesc->groupBy('round');
316
317
        foreach ($groupsDescByRound as $round => $groups) {
318
            $previousRound = $this->getPreviousRound($round);
319
            foreach ($groups->reverse()->values() as $matchNumber => $group) {
320
                $parent = $this->getParentGroup($matchNumber + 1, $previousRound);
321
                $group->parent_id = $parent->id;
322
                $group->save();
323
            }
324
        }
325
    }
326
327
    /**
328
     * Create Bye Groups to adjust tree
329
     * @param $byeCount
330
     * @return Collection
331
     */
332 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...
333
    {
334
        $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...
335
        $byeGroup = new Collection();
336
        for ($i = 0; $i < $byeCount; $i++) {
337
            $byeGroup->push($byeFighter);
338
        }
339
        return $byeGroup;
340
    }
341
342
    /**
343
     * @param Collection $fighters
344
     * @param $frequency
345
     * @param $sizeGroupBy
346
     * @param $bye
347
     * @return Collection
348
     */
349
    private function getFullFighterList(Collection $fighters, $frequency, $sizeGroupBy, $bye): Collection
350
    {
351
        $newFighters = new Collection();
352
        $count = 0;
353
        $byeCount = 0;
354
        foreach ($fighters as $fighter) {
355
            if ($this->shouldInsertBye($frequency, $sizeGroupBy, $count, $byeCount)) {
356
                $newFighters->push($bye);
357
                $byeCount++;
358
            }
359
            $newFighters->push($fighter);
360
            $count++;
361
        }
362
        return $newFighters;
363
    }
364
365
    /**
366
     * @param $frequency
367
     * @param $sizeGroupBy
368
     * @param $count
369
     * @param $byeCount
370
     * @return bool
371
     */
372
    private function shouldInsertBye($frequency, $sizeGroupBy, $count, $byeCount): bool
373
    {
374
        return $frequency != -1 && $count % $frequency == 0 && $byeCount < $sizeGroupBy;
375
    }
376
377
378
    /**
379
     * @param Championship $championship
380
     */
381
    protected static function destroyPreviousFights(Championship $championship)
382
    {
383
        // Delete previous fight for this championship
384
        $arrGroupsId = $championship->fightersGroups()->get()->pluck('id');
385
        Fight::destroy($arrGroupsId);
386
    }
387
}
388