sugarcommerce / countries
ISO 3166-1 country data: codes, names, emoji flags, EU membership, sanctioned and elevated-risk territories. Zero dependencies.
Requires
- php: >=8.5
- ext-mbstring: *
Requires (Dev)
- phpunit/phpunit: ^13.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
ISO 3166-1 country data: codes, names, emoji flags, EU membership, and sanctioned or elevated-risk territories. Zero dependencies.
Why
Every commerce application needs a country dropdown, a "is this the EU" check for VAT, and โ sooner than most people expect โ a sanctions check before taking money. That is usually three separate half-lists scattered across the codebase, each drifting out of date on its own schedule.
All 250 ISO 3166-1 entries live in one class constant, so lookups are array reads with no file I/O and no parsing.
Features
- ๐ All 250 territories โ the full ISO 3166-1 alpha-2 list, not the 190-odd everyone remembers.
- ๐ช๐บ EU membership โ the 27, post-Brexit, for VAT and reverse-charge decisions.
- ๐ซ Sanctions and risk flags โ comprehensively-sanctioned territories, and FATF elevated-risk jurisdictions.
- ๐ณ๏ธ Emoji flags โ derived from the code via regional indicators, so no image assets.
- ๐ Search and reverse lookup โ name to code, and substring search for a picker.
- ๐งผ Input sanitising โ normalises casing and rejects anything that isn't a real code.
Requirements
PHP 8.5+
Installation
composer require sugarcommerce/countries
Usage
use SugarCommerce\Countries\Country; Country::name( 'DE' ); // 'Germany' Country::code( 'Germany' ); // 'DE' Country::flag( 'DE' ); // ๐ฉ๐ช Country::exists( 'ZZ' ); // false Country::sanitize( ' de ' ); // 'DE', or null if not a country
Building a picker
foreach ( Country::all() as $code => $name ) { echo '<option value="' . $code . '">' . Country::flag( $code ) . ' ' . $name . '</option>'; } Country::search( 'ital' ); // [ 'IT' => 'Italy' ] Country::search( 'united', 3 ); // capped
VAT and the EU
if ( Country::is_eu( $customer_country ) ) { // Reverse charge applies when the customer has a VAT number and // isn't in your own country. } Country::eu(); // the 27, as code => name
Risk
Country::is_sanctioned( 'KP' ); // comprehensively sanctioned Country::is_high_risk( 'MM' ); // FATF elevated monitoring
is_sanctioned() covers the territories under comprehensive embargo. It is a coarse first gate for "should this checkout proceed at all", not a compliance product โ it does not screen names, and it does not know about sectoral or entity-level restrictions. Where the money moves through Stripe, their own screening still applies on top.
is_high_risk() is the FATF grey and black lists โ a signal for elevated fraud scrutiny, not a reason to refuse.
Both are point-in-time lists that change by political decision. Pin the version you have reviewed.
Testing
composer install
composer test
26 tests, 321 assertions โ including that every code maps to a name and back, every flag renders as two regional indicators, and the EU list holds exactly 27 members.
License
GPL-2.0-or-later