Completed
Push — master ( 41c1b8...07a877 )
by Dan
02:04
created

Round::dealHands()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 8
ccs 2
cts 2
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 5
nc 1
nop 0
crap 1
1
<?php
2
3
namespace Cysha\Casino\Holdem\Game;
4
5
use Cysha\Casino\Cards\Contracts\CardResults;
6
use Cysha\Casino\Game\Chips;
7
use Cysha\Casino\Game\ChipStackCollection;
8
use Cysha\Casino\Game\Contracts\Dealer as DealerContract;
9
use Cysha\Casino\Game\Contracts\GameParameters;
10
use Cysha\Casino\Game\Contracts\Player as PlayerContract;
11
use Cysha\Casino\Game\PlayerCollection;
12
use Cysha\Casino\Holdem\Exceptions\RoundException;
13
use Cysha\Casino\Holdem\Game\LeftToAct;
14
15
class Round
16
{
17
    /**
18
     * @var Table
19
     */
20
    private $table;
21
22
    /**
23
     * @var ChipStackCollection
24
     */
25
    private $betStacks;
26
27
    /**
28
     * @var PlayerCollection
29
     */
30
    private $foldedPlayers;
31
32
    /**
33
     * @var ChipPotCollection
34
     */
35
    private $chipPots;
36
37
    /**
38
     * @var ChipPot
39
     */
40
    private $currentPot;
41
42
    /**
43
     * @var ActionCollection
44
     */
45
    private $playerActions;
46
47
    /**
48
     * @var PlayerCollection
49
     */
50
    private $leftToAct;
51
52
    /**
53
     * @var GameParameters
54
     */
55
    private $gameRules;
56
57
    /**
58
     * Round constructor.
59
     *
60
     * @param Table          $table
61
     * @param GameParameters $gameRules
62
     */
63
    private function __construct(Table $table, GameParameters $gameRules)
64
    {
65
        $this->table = $table;
66
        $this->chipPots = ChipPotCollection::make();
67
        $this->currentPot = ChipPot::create();
68
        $this->betStacks = ChipStackCollection::make();
69
        $this->foldedPlayers = PlayerCollection::make();
70
        $this->playerActions = ActionCollection::make();
71
        $this->leftToAct = LeftToAct::make();
72
        $this->gameRules = $gameRules;
73
74
        // shuffle the deck ready
75
        $this->dealer()->shuffleDeck();
76
77 50
        // add the default pot to the chipPots
78
        $this->chipPots->push($this->currentPot);
79 50
80 50
        // init the betStacks and actions for each player
81 50
        $this->resetBetStacks();
82 50
        $this->setupLeftToAct();
83 50
    }
84 50
85 50
    /**
86 50
     * Start a Round of poker.
87 50
     *
88 50
     * @param Table          $table
89
     * @param GameParameters $gameRules
90
     *
91 50
     * @return Round
92
     */
93
    public static function start(Table $table, GameParameters $gameRules): Round
94 50
    {
95
        return new static($table, $gameRules);
96
    }
97 50
98 50
    /**
99 50
     * Run the cleanup procedure for an end of Round.
100
     */
101
    public function end()
102
    {
103
        $this->dealer()->checkCommunityCards();
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Cysha\Casino\Game\Contracts\Dealer as the method checkCommunityCards() does only exist in the following implementations of said interface: Cysha\Casino\Holdem\Game\Dealer.

Let’s take a look at an example:

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

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

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

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

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

Available Fixes

  1. Change the type-hint for the parameter:

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

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

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
104
105
        $this->collectChipTotal();
106
107
        $this->distributeWinnings();
108 50
109
        $this->table()->moveButton();
110 50
    }
111
112
    /**
113
     * @return DealerContract
114
     */
115
    public function dealer(): DealerContract
116 11
    {
117
        return $this->table->dealer();
118
    }
119
120 11
    /**
121
     * @return PlayerCollection
122 11
     */
123
    public function players(): PlayerCollection
124 11
    {
125 11
        return $this->table->players();
126
    }
127
128
    /**
129
     * @return PlayerCollection
130 50
     */
131
    public function playersStillIn(): PlayerCollection
132 50
    {
133
        return $this->table->playersSatDown()->diff($this->foldedPlayers());
134
    }
135
136
    /**
137
     * @return PlayerCollection
138 3
     */
139
    public function foldedPlayers(): PlayerCollection
140 3
    {
141
        return $this->foldedPlayers;
142
    }
143
144
    /**
145
     * @return ActionCollection
146 12
     */
147
    public function playerActions(): ActionCollection
148 12
    {
149
        return $this->playerActions;
150
    }
151
152
    /**
153
     * @return LeftToAct
154 19
     */
155
    public function leftToAct(): LeftToAct
156 19
    {
157
        return $this->leftToAct;
158
    }
159
160
    /**
161
     * @return Table
162 1
     */
163
    public function table(): Table
164 1
    {
165
        return $this->table;
166
    }
167
168
    /**
169
     * @return ChipStackCollection
170 35
     */
171
    public function betStacks(): ChipStackCollection
172 35
    {
173
        return $this->betStacks;
174
    }
175
176
    /**
177
     * @return GameParameters
178 36
     */
179
    public function gameRules(): GameParameters
180 36
    {
181
        return $this->gameRules;
182
    }
183
184
    /**
185
     * @return int
186 50
     */
187
    public function betStacksTotal(): int
188 50
    {
189
        return $this->betStacks()->total()->amount();
190
    }
191
192
    public function dealHands()
193
    {
194 13
        $players = $this->table()
195
            ->playersSatDown()
196 13
            ->resetPlayerListFromSeat($this->table()->button() + 1);
197
198
        $this->dealer()->dealHands($players);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Cysha\Casino\Game\Contracts\Dealer as the method dealHands() does only exist in the following implementations of said interface: Cysha\Casino\Holdem\Game\Dealer.

Let’s take a look at an example:

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

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

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

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

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

Available Fixes

  1. Change the type-hint for the parameter:

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

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

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
199
    }
200
201
    /**
202 29
     * Runs over each chipPot and assigns the chips to the winning player.
203
     */
204 29
    private function distributeWinnings()
205
    {
206
        $this->chipPots()
207
            ->reverse()
208
            ->each(function (ChipPot $chipPot) {
209
                // if only 1 player participated to pot, he wins it no arguments
210 22
                if ($chipPot->players()->count() === 1) {
211
                    $potTotal = $chipPot->chips()->total();
212 22
213 22
                    $chipPot->players()->first()->chipStack()->add($potTotal);
214
215
                    $this->chipPots()->remove($chipPot);
216
217
                    return;
218 2
                }
219
220 2
                $activePlayers = $chipPot->players()->diff($this->foldedPlayers());
221
222
                $playerHands = $this->dealer()->hands()->findByPlayers($activePlayers);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Cysha\Casino\Game\Contracts\Dealer as the method hands() does only exist in the following implementations of said interface: Cysha\Casino\Holdem\Game\Dealer.

Let’s take a look at an example:

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

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

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

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

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

Available Fixes

  1. Change the type-hint for the parameter:

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

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

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
223
                $evaluate = $this->dealer()->evaluateHands($this->dealer()->communityCards(), $playerHands);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Cysha\Casino\Game\Contracts\Dealer as the method communityCards() does only exist in the following implementations of said interface: Cysha\Casino\Holdem\Game\Dealer.

Let’s take a look at an example:

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

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

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

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

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

Available Fixes

  1. Change the type-hint for the parameter:

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

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

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
224
225
                // if just 1, the player with that hand wins
226 11
                if ($evaluate->count() === 1) {
227
                    $player = $evaluate->first()->hand()->player();
228 11
                    $potTotal = $chipPot->chips()->total();
229 11
230
                    $player->chipStack()->add($potTotal);
231
232 11
                    $this->chipPots()->remove($chipPot);
233 6
0 ignored issues
show
Coding Style introduced by
Blank line found at end of control structure
Loading history...
234
                } else {
235 6
                    // if > 1 hand is evaluated as highest, split the pot evenly between the players
236
237 6
                    $potTotal = $chipPot->chips()->total();
238
239 6
                    // split the pot between the number of players
240
                    $splitTotal = Chips::fromAmount(($potTotal->amount() / $evaluate->count()));
241
                    $evaluate->each(function (CardResults $result) use ($splitTotal) {
242 11
                        $result->hand()->player()->chipStack()->add($splitTotal);
243
                    });
244 11
245 11
                    $this->chipPots()->remove($chipPot);
246
                }
247
            })
248 11
        ;
249 11
    }
250 11
251
    /**
252 11
     * @param Player $actualPlayer
253
     *
254 11
     * @return bool
255
     */
256
    public function playerIsStillIn(PlayerContract $actualPlayer)
257
    {
258
        $playerCount = $this->playersStillIn()->filter->equals($actualPlayer)->count();
259 1
260
        return $playerCount === 1;
261
    }
262 1
263
    /**
264 1
     * @return PlayerContract
265 1
     */
266
    public function playerWithButton(): PlayerContract
267 1
    {
268
        return $this->table()->locatePlayerWithButton();
269 11
    }
270
271 11
    /**
272
     * @return PlayerContract
273
     */
274
    public function playerWithSmallBlind(): PlayerContract
275
    {
276
        if ($this->table()->playersSatDown()->count() === 2) {
277
            return $this->table()->playersSatDown()->get(0);
278 1
        }
279
280 1
        return $this->table()->playersSatDown()->get($this->table()->button() + 1);
281
    }
282 1
283
    /**
284
     * @return PlayerContract
285
     */
286
    public function playerWithBigBlind(): PlayerContract
287
    {
288 7
        if ($this->table()->playersSatDown()->count() === 2) {
289
            return $this->table()->playersSatDown()->get(1);
290 7
        }
291
292
        return $this->table()->playersSatDown()->get($this->table()->button() + 2);
293
    }
294
295
    /**
296 21
     * @param PlayerContract $player
297
     */
298 21
    public function postSmallBlind(PlayerContract $player)
299 5
    {
300
        // Take chips from player
301
        $chips = $this->smallBlind();
302 16
303
        $this->postBlind($player, $chips);
304
305
        $this->playerActions()->push(new Action($player, Action::SMALL_BLIND, $this->smallBlind()));
0 ignored issues
show
Compatibility introduced by
$player of type object<Cysha\Casino\Game\Contracts\Player> is not a sub-type of object<Cysha\Casino\Holdem\Game\Player>. It seems like you assume a concrete implementation of the interface Cysha\Casino\Game\Contracts\Player to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
306
        $this->leftToAct = $this->leftToAct()->playerHasActioned($player, LeftToAct::SMALL_BLIND);
307
    }
308 4
309
    /**
310 4
     * @param PlayerContract $player
311 1
     */
312
    public function postBigBlind(PlayerContract $player)
313
    {
314 3
        // Take chips from player
315
        $chips = $this->bigBlind();
316
317
        $this->postBlind($player, $chips);
318
319
        $this->playerActions()->push(new Action($player, Action::BIG_BLIND, $this->bigBlind()));
0 ignored issues
show
Compatibility introduced by
$player of type object<Cysha\Casino\Game\Contracts\Player> is not a sub-type of object<Cysha\Casino\Holdem\Game\Player>. It seems like you assume a concrete implementation of the interface Cysha\Casino\Game\Contracts\Player to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
320 34
        $this->leftToAct = $this->leftToAct()->playerHasActioned($player, LeftToAct::BIG_BLIND);
321
    }
322
323 34
    /**
324
     * @return Chips
325 34
     */
326
    private function smallBlind(): Chips
327 34
    {
328 34
        return Chips::fromAmount($this->gameRules()->smallBlind()->amount());
329 34
    }
330
331
    /**
332
     * @return Chips
333
     */
334 34
    private function bigBlind(): Chips
335
    {
336
        return Chips::fromAmount($this->gameRules()->bigBlind()->amount());
337 34
    }
338
339 34
    /**
340
     * @return ChipPot
341 34
     */
342 34
    public function currentPot(): ChipPot
343 34
    {
344
        return $this->currentPot;
345
    }
346
347
    /**
348 34
     * @return ChipPotCollection
349
     */
350 34
    public function chipPots(): ChipPotCollection
351
    {
352
        return $this->chipPots;
353
    }
354
355
    /**
356 34
     * @param PlayerContract $player
357
     *
358 34
     * @return Chips
359
     */
360
    public function playerBetStack(PlayerContract $player): Chips
361
    {
362
        return $this->betStacks->findByPlayer($player);
363
    }
364 14
365
    /**
366 14
     * @param PlayerContract $player
367
     * @param Chips          $chips
368
     */
369
    private function postBlind(PlayerContract $player, $chips)
370
    {
371
        $player->chipStack()->subtract($chips);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Cysha\Casino\Game\Contracts\Player as the method chipStack() does only exist in the following implementations of said interface: Cysha\Casino\Holdem\Game\Player.

Let’s take a look at an example:

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

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

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

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

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

Available Fixes

  1. Change the type-hint for the parameter:

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

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

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
372 20
373
        // Add chips to player's table stack
374 20
        $this->betStacks->put($player->name(), $chips);
375
    }
376
377
    /**
378
     * @return PlayerContract|false
379
     */
380
    public function whosTurnIsIt()
381
    {
382 31
        // if there is only 1 player left cause of heads up etc, return false
383
        $leftToAct = $this->leftToAct();
384 31
        $allIn = $leftToAct->findByAction(LeftToAct::ALL_IN)->count();
0 ignored issues
show
Unused Code introduced by
$allIn is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
385
386
        /*if ($allIn > 0) {
387
        $stillToAct = $leftToAct->findByAction(LeftToAct::STILL_TO_ACT)->count();
388
        // var_dump([$stillToAct, $allIn]);
389
        if ($stillToAct === 0) {
390
        return false;
391 34
        }
392
        }*/
393 34
394
        $nextPlayer = $this->leftToAct()->getNextPlayer();
395
        if ($nextPlayer === null) {
396 34
            return false;
397 34
        }
398
399
        return $this->players()
400
            ->filter(function (PlayerContract $player) use ($nextPlayer) {
401
                return $player->name() === $nextPlayer['player'];
402
            })
403
            ->first()
404 2
        ;
405
    }
406 2
407
    /**
408 2
     * @return ChipPotCollection
409 1
     */
410
    public function collectChipTotal(): ChipPotCollection
411
    {
412 1
        $allInActionsThisRound = $this->leftToAct()->filter(function (array $value) {
413
            return $value['action'] === LeftToAct::ALL_IN;
414
        });
415
416
        $orderedBetStacks = $this->betStacks()
417
            ->reject(function (Chips $chips, $playerName) {
418 36
                $foldedPlayer = $this->foldedPlayers()->findByName($playerName);
419
                if ($foldedPlayer) {
420 36
                    return true;
421
                }
422
423 36
                return false;
424 36
            })
425
            ->sortByChipAmount();
426
427
        if ($allInActionsThisRound->count() > 1 && $orderedBetStacks->unique()->count() > 1) {
0 ignored issues
show
Coding Style introduced by
Blank line found at start of control structure
Loading history...
428
429
            $orderedBetStacks->each(function (Chips $playerChips, $playerName) use ($orderedBetStacks) {
430 20
                $remainingStacks = $orderedBetStacks->filter(function (Chips $chips) {
431
                    return $chips->amount() !== 0;
432
                });
433 20
434 20
                $this->currentPot = ChipPot::create();
435
                $this->chipPots()->push($this->currentPot);
436 20
437 6
                $player = $this->players()->findByName($playerName);
438
                $allInAmount = Chips::fromAmount($orderedBetStacks->findByPlayer($player)->amount());
439 6
440 6
                $remainingStacks->each(function (Chips $chips, $playerName) use ($allInAmount, $orderedBetStacks) {
441 5
                    $player = $this->players()->findByName($playerName);
442
443
                    $stackChips = Chips::fromAmount($allInAmount->amount());
444 6
445 6
                    if (($chips->amount() - $stackChips->amount()) <= 0) {
446 6
                        $stackChips = Chips::fromAmount($chips->amount());
447
                    }
448
449
                    $chips->subtract($stackChips);
450 6
                    $this->currentPot->addChips($stackChips, $player);
0 ignored issues
show
Bug introduced by
It seems like $player defined by $this->players()->findByName($playerName) on line 441 can be null; however, Cysha\Casino\Holdem\Game\ChipPot::addChips() does not accept null, maybe add an additional type check?

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

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

function doesNotAcceptNull(stdClass $x) { }

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

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

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
451 6
                    $orderedBetStacks->put($playerName, Chips::fromAmount($chips->amount()));
452
                });
453 6
            });
454 6
455
            // sort the pots so we get rid of any empty ones
456 6
            $this->chipPots = $this->chipPots
457 6
                ->filter(function (ChipPot $chipPot) {
458
                    return $chipPot->total()->amount() !== 0;
459
                })
460 6
                ->values();
461
462 6
            // grab anyone that folded
463
            $this->betStacks()
464 6
                ->filter(function (Chips $chips, $playerName) {
465 6
                    $foldedPlayer = $this->foldedPlayers()->findByName($playerName);
466
                    if ($foldedPlayer && $chips->amount() > 0) {
467
                        return true;
468 6
                    }
469 6
470 6
                    return false;
471 6
                })
472 6
                ->each(function (Chips $chips, $playerName) use ($orderedBetStacks) {
473
                    $player = $this->players()->findByName($playerName);
474
475 6
                    $stackChips = Chips::fromAmount($chips->amount());
476
477 6
                    $chips->subtract($stackChips);
478 6
                    $this->chipPots->get(0)->addChips($stackChips, $player);
479 6
                    $orderedBetStacks->put($playerName, Chips::fromAmount($chips->amount()));
480
                });
481
        } else {
482 6
            $this->betStacks()->each(function (Chips $chips, $playerName) {
483
                $this->currentPot()->addChips($chips, $this->players()->findByName($playerName));
0 ignored issues
show
Bug introduced by
It seems like $this->players()->findByName($playerName) can be null; however, addChips() does not accept null, maybe add an additional type check?

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

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

function doesNotAcceptNull(stdClass $x) { }

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

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

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
484 6
            });
485 6
        }
486 5
487
        $this->resetBetStacks();
488
489 6
        return $this->chipPots();
490 6
    }
491
492
    /**
493 5
     * Deal the Flop.
494
     */
495 5
    public function dealFlop()
496
    {
497 5
        if ($this->dealer()->communityCards()->count() !== 0) {
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Cysha\Casino\Game\Contracts\Dealer as the method communityCards() does only exist in the following implementations of said interface: Cysha\Casino\Holdem\Game\Dealer.

Let’s take a look at an example:

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

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

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

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

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

Available Fixes

  1. Change the type-hint for the parameter:

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

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

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
498 5
            throw RoundException::flopHasBeenDealt();
499 5
        }
500 6
        if ($player = $this->whosTurnIsIt()) {
501
            throw RoundException::playerStillNeedsToAct($player);
502
        }
503 14
504 14
        $this->collectChipTotal();
505
506
        $seat = $this->table()->findSeat($this->playerWithSmallBlind());
507 20
        $this->resetPlayerList($seat);
508
509 20
        $this->dealer()->dealCommunityCards(3);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Cysha\Casino\Game\Contracts\Dealer as the method dealCommunityCards() does only exist in the following implementations of said interface: Cysha\Casino\Holdem\Game\Dealer.

Let’s take a look at an example:

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

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

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

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

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

Available Fixes

  1. Change the type-hint for the parameter:

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

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

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
510
    }
511
512
    /**
513
     * Deal the turn card.
514
     */
515 17
    public function dealTurn()
516
    {
517 17
        if ($this->dealer()->communityCards()->count() !== 3) {
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Cysha\Casino\Game\Contracts\Dealer as the method communityCards() does only exist in the following implementations of said interface: Cysha\Casino\Holdem\Game\Dealer.

Let’s take a look at an example:

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

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

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

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

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

Available Fixes

  1. Change the type-hint for the parameter:

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

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

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
518 1
            throw RoundException::turnHasBeenDealt();
519
        }
520 17
        if (($player = $this->whosTurnIsIt()) !== false) {
521 1
            throw RoundException::playerStillNeedsToAct($player);
522
        }
523
524 16
        $this->collectChipTotal();
525
526 16
        $seat = $this->table()->findSeat($this->playerWithSmallBlind());
527 16
        $this->resetPlayerList($seat);
528 16
529 16
        $this->dealer()->dealCommunityCards(1);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Cysha\Casino\Game\Contracts\Dealer as the method dealCommunityCards() does only exist in the following implementations of said interface: Cysha\Casino\Holdem\Game\Dealer.

Let’s take a look at an example:

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

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

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

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

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

Available Fixes

  1. Change the type-hint for the parameter:

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

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

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
530 16
    }
531
532
    /**
533 16
     * Deal the river card.
534
     */
535
    public function dealRiver()
536 16
    {
537 16
        if ($this->dealer()->communityCards()->count() !== 4) {
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Cysha\Casino\Game\Contracts\Dealer as the method communityCards() does only exist in the following implementations of said interface: Cysha\Casino\Holdem\Game\Dealer.

Let’s take a look at an example:

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

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

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

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

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

Available Fixes

  1. Change the type-hint for the parameter:

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

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

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
538 16
            throw RoundException::riverHasBeenDealt();
539 16
        }
540
        if (($player = $this->whosTurnIsIt()) !== false) {
541
            throw RoundException::playerStillNeedsToAct($player);
542
        }
543
544 14
        $this->collectChipTotal();
545
546 14
        $seat = $this->table()->findSeat($this->playerWithSmallBlind());
547 2
        $this->resetPlayerList($seat);
548
549 13
        $this->dealer()->dealCommunityCards(1);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Cysha\Casino\Game\Contracts\Dealer as the method dealCommunityCards() does only exist in the following implementations of said interface: Cysha\Casino\Holdem\Game\Dealer.

Let’s take a look at an example:

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

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

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

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

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

Available Fixes

  1. Change the type-hint for the parameter:

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

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

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
550 1
    }
551
552
    /**
553 12
     * @throws RoundException
554 12
     */
555
    public function checkPlayerTryingToAct(PlayerContract $player)
556
    {
557
        $actualPlayer = $this->whosTurnIsIt();
558
        if ($actualPlayer === false) {
559 11
            throw RoundException::noPlayerActionsNeeded();
560
        }
561 11
        if ($player !== $actualPlayer) {
562 2
            throw RoundException::playerTryingToActOutOfTurn($player, $actualPlayer);
563
        }
564 10
    }
565 1
566
    /**
567
     * @param PlayerContract $player
568 9
     *
569 9
     * @throws RoundException
570
     */
571
    public function playerCalls(PlayerContract $player)
572
    {
573
        $this->checkPlayerTryingToAct($player);
574 12
575
        $highestChipBet = $this->highestBet();
576 12
577
        // current highest bet - currentPlayersChipStack
578 12
        $amountLeftToBet = Chips::fromAmount($highestChipBet->amount() - $this->playerBetStack($player)->amount());
579 12
580 12
        $chipStackLeft = Chips::fromAmount($player->chipStack()->amount() - $amountLeftToBet->amount());
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Cysha\Casino\Game\Contracts\Player as the method chipStack() does only exist in the following implementations of said interface: Cysha\Casino\Holdem\Game\Player.

Let’s take a look at an example:

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

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

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

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

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

Available Fixes

  1. Change the type-hint for the parameter:

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

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

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
581 12
582 12
        $action = $chipStackLeft->amount() === 0 ? Action::ALLIN : Action::CALL;
583
        $this->playerActions->push(new Action($player, $action, $amountLeftToBet));
0 ignored issues
show
Compatibility introduced by
$player of type object<Cysha\Casino\Game\Contracts\Player> is not a sub-type of object<Cysha\Casino\Holdem\Game\Player>. It seems like you assume a concrete implementation of the interface Cysha\Casino\Game\Contracts\Player to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
584
585 12
        $this->placeChipBet($player, $amountLeftToBet);
586
587
        $action = $chipStackLeft->amount() === 0 ? LeftToAct::ALL_IN : LeftToAct::ACTIONED;
588 12
        $this->leftToAct = $this->leftToAct()->playerHasActioned($player, $action);
589 12
    }
590
591
    /**
592
     * @param PlayerContract $player
593
     * @param Chips          $chips
594 33
     *
595
     * @throws RoundException
596 33
     */
597 33
    public function playerRaises(PlayerContract $player, Chips $chips)
598 1
    {
599
        $this->checkPlayerTryingToAct($player);
600 33
601 2
        $highestChipBet = $this->highestBet();
602
        if ($chips->amount() < $highestChipBet->amount()) {
603 31
            throw RoundException::raiseNotHighEnough($chips, $highestChipBet);
604
        }
605
606
        $chipStackLeft = Chips::fromAmount($player->chipStack()->amount() - $chips->amount());
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Cysha\Casino\Game\Contracts\Player as the method chipStack() does only exist in the following implementations of said interface: Cysha\Casino\Holdem\Game\Player.

Let’s take a look at an example:

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

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

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

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

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

Available Fixes

  1. Change the type-hint for the parameter:

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

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

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
607
608
        $action = $chipStackLeft->amount() === 0 ? Action::ALLIN : Action::RAISE;
609
        $this->playerActions->push(new Action($player, $action, $chips));
0 ignored issues
show
Compatibility introduced by
$player of type object<Cysha\Casino\Game\Contracts\Player> is not a sub-type of object<Cysha\Casino\Holdem\Game\Player>. It seems like you assume a concrete implementation of the interface Cysha\Casino\Game\Contracts\Player to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
610 22
611
        $this->placeChipBet($player, $chips);
612 22
613
        $action = $chipStackLeft->amount() === 0 ? LeftToAct::ALL_IN : LeftToAct::AGGRESSIVELY_ACTIONED;
614 22
        $this->leftToAct = $this->leftToAct()->playerHasActioned($player, $action);
615
    }
616
617 22
    /**
618
     * @param PlayerContract $player
619 22
     *
620
     * @throws RoundException
621 22
     */
622 22
    public function playerFoldsHand(PlayerContract $player)
623 22
    {
624
        $this->checkPlayerTryingToAct($player);
625
626
        $this->playerActions()->push(new Action($player, Action::FOLD));
0 ignored issues
show
Compatibility introduced by
$player of type object<Cysha\Casino\Game\Contracts\Player> is not a sub-type of object<Cysha\Casino\Holdem\Game\Player>. It seems like you assume a concrete implementation of the interface Cysha\Casino\Game\Contracts\Player to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
627
628
        $this->foldedPlayers->push($player);
629
        $this->leftToAct = $this->leftToAct()->removePlayer($player);
630
    }
631 4
632
    /**
633 4
     * @param PlayerContract $player
634
     *
635 4
     * @throws RoundException
636
     */
637 4
    public function playerPushesAllIn(PlayerContract $player)
638 3
    {
639 3
        $this->checkPlayerTryingToAct($player);
640
641
        // got the players chipStack
642
        $chips = $player->chipStack();
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Cysha\Casino\Game\Contracts\Player as the method chipStack() does only exist in the following implementations of said interface: Cysha\Casino\Holdem\Game\Player.

Let’s take a look at an example:

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

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

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

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

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

Available Fixes

  1. Change the type-hint for the parameter:

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

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

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
643
644
        // gotta create a new chip obj here cause of PHPs /awesome/ objRef ability :D
645
        $this->playerActions()->push(new Action($player, Action::ALLIN, Chips::fromAmount($chips->amount())));
0 ignored issues
show
Compatibility introduced by
$player of type object<Cysha\Casino\Game\Contracts\Player> is not a sub-type of object<Cysha\Casino\Holdem\Game\Player>. It seems like you assume a concrete implementation of the interface Cysha\Casino\Game\Contracts\Player to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
646 14
647
        $this->placeChipBet($player, $chips);
648 14
        $this->leftToAct = $this->leftToAct()->playerHasActioned($player, LeftToAct::ALL_IN);
649
    }
650 13
651
    /**
652 13
     * @param PlayerContract $player
653 13
     *
654 13
     * @throws RoundException
655
     */
656
    public function playerChecks(PlayerContract $player)
657
    {
658
        $this->checkPlayerTryingToAct($player);
659
660
        if ($this->playerBetStack($player)->amount() !== $this->betStacks()->max()->amount()) {
661 14
            throw RoundException::cantCheckWithBetActive();
662
        }
663 14
664
        $this->playerActions()->push(new Action($player, Action::CHECK));
0 ignored issues
show
Compatibility introduced by
$player of type object<Cysha\Casino\Game\Contracts\Player> is not a sub-type of object<Cysha\Casino\Holdem\Game\Player>. It seems like you assume a concrete implementation of the interface Cysha\Casino\Game\Contracts\Player to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
665
        $this->leftToAct = $this->leftToAct()->playerHasActioned($player, LeftToAct::ACTIONED);
666 14
    }
667
668
    /**
669 14
     * @return Chips
670
     */
671 14
    private function highestBet(): Chips
672 14
    {
673 14
        return Chips::fromAmount($this->betStacks()->max(function (Chips $chips) {
674
            return $chips->amount();
675
        }) ?? 0);
676
    }
677
678
    /**
679
     * @param PlayerContract $player
680 16
     * @param Chips          $chips
681
     */
682 16
    private function placeChipBet(PlayerContract $player, Chips $chips)
683
    {
684 15
        if ($player->chipStack()->amount() < $chips->amount()) {
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Cysha\Casino\Game\Contracts\Player as the method chipStack() does only exist in the following implementations of said interface: Cysha\Casino\Holdem\Game\Player.

Let’s take a look at an example:

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

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

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

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

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

Available Fixes

  1. Change the type-hint for the parameter:

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

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

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
685 15
            throw RoundException::notEnoughChipsInChipStack($player, $chips);
686 15
        }
687
688
        // add the chips to the players tableStack first
689
        $this->playerBetStack($player)->add($chips);
690
691 22
        // then remove it off their actual stack
692
        $player->bet($chips);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Cysha\Casino\Game\Contracts\Player as the method bet() does only exist in the following implementations of said interface: Cysha\Casino\Holdem\Game\Player.

Let’s take a look at an example:

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

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

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

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

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

Available Fixes

  1. Change the type-hint for the parameter:

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

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

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
693
    }
694 22
695 22
    /**
696
     * Reset the chip stack for all players.
697
     */
698
    private function resetBetStacks()
699
    {
700
        $this->players()->each(function (PlayerContract $player) {
701
            $this->betStacks->put($player->name(), Chips::zero());
702 30
        });
703
    }
704 30
705 1
    /**
706
     * Reset the leftToAct collection.
707
     */
708
    private function setupLeftToAct()
709 30
    {
710
        if ($this->players()->count() === 2) {
711
            $this->leftToAct = $this->leftToAct()->setup($this->players());
712 30
713 30
            return;
714
        }
715
716
        $this->leftToAct = $this->leftToAct
717
            ->setup($this->players())
718
            ->resetPlayerListFromSeat($this->table()->button() + 1);
719
    }
720 50
721 50
    /**
722 50
     * @param PlayerContract $player
723 50
     */
724
    public function sitPlayerOut(PlayerContract $player)
725
    {
726
        $this->table()->sitPlayerOut($player);
727
        $this->leftToAct = $this->leftToAct()->removePlayer($player);
728 50
    }
729
730 50
    /**
731 4
     * @var int
732
     */
733 4
    public function resetPlayerList(int $seat)
734
    {
735
        $this->leftToAct = $this->leftToAct
736 46
            ->resetActions()
737 46
            ->sortBySeats()
738 46
            ->resetPlayerListFromSeat($seat);
739 46
    }
740
}
741