Completed
Push — master ( 07a877...8fe4e7 )
by Dan
02:23
created

Round::postBigBlind()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 10
ccs 3
cts 3
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 5
nc 1
nop 1
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
                } else {
234
                    // if > 1 hand is evaluated as highest, split the pot evenly between the players
235 6
236
                    $potTotal = $chipPot->chips()->total();
237 6
238
                    // split the pot between the number of players
239 6
                    $splitTotal = Chips::fromAmount(($potTotal->amount() / $evaluate->count()));
240
                    $evaluate->each(function (CardResults $result) use ($splitTotal) {
241
                        $result->hand()->player()->chipStack()->add($splitTotal);
242 11
                    });
243
244 11
                    $this->chipPots()->remove($chipPot);
245 11
                }
246
            })
247
        ;
248 11
    }
249 11
250 11
    /**
251
     * @param Player $actualPlayer
252 11
     *
253
     * @return bool
254 11
     */
255
    public function playerIsStillIn(PlayerContract $actualPlayer)
256
    {
257
        $playerCount = $this->playersStillIn()->filter->equals($actualPlayer)->count();
258
259 1
        return $playerCount === 1;
260
    }
261
262 1
    /**
263
     * @return PlayerContract
264 1
     */
265 1
    public function playerWithButton(): PlayerContract
266
    {
267 1
        return $this->table()->locatePlayerWithButton();
268
    }
269 11
270
    /**
271 11
     * @return PlayerContract
272
     */
273
    public function playerWithSmallBlind(): PlayerContract
274
    {
275
        if ($this->table()->playersSatDown()->count() === 2) {
276
            return $this->table()->playersSatDown()->get(0);
277
        }
278 1
279
        return $this->table()->playersSatDown()->get($this->table()->button() + 1);
280 1
    }
281
282 1
    /**
283
     * @return PlayerContract
284
     */
285
    public function playerWithBigBlind(): PlayerContract
286
    {
287
        if ($this->table()->playersSatDown()->count() === 2) {
288 7
            return $this->table()->playersSatDown()->get(1);
289
        }
290 7
291
        return $this->table()->playersSatDown()->get($this->table()->button() + 2);
292
    }
293
294
    /**
295
     * @param PlayerContract $player
296 21
     */
297
    public function postSmallBlind(PlayerContract $player)
298 21
    {
299 5
        // Take chips from player
300
        $chips = $this->smallBlind();
301
302 16
        $this->postBlind($player, $chips);
303
304
        $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...
305
        $this->leftToAct = $this->leftToAct()->playerHasActioned($player, LeftToAct::SMALL_BLIND);
306
    }
307
308 4
    /**
309
     * @param PlayerContract $player
310 4
     */
311 1
    public function postBigBlind(PlayerContract $player)
312
    {
313
        // Take chips from player
314 3
        $chips = $this->bigBlind();
315
316
        $this->postBlind($player, $chips);
317
318
        $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...
319
        $this->leftToAct = $this->leftToAct()->playerHasActioned($player, LeftToAct::BIG_BLIND);
320 34
    }
321
322
    /**
323 34
     * @return Chips
324
     */
325 34
    private function smallBlind(): Chips
326
    {
327 34
        return Chips::fromAmount($this->gameRules()->smallBlind()->amount());
328 34
    }
329 34
330
    /**
331
     * @return Chips
332
     */
333
    private function bigBlind(): Chips
334 34
    {
335
        return Chips::fromAmount($this->gameRules()->bigBlind()->amount());
336
    }
337 34
338
    /**
339 34
     * @return ChipPot
340
     */
341 34
    public function currentPot(): ChipPot
342 34
    {
343 34
        return $this->currentPot;
344
    }
345
346
    /**
347
     * @return ChipPotCollection
348 34
     */
349
    public function chipPots(): ChipPotCollection
350 34
    {
351
        return $this->chipPots;
352
    }
353
354
    /**
355
     * @param PlayerContract $player
356 34
     *
357
     * @return Chips
358 34
     */
359
    public function playerBetStack(PlayerContract $player): Chips
360
    {
361
        return $this->betStacks->findByPlayer($player);
362
    }
363
364 14
    /**
365
     * @param PlayerContract $player
366 14
     * @param Chips          $chips
367
     */
368
    private function postBlind(PlayerContract $player, $chips)
369
    {
370
        $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...
371
372 20
        // Add chips to player's table stack
373
        $this->betStacks->put($player->name(), $chips);
374 20
    }
375
376
    /**
377
     * @return PlayerContract|false
378
     */
379
    public function whosTurnIsIt()
380
    {
381
        $nextPlayer = $this->leftToAct()->getNextPlayer();
382 31
        if ($nextPlayer === null) {
383
            return false;
384 31
        }
385
386
        return $this->players()
387
            ->filter(function (PlayerContract $player) use ($nextPlayer) {
388
                return $player->name() === $nextPlayer['player'];
389
            })
390
            ->first()
391 34
        ;
392
    }
393 34
394
    /**
395
     * @return ChipPotCollection
396 34
     */
397 34
    public function collectChipTotal(): ChipPotCollection
398
    {
399
        $allInActionsThisRound = $this->leftToAct()->filter(function (array $value) {
400
            return $value['action'] === LeftToAct::ALL_IN;
401
        });
402
403
        $orderedBetStacks = $this->betStacks()
404 2
            ->reject(function (Chips $chips, $playerName) {
405
                $foldedPlayer = $this->foldedPlayers()->findByName($playerName);
406 2
                if ($foldedPlayer) {
407
                    return true;
408 2
                }
409 1
410
                return false;
411
            })
412 1
            ->sortByChipAmount();
413
414
        if ($allInActionsThisRound->count() > 1 && $orderedBetStacks->unique()->count() > 1) {
415
            $orderedBetStacks->each(function (Chips $playerChips, $playerName) use ($orderedBetStacks) {
416
                $remainingStacks = $orderedBetStacks->filter(function (Chips $chips) {
417
                    return $chips->amount() !== 0;
418 36
                });
419
420 36
                $this->currentPot = ChipPot::create();
421
                $this->chipPots()->push($this->currentPot);
422
423 36
                $player = $this->players()->findByName($playerName);
424 36
                $allInAmount = Chips::fromAmount($orderedBetStacks->findByPlayer($player)->amount());
425
426
                $remainingStacks->each(function (Chips $chips, $playerName) use ($allInAmount, $orderedBetStacks) {
427
                    $player = $this->players()->findByName($playerName);
428
429
                    $stackChips = Chips::fromAmount($allInAmount->amount());
430 20
431
                    if (($chips->amount() - $stackChips->amount()) <= 0) {
432
                        $stackChips = Chips::fromAmount($chips->amount());
433 20
                    }
434 20
435
                    $chips->subtract($stackChips);
436 20
                    $this->currentPot->addChips($stackChips, $player);
0 ignored issues
show
Bug introduced by
It seems like $player defined by $this->players()->findByName($playerName) on line 427 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...
437 6
                    $orderedBetStacks->put($playerName, Chips::fromAmount($chips->amount()));
438
                });
439 6
            });
440 6
441 5
            // sort the pots so we get rid of any empty ones
442
            $this->chipPots = $this->chipPots
443
                ->filter(function (ChipPot $chipPot) {
444 6
                    return $chipPot->total()->amount() !== 0;
445 6
                })
446 6
                ->values();
447
448
            // grab anyone that folded
449
            $this->betStacks()
450 6
                ->filter(function (Chips $chips, $playerName) {
451 6
                    $foldedPlayer = $this->foldedPlayers()->findByName($playerName);
452
                    if ($foldedPlayer && $chips->amount() > 0) {
453 6
                        return true;
454 6
                    }
455
456 6
                    return false;
457 6
                })
458
                ->each(function (Chips $chips, $playerName) use ($orderedBetStacks) {
459
                    $player = $this->players()->findByName($playerName);
460 6
461
                    $stackChips = Chips::fromAmount($chips->amount());
462 6
463
                    $chips->subtract($stackChips);
464 6
                    $this->chipPots->get(0)->addChips($stackChips, $player);
465 6
                    $orderedBetStacks->put($playerName, Chips::fromAmount($chips->amount()));
466
                });
467
        } else {
468 6
            $this->betStacks()->each(function (Chips $chips, $playerName) {
469 6
                $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...
470 6
            });
471 6
        }
472 6
473
        $this->resetBetStacks();
474
475 6
        return $this->chipPots();
476
    }
477 6
478 6
    /**
479 6
     * Deal the Flop.
480
     */
481
    public function dealFlop()
482 6
    {
483
        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...
484 6
            throw RoundException::flopHasBeenDealt();
485 6
        }
486 5
        if ($player = $this->whosTurnIsIt()) {
487
            throw RoundException::playerStillNeedsToAct($player);
488
        }
489 6
490 6
        $this->collectChipTotal();
491
492
        $seat = $this->table()->findSeat($this->playerWithSmallBlind());
493 5
        $this->resetPlayerList($seat);
494
495 5
        $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...
496
    }
497 5
498 5
    /**
499 5
     * Deal the turn card.
500 6
     */
501
    public function dealTurn()
502
    {
503 14
        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...
504 14
            throw RoundException::turnHasBeenDealt();
505
        }
506
        if (($player = $this->whosTurnIsIt()) !== false) {
507 20
            throw RoundException::playerStillNeedsToAct($player);
508
        }
509 20
510
        $this->collectChipTotal();
511
512
        $seat = $this->table()->findSeat($this->playerWithSmallBlind());
513
        $this->resetPlayerList($seat);
514
515 17
        $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...
516
    }
517 17
518 1
    /**
519
     * Deal the river card.
520 17
     */
521 1
    public function dealRiver()
522
    {
523
        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...
524 16
            throw RoundException::riverHasBeenDealt();
525
        }
526 16
        if (($player = $this->whosTurnIsIt()) !== false) {
527 16
            throw RoundException::playerStillNeedsToAct($player);
528 16
        }
529 16
530 16
        $this->collectChipTotal();
531
532
        $seat = $this->table()->findSeat($this->playerWithSmallBlind());
533 16
        $this->resetPlayerList($seat);
534
535
        $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...
536 16
    }
537 16
538 16
    /**
539 16
     * @throws RoundException
540
     */
541
    public function checkPlayerTryingToAct(PlayerContract $player)
542
    {
543
        $actualPlayer = $this->whosTurnIsIt();
544 14
        if ($actualPlayer === false) {
545
            throw RoundException::noPlayerActionsNeeded();
546 14
        }
547 2
        if ($player !== $actualPlayer) {
548
            throw RoundException::playerTryingToActOutOfTurn($player, $actualPlayer);
549 13
        }
550 1
    }
551
552
    /**
553 12
     * @param PlayerContract $player
554 12
     *
555
     * @throws RoundException
556
     */
557
    public function playerCalls(PlayerContract $player)
558
    {
559 11
        $this->checkPlayerTryingToAct($player);
560
561 11
        $highestChipBet = $this->highestBet();
562 2
563
        // current highest bet - currentPlayersChipStack
564 10
        $amountLeftToBet = Chips::fromAmount($highestChipBet->amount() - $this->playerBetStack($player)->amount());
565 1
566
        $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...
567
568 9
        $action = $chipStackLeft->amount() === 0 ? Action::ALLIN : Action::CALL;
569 9
        $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...
570
571
        $this->placeChipBet($player, $amountLeftToBet);
572
573
        $action = $chipStackLeft->amount() === 0 ? LeftToAct::ALL_IN : LeftToAct::ACTIONED;
574 12
        $this->leftToAct = $this->leftToAct()->playerHasActioned($player, $action);
575
    }
576 12
577
    /**
578 12
     * @param PlayerContract $player
579 12
     * @param Chips          $chips
580 12
     *
581 12
     * @throws RoundException
582 12
     */
583
    public function playerRaises(PlayerContract $player, Chips $chips)
584
    {
585 12
        $this->checkPlayerTryingToAct($player);
586
587
        $highestChipBet = $this->highestBet();
588 12
        if ($chips->amount() < $highestChipBet->amount()) {
589 12
            throw RoundException::raiseNotHighEnough($chips, $highestChipBet);
590
        }
591
592
        $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...
593
594 33
        $action = $chipStackLeft->amount() === 0 ? Action::ALLIN : Action::RAISE;
595
        $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...
596 33
597 33
        $this->placeChipBet($player, $chips);
598 1
599
        $action = $chipStackLeft->amount() === 0 ? LeftToAct::ALL_IN : LeftToAct::AGGRESSIVELY_ACTIONED;
600 33
        $this->leftToAct = $this->leftToAct()->playerHasActioned($player, $action);
601 2
    }
602
603 31
    /**
604
     * @param PlayerContract $player
605
     *
606
     * @throws RoundException
607
     */
608
    public function playerFoldsHand(PlayerContract $player)
609
    {
610 22
        $this->checkPlayerTryingToAct($player);
611
612 22
        $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...
613
614 22
        $this->foldedPlayers->push($player);
615
        $this->leftToAct = $this->leftToAct()->removePlayer($player);
616
    }
617 22
618
    /**
619 22
     * @param PlayerContract $player
620
     *
621 22
     * @throws RoundException
622 22
     */
623 22
    public function playerPushesAllIn(PlayerContract $player)
624
    {
625
        $this->checkPlayerTryingToAct($player);
626
627
        // got the players chipStack
628
        $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...
629
630
        // gotta create a new chip obj here cause of PHPs /awesome/ objRef ability :D
631 4
        $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...
632
633 4
        $this->placeChipBet($player, $chips);
634
        $this->leftToAct = $this->leftToAct()->playerHasActioned($player, LeftToAct::ALL_IN);
635 4
    }
636
637 4
    /**
638 3
     * @param PlayerContract $player
639 3
     *
640
     * @throws RoundException
641
     */
642
    public function playerChecks(PlayerContract $player)
643
    {
644
        $this->checkPlayerTryingToAct($player);
645
646 14
        if ($this->playerBetStack($player)->amount() !== $this->betStacks()->max()->amount()) {
647
            throw RoundException::cantCheckWithBetActive();
648 14
        }
649
650 13
        $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...
651
        $this->leftToAct = $this->leftToAct()->playerHasActioned($player, LeftToAct::ACTIONED);
652 13
    }
653 13
654 13
    /**
655
     * @return Chips
656
     */
657
    private function highestBet(): Chips
658
    {
659
        return Chips::fromAmount($this->betStacks()->max(function (Chips $chips) {
660
            return $chips->amount();
661 14
        }) ?? 0);
662
    }
663 14
664
    /**
665
     * @param PlayerContract $player
666 14
     * @param Chips          $chips
667
     */
668
    private function placeChipBet(PlayerContract $player, Chips $chips)
669 14
    {
670
        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...
671 14
            throw RoundException::notEnoughChipsInChipStack($player, $chips);
672 14
        }
673 14
674
        // add the chips to the players tableStack first
675
        $this->playerBetStack($player)->add($chips);
676
677
        // then remove it off their actual stack
678
        $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...
679
    }
680 16
681
    /**
682 16
     * Reset the chip stack for all players.
683
     */
684 15
    private function resetBetStacks()
685 15
    {
686 15
        $this->players()->each(function (PlayerContract $player) {
687
            $this->betStacks->put($player->name(), Chips::zero());
688
        });
689
    }
690
691 22
    /**
692
     * Reset the leftToAct collection.
693
     */
694 22
    private function setupLeftToAct()
695 22
    {
696
        if ($this->players()->count() === 2) {
697
            $this->leftToAct = $this->leftToAct()->setup($this->players());
698
699
            return;
700
        }
701
702 30
        $this->leftToAct = $this->leftToAct
703
            ->setup($this->players())
704 30
            ->resetPlayerListFromSeat($this->table()->button() + 1);
705 1
    }
706
707
    /**
708
     * @param PlayerContract $player
709 30
     */
710
    public function sitPlayerOut(PlayerContract $player)
711
    {
712 30
        $this->table()->sitPlayerOut($player);
713 30
        $this->leftToAct = $this->leftToAct()->removePlayer($player);
714
    }
715
716
    /**
717
     * @var int
718
     */
719
    public function resetPlayerList(int $seat)
720 50
    {
721 50
        $this->leftToAct = $this->leftToAct
722 50
            ->resetActions()
723 50
            ->sortBySeats()
724
            ->resetPlayerListFromSeat($seat);
725
    }
726
}
727