garak / card
A PHP library to manage generic card games
Requires
- php: ^8.2
- symfony/polyfill-php84: ^1.37
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.95
- phpstan/phpstan-strict-rules: ^2.0
- phpunit/phpunit: ^11.5 || ^12.5 || ^13.1
README
Introduction
This library offers a few VO classes to use inside Card-related applications:
Card: represents a Card, for example an ace of spades.Rank: represents the rank value of a Card, for example "A" or "7" ("T" is used for 10, to keep the same length).Suit: represents the card suit, for example spades or diamonds.CardBack: represents the back color of a card, for example red or blue. This allows distinguishing between multiple decks in games played with more than one deck.
Some more classes, more elaborate, are available. They are abstract, and thus require a custom implementation to extend them:
Hand: represents a set of Card objects, usually the ones assigned to a playerHandsTrick: represents a trick of hands (think for example Poker, when players show their hands to declare a winner)CardTrick: represents a trick of cards (think for example the 4 cards of a Bridge turn)
Installation
Just use composer require garak/card.
Usage
Example:
<?php require 'vendor/autoload.php'; use Garak\Card\Card; use Garak\Card\Rank; use Garak\Card\Suit; $card = new Card(Rank::Ace, Suit::Diamonds); echo $card; // will output "Ad" $card = new Card(Rank::Seven, Suit::Spades); echo $card->toText(); // will output "7♠" $card = Card::fromRankSuit('Kh'); echo $card->toUnicode(); // will output "🂾"
You can also get a full deck:
<?php require 'vendor/autoload.php'; use Garak\Card\Card; $orderedCards = Card::getDeck(); $shuffledCards = Card::getDeck(shuffle: true); $doubleDeckWithJokers = Card::getDeck(shuffle: true, num: 2, allowJokers: true);
Multiple Decks
When playing with multiple decks, cards can be distinguished by their back color. Each deck automatically gets a back color, cycling through the available values if you request more decks than backs:
<?php require 'vendor/autoload.php'; use Garak\Card\Card; use Garak\Card\CardBack; use Garak\Card\Rank; use Garak\Card\Suit; // Single deck - cards have no back (backward compatible) $singleDeck = Card::getDeck(); $card = $singleDeck[0]; $card->getBack(); // returns null // Multiple decks - cards have different backs $twoDecks = Card::getDeck(num: 2); // First 52 cards have red back, next 52 have blue back // Requesting more than two decks reuses the available backs in order $threeDecks = Card::getDeck(num: 3); // The third deck uses the red back again // Cards with same face but different backs are not equal $redAceOfSpades = new Card(Rank::Ace, Suit::Spades, CardBack::Red); $blueAceOfSpades = new Card(Rank::Ace, Suit::Spades, CardBack::Blue); $redAceOfSpades->isEqual($blueAceOfSpades); // false $redAceOfSpades->isSameFace($blueAceOfSpades); // true // Cards with/without back are different when one side has a back, // but you can still compare just the face when needed. $noBackAceOfSpades = new Card(Rank::Ace, Suit::Spades); $noBackAceOfSpades->isEqual($redAceOfSpades); // false $noBackAceOfSpades->isSameFace($redAceOfSpades); // true $noBackAceOfSpades->getBack(); // returns null
Upgrading from version 0.8
Rank and Suit have been converted from regular classes to backed enums.
The following breaking changes apply.
Instantiation
| Before | After |
|---|---|
new Rank('A') |
Rank::Ace |
new Rank('T') |
Rank::Ten |
new Suit('s') |
Suit::Spades |
new Suit('h') |
Suit::Hearts |
When you only have a string at runtime (e.g. from user input or persistence), use the enum's from() factory:
$rank = Rank::from('A'); // Rank::Ace $suit = Suit::from('s'); // Suit::Spades
Invalid values
Previously invalid values threw \InvalidArgumentException.
They now throw \ValueError (the standard PHP exception for invalid enum values):
// Before try { new Rank('X'); } catch (\InvalidArgumentException $e) { ... } // After try { Rank::from('X'); } catch (\ValueError $e) { ... }
Use Rank::tryFrom('X') / Suit::tryFrom('X') to get null instead of an exception.
Iterating over all ranks or suits
The public static arrays Rank::$ranks and Suit::$suits have been removed.
Use the standard enum cases() method instead:
// Before foreach (Rank::$ranks as $symbol => $intValue) { ... } foreach (Suit::$suits as $symbol => $unicodeChar) { ... } // After foreach (Rank::cases() as $rank) { $symbol = $rank->value; // e.g. 'A' $intValue = $rank->getInt(); // e.g. 14 } foreach (Suit::cases() as $suit) { $symbol = $suit->value; // e.g. 's' $unicodeChar = $suit->toText(); // e.g. '♠' }
Suit::$jokerColors has also been removed; joker suits are now Suit::BlackJoker and Suit::RedJoker.
